diff --git a/.gitignore b/.gitignore index 640bd34b00a..3582f2ee54d 100644 --- a/.gitignore +++ b/.gitignore @@ -144,6 +144,7 @@ tags # .vscode .history +.cache #eclipse project .project @@ -153,4 +154,4 @@ tags .idea cmake-build-debug cmake-build-release -/.metadata/ \ No newline at end of file +/.metadata/ diff --git a/libs/libarchfpga/src/parse_switchblocks.cpp b/libs/libarchfpga/src/parse_switchblocks.cpp index 182d194c5d7..c0b55bcb7fd 100644 --- a/libs/libarchfpga/src/parse_switchblocks.cpp +++ b/libs/libarchfpga/src/parse_switchblocks.cpp @@ -65,6 +65,9 @@ static void parse_comma_separated_wire_points(const char* ch, std::vector */ void read_sb_switchfuncs(pugi::xml_node Node, t_switchblock_inf* sb, const pugiutil::loc_data& loc_data) { @@ -300,34 +367,8 @@ void read_sb_switchfuncs(pugi::xml_node Node, t_switchblock_inf* sb, const pugiu 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); - } + set_switch_func_type(conn, func_type); + func_ptr = &(sb->permutation_map[conn]); /* Here we load the specified switch function(s) */ @@ -404,8 +445,8 @@ static void check_bidir_switchblock(const t_permutation_map* 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}) { + for (e_side from_side : TOTAL_2D_SIDES) { + for (e_side to_side : TOTAL_2D_SIDES) { /* can't connect a switchblock side to itself */ if (from_side == to_side) { continue; diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index 949024ada33..628659b1737 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -176,10 +176,17 @@ enum e_side : unsigned char { RIGHT = 1, BOTTOM = 2, LEFT = 3, - NUM_SIDES + NUM_2D_SIDES = 4, + ABOVE = 5, + UNDER = 7, + NUM_3D_SIDES = 6, }; -constexpr std::array SIDES = {{TOP, RIGHT, BOTTOM, LEFT}}; //Set of all side orientations -constexpr std::array SIDE_STRING = {{"TOP", "RIGHT", "BOTTOM", "LEFT"}}; //String versions of side orientations + +constexpr std::array TOTAL_2D_SIDES = {{TOP, RIGHT, BOTTOM, LEFT}}; //Set of all side orientations +constexpr std::array TOTAL_2D_SIDE_STRINGS = {{"TOP", "RIGHT", "BOTTOM", "LEFT"}}; //String versions of side orientations + +constexpr std::array TOTAL_3D_SIDES = {{TOP, RIGHT, BOTTOM, LEFT, ABOVE, UNDER}}; //Set of all side orientations including different layers +constexpr std::array TOTAL_3D_SIDE_STRINGS = {{"TOP", "RIGHT", "BOTTOM", "LEFT", "ABOVE", "UNDER"}}; //String versions of side orientations including different layers /* pin location distributions */ enum class e_pin_location_distr { diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index 828f935369f..d1062ae0480 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -480,7 +480,7 @@ struct ArchReader { type.pin_height_offset.resize(type.num_pins, 0); type.pinloc.resize({1, 1, 4}, std::vector(type.num_pins, false)); - for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { + for (e_side side : TOTAL_2D_SIDES) { for (int pin = 0; pin < type.num_pins; pin++) { type.pinloc[0][0][side][pin] = true; type.pin_width_offset[pin] = 0; diff --git a/libs/libarchfpga/src/read_xml_arch_file.cpp b/libs/libarchfpga/src/read_xml_arch_file.cpp index f3a9ffaa683..1b002195862 100644 --- a/libs/libarchfpga/src/read_xml_arch_file.cpp +++ b/libs/libarchfpga/src/read_xml_arch_file.cpp @@ -579,7 +579,7 @@ static void LoadPinLoc(pugi::xml_node Locations, int num_sides = 4 * (type->width * type->height); int side_index = 0; int count = 0; - for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { + for (e_side side : TOTAL_2D_SIDES) { for (int width = 0; width < type->width; ++width) { for (int height = 0; height < type->height; ++height) { for (int pin_offset = 0; pin_offset < (type->num_pins / num_sides) + 1; ++pin_offset) { @@ -604,7 +604,7 @@ static void LoadPinLoc(pugi::xml_node Locations, while (ipin < type->num_pins) { for (int width = 0; width < type->width; ++width) { for (int height = 0; height < type->height; ++height) { - for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { + for (e_side side : TOTAL_2D_SIDES) { if (((width == 0 && side == LEFT) || (height == type->height - 1 && side == TOP) || (width == type->width - 1 && side == RIGHT) @@ -645,7 +645,7 @@ static void LoadPinLoc(pugi::xml_node Locations, while (ipin < input_pins.size()) { for (int width = 0; width < type->width; ++width) { for (int height = 0; height < type->height; ++height) { - for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { + for (e_side side : TOTAL_2D_SIDES) { if (ipin < input_pins.size()) { //Pins still to allocate @@ -668,7 +668,7 @@ static void LoadPinLoc(pugi::xml_node Locations, while (ipin < output_pins.size()) { for (int width = 0; width < type->width; ++width) { for (int height = 0; height < type->height; ++height) { - for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { + for (e_side side : TOTAL_2D_SIDES) { if (((width == 0 && side == LEFT) || (height == type->height - 1 && side == TOP) || (width == type->width - 1 && side == RIGHT) @@ -699,8 +699,8 @@ static void LoadPinLoc(pugi::xml_node Locations, for (int layer = 0; layer < num_of_avail_layer; ++layer) { for (int width = 0; width < type->width; ++width) { for (int height = 0; height < type->height; ++height) { - for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { - for (const auto& token : pin_locs->assignments[sub_tile_index][width][height][layer][side]) { + for (e_side side : TOTAL_2D_SIDES) { + for (auto token : pin_locs->assignments[sub_tile_index][width][height][layer][side]) { auto pin_range = ProcessPinString(Locations, &sub_tile, token.c_str(), @@ -3393,9 +3393,9 @@ static void ProcessPinLocations(pugi::xml_node 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) { - for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { - for (const auto& token : pin_locs->assignments[sub_tile_index][w][h][l][side]) { - InstPort inst_port(token); + for (e_side side : TOTAL_2D_SIDES) { + for (auto token : pin_locs->assignments[sub_tile_index][w][h][l][side]) { + InstPort inst_port(token.c_str()); //A pin specification should contain only the block name, and not any instance count information if (inst_port.instance_low_index() != InstPort::UNSPECIFIED || inst_port.instance_high_index() != InstPort::UNSPECIFIED) { @@ -4766,9 +4766,9 @@ static int find_switch_by_name(const t_arch& arch, const std::string& switch_nam } static e_side string_to_side(const std::string& side_str) { - e_side side = NUM_SIDES; + e_side side = NUM_2D_SIDES; if (side_str.empty()) { - side = NUM_SIDES; + side = NUM_2D_SIDES; } else if (side_str == "left") { side = LEFT; } else if (side_str == "right") { diff --git a/libs/librrgraph/src/base/check_rr_graph.cpp b/libs/librrgraph/src/base/check_rr_graph.cpp index b49af0e3324..06617fa620a 100644 --- a/libs/librrgraph/src/base/check_rr_graph.cpp +++ b/libs/librrgraph/src/base/check_rr_graph.cpp @@ -280,12 +280,12 @@ void check_rr_graph(const RRGraphView& rr_graph, rr_graph.node_layer(rr_node)}); std::string pin_name = block_type_pin_index_to_name(block_type, rr_graph.node_pin_num(rr_node), is_flat); /* Print error messages for all the sides that a node may appear */ - for (const e_side& node_side : SIDES) { + for (const e_side& node_side : TOTAL_2D_SIDES) { if (!rr_graph.is_node_on_specific_side(rr_node, node_side)) { continue; } VTR_LOG_ERROR("in check_rr_graph: node %d (%s) at (%d,%d) block=%s side=%s pin=%s has no fanin.\n", - inode, rr_graph.node_type_string(rr_node), rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node), block_type->name, SIDE_STRING[node_side], pin_name.c_str()); + inode, rr_graph.node_type_string(rr_node), rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node), block_type->name, TOTAL_2D_SIDE_STRINGS[node_side], pin_name.c_str()); } } } else { @@ -498,9 +498,13 @@ void check_rr_node(const RRGraphView& rr_graph, tracks_per_node = ((rr_type == CHANX) ? chan_width.x_list[ylow] : chan_width.y_list[xlow]); } - if (ptc_num >= nodes_per_chan) { - VPR_ERROR(VPR_ERROR_ROUTE, - "in check_rr_node: inode %d (type %d) has a ptc_num of %d.\n", inode, rr_type, ptc_num); + //if a chanx/chany has length 0, it means it is used to connect different dice together + //hence, the ptc number can be larger than nodes_per_chan + if(xlow != xhigh || ylow != yhigh) { + if (ptc_num >= nodes_per_chan) { + VPR_ERROR(VPR_ERROR_ROUTE, + "in check_rr_node: inode %d (type %d) has a ptc_num of %d.\n", inode, rr_type, ptc_num); + } } if (capacity != tracks_per_node) { diff --git a/libs/librrgraph/src/base/rr_graph_builder.cpp b/libs/librrgraph/src/base/rr_graph_builder.cpp index 1ef61ec55da..565c99e3f75 100644 --- a/libs/librrgraph/src/base/rr_graph_builder.cpp +++ b/libs/librrgraph/src/base/rr_graph_builder.cpp @@ -39,18 +39,18 @@ void RRGraphBuilder::add_node_to_all_locs(RRNodeId node) { case SOURCE: case SINK: case CHANY: - node_lookup_.add_node(node,node_layer, ix, iy, node_type, node_ptc_num, SIDES[0]); + node_lookup_.add_node(node, node_layer, ix, iy, node_type, node_ptc_num, TOTAL_2D_SIDES[0]); break; case CHANX: /* Currently need to swap x and y for CHANX because of chan, seg convention * TODO: Once the builders is reworked for use consistent (x, y) convention, * the following swapping can be removed */ - node_lookup_.add_node(node,node_layer, iy, ix, node_type, node_ptc_num, SIDES[0]); + node_lookup_.add_node(node, node_layer, iy, ix, node_type, node_ptc_num, TOTAL_2D_SIDES[0]); break; case OPIN: case IPIN: - for (const e_side& side : SIDES) { + for (const e_side& side : TOTAL_2D_SIDES) { if (node_storage_.is_node_on_specific_side(node, side)) { node_lookup_.add_node(node,node_layer, ix, iy, node_type, node_ptc_num, side); } diff --git a/libs/librrgraph/src/base/rr_graph_obj.cpp b/libs/librrgraph/src/base/rr_graph_obj.cpp index dd819770783..fae02caf26e 100644 --- a/libs/librrgraph/src/base/rr_graph_obj.cpp +++ b/libs/librrgraph/src/base/rr_graph_obj.cpp @@ -812,7 +812,7 @@ RRNodeId RRGraph::create_node(const t_rr_type& type) { node_ptc_nums_.push_back(-1); node_cost_indices_.push_back(-1); node_directions_.push_back(Direction::NONE); - node_sides_.push_back(NUM_SIDES); + node_sides_.push_back(NUM_2D_SIDES); node_Rs_.push_back(0.); node_Cs_.push_back(0.); @@ -1133,7 +1133,7 @@ void RRGraph::build_fast_node_lookup() const { if (node_type(node) == OPIN || node_type(node) == IPIN) { iside = node_side(node); } else { - iside = NUM_SIDES; + iside = NUM_2D_SIDES; } if (iside >= node_lookup_[x][y][itype][ptc].size()) { diff --git a/libs/librrgraph/src/base/rr_graph_obj.h b/libs/librrgraph/src/base/rr_graph_obj.h index 3b8f86c928a..5ad31ba7f01 100644 --- a/libs/librrgraph/src/base/rr_graph_obj.h +++ b/libs/librrgraph/src/base/rr_graph_obj.h @@ -510,7 +510,7 @@ class RRGraph { /* Find the edges connecting two nodes */ std::vector find_edges(const RRNodeId& src_node, const RRNodeId& sink_node) const; /* Find a node with given features from internal fast look-up */ - RRNodeId find_node(const short& x, const short& y, const t_rr_type& type, const int& ptc, const e_side& side = NUM_SIDES) const; + RRNodeId find_node(const short& x, const short& y, const t_rr_type& type, const int& ptc, const e_side& side = NUM_2D_SIDES) const; /* Find the number of routing tracks in a routing channel with a given coordinate */ short chan_num_tracks(const short& x, const short& y, const t_rr_type& type) const; @@ -828,7 +828,7 @@ class RRGraph { bool dirty_ = false; /* Fast look-up to search a node by its type, coordinator and ptc_num - * Indexing of fast look-up: [0..xmax][0..ymax][0..NUM_TYPES-1][0..ptc_max][0..NUM_SIDES-1] + * Indexing of fast look-up: [0..xmax][0..ymax][0..NUM_TYPES-1][0..ptc_max][0..NUM_2D_SIDES-1] */ typedef std::vector>>>> NodeLookup; mutable NodeLookup node_lookup_; diff --git a/libs/librrgraph/src/base/rr_graph_storage.cpp b/libs/librrgraph/src/base/rr_graph_storage.cpp index a7c8ce471c8..8d8dcd5ac20 100644 --- a/libs/librrgraph/src/base/rr_graph_storage.cpp +++ b/libs/librrgraph/src/base/rr_graph_storage.cpp @@ -618,13 +618,13 @@ const std::string& t_rr_graph_storage::node_direction_string(RRNodeId id) const } const char* t_rr_graph_storage::node_side_string(RRNodeId id) const { - for (const e_side& side : SIDES) { + for (const e_side& side : TOTAL_2D_SIDES) { if (is_node_on_specific_side(id, side)) { - return SIDE_STRING[side]; + return TOTAL_2D_SIDE_STRINGS[side]; } } /* Not found, return an invalid string*/ - return SIDE_STRING[NUM_SIDES]; + return TOTAL_2D_SIDE_STRINGS[NUM_2D_SIDES]; } void t_rr_graph_storage::set_node_layer(RRNodeId id, short layer) { @@ -771,10 +771,10 @@ 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)); } - std::bitset side_bits = node_storage_[id].dir_side_.sides; + 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", TOTAL_2D_SIDE_STRINGS[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 db791e4cda3..82c8f0b2326 100644 --- a/libs/librrgraph/src/base/rr_graph_storage.h +++ b/libs/librrgraph/src/base/rr_graph_storage.h @@ -213,7 +213,7 @@ class t_rr_graph_storage { * Developers can easily use the following codes with more flexibility * * if (rr_graph.is_node_on_specific_side(id, side)) { - * const char* side_string = SIDE_STRING[side]; + * const char* side_string = TOTAL_2D_SIDE_STRINGS[side]; * } */ const char* node_side_string(RRNodeId id) const; @@ -769,7 +769,7 @@ class t_rr_graph_storage { rr_node_typename[node_data.type_]); } // Return a vector showing only the sides that the node appears - std::bitset side_tt = node_storage[id].dir_side_.sides; + std::bitset side_tt = node_storage[id].dir_side_.sides; return side_tt[size_t(side)]; } diff --git a/libs/librrgraph/src/base/rr_graph_view.h b/libs/librrgraph/src/base/rr_graph_view.h index af5035099b5..b96c7a615b4 100644 --- a/libs/librrgraph/src/base/rr_graph_view.h +++ b/libs/librrgraph/src/base/rr_graph_view.h @@ -199,6 +199,9 @@ class RRGraphView { */ inline int node_length(RRNodeId node) const { VTR_ASSERT(node_type(node) == CHANX || node_type(node) == CHANY); + if(node_direction(node) == Direction::NONE){ + return 0; //length zero wire + } int length = 1 + node_xhigh(node) - node_xlow(node) + node_yhigh(node) - node_ylow(node); VTR_ASSERT_SAFE(length > 0); return length; @@ -264,11 +267,11 @@ class RRGraphView { int node_layer_num = node_layer(node); if (node_type(node) == OPIN || node_type(node) == IPIN) { coordinate_string += "side: ("; //add the side of the routing resource node - for (const e_side& node_side : SIDES) { + for (const e_side& node_side : TOTAL_2D_SIDES) { if (!is_node_on_specific_side(node, node_side)) { continue; } - coordinate_string += std::string(SIDE_STRING[node_side]) + ","; //add the side of the routing resource node + coordinate_string += std::string(TOTAL_2D_SIDE_STRINGS[node_side]) + ","; //add the side of the routing resource node } coordinate_string += ")"; //add the side of the routing resource node // For OPINs and IPINs the starting and ending coordinate are identical, so we can just arbitrarily assign the start to larger values diff --git a/libs/librrgraph/src/base/rr_node_types.h b/libs/librrgraph/src/base/rr_node_types.h index 3e9f89d4c82..3c3c3f91953 100644 --- a/libs/librrgraph/src/base/rr_node_types.h +++ b/libs/librrgraph/src/base/rr_node_types.h @@ -122,7 +122,7 @@ struct t_rr_rc_data { }; // This is the data type of fast lookups of an rr-node given an (rr_type, layer, x, y, and the side) -//[0..num_rr_types-1][0..num_layer-1][0..grid_width-1][0..grid_height-1][0..NUM_SIDES-1][0..max_ptc-1] +//[0..num_rr_types-1][0..num_layer-1][0..grid_width-1][0..grid_height-1][0..NUM_2D_SIDES-1][0..max_ptc-1] typedef std::array, 4>, NUM_RR_TYPES> t_rr_node_indices; #endif diff --git a/libs/librrgraph/src/base/rr_rc_data.cpp b/libs/librrgraph/src/base/rr_rc_data.cpp index fc24bf396c7..438f4c0633c 100644 --- a/libs/librrgraph/src/base/rr_rc_data.cpp +++ b/libs/librrgraph/src/base/rr_rc_data.cpp @@ -17,7 +17,7 @@ short find_create_rr_rc_data(const float R, const float C, std::vector create it + //Not found -> create it rr_rc_data.emplace_back(R, C); itr = --rr_rc_data.end(); //Iterator to inserted value diff --git a/libs/librrgraph/src/base/rr_spatial_lookup.cpp b/libs/librrgraph/src/base/rr_spatial_lookup.cpp index 57e973c0726..6959659be8b 100644 --- a/libs/librrgraph/src/base/rr_spatial_lookup.cpp +++ b/libs/librrgraph/src/base/rr_spatial_lookup.cpp @@ -16,20 +16,20 @@ RRNodeId RRSpatialLookup::find_node(int layer, * - For other types of nodes, there is no need to define a side. However, a default value * is needed when store the node in the fast look-up data structure. * Here we just arbitrary use the first side of the SIDE vector as the default value. - * We may consider to use NUM_SIDES as the default value but it will cause an increase + * We may consider to use NUM_2D_SIDES as the default value but it will cause an increase * in the dimension of the fast look-up data structure. * Please note that in the add_node function, we should keep the SAME convention! */ e_side node_side = side; if (type == IPIN || type == OPIN) { - VTR_ASSERT_MSG(side != NUM_SIDES, "IPIN/OPIN must specify desired side (can not be default NUM_SIDES)"); + VTR_ASSERT_MSG(side != NUM_2D_SIDES, "IPIN/OPIN must specify desired side (can not be default NUM_2D_SIDES)"); } else { VTR_ASSERT_SAFE(type != IPIN && type != OPIN); - node_side = SIDES[0]; + node_side = TOTAL_2D_SIDES[0]; } /* Pre-check: the layer, x, y, side and ptc should be non-negative numbers! Otherwise, return an invalid id */ - if ((layer < 0) || (x < 0) || (y < 0) || (node_side == NUM_SIDES) || (ptc < 0)) { + if ((layer < 0) || (x < 0) || (y < 0) || (node_side == NUM_2D_SIDES) || (ptc < 0)) { return RRNodeId::INVALID(); } @@ -191,9 +191,9 @@ std::vector RRSpatialLookup::find_nodes_at_all_sides(int layer, /* TODO: Consider to access the raw data like find_node() rather than calling find_node() many times, which hurts runtime */ if (rr_type == IPIN || rr_type == OPIN) { - indices.reserve(NUM_SIDES); + indices.reserve(NUM_2D_SIDES); //For pins, we need to look at all the sides of the current grid tile - for (e_side side : SIDES) { + for (e_side side : TOTAL_2D_SIDES) { RRNodeId rr_node_index = find_node(layer, x, y, rr_type, ptc, side); if (rr_node_index) { indices.push_back(rr_node_index); @@ -223,12 +223,12 @@ std::vector RRSpatialLookup::find_grid_nodes_at_all_sides(int layer, std::vector nodes; /* Reserve space to avoid memory fragmentation */ size_t num_nodes = 0; - for (e_side node_side : SIDES) { + for (e_side node_side : TOTAL_2D_SIDES) { num_nodes += find_nodes(layer,x, y, rr_type, node_side).size(); } nodes.reserve(num_nodes); - for (e_side node_side : SIDES) { + for (e_side node_side : TOTAL_2D_SIDES) { std::vector temp_nodes = find_nodes(layer,x, y, rr_type, node_side); nodes.insert(nodes.end(), temp_nodes.begin(), temp_nodes.end()); } @@ -245,7 +245,7 @@ void RRSpatialLookup::reserve_nodes(int layer, /* For non-IPIN/OPIN nodes, the side should always be the TOP side which follows the convention in find_node() API! */ if (type != IPIN && type != OPIN) { - VTR_ASSERT(side == SIDES[0]); + VTR_ASSERT(side == TOTAL_2D_SIDES[0]); } resize_nodes(layer, x, y, type, side); @@ -265,7 +265,7 @@ void RRSpatialLookup::add_node(RRNodeId node, /* For non-IPIN/OPIN nodes, the side should always be the TOP side which follows the convention in find_node() API! */ if (type != IPIN && type != OPIN) { - VTR_ASSERT(side == SIDES[0]); + VTR_ASSERT(side == TOTAL_2D_SIDES[0]); } resize_nodes(layer, x, y, type, side); @@ -293,7 +293,7 @@ bool RRSpatialLookup::remove_node(RRNodeId node, VTR_ASSERT_SAFE(y >= 0); VTR_ASSERT_SAFE(type != NUM_RR_TYPES); VTR_ASSERT_SAFE(ptc >= 0); - VTR_ASSERT_SAFE(side != NUM_SIDES); + VTR_ASSERT_SAFE(side != NUM_2D_SIDES); // Check if the node given is in the spatial lookup at the given indices if (type >= rr_node_indices_.size()) return false; diff --git a/libs/librrgraph/src/base/rr_spatial_lookup.h b/libs/librrgraph/src/base/rr_spatial_lookup.h index bae254d8459..6a4ca5f1b1c 100644 --- a/libs/librrgraph/src/base/rr_spatial_lookup.h +++ b/libs/librrgraph/src/base/rr_spatial_lookup.h @@ -73,7 +73,7 @@ class RRSpatialLookup { int y, t_rr_type type, int ptc, - e_side side = NUM_SIDES) const; + e_side side = NUM_2D_SIDES) const; /** * @brief Returns unique indices of the routing resource nodes in the bounds (xlow, ylow) to (xhigh, yhigh). @@ -96,7 +96,7 @@ class RRSpatialLookup { int yhigh, t_rr_type type, int ptc, - e_side side = NUM_SIDES) const; + e_side side = e_side::NUM_2D_SIDES) const; /** * @brief Returns the indices of the specified routing resource nodes, representing routing tracks in a channel. @@ -148,7 +148,7 @@ class RRSpatialLookup { int y, t_rr_type type, int num_nodes, - e_side side = SIDES[0]); + e_side side = TOTAL_2D_SIDES[0]); /** * @brief Register a node in the fast spatial lookup @@ -181,7 +181,7 @@ class RRSpatialLookup { int y, t_rr_type type, int ptc, - e_side side = SIDES[0]); + e_side side = TOTAL_2D_SIDES[0]); /** * @brief Remove a node in the fast spatial lookup. @@ -205,7 +205,7 @@ class RRSpatialLookup { int y, t_rr_type type, int ptc, - e_side side = SIDES[0]); + e_side side = TOTAL_2D_SIDES[0]); /** * @brief Mirror the last dimension of a look-up, i.e., a list of nodes, from a source coordinate to @@ -286,7 +286,7 @@ class RRSpatialLookup { int x, int y, t_rr_type type, - e_side side = SIDES[0]) const; + e_side side = TOTAL_2D_SIDES[0]) const; /* -- Internal data storage -- */ private: diff --git a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx.h b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx.h index cfb34f754ec..d128bb44a00 100644 --- a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx.h +++ b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx.h @@ -6,6 +6,7 @@ * * Cmdline: uxsdcxx/uxsdcxx.py /home/mohagh18/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd * Input file: /home/mohagh18/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd + * md5sum of input file: 65eddcc840064bbb91d7f4cf0b8bf821 */ @@ -1620,7 +1621,7 @@ constexpr const char *lookup_switch_type[] = {"UXSD_INVALID", "mux", "tristate", constexpr const char *lookup_segment_res_type[] = {"UXSD_INVALID", "GENERAL", "GCLK"}; constexpr const char *lookup_pin_type[] = {"UXSD_INVALID", "OPEN", "OUTPUT", "INPUT"}; constexpr const char *lookup_node_type[] = {"UXSD_INVALID", "CHANX", "CHANY", "SOURCE", "SINK", "OPIN", "IPIN"}; -constexpr const char *lookup_node_direction[] = {"UXSD_INVALID", "INC_DIR", "DEC_DIR", "BI_DIR"}; +constexpr const char *lookup_node_direction[] = {"UXSD_INVALID", "INC_DIR", "DEC_DIR", "BI_DIR", "NONE"}; constexpr const char *lookup_node_clk_res_type[] = {"UXSD_INVALID", "VIRTUAL_SINK"}; constexpr const char *lookup_loc_side[] = {"UXSD_INVALID", "LEFT", "RIGHT", "TOP", "BOTTOM", "RIGHT_LEFT", "RIGHT_BOTTOM", "RIGHT_BOTTOM_LEFT", "TOP_RIGHT", "TOP_BOTTOM", "TOP_LEFT", "TOP_RIGHT_BOTTOM", "TOP_RIGHT_LEFT", "TOP_BOTTOM_LEFT", "TOP_RIGHT_BOTTOM_LEFT", "BOTTOM_LEFT"}; @@ -1856,6 +1857,14 @@ inline enum_node_type lex_enum_node_type(const char *in, bool throw_on_invalid, inline enum_node_direction lex_enum_node_direction(const char *in, bool throw_on_invalid, 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('O', 8, 32) | onechar('N', 16, 32) | onechar('E', 24, 32): + return enum_node_direction::NONE; + break; + default: break; + } + break; case 6: switch(*((triehash_uu32*)&in[0])){ case onechar('B', 0, 32) | onechar('I', 8, 32) | onechar('_', 16, 32) | onechar('D', 24, 32): diff --git a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_capnp.h b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_capnp.h index 2e42a424944..7cfea1aa19b 100644 --- a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_capnp.h +++ b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_capnp.h @@ -6,6 +6,7 @@ * * Cmdline: uxsdcxx/uxsdcap.py /home/mohagh18/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd * Input file: /home/mohagh18/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd + * md5sum of input file: 65eddcc840064bbb91d7f4cf0b8bf821 */ @@ -265,6 +266,8 @@ inline enum_node_direction conv_enum_node_direction(ucap::NodeDirection e, const return enum_node_direction::DEC_DIR; case ucap::NodeDirection::BI_DIR: return enum_node_direction::BI_DIR; + case ucap::NodeDirection::NONE: + return enum_node_direction::NONE; default: (*report_error)("Unknown enum_node_direction"); throw std::runtime_error("Unreachable!"); @@ -281,6 +284,8 @@ inline ucap::NodeDirection conv_to_enum_node_direction(enum_node_direction e) { return ucap::NodeDirection::DEC_DIR; case enum_node_direction::BI_DIR: return ucap::NodeDirection::BI_DIR; + case enum_node_direction::NONE: + return ucap::NodeDirection::NONE; default: throw std::runtime_error("Unknown enum_node_direction"); } diff --git a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_interface.h b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_interface.h index ac0e16d1418..9dbac04ef7f 100644 --- a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_interface.h +++ b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_interface.h @@ -6,6 +6,7 @@ * * Cmdline: uxsdcxx/uxsdcxx.py /home/mohagh18/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd * Input file: /home/mohagh18/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd + * md5sum of input file: 65eddcc840064bbb91d7f4cf0b8bf821 */ @@ -29,7 +30,7 @@ enum class enum_pin_type {UXSD_INVALID = 0, OPEN, OUTPUT, INPUT}; enum class enum_node_type {UXSD_INVALID = 0, CHANX, CHANY, SOURCE, SINK, OPIN, IPIN}; -enum class enum_node_direction {UXSD_INVALID = 0, INC_DIR, DEC_DIR, BI_DIR}; +enum class enum_node_direction {UXSD_INVALID = 0, INC_DIR, DEC_DIR, BI_DIR, NONE}; enum class enum_node_clk_res_type {UXSD_INVALID = 0, VIRTUAL_SINK}; diff --git a/libs/librrgraph/src/io/rr_graph.xsd b/libs/librrgraph/src/io/rr_graph.xsd index cea5a0f7b22..dfb63641cbb 100644 --- a/libs/librrgraph/src/io/rr_graph.xsd +++ b/libs/librrgraph/src/io/rr_graph.xsd @@ -241,6 +241,7 @@ + diff --git a/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h b/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h index 35ec9171ebb..6c820aeb192 100644 --- a/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h +++ b/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h @@ -750,8 +750,8 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { inode, rr_graph.node_type(node.id())); } } else { - std::bitset sides_to_add = from_uxsd_loc_side(side); - for (const e_side& side_to_add : SIDES) { + std::bitset sides_to_add = from_uxsd_loc_side(side); + for (const e_side& side_to_add : TOTAL_2D_SIDES) { if (sides_to_add[side_to_add]) { rr_graph_builder_->add_node_side(node_id, side_to_add); } @@ -768,8 +768,8 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { inline uxsd::enum_loc_side get_node_loc_side(const t_rr_node& node) final { const auto& rr_graph = (*rr_graph_); if (rr_graph.node_type(node.id()) == IPIN || rr_graph.node_type(node.id()) == OPIN) { - std::bitset sides_bitset; - for (const e_side& side : SIDES) { + std::bitset sides_bitset; + for (const e_side& side : TOTAL_2D_SIDES) { if (rr_graph.is_node_on_specific_side(node.id(), side)) { sides_bitset.set(side); } @@ -1849,9 +1849,9 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { /* Alloc the lookup table */ for (t_rr_type rr_type : RR_TYPES) { if (rr_type == CHANX) { - rr_graph_builder.node_lookup().resize_nodes(grid_.get_num_layers(),grid_.height(), grid_.width(), rr_type, NUM_SIDES); + rr_graph_builder.node_lookup().resize_nodes(grid_.get_num_layers(), grid_.height(), grid_.width(), rr_type, NUM_2D_SIDES); } else { - rr_graph_builder.node_lookup().resize_nodes(grid_.get_num_layers(),grid_.width(), grid_.height(), rr_type, NUM_SIDES); + rr_graph_builder.node_lookup().resize_nodes(grid_.get_num_layers(), grid_.width(), grid_.height(), rr_type, NUM_2D_SIDES); } } @@ -1864,8 +1864,8 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { // Enum converters from/to uxsd types - std::bitset from_uxsd_loc_side(uxsd::enum_loc_side side) { - std::bitset side_mask(0x0); + std::bitset from_uxsd_loc_side(uxsd::enum_loc_side side) { + std::bitset side_mask(0x0); switch (side) { case uxsd::enum_loc_side::TOP: side_mask.set(TOP); @@ -1936,7 +1936,7 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { return side_mask; } - uxsd::enum_loc_side to_uxsd_loc_side(std::bitset sides) { + uxsd::enum_loc_side to_uxsd_loc_side(std::bitset sides) { // Error out when // - the side has no valid bits // - the side is beyond the mapping range: this is to warn any changes on side truth table which may cause the mapping failed @@ -1956,6 +1956,8 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { return Direction::DEC; case uxsd::enum_node_direction::BI_DIR: return Direction::BIDIR; + case uxsd::enum_node_direction::NONE: + return Direction::NONE; default: report_error( "Invalid node direction %d", direction); @@ -1970,6 +1972,8 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { return uxsd::enum_node_direction::DEC_DIR; case Direction::BIDIR: return uxsd::enum_node_direction::BI_DIR; + case Direction::NONE: + return uxsd::enum_node_direction::NONE; default: report_error( "Invalid direction %d", direction); diff --git a/libs/librrgraph/src/utils/describe_rr_node.cpp b/libs/librrgraph/src/utils/describe_rr_node.cpp index a4982cce8f7..6383e3489cc 100644 --- a/libs/librrgraph/src/utils/describe_rr_node.cpp +++ b/libs/librrgraph/src/utils/describe_rr_node.cpp @@ -11,7 +11,6 @@ std::string describe_rr_node(const RRGraphView& rr_graph, bool is_flat) { std::string msg = vtr::string_fmt("RR node: %d", inode); - if (rr_graph.node_type(inode) == CHANX || rr_graph.node_type(inode) == CHANY) { auto cost_index = rr_graph.node_cost_index(inode); diff --git a/libs/libvtrcapnproto/gen/rr_graph_uxsdcxx.capnp b/libs/libvtrcapnproto/gen/rr_graph_uxsdcxx.capnp index 4ea37de3f7e..d78d2dd2b3d 100644 --- a/libs/libvtrcapnproto/gen/rr_graph_uxsdcxx.capnp +++ b/libs/libvtrcapnproto/gen/rr_graph_uxsdcxx.capnp @@ -4,6 +4,7 @@ # # Cmdline: uxsdcxx/uxsdcap.py /home/mohagh18/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd # Input file: /home/mohagh18/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd + # md5sum of input file: 65eddcc840064bbb91d7f4cf0b8bf821 @0xe787bf7696810419; @@ -47,6 +48,7 @@ enum NodeDirection { incDir @1; decDir @2; biDir @3; + none @4; } enum NodeClkResType { diff --git a/utils/route_diag/src/main.cpp b/utils/route_diag/src/main.cpp index 47637885363..7b9c170fbe7 100644 --- a/utils/route_diag/src/main.cpp +++ b/utils/route_diag/src/main.cpp @@ -208,8 +208,7 @@ static void profile_source(const Netlist<>& net_list, successfully_routed = profiler.calculate_delay(RRNodeId(source_rr_node), RRNodeId(sink_rr_node), router_opts, - &delays[sink_x][sink_y], - layer_num); + &delays[sink_x][sink_y]); } if (successfully_routed) { diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index 2e7d273c267..fd2229b8b79 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -496,6 +496,7 @@ static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts) RouterOpts->generate_rr_node_overuse_report = Options.generate_rr_node_overuse_report; RouterOpts->flat_routing = Options.flat_routing; RouterOpts->has_choking_spot = Options.has_choking_spot; + RouterOpts->custom_3d_sb_fanin_fanout = Options.custom_3d_sb_fanin_fanout; RouterOpts->with_timing_analysis = Options.timing_analysis; } diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index bf51e37b059..5b77f5330a7 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -2495,6 +2495,13 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio .default_value("false") .show_in(argparse::ShowIn::HELP_ONLY); + route_grp.add_argument(args.custom_3d_sb_fanin_fanout, "--custom_3d_sb_fanin_fanout") + .help( + "Specifies the number of tracks that can drive a 3D switch block connection" + "and the number of tracks that can be driven by a 3D switch block connection") + .default_value("1") + .show_in(argparse::ShowIn::HELP_ONLY); + auto& route_timing_grp = parser.add_argument_group("timing-driven routing options"); route_timing_grp.add_argument(args.astar_fac, "--astar_fac") diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index de98e9c3ca8..c9aedd2fe42 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -218,6 +218,7 @@ struct t_options { argparse::ArgValue reorder_rr_graph_nodes_seed; argparse::ArgValue flat_routing; argparse::ArgValue has_choking_spot; + argparse::ArgValue custom_3d_sb_fanin_fanout; /* Timing-driven router options only */ argparse::ArgValue astar_fac; diff --git a/vpr/src/base/read_route.cpp b/vpr/src/base/read_route.cpp index 998e0c5434f..269b273ab8b 100644 --- a/vpr/src/base/read_route.cpp +++ b/vpr/src/base/read_route.cpp @@ -459,8 +459,9 @@ static void process_global_blocks(const Netlist<>& net_list, std::ifstream& fp, if (block_loc.loc.x != x || block_loc.loc.y != y) { vpr_throw(VPR_ERROR_ROUTE, filename, lineno, - "The placement coordinates (%d, %d) of %d block does not match given (%d, %d)", - x, y, block_loc.loc.x, block_loc.loc.y); + "The placement coordinates (%d,%d,%d) of %zu block does not match given (%d,%d,%d)", + x, y, layer_num, size_t(bnum), + block_loc.loc.x, block_loc.loc.y, block_loc.loc.layer); } auto pin_class = get_class_range_for_block(bnum, is_flat); @@ -496,9 +497,9 @@ static void format_coordinates(int& layer_num, int& x, int& y, std::string coord y = coords[1]; } else { VTR_ASSERT(coords.size() == 3); - layer_num = coords[0]; - x = coords[1]; - y = coords[2]; + x = coords[0]; + y = coords[1]; + layer_num = coords[2]; } } @@ -597,12 +598,12 @@ void print_route(const Netlist<>& net_list, int layer_num = rr_graph.node_layer(inode); fprintf(fp, "Node:\t%zu\t%6s (%d,%d,%d) ", size_t(inode), - rr_graph.node_type_string(inode), layer_num, ilow, jlow); + rr_graph.node_type_string(inode), ilow, jlow, layer_num); if ((ilow != rr_graph.node_xhigh(inode)) || (jlow != rr_graph.node_yhigh(inode))) - fprintf(fp, "to (%d,%d) ", rr_graph.node_xhigh(inode), - rr_graph.node_yhigh(inode)); + fprintf(fp, "to (%d,%d,%d) ", rr_graph.node_xhigh(inode), + rr_graph.node_yhigh(inode), layer_num); switch (rr_type) { case IPIN: @@ -683,11 +684,12 @@ void print_route(const Netlist<>& net_list, int iclass = get_block_pin_class_num(block_id, pin_id, is_flat); t_block_loc blk_loc; blk_loc = get_block_loc(block_id, is_flat); - fprintf(fp, "Block %s (#%zu) at (%d,%d), Pin class %d.\n", + fprintf(fp, "Block %s (#%zu) at (%d,%d,%d), Pin class %d.\n", net_list.block_name(block_id).c_str(), size_t(block_id), blk_loc.loc.x, blk_loc.loc.y, + blk_loc.loc.layer, iclass); } } diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index e169a9e82a5..cf5f1062f96 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1412,6 +1412,8 @@ struct t_router_opts { bool flat_routing; bool has_choking_spot; + int custom_3d_sb_fanin_fanout = 1; + bool with_timing_analysis; // Options related to rr_node reordering, for testing and possible cache optimization diff --git a/vpr/src/draw/draw_rr.cpp b/vpr/src/draw/draw_rr.cpp index f2b573f69e7..02645f6baf5 100644 --- a/vpr/src/draw/draw_rr.cpp +++ b/vpr/src/draw/draw_rr.cpp @@ -563,7 +563,7 @@ void draw_rr_pin(RRNodeId inode, const ezgl::color& color, ezgl::renderer* g) { /* As nodes may appear on more than one side, walk through the possible nodes * - draw the pin on each side that it appears */ - for (const e_side& pin_side : SIDES) { + for (const e_side& pin_side : TOTAL_2D_SIDES) { if (!rr_graph.is_node_on_specific_side(RRNodeId(inode), pin_side)) { continue; } @@ -719,7 +719,7 @@ RRNodeId draw_check_rr_node_hit(float click_x, float click_y) { int height_offset = device_ctx.grid.get_height_offset({i, j, layer_num}); int ipin = rr_graph.node_pin_num(inode); float xcen, ycen; - for (const e_side& iside : SIDES) { + for (const e_side& iside : TOTAL_2D_SIDES) { // If pin exists on this side of the block, then get pin coordinates if (type->pinloc[width_offset][height_offset][size_t(iside)][ipin]) { draw_get_rr_pin_coords(inode, &xcen, &ycen, iside); @@ -926,7 +926,8 @@ void draw_get_rr_pin_coords(const t_rr_node& node, float* xcen, float* ycen, con default: vpr_throw(VPR_ERROR_OTHER, __FILE__, __LINE__, - "in draw_get_rr_pin_coords: Unexpected side.\n"); + "in draw_get_rr_pin_coords: Unexpected side %s.\n", + TOTAL_2D_SIDE_STRINGS[pin_side]); break; } diff --git a/vpr/src/draw/draw_rr_edges.cpp b/vpr/src/draw/draw_rr_edges.cpp index d8aee31c89b..274e02e0fb3 100644 --- a/vpr/src/draw/draw_rr_edges.cpp +++ b/vpr/src/draw/draw_rr_edges.cpp @@ -103,7 +103,7 @@ void draw_chany_to_chany_edge(RRNodeId from_node, RRNodeId to_node, short switch if (rr_graph.node_direction(to_node) == Direction::INC) { /* INC wire starts at bottom edge */ y2 = to_chan.bottom(); - /* since no U-turns from_track must be INC as well */ + /* since no U-turns from_tracks must be INC as well */ y1 = draw_coords->tile_y[to_ylow - 1] + draw_coords->get_tile_width(); } else { /* DEC wire starts at top edge */ @@ -194,7 +194,7 @@ void draw_chanx_to_chanx_edge(RRNodeId from_node, RRNodeId to_node, short switch if (rr_graph.node_direction(to_node) == Direction::INC) { /* INC wire starts at leftmost edge */ VTR_ASSERT(from_xlow < to_xlow); x2 = to_chan.left(); - /* since no U-turns from_track must be INC as well */ + /* since no U-turns from_tracks must be INC as well */ x1 = draw_coords->tile_x[to_xlow - 1] + draw_coords->get_tile_width(); } else { /* DEC wire starts at rightmost edge */ @@ -324,7 +324,7 @@ void draw_pin_to_pin(RRNodeId opin_node, RRNodeId ipin_node, ezgl::renderer* g) */ float x1 = 0, y1 = 0; std::vector opin_candidate_sides; - for (const e_side& opin_candidate_side : SIDES) { + for (const e_side& opin_candidate_side : TOTAL_2D_SIDES) { if (rr_graph.is_node_on_specific_side(opin_node, opin_candidate_side)) { opin_candidate_sides.push_back(opin_candidate_side); } @@ -334,7 +334,7 @@ void draw_pin_to_pin(RRNodeId opin_node, RRNodeId ipin_node, ezgl::renderer* g) float x2 = 0, y2 = 0; std::vector ipin_candidate_sides; - for (const e_side& ipin_candidate_side : SIDES) { + for (const e_side& ipin_candidate_side : TOTAL_2D_SIDES) { if (rr_graph.is_node_on_specific_side(ipin_node, ipin_candidate_side)) { ipin_candidate_sides.push_back(ipin_candidate_side); } @@ -355,7 +355,7 @@ void draw_pin_to_sink(RRNodeId ipin_node, RRNodeId sink_node, ezgl::renderer* g) float x1 = 0, y1 = 0; /* Draw the line for each ipin on different sides */ - for (const e_side& pin_side : SIDES) { + for (const e_side& pin_side : TOTAL_2D_SIDES) { if (!rr_graph.is_node_on_specific_side(ipin_node, pin_side)) { continue; } @@ -381,7 +381,7 @@ void draw_source_to_pin(RRNodeId source_node, RRNodeId opin_node, ezgl::renderer draw_get_rr_src_sink_coords(rr_graph.rr_nodes()[size_t(source_node)], &x1, &y1); /* Draw the line for each ipin on different sides */ - for (const e_side& pin_side : SIDES) { + for (const e_side& pin_side : TOTAL_2D_SIDES) { if (!rr_graph.is_node_on_specific_side(opin_node, pin_side)) { continue; } @@ -455,7 +455,7 @@ void draw_pin_to_chan_edge(RRNodeId pin_node, RRNodeId chan_node, ezgl::renderer * the actual offset of the pin in the context of grid width and height */ std::vector pin_candidate_sides; - for (const e_side& pin_candidate_side : SIDES) { + for (const e_side& pin_candidate_side : TOTAL_2D_SIDES) { if ((rr_graph.is_node_on_specific_side(pin_node, pin_candidate_side)) && (grid_type->pinloc[width_offset][height_offset][pin_candidate_side][rr_graph.node_pin_num(pin_node)])) { pin_candidate_sides.push_back(pin_candidate_side); @@ -464,7 +464,7 @@ void draw_pin_to_chan_edge(RRNodeId pin_node, RRNodeId chan_node, ezgl::renderer /* Only 1 side will be picked in the end * Any rr_node of a grid should have at least 1 side!!! */ - e_side pin_side = NUM_SIDES; + e_side pin_side = NUM_2D_SIDES; const t_rr_type channel_type = rr_graph.node_type(chan_node); if (1 == pin_candidate_sides.size()) { pin_side = pin_candidate_sides[0]; @@ -483,7 +483,7 @@ void draw_pin_to_chan_edge(RRNodeId pin_node, RRNodeId chan_node, ezgl::renderer VTR_ASSERT(pin_candidate_sides.end() != std::find(pin_candidate_sides.begin(), pin_candidate_sides.end(), pin_side)); } /* Sanity check */ - VTR_ASSERT(NUM_SIDES != pin_side); + VTR_ASSERT(NUM_2D_SIDES != pin_side); /* Now we determine which side to be used, calculate the offset for the pin to be drawn * - For the pin locates above/right to the grid (at the top/right side), diff --git a/vpr/src/draw/search_bar.cpp b/vpr/src/draw/search_bar.cpp index 2842577b2fa..756dfd9d976 100644 --- a/vpr/src/draw/search_bar.cpp +++ b/vpr/src/draw/search_bar.cpp @@ -251,7 +251,7 @@ void auto_zoom_rr_node(RRNodeId rr_node_id) { int ipin = rr_graph.node_ptc_num(rr_node_id); float xcen, ycen; - for (const e_side& iside : SIDES) { + for (const e_side& iside : TOTAL_2D_SIDES) { if (type->pinloc[width_offset][height_offset][size_t(iside)][ipin]) { draw_get_rr_pin_coords(rr_node_id, &xcen, &ycen, iside); rr_node = {{xcen - draw_coords->pin_size, ycen - draw_coords->pin_size}, diff --git a/vpr/src/pack/post_routing_pb_pin_fixup.cpp b/vpr/src/pack/post_routing_pb_pin_fixup.cpp index 2126c0b7b1a..e8c2c34a126 100644 --- a/vpr/src/pack/post_routing_pb_pin_fixup.cpp +++ b/vpr/src/pack/post_routing_pb_pin_fixup.cpp @@ -107,7 +107,7 @@ static void update_cluster_pin_with_post_routing_results(const Netlist<>& net_li * Deposit all the sides */ if (wanted_sides.empty()) { - for (e_side side : {TOP, BOTTOM, LEFT, RIGHT}) { + for (e_side side : TOTAL_2D_SIDES) { wanted_sides.push_back(side); } } diff --git a/vpr/src/place/place.cpp b/vpr/src/place/place.cpp index 2cd55402d47..c61db2ee0fc 100644 --- a/vpr/src/place/place.cpp +++ b/vpr/src/place/place.cpp @@ -1589,27 +1589,21 @@ static bool is_cube_bb(const e_place_bounding_box_mode place_bb_mode, bool cube_bb; const int number_layers = g_vpr_ctx.device().grid.get_num_layers(); - // If the FPGA has only one layer, then we can only use cube bounding box - if (number_layers == 1) { - cube_bb = true; - } else { - VTR_ASSERT(number_layers > 1); - if (place_bb_mode == AUTO_BB) { - // If the auto_bb is used, we analyze the RR graph to see whether is there any inter-layer connection that is not - // originated from OPIN. If there is any, cube BB is chosen, otherwise, per-layer bb is chosen. - if (inter_layer_connections_limited_to_opin(rr_graph)) { - cube_bb = false; - } else { - cube_bb = true; - } - } else if (place_bb_mode == CUBE_BB) { - // The user has specifically asked for CUBE_BB - cube_bb = true; - } else { - // The user has specifically asked for PER_LAYER_BB - VTR_ASSERT_SAFE(place_bb_mode == PER_LAYER_BB); + if (place_bb_mode == AUTO_BB) { + // If the auto_bb is used, we analyze the RR graph to see whether is there any inter-layer connection that is not + // originated from OPIN. If there is any, cube BB is chosen, otherwise, per-layer bb is chosen. + if (number_layers > 1 && inter_layer_connections_limited_to_opin(rr_graph)) { cube_bb = false; + } else { + cube_bb = true; } + } else if (place_bb_mode == CUBE_BB) { + // The user has specifically asked for CUBE_BB + cube_bb = true; + } else { + // The user has specifically asked for PER_LAYER_BB + VTR_ASSERT_SAFE(place_bb_mode == PER_LAYER_BB); + cube_bb = false; } return cube_bb; @@ -2074,14 +2068,15 @@ static int check_block_placement_consistency(const BlkLocRegistry& blk_loc_regis || !is_sub_tile_compatible(physical_tile, logical_block, loc.sub_tile)) { VTR_LOG_ERROR( - "Block %zu's location is (%d,%d,%d) but found in grid at (%zu,%zu,%d,%d).\n", + "Block %zu's location is (%d,%d,%d,%d) but found in grid at (%d,%d,%d,%d).\n", size_t(bnum), loc.x, loc.y, loc.sub_tile, - tile_loc.x, - tile_loc.y, - tile_loc.layer_num, + loc.layer, + i, + j, + k, layer_num); error++; } diff --git a/vpr/src/place/place_delay_model.cpp b/vpr/src/place/place_delay_model.cpp index ea21d581273..aa6896a62fe 100644 --- a/vpr/src/place/place_delay_model.cpp +++ b/vpr/src/place/place_delay_model.cpp @@ -30,34 +30,27 @@ float DeltaDelayModel::delay(const t_physical_tile_loc& from_loc, int /*from_pin int delta_x = std::abs(from_loc.x - to_loc.x); int delta_y = std::abs(from_loc.y - to_loc.y); - // TODO: This is compatible with the case that only OPINs are connected to other layers. - // Ideally, I should check whether OPINs are conneced or IPINs and use the correct layer. - // If both are connected, minimum should be taken. In the case that channels are also connected, - // I haven't thought about what to do. - float cross_layer_td = 0; - if (from_loc.layer_num != to_loc.layer_num) { - VTR_ASSERT(std::isfinite(cross_layer_delay_)); - cross_layer_td = cross_layer_delay_; - } - return delays_[to_loc.layer_num][delta_x][delta_y] + cross_layer_td; + return delays_[from_loc.layer_num][to_loc.layer_num][delta_x][delta_y]; } void DeltaDelayModel::dump_echo(std::string filepath) const { FILE* f = vtr::fopen(filepath.c_str(), "w"); fprintf(f, " "); - for (size_t layer_num = 0; layer_num < delays_.dim_size(0); ++layer_num) { - fprintf(f, " %9zu", layer_num); - fprintf(f, "\n"); - for (size_t dx = 0; dx < delays_.dim_size(1); ++dx) { - fprintf(f, " %9zu", dx); - } - fprintf(f, "\n"); - for (size_t dy = 0; dy < delays_.dim_size(2); ++dy) { - fprintf(f, "%9zu", dy); - for (size_t dx = 0; dx < delays_.dim_size(1); ++dx) { - fprintf(f, " %9.2e", delays_[layer_num][dx][dy]); + for (size_t from_layer_num = 0; from_layer_num < delays_.dim_size(0); ++from_layer_num) { + for (size_t to_layer_num = 0; to_layer_num < delays_.dim_size(1); ++to_layer_num) { + fprintf(f, " %9zu", from_layer_num); + fprintf(f, "\n"); + for (size_t dx = 0; dx < delays_.dim_size(2); ++dx) { + fprintf(f, " %9zu", dx); } fprintf(f, "\n"); + for (size_t dy = 0; dy < delays_.dim_size(3); ++dy) { + fprintf(f, "%9zu", dy); + for (size_t dx = 0; dx < delays_.dim_size(2); ++dx) { + fprintf(f, " %9.2e", delays_[from_layer_num][to_layer_num][dx][dy]); + } + fprintf(f, "\n"); + } } } vtr::fclose(f); @@ -241,7 +234,7 @@ void DeltaDelayModel::read(const std::string& file) { // // The second argument should be of type Matrix::Reader where X is the // capnproto element type. - ToNdMatrix<3, VprFloatEntry, float>(&delays_, model.getDelays(), ToFloat); + ToNdMatrix<4, VprFloatEntry, float>(&delays_, model.getDelays(), ToFloat); } void DeltaDelayModel::write(const std::string& file) const { @@ -257,7 +250,7 @@ void DeltaDelayModel::write(const std::string& file) const { // Matrix message. It is the mirror function of ToNdMatrix described in // read above. auto delay_values = model.getDelays(); - FromNdMatrix<3, VprFloatEntry, float>(&delay_values, delays_, FromFloat); + FromNdMatrix<4, VprFloatEntry, float>(&delay_values, delays_, FromFloat); // writeMessageToFile writes message to the specified file. writeMessageToFile(file, &builder); @@ -270,9 +263,9 @@ void OverrideDelayModel::read(const std::string& file) { ::capnp::ReaderOptions opts = default_large_capnp_opts(); ::capnp::FlatArrayMessageReader reader(f.getData(), opts); - vtr::NdMatrix delays; + vtr::NdMatrix delays; auto model = reader.getRoot(); - ToNdMatrix<3, VprFloatEntry, float>(&delays, model.getDelays(), ToFloat); + ToNdMatrix<4, VprFloatEntry, float>(&delays, model.getDelays(), ToFloat); base_delay_model_ = std::make_unique(cross_layer_delay_, delays, is_flat_); @@ -300,7 +293,7 @@ void OverrideDelayModel::write(const std::string& file) const { auto model = builder.initRoot(); auto delays = model.getDelays(); - FromNdMatrix<3, VprFloatEntry, float>(&delays, base_delay_model_->delays(), FromFloat); + FromNdMatrix<4, VprFloatEntry, float>(&delays, base_delay_model_->delays(), FromFloat); // Non-scalar capnproto fields should be first initialized with // init(count), and then accessed from the returned diff --git a/vpr/src/place/place_delay_model.h b/vpr/src/place/place_delay_model.h index 5f61b856405..05fba845f0a 100644 --- a/vpr/src/place/place_delay_model.h +++ b/vpr/src/place/place_delay_model.h @@ -95,7 +95,7 @@ class DeltaDelayModel : public PlaceDelayModel { : cross_layer_delay_(min_cross_layer_delay) , is_flat_(is_flat) {} DeltaDelayModel(float min_cross_layer_delay, - vtr::NdMatrix delta_delays, + vtr::NdMatrix delta_delays, bool is_flat) : delays_(std::move(delta_delays)) , cross_layer_delay_(min_cross_layer_delay) @@ -111,15 +111,12 @@ class DeltaDelayModel : public PlaceDelayModel { void read(const std::string& file) override; void write(const std::string& file) const override; - const vtr::NdMatrix& delays() const { + const vtr::NdMatrix& delays() const { return delays_; } private: - vtr::NdMatrix delays_; // [0..num_layers-1][0..max_dx][0..max_dy] - /** - * @brief The minimum delay of inter-layer connections - */ + vtr::NdMatrix delays_; // [0..num_layers-1][0..max_dx][0..max_dy] float cross_layer_delay_; /** * @brief Indicates whether the router is a two-stage or run-flat diff --git a/vpr/src/place/timing_place_lookup.cpp b/vpr/src/place/timing_place_lookup.cpp index c16a0d6dbad..f7c333aa53e 100644 --- a/vpr/src/place/timing_place_lookup.cpp +++ b/vpr/src/place/timing_place_lookup.cpp @@ -70,7 +70,8 @@ static t_chan_width setup_chan_width(const t_router_opts& router_opts, static float route_connection_delay( RouterDelayProfiler& route_profiler, - int layer_num, + int from_layer_num, + int to_layer_num, int source_x_loc, int source_y_loc, int sink_x_loc, @@ -89,6 +90,7 @@ typedef std::function&, @@ -98,7 +100,8 @@ typedef std::function>& matrix, - int layer_num, + int from_layer_num, + int to_layer_num, int source_x, int source_y, int start_x, @@ -113,7 +116,8 @@ static void generic_compute_matrix_iterative_astar( static void generic_compute_matrix_dijkstra_expansion( RouterDelayProfiler& route_profiler, vtr::Matrix>& matrix, - int layer_num, + int from_layer_num, + int to_layer_num, int source_x, int source_y, int start_x, @@ -125,7 +129,7 @@ static void generic_compute_matrix_dijkstra_expansion( const std::set& allowed_types, bool is_flat); -static vtr::NdMatrix compute_delta_delays( +static vtr::NdMatrix compute_delta_delays( RouterDelayProfiler& route_profiler, const t_placer_opts& palcer_opts, const t_router_opts& router_opts, @@ -135,7 +139,7 @@ static vtr::NdMatrix compute_delta_delays( float delay_reduce(std::vector& delays, e_reducer reducer); -static vtr::NdMatrix compute_delta_delay_model( +static vtr::NdMatrix compute_delta_delay_model( RouterDelayProfiler& route_profiler, const t_placer_opts& placer_opts, const t_router_opts& router_opts, @@ -160,14 +164,17 @@ static bool find_direct_connect_sample_locations(const t_direct_inf* direct, RRNodeId& out_src_node, RRNodeId& out_sink_node); -static bool verify_delta_delays(const vtr::NdMatrix& delta_delays); +static bool verify_delta_delays(const vtr::NdMatrix& delta_delays); static int get_longest_segment_length(std::vector& segment_inf); -static void fix_empty_coordinates(vtr::NdMatrix& delta_delays); -static void fix_uninitialized_coordinates(vtr::NdMatrix& delta_delays); +static void fix_empty_coordinates(vtr::NdMatrix& delta_delays); +static void fix_uninitialized_coordinates(vtr::NdMatrix& delta_delays); -static float find_neightboring_average(vtr::NdMatrix& matrix, t_physical_tile_loc tile_loc, int max_distance); +static float find_neightboring_average(vtr::NdMatrix& matrix, + int from_layer, + t_physical_tile_loc to_tile_loc, + int max_distance); /******* Globally Accessible Functions **********/ @@ -368,7 +375,8 @@ static t_chan_width setup_chan_width(const t_router_opts& router_opts, static float route_connection_delay( RouterDelayProfiler& route_profiler, - int layer_num, + int from_layer_num, + int to_layer_num, int source_x, int source_y, int sink_x, @@ -384,18 +392,18 @@ static float route_connection_delay( bool successfully_routed = false; //Get the rr nodes to route between - auto best_driver_ptcs = get_best_classes(DRIVER, device_ctx.grid.get_physical_type({source_x, source_y, layer_num})); - auto best_sink_ptcs = get_best_classes(RECEIVER, device_ctx.grid.get_physical_type({sink_x, sink_y, layer_num})); + auto best_driver_ptcs = get_best_classes(DRIVER, device_ctx.grid.get_physical_type({source_x, source_y, from_layer_num})); + auto best_sink_ptcs = get_best_classes(RECEIVER, device_ctx.grid.get_physical_type({sink_x, sink_y, to_layer_num})); for (int driver_ptc : best_driver_ptcs) { VTR_ASSERT(driver_ptc != OPEN); - RRNodeId source_rr_node = device_ctx.rr_graph.node_lookup().find_node(layer_num, source_x, source_y, SOURCE, driver_ptc); + RRNodeId source_rr_node = device_ctx.rr_graph.node_lookup().find_node(from_layer_num, source_x, source_y, SOURCE, driver_ptc); VTR_ASSERT(source_rr_node != RRNodeId::INVALID()); for (int sink_ptc : best_sink_ptcs) { VTR_ASSERT(sink_ptc != OPEN); - RRNodeId sink_rr_node = device_ctx.rr_graph.node_lookup().find_node(layer_num, sink_x, sink_y, SINK, sink_ptc); + RRNodeId sink_rr_node = device_ctx.rr_graph.node_lookup().find_node(to_layer_num, sink_x, sink_y, SINK, sink_ptc); if (sink_rr_node == RRNodeId::INVALID()) continue; @@ -409,8 +417,7 @@ static float route_connection_delay( successfully_routed = route_profiler.calculate_delay( source_rr_node, sink_rr_node, router_opts, - &net_delay_value, - layer_num); + &net_delay_value); } if (successfully_routed) break; @@ -420,7 +427,7 @@ static float route_connection_delay( if (!successfully_routed) { VTR_LOG_WARN("Unable to route between blocks at (%d,%d,%d) and (%d,%d,%d) to characterize delay (setting to %g)\n", - layer_num, source_x, source_y, layer_num, sink_x, sink_y, net_delay_value); + source_x, source_y, from_layer_num, sink_x, sink_y, to_layer_num, net_delay_value); } return (net_delay_value); @@ -443,7 +450,8 @@ static void add_delay_to_matrix( static void generic_compute_matrix_dijkstra_expansion( RouterDelayProfiler& /*route_profiler*/, vtr::Matrix>& matrix, - int layer_num, + int from_layer_num, + int to_layer_num, int source_x, int source_y, int start_x, @@ -456,7 +464,7 @@ static void generic_compute_matrix_dijkstra_expansion( bool is_flat) { auto& device_ctx = g_vpr_ctx.device(); - t_physical_tile_type_ptr src_type = device_ctx.grid.get_physical_type({source_x, source_y, layer_num}); + t_physical_tile_type_ptr src_type = device_ctx.grid.get_physical_type({source_x, source_y, from_layer_num}); bool is_allowed_type = allowed_types.empty() || allowed_types.find(src_type->name) != allowed_types.end(); if (src_type == device_ctx.EMPTY_PHYSICAL_TILE_TYPE || !is_allowed_type) { for (int sink_x = start_x; sink_x <= end_x; sink_x++) { @@ -483,10 +491,10 @@ static void generic_compute_matrix_dijkstra_expansion( vtr::Matrix found_matrix({matrix.dim_size(0), matrix.dim_size(1)}, false); - auto best_driver_ptcs = get_best_classes(DRIVER, device_ctx.grid.get_physical_type({source_x, source_y, layer_num})); + auto best_driver_ptcs = get_best_classes(DRIVER, device_ctx.grid.get_physical_type({source_x, source_y, from_layer_num})); for (int driver_ptc : best_driver_ptcs) { VTR_ASSERT(driver_ptc != OPEN); - RRNodeId source_rr_node = device_ctx.rr_graph.node_lookup().find_node(layer_num, source_x, source_y, SOURCE, driver_ptc); + RRNodeId source_rr_node = device_ctx.rr_graph.node_lookup().find_node(from_layer_num, source_x, source_y, SOURCE, driver_ptc); VTR_ASSERT(source_rr_node != RRNodeId::INVALID()); auto delays = calculate_all_path_delays_from_rr_node(source_rr_node, router_opts, is_flat); @@ -501,7 +509,7 @@ static void generic_compute_matrix_dijkstra_expansion( continue; } - t_physical_tile_type_ptr sink_type = device_ctx.grid.get_physical_type({sink_x, sink_y, layer_num}); + t_physical_tile_type_ptr sink_type = device_ctx.grid.get_physical_type({sink_x, sink_y, to_layer_num}); if (sink_type == device_ctx.EMPTY_PHYSICAL_TILE_TYPE) { if (matrix[delta_x][delta_y].empty()) { //Only set empty target if we don't already have a valid delta delay @@ -517,10 +525,10 @@ static void generic_compute_matrix_dijkstra_expansion( } } else { bool found_a_sink = false; - auto best_sink_ptcs = get_best_classes(RECEIVER, device_ctx.grid.get_physical_type({sink_x, sink_y, layer_num})); + auto best_sink_ptcs = get_best_classes(RECEIVER, device_ctx.grid.get_physical_type({sink_x, sink_y, to_layer_num})); for (int sink_ptc : best_sink_ptcs) { VTR_ASSERT(sink_ptc != OPEN); - RRNodeId sink_rr_node = device_ctx.rr_graph.node_lookup().find_node(layer_num, sink_x, sink_y, SINK, sink_ptc); + RRNodeId sink_rr_node = device_ctx.rr_graph.node_lookup().find_node(to_layer_num, sink_x, sink_y, SINK, sink_ptc); if (sink_rr_node == RRNodeId::INVALID()) continue; @@ -568,8 +576,14 @@ static void generic_compute_matrix_dijkstra_expansion( int delta_y = abs(sink_y - source_y); if (!found_matrix[delta_x][delta_y]) { add_delay_to_matrix(&matrix, delta_x, delta_y, IMPOSSIBLE_DELTA); - VTR_LOG_WARN("Unable to route between blocks at (%d,%d) and (%d,%d) to characterize delay (setting to %g)\n", - source_x, source_y, sink_x, sink_y, IMPOSSIBLE_DELTA); + VTR_LOG_WARN("Unable to route between blocks at (%d,%d,%d) and (%d,%d,%d) to characterize delay (setting to %g)\n", + source_x, + source_y, + from_layer_num, + sink_x, + sink_y, + to_layer_num, + IMPOSSIBLE_DELTA); } } } @@ -578,7 +592,8 @@ static void generic_compute_matrix_dijkstra_expansion( static void generic_compute_matrix_iterative_astar( RouterDelayProfiler& route_profiler, vtr::Matrix>& matrix, - int layer_num, + int from_layer_num, + int to_layer_num, int source_x, int source_y, int start_x, @@ -601,8 +616,8 @@ static void generic_compute_matrix_iterative_astar( delta_x = abs(sink_x - source_x); delta_y = abs(sink_y - source_y); - t_physical_tile_type_ptr src_type = device_ctx.grid.get_physical_type({source_x, source_y, layer_num}); - t_physical_tile_type_ptr sink_type = device_ctx.grid.get_physical_type({sink_x, sink_y, layer_num}); + t_physical_tile_type_ptr src_type = device_ctx.grid.get_physical_type({source_x, source_y, from_layer_num}); + t_physical_tile_type_ptr sink_type = device_ctx.grid.get_physical_type({sink_x, sink_y, to_layer_num}); bool src_or_target_empty = (src_type == device_ctx.EMPTY_PHYSICAL_TILE_TYPE || sink_type == device_ctx.EMPTY_PHYSICAL_TILE_TYPE); @@ -624,7 +639,15 @@ static void generic_compute_matrix_iterative_astar( } else { //Valid start/end - float delay = route_connection_delay(route_profiler, layer_num, source_x, source_y, sink_x, sink_y, router_opts, measure_directconnect); + float delay = route_connection_delay(route_profiler, + from_layer_num, + to_layer_num, + source_x, + source_y, + sink_x, + sink_y, + router_opts, + measure_directconnect); #ifdef VERBOSE VTR_LOG("Computed delay: %12g delta: %d,%d (src: %d,%d sink: %d,%d)\n", @@ -645,7 +668,7 @@ static void generic_compute_matrix_iterative_astar( } } -static vtr::NdMatrix compute_delta_delays( +static vtr::NdMatrix compute_delta_delays( RouterDelayProfiler& route_profiler, const t_placer_opts& placer_opts, const t_router_opts& router_opts, @@ -659,195 +682,196 @@ static vtr::NdMatrix compute_delta_delays( auto& device_ctx = g_vpr_ctx.device(); auto& grid = device_ctx.grid; - vtr::NdMatrix delta_delays({static_cast(grid.get_num_layers()), grid.width(), grid.height()}); + vtr::NdMatrix delta_delays({static_cast(grid.get_num_layers()), static_cast(grid.get_num_layers()), grid.width(), grid.height()}); - for (int layer_num = 0; layer_num < grid.get_num_layers(); layer_num++) { - vtr::Matrix> sampled_delta_delays({grid.width(), grid.height()}); + for (int from_layer_num = 0; from_layer_num < grid.get_num_layers(); from_layer_num++) { + for (int to_layer_num = 0; to_layer_num < grid.get_num_layers(); to_layer_num++) { + vtr::NdMatrix, 2> sampled_delta_delays({grid.width(), grid.height()}); - size_t mid_x = vtr::nint(grid.width() / 2); - size_t mid_y = vtr::nint(grid.height() / 2); + size_t mid_x = vtr::nint(grid.width() / 2); + size_t mid_y = vtr::nint(grid.height() / 2); - size_t low_x = std::min(longest_length, mid_x); - size_t low_y = std::min(longest_length, mid_y); - size_t high_x = mid_x; - size_t high_y = mid_y; - if (longest_length <= grid.width()) { - high_x = std::max(grid.width() - longest_length, mid_x); - } - if (longest_length <= grid.height()) { - high_y = std::max(grid.height() - longest_length, mid_y); - } + size_t low_x = std::min(longest_length, mid_x); + size_t low_y = std::min(longest_length, mid_y); + size_t high_x = mid_x; + size_t high_y = mid_y; + if (longest_length <= grid.width()) { + high_x = std::max(grid.width() - longest_length, mid_x); + } + if (longest_length <= grid.height()) { + high_y = std::max(grid.height() - longest_length, mid_y); + } - std::set allowed_types; - if (!placer_opts.allowed_tiles_for_delay_model.empty()) { - auto allowed_types_vector = vtr::split(placer_opts.allowed_tiles_for_delay_model, ","); - for (const auto& type : allowed_types_vector) { - allowed_types.insert(type); + std::set allowed_types; + if (!placer_opts.allowed_tiles_for_delay_model.empty()) { + auto allowed_types_vector = vtr::split(placer_opts.allowed_tiles_for_delay_model, ","); + for (const auto& type : allowed_types_vector) { + allowed_types.insert(type); + } } - } - // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - // + | | + - // + A | B | C + - // + | | + - // +-----------------\-----------------------.---------------+ - // + | | + - // + | | + - // + | | + - // + | | + - // + D | E | F + - // + | | + - // + | | + - // + | | + - // + | | + - // +-----------------*-----------------------/---------------+ - // + | | + - // + G | H | I + - // + | | + - // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - // - // * = (low_x, low_y) - // . = (high_x, high_y) - // / = (high_x, low_y) - // \ = (low_x, high_y) - // + = device edge - - //Find the lowest y location on the left edge with a non-empty block - int y = 0; - int x = 0; - t_physical_tile_type_ptr src_type = nullptr; - for (x = 0; x < (int)grid.width(); ++x) { - for (y = 0; y < (int)grid.height(); ++y) { - auto type = grid.get_physical_type({x, y, layer_num}); + // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // + | | + + // + A | B | C + + // + | | + + // +-----------------\-----------------------.---------------+ + // + | | + + // + | | + + // + | | + + // + | | + + // + D | E | F + + // + | | + + // + | | + + // + | | + + // + | | + + // +-----------------*-----------------------/---------------+ + // + | | + + // + G | H | I + + // + | | + + // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // + // * = (low_x, low_y) + // . = (high_x, high_y) + // / = (high_x, low_y) + // \ = (low_x, high_y) + // + = device edge + + //Find the lowest y location on the left edge with a non-empty block + int y = 0; + int x = 0; + t_physical_tile_type_ptr src_type = nullptr; + for (x = 0; x < (int)grid.width(); ++x) { + for (y = 0; y < (int)grid.height(); ++y) { + auto type = grid.get_physical_type({x, y, from_layer_num}); - if (type != device_ctx.EMPTY_PHYSICAL_TILE_TYPE) { - if (!allowed_types.empty() && allowed_types.find(std::string(type->name)) == allowed_types.end()) { - continue; + if (type != device_ctx.EMPTY_PHYSICAL_TILE_TYPE) { + if (!allowed_types.empty() && allowed_types.find(std::string(type->name)) == allowed_types.end()) { + continue; + } + src_type = type; + break; } - src_type = type; + } + if (src_type) { break; } } - if (src_type) { - break; + VTR_ASSERT(src_type != nullptr); + + t_compute_delta_delay_matrix generic_compute_matrix; + switch (placer_opts.place_delta_delay_matrix_calculation_method) { + case e_place_delta_delay_algorithm::ASTAR_ROUTE: + generic_compute_matrix = generic_compute_matrix_iterative_astar; + break; + case e_place_delta_delay_algorithm::DIJKSTRA_EXPANSION: + generic_compute_matrix = generic_compute_matrix_dijkstra_expansion; + break; + default: + VPR_FATAL_ERROR(VPR_ERROR_PLACE, "Unknown place_delta_delay_matrix_calculation_method %d", placer_opts.place_delta_delay_matrix_calculation_method); } - } - VTR_ASSERT(src_type != nullptr); - - t_compute_delta_delay_matrix generic_compute_matrix; - switch (placer_opts.place_delta_delay_matrix_calculation_method) { - case e_place_delta_delay_algorithm::ASTAR_ROUTE: - generic_compute_matrix = generic_compute_matrix_iterative_astar; - break; - case e_place_delta_delay_algorithm::DIJKSTRA_EXPANSION: - generic_compute_matrix = generic_compute_matrix_dijkstra_expansion; - break; - default: - VPR_FATAL_ERROR(VPR_ERROR_PLACE, "Unknown place_delta_delay_matrix_calculation_method %d", placer_opts.place_delta_delay_matrix_calculation_method); - } #ifdef VERBOSE - VTR_LOG("Computing from lower left edge (%d,%d):\n", x, y); + VTR_LOG("Computing from lower left edge (%d,%d):\n", x, y); #endif - generic_compute_matrix(route_profiler, sampled_delta_delays, - layer_num, - x, y, - x, y, - grid.width() - 1, grid.height() - 1, - router_opts, - measure_directconnect, allowed_types, - is_flat); - - //Find the lowest x location on the bottom edge with a non-empty block - src_type = nullptr; - for (y = 0; y < (int)grid.height(); ++y) { - for (x = 0; x < (int)grid.width(); ++x) { - auto type = grid.get_physical_type({x, y, layer_num}); + generic_compute_matrix(route_profiler, sampled_delta_delays, + from_layer_num, to_layer_num, + x, y, + x, y, + grid.width() - 1, grid.height() - 1, + router_opts, + measure_directconnect, allowed_types, + is_flat); + + //Find the lowest x location on the bottom edge with a non-empty block + src_type = nullptr; + for (y = 0; y < (int)grid.height(); ++y) { + for (x = 0; x < (int)grid.width(); ++x) { + auto type = grid.get_physical_type({x, y, from_layer_num}); - if (type != device_ctx.EMPTY_PHYSICAL_TILE_TYPE) { - if (!allowed_types.empty() && allowed_types.find(std::string(type->name)) == allowed_types.end()) { - continue; + if (type != device_ctx.EMPTY_PHYSICAL_TILE_TYPE) { + if (!allowed_types.empty() && allowed_types.find(std::string(type->name)) == allowed_types.end()) { + continue; + } + src_type = type; + break; } - src_type = type; + } + if (src_type) { break; } } - if (src_type) { - break; - } - } - VTR_ASSERT(src_type != nullptr); + VTR_ASSERT(src_type != nullptr); #ifdef VERBOSE - VTR_LOG("Computing from left bottom edge (%d,%d):\n", x, y); + VTR_LOG("Computing from left bottom edge (%d,%d):\n", x, y); #endif - generic_compute_matrix(route_profiler, sampled_delta_delays, - layer_num, - x, y, - x, y, - grid.width() - 1, grid.height() - 1, - router_opts, - measure_directconnect, allowed_types, - is_flat); - - //Since the other delta delay values may have suffered from edge effects, - //we recalculate deltas within regions B, C, E, F + generic_compute_matrix(route_profiler, sampled_delta_delays, + from_layer_num, to_layer_num, + x, y, + x, y, + grid.width() - 1, grid.height() - 1, + router_opts, + measure_directconnect, allowed_types, + is_flat); + + //Since the other delta delay values may have suffered from edge effects, + //we recalculate deltas within regions B, C, E, F #ifdef VERBOSE - VTR_LOG("Computing from low/low:\n"); + VTR_LOG("Computing from low/low:\n"); #endif - generic_compute_matrix(route_profiler, sampled_delta_delays, - layer_num, - low_x, low_y, - low_x, low_y, - grid.width() - 1, grid.height() - 1, - router_opts, - measure_directconnect, allowed_types, - is_flat); - - //Since the other delta delay values may have suffered from edge effects, - //we recalculate deltas within regions D, E, G, H + generic_compute_matrix(route_profiler, sampled_delta_delays, + from_layer_num, to_layer_num, + low_x, low_y, + low_x, low_y, + grid.width() - 1, grid.height() - 1, + router_opts, + measure_directconnect, allowed_types, + is_flat); + + //Since the other delta delay values may have suffered from edge effects, + //we recalculate deltas within regions D, E, G, H #ifdef VERBOSE - VTR_LOG("Computing from high/high:\n"); + VTR_LOG("Computing from high/high:\n"); #endif - generic_compute_matrix(route_profiler, sampled_delta_delays, - layer_num, - high_x, high_y, - 0, 0, - high_x, high_y, - router_opts, - measure_directconnect, allowed_types, - is_flat); - - //Since the other delta delay values may have suffered from edge effects, - //we recalculate deltas within regions A, B, D, E + generic_compute_matrix(route_profiler, sampled_delta_delays, + from_layer_num, to_layer_num, + high_x, high_y, + 0, 0, + high_x, high_y, + router_opts, + measure_directconnect, allowed_types, + is_flat); + + //Since the other delta delay values may have suffered from edge effects, + //we recalculate deltas within regions A, B, D, E #ifdef VERBOSE - VTR_LOG("Computing from high/low:\n"); + VTR_LOG("Computing from high/low:\n"); #endif - generic_compute_matrix(route_profiler, sampled_delta_delays, - layer_num, - high_x, low_y, - 0, low_y, - high_x, grid.height() - 1, - router_opts, - measure_directconnect, allowed_types, - is_flat); - - //Since the other delta delay values may have suffered from edge effects, - //we recalculate deltas within regions E, F, H, I + generic_compute_matrix(route_profiler, sampled_delta_delays, + from_layer_num, to_layer_num, + high_x, low_y, + 0, low_y, + high_x, grid.height() - 1, + router_opts, + measure_directconnect, allowed_types, + is_flat); + + //Since the other delta delay values may have suffered from edge effects, + //we recalculate deltas within regions E, F, H, I #ifdef VERBOSE - VTR_LOG("Computing from low/high:\n"); + VTR_LOG("Computing from low/high:\n"); #endif - generic_compute_matrix(route_profiler, sampled_delta_delays, - layer_num, - low_x, high_y, - low_x, 0, - grid.width() - 1, high_y, - router_opts, - measure_directconnect, allowed_types, - is_flat); - - for (size_t dx = 0; dx < sampled_delta_delays.dim_size(0); ++dx) { - for (size_t dy = 0; dy < sampled_delta_delays.dim_size(1); ++dy) { - delta_delays[layer_num][dx][dy] = delay_reduce(sampled_delta_delays[dx][dy], placer_opts.delay_model_reducer); + generic_compute_matrix(route_profiler, sampled_delta_delays, + from_layer_num, to_layer_num, + low_x, high_y, + low_x, 0, + grid.width() - 1, high_y, + router_opts, + measure_directconnect, allowed_types, + is_flat); + for (size_t dx = 0; dx < sampled_delta_delays.dim_size(0); ++dx) { + for (size_t dy = 0; dy < sampled_delta_delays.dim_size(1); ++dy) { + delta_delays[from_layer_num][to_layer_num][dx][dy] = delay_reduce(sampled_delta_delays[dx][dy], placer_opts.delay_model_reducer); + } } } } @@ -895,19 +919,20 @@ float delay_reduce(std::vector& delays, e_reducer reducer) { * we return IMPOSSIBLE_DELTA. */ static float find_neightboring_average( - vtr::NdMatrix& matrix, - t_physical_tile_loc tile_loc, + vtr::NdMatrix& matrix, + int from_layer, + t_physical_tile_loc to_tile_loc, int max_distance) { float sum = 0; int counter = 0; - int endx = matrix.end_index(1); - int endy = matrix.end_index(2); + int endx = matrix.end_index(2); + int endy = matrix.end_index(3); int delx, dely; - int x = tile_loc.x; - int y = tile_loc.y; - int layer_num = tile_loc.layer_num; + int x = to_tile_loc.x; + int y = to_tile_loc.y; + int to_layer = to_tile_loc.layer_num; for (int distance = 1; distance <= max_distance; ++distance) { for (delx = x - distance; delx <= x + distance; delx++) { @@ -922,11 +947,11 @@ static float find_neightboring_average( continue; } - if (matrix[layer_num][delx][dely] == EMPTY_DELTA || matrix[layer_num][delx][dely] == IMPOSSIBLE_DELTA) { + if (matrix[from_layer][to_layer][delx][dely] == EMPTY_DELTA || matrix[from_layer][to_layer][delx][dely] == IMPOSSIBLE_DELTA) { continue; } counter++; - sum += matrix[layer_num][delx][dely]; + sum += matrix[from_layer][to_layer][delx][dely]; } } if (counter != 0) { @@ -937,7 +962,7 @@ static float find_neightboring_average( return IMPOSSIBLE_DELTA; } -static void fix_empty_coordinates(vtr::NdMatrix& delta_delays) { +static void fix_empty_coordinates(vtr::NdMatrix& delta_delays) { // Set any empty delta's to the average of it's neighbours // // Empty coordinates may occur if the sampling location happens to not have @@ -945,32 +970,40 @@ static void fix_empty_coordinates(vtr::NdMatrix& delta_delays) { // would return a result, so we fill in the empty holes with a small // neighbour average. constexpr int kMaxAverageDistance = 2; - for (int layer_num = 0; layer_num < (int)delta_delays.dim_size(0); ++layer_num) { - for (int delta_x = 0; delta_x < (int)delta_delays.dim_size(1); ++delta_x) { - for (int delta_y = 0; delta_y < (int)delta_delays.dim_size(2); ++delta_y) { - if (delta_delays[layer_num][delta_x][delta_y] == EMPTY_DELTA) { - delta_delays[layer_num][delta_x][delta_y] = find_neightboring_average(delta_delays, {delta_x, delta_y, layer_num}, kMaxAverageDistance); + for (int from_layer = 0; from_layer < (int)delta_delays.dim_size(0); ++from_layer) { + for (int to_layer = 0; to_layer < (int)delta_delays.dim_size(1); ++to_layer) { + for (int delta_x = 0; delta_x < (int)delta_delays.dim_size(2); ++delta_x) { + for (int delta_y = 0; delta_y < (int)delta_delays.dim_size(3); ++delta_y) { + if (delta_delays[from_layer][to_layer][delta_x][delta_y] == EMPTY_DELTA) { + delta_delays[from_layer][to_layer][delta_x][delta_y] = + find_neightboring_average(delta_delays, + from_layer, + {delta_x, delta_y, to_layer}, + kMaxAverageDistance); + } } } } } } -static void fix_uninitialized_coordinates(vtr::NdMatrix& delta_delays) { +static void fix_uninitialized_coordinates(vtr::NdMatrix& delta_delays) { // Set any empty delta's to the average of it's neighbours - for (size_t layer_num = 0; layer_num < delta_delays.dim_size(0); ++layer_num) { - for (size_t delta_x = 0; delta_x < delta_delays.dim_size(1); ++delta_x) { - for (size_t delta_y = 0; delta_y < delta_delays.dim_size(2); ++delta_y) { - if (delta_delays[layer_num][delta_x][delta_y] == UNINITIALIZED_DELTA) { - delta_delays[layer_num][delta_x][delta_y] = IMPOSSIBLE_DELTA; + for (size_t from_layer_num = 0; from_layer_num < delta_delays.dim_size(0); ++from_layer_num) { + for (size_t to_layer_num = 0; to_layer_num < delta_delays.dim_size(1); ++to_layer_num) { + for (size_t delta_x = 0; delta_x < delta_delays.dim_size(2); ++delta_x) { + for (size_t delta_y = 0; delta_y < delta_delays.dim_size(3); ++delta_y) { + if (delta_delays[from_layer_num][to_layer_num][delta_x][delta_y] == UNINITIALIZED_DELTA) { + delta_delays[from_layer_num][to_layer_num][delta_x][delta_y] = IMPOSSIBLE_DELTA; + } } } } } } -static void fill_impossible_coordinates(vtr::NdMatrix& delta_delays) { +static void fill_impossible_coordinates(vtr::NdMatrix& delta_delays) { // Set any impossible delta's to the average of it's neighbours // // Impossible coordinates may occur if an IPIN cannot be reached from the @@ -983,19 +1016,21 @@ static void fill_impossible_coordinates(vtr::NdMatrix& delta_delays) { // filling these gaps. It is more important to have a poor predication, // than a invalid value and causing a slack assertion. constexpr int kMaxAverageDistance = 5; - for (int layer_num = 0; layer_num < (int)delta_delays.dim_size(0); ++layer_num) { - for (int delta_x = 0; delta_x < (int)delta_delays.dim_size(1); ++delta_x) { - for (int delta_y = 0; delta_y < (int)delta_delays.dim_size(2); ++delta_y) { - if (delta_delays[layer_num][delta_x][delta_y] == IMPOSSIBLE_DELTA) { - delta_delays[layer_num][delta_x][delta_y] = find_neightboring_average( - delta_delays, {delta_x, delta_y, layer_num}, kMaxAverageDistance); + for (int from_layer_num = 0; from_layer_num < (int)delta_delays.dim_size(0); ++from_layer_num) { + for (int to_layer_num = 0; to_layer_num < (int)delta_delays.dim_size(1); ++to_layer_num) { + for (int delta_x = 0; delta_x < (int)delta_delays.dim_size(2); ++delta_x) { + for (int delta_y = 0; delta_y < (int)delta_delays.dim_size(3); ++delta_y) { + if (delta_delays[from_layer_num][to_layer_num][delta_x][delta_y] == IMPOSSIBLE_DELTA) { + delta_delays[from_layer_num][to_layer_num][delta_x][delta_y] = find_neightboring_average( + delta_delays, from_layer_num, {delta_x, delta_y, to_layer_num}, kMaxAverageDistance); + } } } } } } -static vtr::NdMatrix compute_delta_delay_model( +static vtr::NdMatrix compute_delta_delay_model( RouterDelayProfiler& route_profiler, const t_placer_opts& placer_opts, const t_router_opts& router_opts, @@ -1003,7 +1038,7 @@ static vtr::NdMatrix compute_delta_delay_model( int longest_length, bool is_flat) { vtr::ScopedStartFinishTimer timer("Computing delta delays"); - vtr::NdMatrix delta_delays = compute_delta_delays(route_profiler, + vtr::NdMatrix delta_delays = compute_delta_delays(route_profiler, placer_opts, router_opts, measure_directconnect, @@ -1089,7 +1124,7 @@ static bool find_direct_connect_sample_locations(const t_direct_inf* direct, //Check that the from pin exists at this from location //(with multi-width/height blocks pins may not exist at all locations) bool from_pin_found = false; - if (direct->from_side != NUM_SIDES) { + if (direct->from_side != NUM_2D_SIDES) { RRNodeId from_pin_rr = node_lookup.find_node(layer_num, x, y, OPIN, from_pin, direct->from_side); from_pin_found = from_pin_rr.is_valid(); } else { @@ -1105,7 +1140,7 @@ static bool find_direct_connect_sample_locations(const t_direct_inf* direct, //Check that the from pin exists at this from location //(with multi-width/height blocks pins may not exist at all locations) bool to_pin_found = false; - if (direct->to_side != NUM_SIDES) { + if (direct->to_side != NUM_2D_SIDES) { RRNodeId to_pin_rr = node_lookup.find_node(layer_num, to_x, to_y, IPIN, to_pin, direct->to_side); to_pin_found = (to_pin_rr != RRNodeId::INVALID()); } else { @@ -1164,19 +1199,21 @@ static bool find_direct_connect_sample_locations(const t_direct_inf* direct, return true; } -static bool verify_delta_delays(const vtr::NdMatrix& delta_delays) { +static bool verify_delta_delays(const vtr::NdMatrix& delta_delays) { auto& device_ctx = g_vpr_ctx.device(); auto& grid = device_ctx.grid; - for (int layer_num = 0; layer_num < grid.get_num_layers(); ++layer_num) { - for (size_t x = 0; x < grid.width(); ++x) { - for (size_t y = 0; y < grid.height(); ++y) { - float delta_delay = delta_delays[layer_num][x][y]; + for (int from_layer_num = 0; from_layer_num < grid.get_num_layers(); ++from_layer_num) { + for (int to_layer_num = 0; to_layer_num < grid.get_num_layers(); ++to_layer_num) { + for (size_t x = 0; x < grid.width(); ++x) { + for (size_t y = 0; y < grid.height(); ++y) { + float delta_delay = delta_delays[from_layer_num][to_layer_num][x][y]; - if (delta_delay < 0.) { - VPR_ERROR(VPR_ERROR_PLACE, - "Found invaild negative delay %g for delta (%d,%d)", - delta_delay, x, y); + if (delta_delay < 0.) { + VPR_ERROR(VPR_ERROR_PLACE, + "Found invaild negative delay %g for delta [%d,%d,%d,%d]", + delta_delay, from_layer_num, to_layer_num, x, y); + } } } } @@ -1246,7 +1283,7 @@ void OverrideDelayModel::compute_override_delay_model( if (sampled_rr_pairs.count({src_rr, sink_rr})) continue; float direct_connect_delay = std::numeric_limits::quiet_NaN(); - bool found_routing_path = route_profiler.calculate_delay(src_rr, sink_rr, router_opts2, &direct_connect_delay, OPEN); + bool found_routing_path = route_profiler.calculate_delay(src_rr, sink_rr, router_opts2, &direct_connect_delay); if (found_routing_path) { set_delay_override(from_type->index, from_pin_class, to_type->index, to_pin_class, direct->x_offset, direct->y_offset, direct_connect_delay); diff --git a/vpr/src/route/build_switchblocks.cpp b/vpr/src/route/build_switchblocks.cpp index 3e9f8386312..22714b7b224 100644 --- a/vpr/src/route/build_switchblocks.cpp +++ b/vpr/src/route/build_switchblocks.cpp @@ -201,6 +201,7 @@ static void count_wire_type_sizes(const t_chan_seg_details* channel, int nodes_p static void compute_wire_connections( int x_coord, int y_coord, + int layer_coord, enum e_side from_side, enum e_side to_side, const t_chan_details& chan_details_x, @@ -223,8 +224,10 @@ static void compute_wireconn_connections( Switchblock_Lookup sb_conn, int from_x, int from_y, + int from_layer, int to_x, int to_y, + int to_layer, t_rr_type from_chan_type, t_rr_type to_chan_type, const t_wire_type_sizes* wire_type_sizes_x, @@ -237,7 +240,24 @@ static void compute_wireconn_connections( static int evaluate_num_conns_formula(t_wireconn_scratchpad* scratchpad, std::string num_conns_formula, int from_wire_count, int to_wire_count); -/* returns the wire indices belonging to the types in 'wire_type_vec' and switchpoints in 'points' at the given channel segment */ +/** + * + * @brief calculates the wire indices belonging to the types in types in 'wire_type_sizes' and switchpoints in 'points' at the given channel segment + * + * @param grid device grid + * @param chan_details channel segment details (length, start and end points, ...) + * @param chan_type channel type (CHANX/CHANY) + * @param x the wire x-coordinate + * @param y the wire y-coordinate + * @param side switch block side (top/right/bottom/left/above/under) + * @param wire_switchpoints_vec valid switch points at the given channel segment + * @param wire_type_sizes valid wire types + * @param is_dest whether wires are source or destination within a switch block connection + * @param order switchpoint order (fixed, shuffled) specified in the architecture file + * @param rand_state used to randomly shuffle switchpoint if required (shuffled order) + * @param output_wires collected wire indices that matches the specified types and switchpoints + * + */ static void get_switchpoint_wires( const DeviceGrid& grid, const t_chan_seg_details* chan_details, @@ -253,10 +273,38 @@ static void get_switchpoint_wires( std::vector* output_wires, std::vector* scratch_wires); -static const t_chan_details& index_into_correct_chan(int tile_x, int tile_y, enum e_side side, const t_chan_details& chan_details_x, const t_chan_details& chan_details_y, int* chan_x, int* chan_y, t_rr_type* chan_type); +/** + * @brief finds the correct channel (x or y), and the coordinates to index into it based on the + * specified tile coordinates (x,y,layer) and the switch block side. + * + * @param tile_x x-coordinate of the tile + * @param tile_y y-coordinate of the tile + * @param tile_layer layer-coordinate of the tile + * @param src_side switch block source side + * @param dest_side swtich block destination side + * @param chan_details_x x-channel segment details (length, start and end points, ...) + * @param chan_details_y x-channel segment details (length, start and end points, ...) + * @param chan_x x-coordinate of the channel + * @param chan_y y-coordinate of the channel + * @param chan_layer layer_coordinate of the channel + * @param chan_type chan type that the function index into + * + * @return returns the type of channel that we are indexing into (ie, CHANX or CHANY) and channel coordinates and type + */ +static const t_chan_details& index_into_correct_chan(int tile_x, int tile_y, int tile_layer, enum e_side src_side, enum e_side dest_side, const t_chan_details& chan_details_x, const t_chan_details& chan_details_y, int& chan_x, int& chan_y, int& chan_layer, t_rr_type& chan_type); -/* checks whether the specified coordinates are out of bounds */ -static bool coords_out_of_bounds(const DeviceGrid& grid, int x_coord, int y_coord, e_rr_type chan_type); +/** + * @brief check whether a specific track location is valid within the device grid + * + * @param grid device grid + * @param x_coord x-coordinate of the location + * @param y_coord y-coordinate of the location + * @param layer_coord layer-coordinate of the location + * @param chan_type track channel type (CHANX or CHANY), required since device perimeter does not have certain channels + * + * @return true if the track location is outside the device grid, false otherwise. + */ +static bool coords_out_of_bounds(const DeviceGrid& grid, int x_coord, int y_coord, int layer_coord, e_rr_type chan_type); /* returns the subsegment number of the specified wire at seg_coord*/ static int get_wire_subsegment_num(const DeviceGrid& grid, e_rr_type chan_type, const t_chan_seg_details& wire_details, int seg_coord); @@ -267,27 +315,80 @@ int get_wire_segment_length(const DeviceGrid& grid, e_rr_type chan_type, const t * of seg_coord, and connection to the sb_side of the switchblock */ static int get_switchpoint_of_wire(const DeviceGrid& grid, e_rr_type chan_type, const t_chan_seg_details& wire_details, int seg_coord, e_side sb_side); -/* returns true if the coordinates x/y do not correspond to the location specified by 'location' */ -static bool sb_not_here(const DeviceGrid& grid, int x, int y, e_sb_location location); +/** + * @brief check whether a switch block exists in a specified coordinate within the device grid + * + * @param grid device grid + * @param inter_cluster_rr used to check whether inter-cluster programmable routing resources exist in the current layer + * @param x x-coordinate of the location + * @param y y-coordinate of the location + * @param layer layer-coordinate of the location + * @param location location of the switch block according to custom switch block description in the architecture file + * + * @return true if a switch block exists at the specified location, false otherwise. + */ +static bool sb_not_here(const DeviceGrid& grid, const std::vector& inter_cluster_rr, int x, int y, int layer, e_sb_location location); + +/** + * @brief check whether specified coordinate is located at the device grid corner and a switch block exists there + * + * @param grid device grid + * @param inter_cluster_rr used to check whether inter-cluster programmable routing resources exist in the current layer + * @param x x-coordinate of the location + * @param y y-coordinate of the location + * @param layer layer-coordinate of the location + * + * @return true if the specified coordinate represents a corner location within the device grid and a switch block exists there, false otherwise. + */ +static bool is_corner_sb(const DeviceGrid& grid, const std::vector& inter_cluster_rr, int x, int y, int layer); -/* checks if the specified coordinates represent a corner of the FPGA */ -static bool is_corner(const DeviceGrid& grid, int x, int y); +/** + * @brief check whether specified coordinate is located at one of the perimeter device grid locations and a switch block exists there + * + * @param grid device grid + * @param inter_cluster_rr used to check whether inter-cluster programmable routing resources exist in the current layer + * @param x x-coordinate of the location + * @param y y-coordinate of the location + * @param layer layer-coordinate of the location + * + * @return true if the specified coordinate represents a perimeter location within the device grid and a switch block exists there, false otherwise. + */ +static bool is_perimeter_sb(const DeviceGrid& grid, const std::vector& inter_cluster_rr, int x, int y, int layer); -/* checks if the specified coordinates correspond to one of the perimeter switchblocks */ -static bool is_perimeter(const DeviceGrid& grid, int x, int y); +/** + * @brief check whether specified coordinate is located at core of the device grid (not perimeter) and a switch block exists there + * + * @param grid device grid + * @param inter_cluster_rr used to check whether inter-cluster programmable routing resources exist in the current layer + * @param x x-coordinate of the location + * @param y y-coordinate of the location + * @param layer layer-coordinate of the location + * + * @return true if the specified coordinate represents a core location within the device grid and a switch block exists there, false otherwise. + */ +static bool is_core_sb(const DeviceGrid& grid, const std::vector& inter_cluster_rr, int x, int y, int layer); -/* checks if the specified coordinates correspond to the core of the FPGA (i.e. not perimeter) */ -static bool is_core(const DeviceGrid& grid, int x, int y); +/** + * @brief check whether specified layer has inter-cluster programmable routing resources or not. + * + * @param grid device grid + * @param inter_cluster_rr inter-cluster programmable routing resources availability within different layers in multi-die FPGAs + * @param layer a valid layer index within the device grid, must be between [0..num_layer-1] + * + * @return true if the specified layer contain inter-cluster programmable routing resources, false otherwise. + */ +static bool is_prog_routing_avail(const DeviceGrid& grid, const std::vector& inter_cluster_rr, int layer); /* adjusts a negative destination wire index calculated from a permutation formula */ static int adjust_formula_result(int dest_wire, int src_W, int dest_W, int connection_ind); /************ Function Definitions ************/ -/* allocate and build the switchblock permutation map */ + t_sb_connection_map* alloc_and_load_switchblock_permutations(const t_chan_details& chan_details_x, const t_chan_details& chan_details_y, const DeviceGrid& grid, - std::vector switchblocks, + const std::vector& inter_cluster_rr, + const std::vector& switchblocks, t_chan_width* nodes_per_chan, e_directionality directionality, vtr::RandState& rand_state) { @@ -324,20 +425,23 @@ t_sb_connection_map* alloc_and_load_switchblock_permutations(const t_chan_detail if (directionality != sb.directionality) { VPR_FATAL_ERROR(VPR_ERROR_ARCH, "alloc_and_load_switchblock_connections: Switchblock %s does not match directionality of architecture\n", sb.name.c_str()); } - /* Iterate over the x,y coordinates spanning the FPGA. */ - for (size_t x_coord = 0; x_coord < grid.width(); x_coord++) { - for (size_t y_coord = 0; y_coord <= grid.height(); y_coord++) { - if (sb_not_here(grid, x_coord, y_coord, sb.location)) { - continue; - } - /* now we iterate over all the potential side1->side2 connections */ - for (e_side from_side : {TOP, RIGHT, BOTTOM, LEFT}) { - for (e_side to_side : {TOP, RIGHT, BOTTOM, LEFT}) { - /* Fill appropriate entry of the sb_conns map with vector specifying the wires - * the current wire will connect to */ - compute_wire_connections(x_coord, y_coord, from_side, to_side, - chan_details_x, chan_details_y, &sb, grid, - &wire_type_sizes_x, &wire_type_sizes_y, directionality, sb_conns, rand_state, &scratchpad); + /* Iterate over the x,y, layer coordinates spanning the FPGA, filling in all the switch blocks that exist */ + for (int layer_coord = 0; layer_coord < grid.get_num_layers(); layer_coord++) { + for (size_t x_coord = 0; x_coord < grid.width(); x_coord++) { + for (size_t y_coord = 0; y_coord <= grid.height(); y_coord++) { + if (sb_not_here(grid, inter_cluster_rr, x_coord, y_coord, layer_coord, sb.location)) { + continue; + } + /* now we iterate over all the potential side1->side2 connections */ + for (e_side from_side : TOTAL_3D_SIDES) { + for (e_side to_side : TOTAL_3D_SIDES) { + /* Fill appropriate entry of the sb_conns map with vector specifying the wires + * the current wire will connect to */ + compute_wire_connections(x_coord, y_coord, layer_coord, from_side, to_side, + chan_details_x, chan_details_y, &sb, grid, + &wire_type_sizes_x, &wire_type_sizes_y, directionality, sb_conns, + rand_state, &scratchpad); + } } } } @@ -361,8 +465,7 @@ void free_switchblock_permutations(t_sb_connection_map* sb_conns) { return; } -/* returns true if the coordinates x/y do not correspond to the location specified by 'location' */ -static bool sb_not_here(const DeviceGrid& grid, int x, int y, e_sb_location location) { +static bool sb_not_here(const DeviceGrid& grid, const std::vector& inter_cluster_rr, int x, int y, int layer, e_sb_location location) { bool sb_not_here = true; switch (location) { @@ -370,22 +473,22 @@ static bool sb_not_here(const DeviceGrid& grid, int x, int y, e_sb_location loca sb_not_here = false; break; case E_PERIMETER: - if (is_perimeter(grid, x, y)) { + if (is_perimeter_sb(grid, inter_cluster_rr, x, y, layer)) { sb_not_here = false; } break; case E_CORNER: - if (is_corner(grid, x, y)) { + if (is_corner_sb(grid, inter_cluster_rr, x, y, layer)) { sb_not_here = false; } break; case E_CORE: - if (is_core(grid, x, y)) { + if (is_core_sb(grid, inter_cluster_rr, x, y, layer)) { sb_not_here = false; } break; case E_FRINGE: - if (is_perimeter(grid, x, y) && !is_corner(grid, x, y)) { + if (is_perimeter_sb(grid, inter_cluster_rr, x, y, layer) && !is_corner_sb(grid, inter_cluster_rr, x, y, layer)) { sb_not_here = false; } break; @@ -396,8 +499,10 @@ static bool sb_not_here(const DeviceGrid& grid, int x, int y, e_sb_location loca return sb_not_here; } -/* checks if the specified coordinates represent a corner of the FPGA */ -static bool is_corner(const DeviceGrid& grid, int x, int y) { +static bool is_corner_sb(const DeviceGrid& grid, const std::vector& inter_cluster_rr, int x, int y, int layer) { + if (!is_prog_routing_avail(grid, inter_cluster_rr, layer)) { + return false; + } bool is_corner = false; if ((x == 0 && y == 0) || (x == 0 && y == int(grid.height()) - 2) || //-2 for no perim channels (x == int(grid.width()) - 2 && y == 0) || //-2 for no perim channels @@ -407,8 +512,10 @@ static bool is_corner(const DeviceGrid& grid, int x, int y) { return is_corner; } -/* checks if the specified coordinates correspond to one of the perimeter switchblocks */ -static bool is_perimeter(const DeviceGrid& grid, int x, int y) { +static bool is_perimeter_sb(const DeviceGrid& grid, const std::vector& inter_cluster_rr, int x, int y, int layer) { + if (!is_prog_routing_avail(grid, inter_cluster_rr, layer)) { + return false; + } bool is_perimeter = false; if (x == 0 || x == int(grid.width()) - 2 || y == 0 || y == int(grid.height()) - 2) { is_perimeter = true; @@ -416,13 +523,25 @@ static bool is_perimeter(const DeviceGrid& grid, int x, int y) { return is_perimeter; } -/* checks if the specified coordinates correspond to the core of the FPGA (i.e. not perimeter) */ -static bool is_core(const DeviceGrid& grid, int x, int y) { - bool is_core = !is_perimeter(grid, x, y); +static bool is_core_sb(const DeviceGrid& grid, const std::vector& inter_cluster_rr, int x, int y, int layer) { + if (!is_prog_routing_avail(grid, inter_cluster_rr, layer)) { + return false; + } + bool is_core = !is_perimeter_sb(grid, inter_cluster_rr, x, y, layer); return is_core; } -/* Counts the number of wires in each wire type in the specified channel */ +static bool is_prog_routing_avail(const DeviceGrid& grid, const std::vector& inter_cluster_rr, int layer) { + bool is_prog_avail = true; + //make sure layer number is legal + VTR_ASSERT(layer >= 0 && layer < grid.get_num_layers()); + //check if the current layer has programmable routing resources before trying to build a custom switch blocks + if (!inter_cluster_rr.at(layer)) { + is_prog_avail = false; + } + return is_prog_avail; +} + static void count_wire_type_sizes(const t_chan_seg_details* channel, int nodes_per_chan, t_wire_type_sizes* wire_type_sizes) { vtr::string_view wire_type; vtr::string_view new_type; @@ -454,7 +573,6 @@ static void count_wire_type_sizes(const t_chan_seg_details* channel, int nodes_p return; } -/* returns the wire indices belonging to the types in 'wire_type_vec' and switchpoints in 'points' at the given channel segment */ static void get_switchpoint_wires( const DeviceGrid& grid, const t_chan_seg_details* chan_details, @@ -551,16 +669,14 @@ static void get_switchpoint_wires( } } -/* Compute the wire(s) that the wire at (x, y, from_side, to_side) should connect to. - * sb_conns is updated with the result */ -static void compute_wire_connections(int x_coord, int y_coord, enum e_side from_side, enum e_side to_side, const t_chan_details& chan_details_x, const t_chan_details& chan_details_y, t_switchblock_inf* sb, const DeviceGrid& grid, const t_wire_type_sizes* wire_type_sizes_x, const t_wire_type_sizes* wire_type_sizes_y, e_directionality directionality, t_sb_connection_map* sb_conns, vtr::RandState& rand_state, t_wireconn_scratchpad* scratchpad) { - int from_x, from_y; /* index into source channel */ - int to_x, to_y; /* index into destination channel */ +static void compute_wire_connections(int x_coord, int y_coord, int layer_coord, enum e_side from_side, enum e_side to_side, const t_chan_details& chan_details_x, const t_chan_details& chan_details_y, t_switchblock_inf* sb, const DeviceGrid& grid, const t_wire_type_sizes* wire_type_sizes_x, const t_wire_type_sizes* wire_type_sizes_y, e_directionality directionality, t_sb_connection_map* sb_conns, vtr::RandState& rand_state, t_wireconn_scratchpad* scratchpad) { + int from_x, from_y, from_layer; /* index into source channel */ + int to_x, to_y, to_layer; /* index into destination channel */ t_rr_type from_chan_type, to_chan_type; /* the type of channel - i.e. CHANX or CHANY */ - from_x = from_y = to_x = to_y = UNDEFINED; + from_x = from_y = to_x = to_y = from_layer = to_layer = UNDEFINED; - SB_Side_Connection side_conn(from_side, to_side); /* for indexing into this switchblock's permutation funcs */ - Switchblock_Lookup sb_conn(x_coord, y_coord, from_side, to_side); /* for indexing into FPGA's switchblock map */ + SB_Side_Connection side_conn(from_side, to_side); /* for indexing into this switchblock's permutation funcs */ + Switchblock_Lookup sb_conn(x_coord, y_coord, layer_coord, from_side, to_side); /* for indexing into FPGA's switchblock map */ /* can't connect a switchblock side to itself */ if (from_side == to_side) { @@ -573,18 +689,18 @@ static void compute_wire_connections(int x_coord, int y_coord, enum e_side from_ } /* find the correct channel, and the coordinates to index into it for both the source and - * destination channels. also return the channel type (ie chanx/chany) into which we are + * destination channels. also return the channel type (ie chanx/chany/both) into which we are * indexing */ /* details for source channel */ - const t_chan_details& from_chan_details = index_into_correct_chan(x_coord, y_coord, from_side, chan_details_x, chan_details_y, - &from_x, &from_y, &from_chan_type); + const t_chan_details& from_chan_details = index_into_correct_chan(x_coord, y_coord, layer_coord, from_side, to_side, chan_details_x, chan_details_y, + from_x, from_y, from_layer, from_chan_type); /* details for destination channel */ - const t_chan_details& to_chan_details = index_into_correct_chan(x_coord, y_coord, to_side, chan_details_x, chan_details_y, - &to_x, &to_y, &to_chan_type); + const t_chan_details& to_chan_details = index_into_correct_chan(x_coord, y_coord, layer_coord, to_side, from_side, chan_details_x, chan_details_y, + to_x, to_y, to_layer, to_chan_type); /* make sure from_x/y and to_x/y aren't out of bounds */ - if (coords_out_of_bounds(grid, to_x, to_y, to_chan_type) || coords_out_of_bounds(grid, from_x, from_y, from_chan_type)) { + if (coords_out_of_bounds(grid, to_x, to_y, to_layer, to_chan_type) || coords_out_of_bounds(grid, from_x, from_y, from_layer, from_chan_type)) { return; } @@ -596,6 +712,7 @@ static void compute_wire_connections(int x_coord, int y_coord, enum e_side from_ if (to_chan_type == CHANY) { wire_type_sizes_to = wire_type_sizes_y; } + /* iterate over all the wire connections specified for this switch block */ for (int iconn = 0; iconn < (int)sb->wireconns.size(); iconn++) { /* pointer to a connection specification between wire types/subsegment_nums */ @@ -604,7 +721,7 @@ static void compute_wire_connections(int x_coord, int y_coord, enum e_side from_ /* compute the destination wire segments to which the source wire segment should connect based on the * current wireconn */ compute_wireconn_connections(grid, directionality, from_chan_details, to_chan_details, - sb_conn, from_x, from_y, to_x, to_y, from_chan_type, to_chan_type, wire_type_sizes_from, + sb_conn, from_x, from_y, from_layer, to_x, to_y, to_layer, from_chan_type, to_chan_type, wire_type_sizes_from, wire_type_sizes_to, sb, wireconn_ptr, sb_conns, rand_state, scratchpad); } @@ -623,8 +740,10 @@ static void compute_wireconn_connections( Switchblock_Lookup sb_conn, int from_x, int from_y, + int from_layer, int to_x, int to_y, + int to_layer, t_rr_type from_chan_type, t_rr_type to_chan_type, const t_wire_type_sizes* wire_type_sizes_from, @@ -636,25 +755,30 @@ static void compute_wireconn_connections( t_wireconn_scratchpad* scratchpad) { constexpr bool verbose = false; - /* vectors that will contain indices of the wires belonging to the source/dest wire types/points */ + //choose the from_side to be the same as to_side if the connection is travelling across dice in multi-die FPGAs + auto from_side = (sb_conn.from_side != ABOVE && sb_conn.from_side != UNDER) ? sb_conn.from_side : sb_conn.to_side; + //choose the to_side to be the same as from_side if the connection is travelling across dice in multi-die FPGAs + auto to_side = (sb_conn.to_side != ABOVE && sb_conn.to_side != UNDER) ? sb_conn.to_side : sb_conn.from_side; - get_switchpoint_wires(grid, - from_chan_details[from_x][from_y].data(), from_chan_type, from_x, from_y, sb_conn.from_side, + /* vectors that will contain indices of the wires belonging to the source/dest wire types/points */ + get_switchpoint_wires(grid, from_chan_details[from_x][from_y].data(), from_chan_type, from_x, from_y, from_side, wireconn_ptr->from_switchpoint_set, wire_type_sizes_from, false, wireconn_ptr->from_switchpoint_order, rand_state, &scratchpad->potential_src_wires, &scratchpad->scratch_wires); - get_switchpoint_wires(grid, - to_chan_details[to_x][to_y].data(), to_chan_type, to_x, to_y, sb_conn.to_side, - wireconn_ptr->to_switchpoint_set, wire_type_sizes_to, true, wireconn_ptr->to_switchpoint_order, rand_state, &scratchpad->potential_dest_wires, + + get_switchpoint_wires(grid, to_chan_details[to_x][to_y].data(), to_chan_type, to_x, to_y, to_side, + wireconn_ptr->to_switchpoint_set, wire_type_sizes_to, true, + wireconn_ptr->to_switchpoint_order, rand_state, &scratchpad->potential_dest_wires, &scratchpad->scratch_wires); const auto& potential_src_wires = scratchpad->potential_src_wires; const auto& potential_dest_wires = scratchpad->potential_dest_wires; - VTR_LOGV(verbose, "SB_LOC: %d,%d %s->%s\n", sb_conn.x_coord, sb_conn.y_coord, SIDE_STRING[sb_conn.from_side], SIDE_STRING[sb_conn.to_side]); +#ifdef VERBOSE_RR + VTR_LOGV(verbose, "SB_LOC: %d,%d %s->%s\n", sb_conn.x_coord, sb_conn.y_coord, TOTAL_2D_SIDE_STRINGS[sb_conn.from_side], TOTAL_2D_SIDE_STRINGS[sb_conn.to_side]); //Define to print out specific wire-switchpoints used in to/from sets, if verbose is set true -#if 0 + for (auto from_set : wireconn_ptr->from_switchpoint_set) { VTR_LOGV(verbose, " FROM_SET: %s @", from_set.segment_name.c_str()); for (int switchpoint : from_set.switchpoints) { @@ -719,13 +843,13 @@ static void compute_wireconn_connections( if (sb_conn.from_side == TOP || sb_conn.from_side == RIGHT) { continue; } - VTR_ASSERT(sb_conn.from_side == BOTTOM || sb_conn.from_side == LEFT); + VTR_ASSERT(sb_conn.from_side == BOTTOM || sb_conn.from_side == LEFT || sb_conn.from_side == ABOVE || sb_conn.from_side == UNDER); } else if (from_wire_direction == Direction::DEC) { /* a wire heading in the decreasing direction can only connect from the TOP or RIGHT sides of a switch block */ if (sb_conn.from_side == BOTTOM || sb_conn.from_side == LEFT) { continue; } - VTR_ASSERT(sb_conn.from_side == TOP || sb_conn.from_side == RIGHT); + VTR_ASSERT(sb_conn.from_side == TOP || sb_conn.from_side == RIGHT || sb_conn.from_side == ABOVE || sb_conn.from_side == UNDER); } else { VTR_ASSERT(from_wire_direction == Direction::BIDIR); } @@ -757,12 +881,19 @@ static void compute_wireconn_connections( t_switchblock_edge sb_edge; sb_edge.from_wire = from_wire; sb_edge.to_wire = to_wire; + sb_edge.from_wire_layer = from_layer; + sb_edge.to_wire_layer = to_layer; - // if the switch override has been set, use that. Otherwise use default + // if the switch override has been set, use that, Otherwise use default if (wireconn_ptr->switch_override_indx != DEFAULT_SWITCH) { sb_edge.switch_ind = wireconn_ptr->switch_override_indx; + } else if (from_layer == to_layer) { + sb_edge.switch_ind = to_chan_details[to_x][to_y][to_wire].arch_wire_switch(); + sb_edge.switch_ind_between_layers = -1; //the connection does not cross any layers } else { + VTR_ASSERT(from_layer != to_layer); sb_edge.switch_ind = to_chan_details[to_x][to_y][to_wire].arch_wire_switch(); + sb_edge.switch_ind_between_layers = to_chan_details[to_x][to_y][to_wire].arch_opin_between_dice_switch(); } VTR_LOGV(verbose, " make_conn: %d -> %d switch=%d\n", sb_edge.from_wire, sb_edge.to_wire, sb_edge.switch_ind); @@ -775,9 +906,9 @@ static void compute_wireconn_connections( std::swap(sb_reverse_edge.from_wire, sb_reverse_edge.to_wire); //Since we are implementing the reverse connection we have swapped from and to. // - //Coverity flags this (false positive), so annotatate so coverity ignores it: + //Coverity flags this (false positive), so annotate coverity ignores it: // coverity[swapped_arguments : Intentional] - Switchblock_Lookup sb_conn_reverse(sb_conn.x_coord, sb_conn.y_coord, sb_conn.to_side, sb_conn.from_side); + Switchblock_Lookup sb_conn_reverse(sb_conn.x_coord, sb_conn.y_coord, sb_conn.layer_coord, sb_conn.to_side, sb_conn.from_side); (*sb_conns)[sb_conn_reverse].push_back(sb_reverse_edge); } } @@ -794,64 +925,85 @@ static int evaluate_num_conns_formula(t_wireconn_scratchpad* scratchpad, std::st return scratchpad->formula_parser.parse_formula(num_conns_formula, vars); } -/* Here we find the correct channel (x or y), and the coordinates to index into it based on the - * specified tile coordinates and the switchblock side. Also returns the type of channel - * that we are indexing into (ie, CHANX or CHANY */ -static const t_chan_details& index_into_correct_chan(int tile_x, int tile_y, enum e_side side, const t_chan_details& chan_details_x, const t_chan_details& chan_details_y, int* set_x, int* set_y, t_rr_type* chan_type) { - *chan_type = CHANX; - +static const t_chan_details& index_into_correct_chan(int tile_x, int tile_y, int tile_layer, enum e_side src_side, enum e_side dest_side, const t_chan_details& chan_details_x, const t_chan_details& chan_details_y, int& chan_x, int& chan_y, int& chan_layer, t_rr_type& chan_type) { + chan_type = CHANX; /* here we use the VPR convention that a tile 'owns' the channels directly to the right * and above it */ - switch (side) { + switch (src_side) { case TOP: - /* this is y-channel belonging to tile above */ - *set_x = tile_x; - *set_y = tile_y + 1; - *chan_type = CHANY; + /* this is y-channel belonging to tile above in the same layer */ + chan_x = tile_x; + chan_y = tile_y + 1; + chan_layer = tile_layer; + chan_type = CHANY; return chan_details_y; break; case RIGHT: - /* this is x-channel belonging to tile to the right */ - *set_x = tile_x + 1; - *set_y = tile_y; - *chan_type = CHANX; + /* this is x-channel belonging to tile to the right in the same layer */ + chan_x = tile_x + 1; + chan_y = tile_y; + chan_layer = tile_layer; + chan_type = CHANX; return chan_details_x; break; case BOTTOM: - /* this is y-channel on the right of the tile */ - *set_x = tile_x; - *set_y = tile_y; - *chan_type = CHANY; + /* this is y-channel on the right of the tile in the same layer */ + chan_x = tile_x; + chan_y = tile_y; + chan_type = CHANY; + chan_layer = tile_layer; return chan_details_y; break; case LEFT: - /* this is x-channel on top of the tile */ - *set_x = tile_x; - *set_y = tile_y; - *chan_type = CHANX; + /* this is x-channel on top of the tile in the same layer*/ + chan_x = tile_x; + chan_y = tile_y; + chan_type = CHANX; + chan_layer = tile_layer; return chan_details_x; break; + case ABOVE: + /* this is x-channel and y-channel on the same tile location in layer above the current layer */ + chan_x = tile_x; + chan_y = tile_y; + chan_layer = tile_layer + 1; + chan_type = (dest_side == RIGHT || dest_side == LEFT) ? CHANX : CHANY; + return (dest_side == RIGHT || dest_side == LEFT) ? chan_details_x : chan_details_y; + break; + case UNDER: + /* this is x-channel and y-channel on the same tile location in layer under the current layer */ + chan_x = tile_x; + chan_y = tile_y; + chan_layer = tile_layer - 1; + chan_type = (dest_side == RIGHT || dest_side == LEFT) ? CHANX : CHANY; + return (dest_side == RIGHT || dest_side == LEFT) ? chan_details_x : chan_details_y; + break; default: - VPR_FATAL_ERROR(VPR_ERROR_ARCH, "index_into_correct_chan: unknown side specified: %d\n", side); + VPR_FATAL_ERROR(VPR_ERROR_ARCH, "index_into_correct_chan: unknown side specified: %d\n", src_side); break; } VTR_ASSERT(false); return chan_details_x; //Unreachable } -/* checks whether the specified coordinates are out of bounds */ -static bool coords_out_of_bounds(const DeviceGrid& grid, int x_coord, int y_coord, e_rr_type chan_type) { +static bool coords_out_of_bounds(const DeviceGrid& grid, int x_coord, int y_coord, int layer_coord, e_rr_type chan_type) { bool result = true; + /* the layer that channel is located at must be legal regardless of chan_type*/ + if (layer_coord < 0 || layer_coord > grid.get_num_layers()) { + return result; + } + if (CHANX == chan_type) { - if (x_coord <= 0 || x_coord >= int(grid.width()) - 1 || /* there is no x-channel at x=0 */ - y_coord < 0 || y_coord >= int(grid.height()) - 1) { + /* there is no x-channel at x=0 */ + if (x_coord <= 0 || x_coord >= int(grid.width()) - 1 || y_coord < 0 || y_coord >= int(grid.height()) - 1) { result = true; } else { result = false; } } else if (CHANY == chan_type) { - if (x_coord < 0 || x_coord >= int(grid.width()) - 1 || y_coord <= 0 || y_coord >= int(grid.height()) - 1) { /* there is no y-channel at y=0 */ + /* there is no y-channel at y=0 */ + if (x_coord < 0 || x_coord >= int(grid.width()) - 1 || y_coord <= 0 || y_coord >= int(grid.height()) - 1) { result = true; } else { result = false; @@ -863,7 +1015,6 @@ static bool coords_out_of_bounds(const DeviceGrid& grid, int x_coord, int y_coor return result; } -/* returns the subsegment number of the specified wire at seg_coord */ static int get_wire_subsegment_num(const DeviceGrid& grid, e_rr_type chan_type, const t_chan_seg_details& wire_details, int seg_coord) { /* We get wire subsegment number by comparing the wire's seg_coord to the seg_start of the wire. * The offset between seg_start (or seg_end) and seg_coord is the subsegment number @@ -927,8 +1078,6 @@ int get_wire_segment_length(const DeviceGrid& grid, e_rr_type chan_type, const t return wire_length; } -/* Returns the switchpoint of the wire specified by wire_details at a segment coordinate - * of seg_coord, and connection to the sb_side of the switchblock */ static int get_switchpoint_of_wire(const DeviceGrid& grid, e_rr_type chan_type, const t_chan_seg_details& wire_details, int seg_coord, e_side sb_side) { /* this function calculates the switchpoint of a given wire by first calculating * the subsegmennt number of the specified wire. For instance, for a wire with L=4: diff --git a/vpr/src/route/build_switchblocks.h b/vpr/src/route/build_switchblocks.h index 68e60da3824..c0db665b956 100644 --- a/vpr/src/route/build_switchblocks.h +++ b/vpr/src/route/build_switchblocks.h @@ -19,23 +19,30 @@ class Switchblock_Lookup { public: int x_coord; /* x coordinate of switchblock connection */ //TODO: redundant comment?? add range int y_coord; /* y coordinate of switchblock connection */ + int layer_coord; /* layer number of switchblock */ e_side from_side; /* source side of switchblock connection */ e_side to_side; /* destination side of switchblock connection */ /* Empty constructor initializes everything to 0 */ Switchblock_Lookup() { - x_coord = y_coord = -1; //TODO: use set function + x_coord = y_coord = layer_coord = -1; //TODO: use set function } /* Constructor for initializing member variables */ + Switchblock_Lookup(int set_x, int set_y, int set_layer, e_side set_from, e_side set_to) { + this->set_coords(set_x, set_y, set_layer, set_from, set_to); //TODO: use set function + } + + /* Constructor for initializing member variables with default layer number (0), used for single die FPGA */ Switchblock_Lookup(int set_x, int set_y, e_side set_from, e_side set_to) { - this->set_coords(set_x, set_y, set_from, set_to); //TODO: use set function + this->set_coords(set_x, set_y, 0, set_from, set_to); } /* Function for setting the segment coordinates */ - void set_coords(int set_x, int set_y, e_side set_from, e_side set_to) { + void set_coords(int set_x, int set_y, int set_layer, e_side set_from, e_side set_to) { x_coord = set_x; y_coord = set_y; + layer_coord = set_layer; from_side = set_from; to_side = set_to; } @@ -44,7 +51,8 @@ class Switchblock_Lookup { bool operator==(const Switchblock_Lookup& obj) const { bool result; if (x_coord == obj.x_coord && y_coord == obj.y_coord - && from_side == obj.from_side && to_side == obj.to_side) { + && from_side == obj.from_side && to_side == obj.to_side + && layer_coord == obj.layer_coord) { result = true; } else { result = false; @@ -55,22 +63,33 @@ class Switchblock_Lookup { struct t_hash_Switchblock_Lookup { size_t operator()(const Switchblock_Lookup& obj) const noexcept { - //TODO: use vtr::hash_combine - size_t result; - result = ((((std::hash()(obj.x_coord) - ^ std::hash()(obj.y_coord) << 10) - ^ std::hash()((int)obj.from_side) << 20) - ^ std::hash()((int)obj.to_side) << 30)); - return result; + std::size_t hash = std::hash{}(obj.x_coord); + vtr::hash_combine(hash, obj.y_coord); + vtr::hash_combine(hash, obj.layer_coord); + vtr::hash_combine(hash, obj.from_side); + vtr::hash_combine(hash, obj.to_side); + return hash; } }; -/* contains the index of the destination wire segment within a channel - * and the index of the switch used to connect to it */ +/** + * @brief contains the required information to build an RR graph edge for a switch block connection + * + * @from_wire source wire ptc_num index in a channel + * @to_wire destination wire ptc_num index in a channel + * @switch_ind RR graph switch index that connects the source wire to the destination wire that connect two tracks in same layer + * @switch_ind_between_layers RR graph switch index that connects two tracks in different layers + * @from_wire_layer the layer index that the source wire is located at + * @to_wire_layer the layer index that the destination wire is located at + * + */ struct t_switchblock_edge { short from_wire; short to_wire; short switch_ind; + short switch_ind_between_layers; + short from_wire_layer; + short to_wire_layer; }; /* Switchblock connections are made as [x][y][from_side][to_side][from_wire_ind]. @@ -83,10 +102,27 @@ typedef std::unordered_map, /************ Functions ************/ -/* allocate and build switch block permutation map */ -t_sb_connection_map* alloc_and_load_switchblock_permutations(const t_chan_details& chan_details_x, const t_chan_details& chan_details_y, const DeviceGrid& grid, std::vector switchblocks, t_chan_width* nodes_per_chan, enum e_directionality directionality, vtr::RandState& rand_state); +/** + * @brief allocates and builds switch block permutation map + * + * @param chan_details_x channel-x details (length, start and end points, ...) + * @param chan_details_y channel-y details (length, start and end points, ...) + * @param grid device grid + * @param inter_cluster_rr used to check if a certain layer contain inter-cluster programmable routing resources (wires and switch blocks) + * @param switchblocks switch block information extracted from the architecture file + * @param nodes_per_chan number of track in each channel (x,y) + * @param directionality specifies the switch block edges direction (unidirectional or bidirectional) + * @param rand_state initialize the random number generator (RNG) + * + * @return creates a map between switch blocks (key) and their corresponding edges (value). + */ +t_sb_connection_map* alloc_and_load_switchblock_permutations(const t_chan_details& chan_details_x, const t_chan_details& chan_details_y, const DeviceGrid& grid, const std::vector& inter_cluster_rr, const std::vector& switchblocks, t_chan_width* nodes_per_chan, enum e_directionality directionality, vtr::RandState& rand_state); -/* deallocates switch block connections sparse array */ +/** + * @brief deallocates switch block connections sparse array + * + * @param sb_conns switch block permutation map + */ void free_switchblock_permutations(t_sb_connection_map* sb_conns); #endif diff --git a/vpr/src/route/clock_connection_builders.cpp b/vpr/src/route/clock_connection_builders.cpp index d7c41cae408..d7b0f831e44 100644 --- a/vpr/src/route/clock_connection_builders.cpp +++ b/vpr/src/route/clock_connection_builders.cpp @@ -283,7 +283,7 @@ void ClockToPinsConnection::create_switches(const ClockRRGraphBuilder& clock_gra continue; } - for (e_side side : SIDES) { + for (e_side side : TOTAL_2D_SIDES) { //Don't connect pins which are not adjacent to channels around the perimeter if ((x == 0 && side != RIGHT) || (x == (int)grid.width() - 1 && side != LEFT) || (y == 0 && side != TOP) || (y == (int)grid.height() - 1 && side != BOTTOM)) { continue; diff --git a/vpr/src/route/connection_router.cpp b/vpr/src/route/connection_router.cpp index 4074d6d283f..5409d5ec49f 100644 --- a/vpr/src/route/connection_router.cpp +++ b/vpr/src/route/connection_router.cpp @@ -147,7 +147,7 @@ std::tuple ConnectionRouter::timing_driven_route_conne if (cheapest == nullptr) { //Found no path, that may be due to an unlucky choice of existing route tree sub-set, //try again with the full route tree to be sure this is not an artifact of high-fanout routing - VTR_LOG_WARN("No routing path found in high-fanout mode for net connection (to sink_rr %d), retrying with full route tree\n", sink_node); + VTR_LOG_WARN("No routing path found in high-fanout mode for net %zu connection (to sink_rr %d), retrying with full route tree\n", size_t(conn_params.net_id_), sink_node); //Reset any previously recorded node costs so timing_driven_route_connection() //starts over from scratch. @@ -379,10 +379,11 @@ void ConnectionRouter::timing_driven_expand_cheapest(t_heap* cheapest, VTR_LOGV_DEBUG(router_debug_, " Better cost to %d\n", inode); VTR_LOGV_DEBUG(router_debug_, " New total cost: %g\n", new_total_cost); VTR_LOGV_DEBUG(router_debug_, " New back cost: %g\n", new_back_cost); - VTR_LOGV_DEBUG(router_debug_ && (rr_nodes_.node_type(RRNodeId(cheapest->index)) != t_rr_type::SOURCE), " Setting path costs for associated node %d (from %d edge %zu)\n", - cheapest->index, - static_cast(rr_graph_->edge_src_node(cheapest->prev_edge())), - static_cast(cheapest->prev_edge())); + VTR_LOGV_DEBUG(router_debug_ && (cheapest->prev_edge() != RREdgeId::INVALID()), + " Setting path costs for associated node %d (from %d edge %zu)\n", + cheapest->index, + static_cast(rr_graph_->edge_src_node(cheapest->prev_edge())), + static_cast(cheapest->prev_edge())); update_cheapest(cheapest, route_inf); @@ -977,6 +978,8 @@ t_bb ConnectionRouter::add_high_fanout_route_tree_to_heap( int target_bin_x = grid_to_bin_x(rr_graph_->node_xlow(target_node), spatial_rt_lookup); int target_bin_y = grid_to_bin_y(rr_graph_->node_ylow(target_node), spatial_rt_lookup); + auto target_layer = rr_graph_->node_layer(target_node); + int chan_nodes_added = 0; t_bb highfanout_bb; @@ -984,12 +987,13 @@ t_bb ConnectionRouter::add_high_fanout_route_tree_to_heap( highfanout_bb.xmax = rr_graph_->node_xhigh(target_node); highfanout_bb.ymin = rr_graph_->node_ylow(target_node); highfanout_bb.ymax = rr_graph_->node_yhigh(target_node); - highfanout_bb.layer_min = rr_graph_->node_layer(target_node); - highfanout_bb.layer_max = rr_graph_->node_layer(target_node); + highfanout_bb.layer_min = target_layer; + highfanout_bb.layer_max = target_layer; //Add existing routing starting from the target bin. //If the target's bin has insufficient existing routing add from the surrounding bins bool done = false; + bool found_node_on_same_layer = false; for (int dx : {0, -1, +1}) { size_t bin_x = target_bin_x + dx; @@ -1015,6 +1019,10 @@ t_bb ConnectionRouter::add_high_fanout_route_tree_to_heap( if (!inside_bb(rr_node_to_add, net_bounding_box)) continue; + auto rt_node_layer_num = rr_graph_->node_layer(rr_node_to_add); + if (rt_node_layer_num == target_layer) + found_node_on_same_layer = true; + // Put the node onto the heap add_route_tree_node_to_heap(rt_node, target_node, cost_params, net_bounding_box); @@ -1027,7 +1035,7 @@ t_bb ConnectionRouter::add_high_fanout_route_tree_to_heap( } constexpr int SINGLE_BIN_MIN_NODES = 2; - if (dx == 0 && dy == 0 && chan_nodes_added > SINGLE_BIN_MIN_NODES) { + if (dx == 0 && dy == 0 && chan_nodes_added > SINGLE_BIN_MIN_NODES && found_node_on_same_layer) { //Target bin contained at least minimum amount of routing // //We require at least SINGLE_BIN_MIN_NODES to be added. @@ -1041,7 +1049,7 @@ t_bb ConnectionRouter::add_high_fanout_route_tree_to_heap( if (done) break; } - if (chan_nodes_added == 0) { //If the target bin, and it's surrounding bins were empty, just add the full route tree + if (chan_nodes_added == 0 || !found_node_on_same_layer) { //If the target bin, and it's surrounding bins were empty, just add the full route tree add_route_tree_to_heap(rt_root, target_node, cost_params, net_bounding_box); return net_bounding_box; } else { diff --git a/vpr/src/route/router_delay_profiling.cpp b/vpr/src/route/router_delay_profiling.cpp index 256c4fcb933..e37744ab70a 100644 --- a/vpr/src/route/router_delay_profiling.cpp +++ b/vpr/src/route/router_delay_profiling.cpp @@ -51,8 +51,7 @@ RouterDelayProfiler::RouterDelayProfiler(const Netlist<>& net_list, bool RouterDelayProfiler::calculate_delay(RRNodeId source_node, RRNodeId sink_node, const t_router_opts& router_opts, - float* net_delay, - int layer_num) { + float* net_delay) { /* Returns true as long as found some way to hook up this net, even if that * * way resulted in overuse of resources (congestion). If there is no way * * to route this net, even ignoring congestion, it returns false. In this * @@ -83,14 +82,8 @@ bool RouterDelayProfiler::calculate_delay(RRNodeId source_node, bounding_box.xmax = device_ctx.grid.width() + 1; bounding_box.ymin = 0; bounding_box.ymax = device_ctx.grid.height() + 1; - // If layer num is not specified, it means the BB should cover all layers - if (layer_num == OPEN) { - bounding_box.layer_min = 0; - bounding_box.layer_max = device_ctx.grid.get_num_layers() - 1; - } else { - bounding_box.layer_min = layer_num; - bounding_box.layer_max = layer_num; - } + bounding_box.layer_min = 0; + bounding_box.layer_max = device_ctx.grid.get_num_layers() - 1; t_conn_cost_params cost_params; cost_params.criticality = 1.; @@ -109,6 +102,9 @@ bool RouterDelayProfiler::calculate_delay(RRNodeId source_node, -1, false, std::unordered_map()); + if (size_t(sink_node) == 778060 && size_t(source_node) == 14) { + router_.set_router_debug(true); + } std::tie(found_path, std::ignore, cheapest) = router_.timing_driven_route_connection_from_route_tree( tree.root(), sink_node, @@ -117,6 +113,8 @@ bool RouterDelayProfiler::calculate_delay(RRNodeId source_node, router_stats, conn_params); + router_.set_router_debug(false); + if (found_path) { VTR_ASSERT(cheapest.index == sink_node); diff --git a/vpr/src/route/router_delay_profiling.h b/vpr/src/route/router_delay_profiling.h index 71753a4cb91..bda721e1a24 100644 --- a/vpr/src/route/router_delay_profiling.h +++ b/vpr/src/route/router_delay_profiling.h @@ -29,8 +29,7 @@ class RouterDelayProfiler { bool calculate_delay(RRNodeId source_node, RRNodeId sink_node, const t_router_opts& router_opts, - float* net_delay, - int layer_num); + float* net_delay); /** * @param physical_tile_type_idx diff --git a/vpr/src/route/router_lookahead_map_utils.cpp b/vpr/src/route/router_lookahead_map_utils.cpp index c48ee90e073..deb48779d8a 100644 --- a/vpr/src/route/router_lookahead_map_utils.cpp +++ b/vpr/src/route/router_lookahead_map_utils.cpp @@ -1475,12 +1475,11 @@ static std::pair get_adjusted_rr_pin_position(const RRNodeId rr) { * However, current test show that the simple strategy provides * a good trade-off between runtime and quality of results */ - auto it = std::find_if(SIDES.begin(), SIDES.end(), [&](const e_side candidate_side) { + auto it = std::find_if(TOTAL_2D_SIDES.begin(), TOTAL_2D_SIDES.end(), [&](const e_side candidate_side) { return rr_graph.is_node_on_specific_side(rr, candidate_side); }); - - e_side rr_side = (it != SIDES.end()) ? *it : NUM_SIDES; - VTR_ASSERT_SAFE(NUM_SIDES != rr_side); + e_side rr_side = (it != TOTAL_2D_SIDES.end()) ? *it : NUM_2D_SIDES; + VTR_ASSERT_SAFE(NUM_2D_SIDES != rr_side); if (rr_side == LEFT) { x -= 1; diff --git a/vpr/src/route/rr_graph.cpp b/vpr/src/route/rr_graph.cpp index a5129dc5cf5..54b9f1ab11c 100644 --- a/vpr/src/route/rr_graph.cpp +++ b/vpr/src/route/rr_graph.cpp @@ -199,6 +199,7 @@ static std::function alloc_and_load_rr_graph(RRGraphBuilder const t_chan_width& chan_width, const int wire_to_ipin_switch, const int wire_to_pin_between_dice_switch, + const int custom_3d_sb_fanin_fanout, const int delayless_switch, const enum e_directionality directionality, bool* Fc_clipped, @@ -479,12 +480,13 @@ static std::unordered_set get_chain_pins(std::vector chai static void build_rr_chan(RRGraphBuilder& rr_graph_builder, const int layer, - const int i, - const int j, + const int x_coord, + const int y_coord, const t_rr_type chan_type, const t_track_to_pin_lookup& track_to_pin_lookup, t_sb_connection_map* sb_conn_map, const vtr::NdMatrix, 3>& switch_block_conn, + vtr::NdMatrix& num_of_3d_conns_custom_SB, const int cost_index_offset, const t_chan_width& nodes_per_chan, const DeviceGrid& grid, @@ -493,11 +495,32 @@ static void build_rr_chan(RRGraphBuilder& rr_graph_builder, const int Fs_per_side, const t_chan_details& chan_details_x, const t_chan_details& chan_details_y, - t_rr_edge_info_set& created_rr_edges, + t_rr_edge_info_set& rr_edges_to_create, + t_rr_edge_info_set& des_3d_rr_edges_to_create, const int wire_to_ipin_switch, const int wire_to_pin_between_dice_switch, + const int custom_3d_sb_fanin_fanout, + const int delayless_switch, const enum e_directionality directionality); +/** + * @brief builds the extra length-0 CHANX nodes to handle 3D custom switchblocks edges in the RR graph. + * @param rr_graph_builder RRGraphBuilder data structure which allows data modification on a routing resource graph + * @param layer switch block layer-coordinate + * @param x_coord switch block x_coordinate + * @param y_coord switch block y-coordinate + * @param const_index_offset index to the correct node type for RR node cost initialization + * @param nodes_per_chan number of tracks per channel (x, y) + * @param chan_details_x channel-x details (length, start and end points, ...) + */ +static void build_inter_die_custom_sb_rr_chan(RRGraphBuilder& rr_graph_builder, + const int layer, + const int x_coord, + const int y_coord, + const int const_index_offset, + const t_chan_width& nodes_per_chan, + const t_chan_details& chan_details_x); + void uniquify_edges(t_rr_edge_info_set& rr_edges_to_create); void alloc_and_load_edges(RRGraphBuilder& rr_graph_builder, @@ -649,6 +672,7 @@ static void build_rr_graph(const t_graph_type graph_type, const int global_route_switch, const int wire_to_arch_ipin_switch, const int wire_to_pin_between_dice_switch, + const int custom_3d_sb_fanin_fanout, const int delayless_switch, const float R_minW_nmos, const float R_minW_pmos, @@ -752,6 +776,7 @@ void create_rr_graph(const t_graph_type graph_type, det_routing_arch->global_route_switch, det_routing_arch->wire_to_arch_ipin_switch, det_routing_arch->wire_to_arch_ipin_switch_between_dice, + router_opts.custom_3d_sb_fanin_fanout, det_routing_arch->delayless_switch, det_routing_arch->R_minW_nmos, det_routing_arch->R_minW_pmos, @@ -976,6 +1001,7 @@ static void build_rr_graph(const t_graph_type graph_type, const int global_route_switch, const int wire_to_arch_ipin_switch, const int wire_to_pin_between_dice_switch, + const int custom_3d_sb_fanin_fanout, const int delayless_switch, const float R_minW_nmos, const float R_minW_pmos, @@ -1228,13 +1254,14 @@ static void build_rr_graph(const t_graph_type graph_type, //is deterministic -- always producing the same RR graph. constexpr unsigned SWITCHPOINT_RNG_SEED = 1; vtr::RandState switchpoint_rand_state = SWITCHPOINT_RNG_SEED; + const auto inter_cluster_prog_rr = device_ctx.inter_cluster_prog_routing_resources; if (is_global_graph) { switch_block_conn = alloc_and_load_switch_block_conn(&nodes_per_chan, SUBSET, 3); } else if (BI_DIRECTIONAL == directionality) { if (sb_type == CUSTOM) { sb_conn_map = alloc_and_load_switchblock_permutations(chan_details_x, chan_details_y, - grid, + grid, inter_cluster_prog_rr, switchblocks, &nodes_per_chan, directionality, switchpoint_rand_state); } else { @@ -1246,6 +1273,7 @@ static void build_rr_graph(const t_graph_type graph_type, if (sb_type == CUSTOM) { sb_conn_map = alloc_and_load_switchblock_permutations(chan_details_x, chan_details_y, grid, + inter_cluster_prog_rr, switchblocks, &nodes_per_chan, directionality, switchpoint_rand_state); } else { @@ -1269,6 +1297,19 @@ static void build_rr_graph(const t_graph_type graph_type, } /* END SB LOOKUP */ + /* check whether RR graph need to allocate new nodes for 3D custom switch blocks. + * To avoid wasting memory, the data structures are only allocated if a custom switch block + * is described in the architecture file and we have more than one die in device grid. + */ + if (grid.get_num_layers() > 1 && sb_type == CUSTOM) { + //keep how many nodes each switchblock requires for each x,y location + auto extra_nodes_per_switchblock = get_number_track_to_track_inter_die_conn(sb_conn_map,custom_3d_sb_fanin_fanout, device_ctx.rr_graph_builder); + //allocate new nodes in each switchblocks + alloc_and_load_inter_die_rr_node_indices(device_ctx.rr_graph_builder, &nodes_per_chan, grid, extra_nodes_per_switchblock, &num_rr_nodes); + device_ctx.rr_graph_builder.resize_nodes(num_rr_nodes); + extra_nodes_per_switchblock.clear(); + } + /* START IPIN MAP */ /* Create ipin map lookups */ @@ -1362,6 +1403,7 @@ static void build_rr_graph(const t_graph_type graph_type, nodes_per_chan, wire_to_arch_ipin_switch, wire_to_pin_between_dice_switch, + custom_3d_sb_fanin_fanout, delayless_switch, directionality, &Fc_clipped, @@ -1454,6 +1496,7 @@ static void build_rr_graph(const t_graph_type graph_type, if (!chan_details_x.empty() || !chan_details_y.empty()) { free_chan_details(chan_details_x, chan_details_y); } + if (sb_conn_map) { free_switchblock_permutations(sb_conn_map); sb_conn_map = nullptr; @@ -1461,6 +1504,7 @@ static void build_rr_graph(const t_graph_type graph_type, track_to_pin_lookup_x.clear(); track_to_pin_lookup_y.clear(); + if (clb_to_clb_directs != nullptr) { delete[] clb_to_clb_directs; } @@ -2040,6 +2084,7 @@ static std::function alloc_and_load_rr_graph(RRGraphBuilder const t_chan_width& chan_width, const int wire_to_ipin_switch, const int wire_to_pin_between_dice_switch, + const int custom_3d_sb_fanin_fanout, const int delayless_switch, const enum e_directionality directionality, bool* Fc_clipped, @@ -2123,7 +2168,7 @@ static std::function alloc_and_load_rr_graph(RRGraphBuilder for (int layer = 0; layer < grid.get_num_layers(); layer++) { for (size_t i = 0; i < grid.width(); ++i) { for (size_t j = 0; j < grid.height(); ++j) { - for (e_side side : SIDES) { + for (e_side side : TOTAL_2D_SIDES) { if (BI_DIRECTIONAL == directionality) { build_bidir_rr_opins(rr_graph_builder, rr_graph, layer, i, j, side, opin_to_track_map, Fc_out, rr_edges_to_create, chan_details_x, @@ -2160,23 +2205,45 @@ static std::function alloc_and_load_rr_graph(RRGraphBuilder /* Build channels */ VTR_ASSERT(Fs % 3 == 0); - for (int layer = 0; layer < grid.get_num_layers(); ++layer) { - auto& device_ctx = g_vpr_ctx.device(); - /* Skip the current die if architecture file specifies that it doesn't require inter-cluster programmable resource routing */ - if (!device_ctx.inter_cluster_prog_routing_resources.at(layer)) { - continue; - } - for (size_t i = 0; i < grid.width() - 1; ++i) { - for (size_t j = 0; j < grid.height() - 1; ++j) { + /* In case of multi-die FPGA and a custom 3D SB, we keep track of how many 3D connections have been already made for each x,y location */ + vtr::NdMatrix num_of_3d_conns_custom_SB; + + t_rr_edge_info_set des_3d_rr_edges_to_create; + if(grid.get_num_layers() > 1 && sb_conn_map != nullptr){ + num_of_3d_conns_custom_SB.resize(std::array{grid.width(), grid.height()}, 0); + } + + for (size_t i = 0; i < grid.width() - 1; ++i) { + for (size_t j = 0; j < grid.height() - 1; ++j) { + for (int layer = 0; layer < grid.get_num_layers(); ++layer) { + auto &device_ctx = g_vpr_ctx.device(); + /* Skip the current die if architecture file specifies that it doesn't require inter-cluster programmable resource routing */ + if (!device_ctx.inter_cluster_prog_routing_resources.at(layer)) { + continue; + } + /* In multi-die FPGAs with track-to-track connections between layers, we need to load newly added length-0 CHANX nodes + * These extra nodes can be driven from many tracks in the source layer and can drive multiple tracks in the destination layer, + * since these die-crossing connections have more delays. + */ + if (grid.get_num_layers() > 1 && sb_conn_map != nullptr) { + //custom switch block defined in the architecture + VTR_ASSERT(sblock_pattern.empty() && switch_block_conn.empty()); + build_inter_die_custom_sb_rr_chan(rr_graph_builder, layer, i, j, CHANX_COST_INDEX_START, chan_width, + chan_details_x); + } + if (i > 0) { int tracks_per_chan = ((is_global_graph) ? 1 : chan_width.x_list[j]); - build_rr_chan(rr_graph_builder, layer, i, j, CHANX, track_to_pin_lookup_x, sb_conn_map, switch_block_conn, - CHANX_COST_INDEX_START, + build_rr_chan(rr_graph_builder, layer, i, j, CHANX, track_to_pin_lookup_x, sb_conn_map, + switch_block_conn, + num_of_3d_conns_custom_SB, CHANX_COST_INDEX_START, chan_width, grid, tracks_per_chan, sblock_pattern, Fs / 3, chan_details_x, chan_details_y, - rr_edges_to_create, + rr_edges_to_create, des_3d_rr_edges_to_create, wire_to_ipin_switch, wire_to_pin_between_dice_switch, + custom_3d_sb_fanin_fanout, + delayless_switch, directionality); //Create the actual CHAN->CHAN edges @@ -2188,13 +2255,16 @@ static std::function alloc_and_load_rr_graph(RRGraphBuilder } if (j > 0) { int tracks_per_chan = ((is_global_graph) ? 1 : chan_width.y_list[i]); - build_rr_chan(rr_graph_builder, layer, i, j, CHANY, track_to_pin_lookup_y, sb_conn_map, switch_block_conn, - CHANX_COST_INDEX_START + num_seg_types_x, + build_rr_chan(rr_graph_builder, layer, i, j, CHANY, track_to_pin_lookup_y, sb_conn_map, + switch_block_conn, + num_of_3d_conns_custom_SB, CHANX_COST_INDEX_START + num_seg_types_x, chan_width, grid, tracks_per_chan, sblock_pattern, Fs / 3, chan_details_x, chan_details_y, - rr_edges_to_create, + rr_edges_to_create, des_3d_rr_edges_to_create, wire_to_ipin_switch, wire_to_pin_between_dice_switch, + custom_3d_sb_fanin_fanout, + delayless_switch, directionality); //Create the actual CHAN->CHAN edges @@ -2207,6 +2277,15 @@ static std::function alloc_and_load_rr_graph(RRGraphBuilder } } } + + if(grid.get_num_layers() > 1 && sb_conn_map != nullptr){ + uniquify_edges(des_3d_rr_edges_to_create); + alloc_and_load_edges(rr_graph_builder, des_3d_rr_edges_to_create); + num_edges += des_3d_rr_edges_to_create.size(); + des_3d_rr_edges_to_create.clear(); + } + + VTR_LOG("CHAN->CHAN type edge count:%d\n", num_edges); num_edges = 0; std::function update_chan_width = [](t_chan_width*) noexcept {}; @@ -2399,7 +2478,7 @@ static void add_pins_rr_graph(RRGraphBuilder& rr_graph_builder, std::vector x_offset_vec; std::vector y_offset_vec; std::vector pin_sides_vec; - std::tie(x_offset_vec, y_offset_vec, pin_sides_vec) = get_pin_coordinates(physical_type, pin_num, std::vector(SIDES.begin(), SIDES.end())); + std::tie(x_offset_vec, y_offset_vec, pin_sides_vec) = get_pin_coordinates(physical_type, pin_num, std::vector(TOTAL_2D_SIDES.begin(), TOTAL_2D_SIDES.end())); VTR_ASSERT(!pin_sides_vec.empty()); for (int pin_coord = 0; pin_coord < (int)pin_sides_vec.size(); pin_coord++) { int x_offset = x_offset_vec[pin_coord]; @@ -3047,6 +3126,7 @@ static void build_rr_chan(RRGraphBuilder& rr_graph_builder, const t_track_to_pin_lookup& track_to_pin_lookup, t_sb_connection_map* sb_conn_map, const vtr::NdMatrix, 3>& switch_block_conn, + vtr::NdMatrix& num_of_3d_conns_custom_SB, const int cost_index_offset, const t_chan_width& nodes_per_chan, const DeviceGrid& grid, @@ -3056,8 +3136,11 @@ static void build_rr_chan(RRGraphBuilder& rr_graph_builder, const t_chan_details& chan_details_x, const t_chan_details& chan_details_y, t_rr_edge_info_set& rr_edges_to_create, + t_rr_edge_info_set& des_3d_rr_edges_to_create, const int wire_to_ipin_switch, const int wire_to_pin_between_dice_switch, + const int custom_3d_sb_fanin_fanout, + const int delayless_switch, const enum e_directionality directionality) { /* this function builds both x and y-directed channel segments, so set up our * coordinates based on channel type */ @@ -3137,10 +3220,10 @@ static void build_rr_chan(RRGraphBuilder& rr_graph_builder, } if (to_seg_details->length() > 0) { get_track_to_tracks(rr_graph_builder, layer, chan_coord, start, track, chan_type, chan_coord, - opposite_chan_type, seg_dimension, max_opposite_chan_width, grid, - Fs_per_side, sblock_pattern, node, rr_edges_to_create, - from_seg_details, to_seg_details, opposite_chan_details, - directionality, + opposite_chan_type, seg_dimension, max_opposite_chan_width, grid, + Fs_per_side, sblock_pattern, num_of_3d_conns_custom_SB, node, rr_edges_to_create, + des_3d_rr_edges_to_create, from_seg_details, to_seg_details, opposite_chan_details, + directionality,custom_3d_sb_fanin_fanout,delayless_switch, switch_block_conn, sb_conn_map); } } @@ -3157,10 +3240,10 @@ static void build_rr_chan(RRGraphBuilder& rr_graph_builder, } if (to_seg_details->length() > 0) { get_track_to_tracks(rr_graph_builder, layer, chan_coord, start, track, chan_type, chan_coord + 1, - opposite_chan_type, seg_dimension, max_opposite_chan_width, grid, - Fs_per_side, sblock_pattern, node, rr_edges_to_create, - from_seg_details, to_seg_details, opposite_chan_details, - directionality, switch_block_conn, sb_conn_map); + opposite_chan_type, seg_dimension, max_opposite_chan_width, grid, + Fs_per_side, sblock_pattern, num_of_3d_conns_custom_SB, node, rr_edges_to_create, + des_3d_rr_edges_to_create, from_seg_details, to_seg_details, opposite_chan_details, + directionality,custom_3d_sb_fanin_fanout, delayless_switch, switch_block_conn, sb_conn_map); } } @@ -3189,10 +3272,10 @@ static void build_rr_chan(RRGraphBuilder& rr_graph_builder, } if (to_seg_details->length() > 0) { get_track_to_tracks(rr_graph_builder, layer, chan_coord, start, track, chan_type, target_seg, - chan_type, seg_dimension, max_chan_width, grid, - Fs_per_side, sblock_pattern, node, rr_edges_to_create, - from_seg_details, to_seg_details, from_chan_details, - directionality, + chan_type, seg_dimension, max_chan_width, grid, + Fs_per_side, sblock_pattern, num_of_3d_conns_custom_SB, node, rr_edges_to_create, + des_3d_rr_edges_to_create, from_seg_details, to_seg_details, from_chan_details, + directionality,custom_3d_sb_fanin_fanout, delayless_switch, switch_block_conn, sb_conn_map); } } @@ -3227,6 +3310,55 @@ static void build_rr_chan(RRGraphBuilder& rr_graph_builder, } } +static void build_inter_die_custom_sb_rr_chan(RRGraphBuilder& rr_graph_builder, + const int layer, + const int x_coord, + const int y_coord, + const int const_index_offset, + const t_chan_width& nodes_per_chan, + const t_chan_details& chan_details_x) { + auto& mutable_device_ctx = g_vpr_ctx.mutable_device(); + const t_chan_seg_details* seg_details = chan_details_x[x_coord][y_coord].data(); + + /* 3D connections within the switch blocks use some extra length-0 CHANX node to allow a single 3D connection to be driven + * by multiple tracks in the source layer, and drives multiple tracks in the destination layer. + * These nodes has already been added to RRGraph builder, this function will go through all added nodes + * with specific location (layer, x_coord, y_coord) and sets their attributes. + * + * The extra length-0 nodes have the following attributes to make them distinigushable form normal chanx wires (e.g., length-4): + * 1) type: CHANX (could have used either CHANX or CHANY, we used CHANX) + * 2) ptc_num: [max_chan_width : max_chan_width + num_of_3d_connections - 1] + * 3) length: 0 + * 4) xhigh=xlow, yhigh=ylow + * 5) directionality: NONE (neither incremental nor decremental in 2D space) + */ + int start_track = nodes_per_chan.max; + int offset = 0; + + while (true) { //going through allocated nodes until no nodes are found within the RRGraph builder + RRNodeId node = rr_graph_builder.node_lookup().find_node(layer, x_coord, y_coord, CHANX, start_track + offset); + if (node) { + rr_graph_builder.set_node_layer(node, layer); + rr_graph_builder.set_node_coordinates(node, x_coord, y_coord, x_coord, y_coord); + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId( + const_index_offset + seg_details[start_track - 1].index())); + rr_graph_builder.set_node_capacity(node, 1); /* GLOBAL routing handled elsewhere */ + float R = 0; + float C = 0; + rr_graph_builder.set_node_rc_index(node, NodeRCIndex( + find_create_rr_rc_data(R, C, mutable_device_ctx.rr_rc_data))); + + rr_graph_builder.set_node_type(node, CHANX); + rr_graph_builder.set_node_track_num(node, start_track + offset); + rr_graph_builder.set_node_direction(node, Direction::NONE); + + offset++; + } else { + break; + } + } +} + void uniquify_edges(t_rr_edge_info_set& rr_edges_to_create) { std::stable_sort(rr_edges_to_create.begin(), rr_edges_to_create.end()); rr_edges_to_create.erase(std::unique(rr_edges_to_create.begin(), rr_edges_to_create.end()), rr_edges_to_create.end()); @@ -3350,7 +3482,7 @@ static vtr::NdMatrix alloc_and_load_pin_to_seg_type(const e_pin_type pin size_t(Type->width), //[0..width-1] size_t(Type->height), //[0..height-1] size_t(grid.get_num_layers()), //[0..layer-1] - NUM_SIDES, //[0..NUM_SIDES-1] + NUM_2D_SIDES, //[0..NUM_2D_SIDES-1] size_t(Fc) //[0..Fc-1] }, OPEN); //Unconnected @@ -3363,7 +3495,7 @@ static vtr::NdMatrix alloc_and_load_pin_to_seg_type(const e_pin_type pin size_t(Type->width), //[0..width-1] size_t(Type->height), //[0..height-1] size_t(grid.get_num_layers()), //[0..layer-1] - NUM_SIDES //[0..NUM_SIDES-1] + NUM_2D_SIDES //[0..NUM_2D_SIDES-1] }, 0); @@ -3376,7 +3508,7 @@ static vtr::NdMatrix alloc_and_load_pin_to_seg_type(const e_pin_type pin size_t(Type->width), //[0..width-1] size_t(Type->height), //[0..height-1] size_t(grid.get_num_layers()), //[0..layer-1] - NUM_SIDES, //[0..NUM_SIDES-1] + NUM_2D_SIDES, //[0..NUM_2D_SIDES-1] size_t(Type->num_pins) * size_t(grid.get_num_layers()) //[0..num_pins * num_layers-1] }, -1); //Defensive coding: Initialize to invalid @@ -3386,7 +3518,7 @@ static vtr::NdMatrix alloc_and_load_pin_to_seg_type(const e_pin_type pin size_t(Type->width), //[0..width-1] size_t(Type->height), //[0..height-1] size_t(grid.get_num_layers()), //[0..layer-1] - NUM_SIDES //[0..NUM_SIDES-1] + NUM_2D_SIDES //[0..NUM_2D_SIDES-1] }, 0); @@ -3403,7 +3535,7 @@ static vtr::NdMatrix alloc_and_load_pin_to_seg_type(const e_pin_type pin for (auto type_layer_index : type_layer) { for (int width = 0; width < Type->width; ++width) { for (int height = 0; height < Type->height; ++height) { - for (e_side side : SIDES) { + for (e_side side : TOTAL_2D_SIDES) { if (Type->pinloc[width][height][side][pin] == 1) { for (auto i = 0; i < (int)get_layers_connected_to_pin(Type, type_layer_index, pin).size(); i++) { dir_list[width][height][type_layer_index][side][num_dir[width][height][type_layer_index][side]] = pin; @@ -3422,7 +3554,7 @@ static vtr::NdMatrix alloc_and_load_pin_to_seg_type(const e_pin_type pin int num_phys_pins = 0; for (int width = 0; width < Type->width; ++width) { for (int height = 0; height < Type->height; ++height) { - for (e_side side : SIDES) { + for (e_side side : TOTAL_2D_SIDES) { num_phys_pins += num_dir[width][height][layer][side]; /* Num. physical pins per type */ } } @@ -3676,7 +3808,7 @@ static void load_uniform_connection_block_pattern(vtr::NdMatrix& tracks_ * counts will not get too big. */ std::vector>>> excess_tracks_selected; - excess_tracks_selected.resize(NUM_SIDES); + excess_tracks_selected.resize(NUM_2D_SIDES); for (int i = 0; i < num_phys_pins; ++i) { int width = pin_locations[i].width_offset; @@ -3686,7 +3818,7 @@ static void load_uniform_connection_block_pattern(vtr::NdMatrix& tracks_ max_height = std::max(max_height, height); } - for (int iside = 0; iside < NUM_SIDES; iside++) { + for (int iside = 0; iside < NUM_2D_SIDES; iside++) { excess_tracks_selected[iside].resize(max_width + 1); for (int dx = 0; dx <= max_width; dx++) { @@ -4296,7 +4428,7 @@ static int get_opin_direct_connections(RRGraphBuilder& rr_graph_builder, /* Find matching direct clb-to-clb connections with the same type as current grid location */ if (clb_to_clb_directs[i].from_clb_type == curr_type) { //We are at a valid starting point - if (directs[i].from_side != NUM_SIDES && directs[i].from_side != side) continue; + if (directs[i].from_side != NUM_2D_SIDES && directs[i].from_side != side) continue; //Offset must be in range if (x + directs[i].x_offset < int(device_ctx.grid.width() - 1) @@ -4364,7 +4496,7 @@ static int get_opin_direct_connections(RRGraphBuilder& rr_graph_builder, /* Add new ipin edge to list of edges */ std::vector inodes; - if (directs[i].to_side != NUM_SIDES) { + if (directs[i].to_side != NUM_2D_SIDES) { //Explicit side specified, only create if pin exists on that side RRNodeId inode = rr_graph_builder.node_lookup().find_node(layer, x + directs[i].x_offset, y + directs[i].y_offset, IPIN, ipin, directs[i].to_side); if (inode) { @@ -4509,7 +4641,7 @@ static RRNodeId pick_best_direct_connect_target_rr_node(const RRGraphView& rr_gr float best_dist = std::numeric_limits::infinity(); RRNodeId best_rr = RRNodeId::INVALID(); - for (const e_side& from_side : SIDES) { + for (const e_side& from_side : TOTAL_2D_SIDES) { /* Bypass those side where the node does not appear */ if (!rr_graph.is_node_on_specific_side(from_rr, from_side)) { continue; @@ -4520,7 +4652,7 @@ static RRNodeId pick_best_direct_connect_target_rr_node(const RRGraphView& rr_gr float to_dist = std::abs(rr_graph.node_xlow(from_rr) - rr_graph.node_xlow(to_rr)) + std::abs(rr_graph.node_ylow(from_rr) - rr_graph.node_ylow(to_rr)); - for (const e_side& to_side : SIDES) { + for (const e_side& to_side : TOTAL_2D_SIDES) { /* Bypass those side where the node does not appear */ if (!rr_graph.is_node_on_specific_side(to_rr, to_side)) { continue; diff --git a/vpr/src/route/rr_graph2.cpp b/vpr/src/route/rr_graph2.cpp index e2d922baaad..39c1e0ab697 100644 --- a/vpr/src/route/rr_graph2.cpp +++ b/vpr/src/route/rr_graph2.cpp @@ -93,8 +93,70 @@ static int get_unidir_track_to_chan_seg(RRGraphBuilder& rr_graph_builder, RRNodeId from_rr_node, t_rr_edge_info_set& rr_edges_to_create); +/** + * @brief creates the RR graph edges corresponding to switch blocks permutation map + * + * @param rr_graph_builder RRGraphBuilder data structure which allows data modification on a routing resource graph + * @param tile_x x-coordinate of the switch block + * @param tile_y y-coordinate of the switch block + * @param layer layer-coordinate of the switch block + * @param max_chan_width number of available tracks within the channel + * @param from_side switch block connection source side + * @param from_wire switch block connection source wire index (ptc_num) within the channel + * @param from_rr_node switch block connection source wire RRNode index + * @param to_side switch block connection destination side + * @param to_x switch block connection destination x-coordinate + * @param to_y switch block connection destination y-coordinate + * @param to_chan_type switch block connection destination channel type (CHANX or CHANY) + * @param switch_override used to set the correct switch index for the RR graph edge + * @param sb_conn_map switch block permutation map + * @param rr_edges_to_create Total RR edges count + * @param edge_count number of RR edges that this function creates + */ +static void get_switchblocks_edges(RRGraphBuilder& rr_graph_builder, + const int tile_x, + const int tile_y, + const int layer, + const int max_chan_width, + const e_side from_side, + const int from_wire, + RRNodeId from_rr_node, + const e_side to_side, + const int to_x, + const int to_y, + const t_rr_type to_chan_type, + const int switch_override, + const int custom_3d_sb_fanin_fanout, + const int delayless_switch, + t_sb_connection_map* sb_conn_map, + vtr::NdMatrix& num_of_3d_conns_custom_SB, + t_rr_edge_info_set& rr_edges_to_create, + t_rr_edge_info_set& des_3d_rr_edges_to_create, + int& edge_count); + +/* + * @brief Figures out the edges that should connect the given wire segment to the given channel segment, adds these edges to 'rr_edge_to_create' + * + * @param rr_graph_builder RRGraphBuilder data structure which allows data modification on a routing resource graph + * @param layer the channel segment layer-coordinate + * @param max_chan_width number of tracks per channel + * @param from_track source track index (ptc_num) within the channel + * @param to_chan destination coordinate (x or y) based on chan type + * @param to_seg destination segment coordinate (x or y) based on chan type + * @param to_chan_type destination wire segment channel type (CHANX or CHANY) + * @param from_side swtich block connection source side + * @param to_side swtich block connection destination side + * @param swtich_override used to set the correct switch index for the RR graph edge + * @param sb_conn_map switch block permutation map, created based on the architecture file + * @param from_rr_node the source wire segment RRNodeID + * @param rr_edges_to_create keeps the created edges + * + * @return the number of edges added to 'rr_edge_to_create' + */ + static int get_track_to_chan_seg(RRGraphBuilder& rr_graph_builder, const int layer, + const int max_chan_width, const int from_track, const int to_chan, const int to_seg, @@ -102,9 +164,49 @@ static int get_track_to_chan_seg(RRGraphBuilder& rr_graph_builder, const e_side from_side, const e_side to_side, const int swtich_override, + const int custom_3d_sb_fanin_fanout, + const int delayless_switch, t_sb_connection_map* sb_conn_map, + vtr::NdMatrix& num_of_3d_conns_custom_SB, RRNodeId from_rr_node, - t_rr_edge_info_set& rr_edges_to_create); + t_rr_edge_info_set& rr_edges_to_create, + t_rr_edge_info_set& des_3d_rr_edges_to_create); + +/** + * @brief checks if a specific switch block edge is crossing any layer to create 3D custom switch blocks + * + * @param src_side switch block edge source side + * @param dest_side switch block edge destination side + * + * @return true if the connection going to another layer, false otherwise. + */ +static bool is_sb_conn_layer_crossing(enum e_side src_side, enum e_side dest_side); + +/** + * @brief finds corresponding RR nodes for a 3D SB edge and fill 3D custom switch block information (offset to correct extra CHANX nodes, source tracks, ..) + * + * @param rr_graph_builder RRGraphBuilder data structure which allows data modification on a routing resource graph + * @param x x-coordinate of both src and dest wires + * @param y y-coordinate of both src and dest wires + * @param from_wire from wire index within channel width + * @param from_wire_layer from wire layer-coordinate + * @param from_wire_type from wire type (CHANX/CHANY) + * @param to_wire to wire index within channel width + * @param to_wire_layer to wire layer-coordinate + * @param to_wire_type to wire type (CHANX/CHANY) + * @param curr_switchblocks_offset number of extra nodes that has already been added to the current switch block + * + * @ returns whether both source track RR node and destination RR node exist + */ +static bool check_3d_SB_RRnodes(RRGraphBuilder& rr_graph_builder, + int x, + int y, + int from_wire, + int from_wire_layer, + e_rr_type from_wire_type, + int to_wire, + int to_wire_layer, + e_rr_type to_wire_type); static int vpr_to_phy_track(const int itrack, const int chan_num, @@ -703,7 +805,7 @@ int get_bidir_opin_connections(RRGraphBuilder& rr_graph_builder, num_conn = 0; /* [0..device_ctx.num_block_types-1][0..num_pins-1][0..width][0..height][0..3][0..Fc-1] */ - for (e_side side : SIDES) { + for (e_side side : TOTAL_2D_SIDES) { /* Figure out coords of channel segment based on side */ tr_i = ((side == LEFT) ? (i - 1) : i); tr_j = ((side == BOTTOM) ? (j - 1) : j); @@ -1134,6 +1236,122 @@ static void load_chan_rr_indices(const int max_chan_width, } } +static bool is_sb_conn_layer_crossing(enum e_side src_side, enum e_side dest_side) { + if (src_side < NUM_2D_SIDES && dest_side < NUM_2D_SIDES) { + return false; + } + + if (src_side == dest_side) { + return false; + } + + return true; +} + +static bool check_3d_SB_RRnodes(RRGraphBuilder& rr_graph_builder, int x, int y, int from_wire, int from_wire_layer, e_rr_type from_wire_type, int to_wire, int to_wire_layer, e_rr_type to_wire_type) { + RRNodeId from_inode = rr_graph_builder.node_lookup().find_node(from_wire_layer, x, y, from_wire_type, from_wire); + RRNodeId to_inode = rr_graph_builder.node_lookup().find_node(to_wire_layer, x, y, to_wire_type, to_wire); + + if (from_inode && to_inode) { + return true; + } + + return false; +} + +vtr::NdMatrix get_number_track_to_track_inter_die_conn(t_sb_connection_map* sb_conn_map, + const int custom_3d_sb_fanin_fanout, + RRGraphBuilder& rr_graph_builder) { + auto& grid_ctx = g_vpr_ctx.device().grid; + vtr::NdMatrix extra_nodes_per_switchblocks; + extra_nodes_per_switchblocks.resize(std::array{grid_ctx.width(), grid_ctx.height()}, 0); + + for (size_t y = 0; y < grid_ctx.height(); y++) { + for (size_t x = 0; x < grid_ctx.width(); x++) { + for (auto layer = 0; layer < grid_ctx.get_num_layers(); layer++) { + int num_of_3d_conn = 0; + for (auto from_side : TOTAL_3D_SIDES) { + for (auto to_side : TOTAL_3D_SIDES) { + if (!is_sb_conn_layer_crossing(from_side, to_side)) { //this connection is not crossing any layer + continue; + } else { + Switchblock_Lookup sb_coord(x, y, layer, from_side, to_side); + if (sb_conn_map->count(sb_coord) > 0) { + std::vector& conn_vector = (*sb_conn_map)[sb_coord]; + for (int iconn = 0; iconn < (int)conn_vector.size(); ++iconn) { + //check if both from_node and to_node exists in the rr-graph + //CHANY -> CHANX connection + if(check_3d_SB_RRnodes(rr_graph_builder, x, y, conn_vector[iconn].from_wire, + conn_vector[iconn].from_wire_layer, CHANY, + conn_vector[iconn].to_wire, conn_vector[iconn].to_wire_layer, + CHANX)){ + num_of_3d_conn++; + } + //CHANX -> CHANY connection + if(check_3d_SB_RRnodes(rr_graph_builder, x, y, conn_vector[iconn].from_wire, + conn_vector[iconn].from_wire_layer, CHANX, + conn_vector[iconn].to_wire, conn_vector[iconn].to_wire_layer, + CHANY)){ + num_of_3d_conn++; + } + } + } + } + } + } + extra_nodes_per_switchblocks[x][y] += ((num_of_3d_conn + custom_3d_sb_fanin_fanout - 1)/ custom_3d_sb_fanin_fanout); + } + } + } + return extra_nodes_per_switchblocks; +} + +void alloc_and_load_inter_die_rr_node_indices(RRGraphBuilder& rr_graph_builder, + const t_chan_width* nodes_per_chan, + const DeviceGrid& grid, + const vtr::NdMatrix& extra_nodes_per_switchblock, + int* index) { + /* + * In case of multi-die FPGAs, we add extra nodes (could have used either CHANX or CHANY; we chose to use all CHANX) to + * support inter-die communication coming from switch blocks (connection between two tracks in different layers) + * The extra nodes have the following attribute: + * 1) type = CHANX + * 2) length = 0 (xhigh = xlow, yhigh = ylow) + * 3) ptc = [max_chanx_width:max_chanx_width+number_of_connection-1] + * 4) direction = NONE + */ + auto& device_ctx = g_vpr_ctx.device(); + + for (int layer = 0; layer < grid.get_num_layers(); layer++) { + /* Skip the current die if architecture file specifies that it doesn't have global resource routing */ + if (!device_ctx.inter_cluster_prog_routing_resources.at(layer)) { + continue; + } + for (size_t y = 0; y < grid.height() - 1; ++y) { + for (size_t x = 1; x < grid.width() - 1; ++x) { + //count how many track-to-track connection go from current layer to other layers + int conn_count = extra_nodes_per_switchblock[x][y]; + + //skip if no connection is required + if (conn_count == 0) { + continue; + } + + //reserve extra nodes for inter-die track-to-track connection + rr_graph_builder.node_lookup().reserve_nodes(layer, y, x, CHANX, conn_count + nodes_per_chan->max); + for (int rr_node_offset = 0; rr_node_offset < conn_count; rr_node_offset++) { + RRNodeId inode = rr_graph_builder.node_lookup().find_node(layer, x, y, CHANX, nodes_per_chan->max + rr_node_offset); + if (!inode) { + inode = RRNodeId(*index); + ++(*index); + rr_graph_builder.node_lookup().add_node(inode, layer, y, x, CHANX, nodes_per_chan->max + rr_node_offset); + } + } + } + } + } +} + /* As the rr_indices builders modify a local copy of indices, use the local copy in the builder * TODO: these building functions should only talk to a RRGraphBuilder object */ @@ -1215,7 +1433,7 @@ static void load_block_rr_indices(RRGraphBuilder& rr_graph_builder, * Deposit all the sides */ if (wanted_sides.empty()) { - for (e_side side : {TOP, BOTTOM, LEFT, RIGHT}) { + for (e_side side : TOTAL_2D_SIDES) { wanted_sides.push_back(side); } } @@ -1301,8 +1519,8 @@ static void add_classes_spatial_lookup(RRGraphBuilder& rr_graph_builder, int* index) { for (int x_tile = root_x; x_tile < (root_x + block_width); x_tile++) { for (int y_tile = root_y; y_tile < (root_y + block_height); y_tile++) { - rr_graph_builder.node_lookup().reserve_nodes(layer, x_tile, y_tile, SOURCE, class_num_vec.size(), SIDES[0]); - rr_graph_builder.node_lookup().reserve_nodes(layer, x_tile, y_tile, SINK, class_num_vec.size(), SIDES[0]); + rr_graph_builder.node_lookup().reserve_nodes(layer, x_tile, y_tile, SOURCE, class_num_vec.size(), TOTAL_2D_SIDES[0]); + rr_graph_builder.node_lookup().reserve_nodes(layer, x_tile, y_tile, SINK, class_num_vec.size(), TOTAL_2D_SIDES[0]); } } @@ -1352,9 +1570,9 @@ void alloc_and_load_rr_node_indices(RRGraphBuilder& rr_graph_builder, /* Alloc the lookup table */ for (t_rr_type rr_type : RR_TYPES) { if (rr_type == CHANX) { - rr_graph_builder.node_lookup().resize_nodes(grid.get_num_layers(), grid.height(), grid.width(), rr_type, NUM_SIDES); + rr_graph_builder.node_lookup().resize_nodes(grid.get_num_layers(), grid.height(), grid.width(), rr_type, NUM_2D_SIDES); } else { - rr_graph_builder.node_lookup().resize_nodes(grid.get_num_layers(), grid.width(), grid.height(), rr_type, NUM_SIDES); + rr_graph_builder.node_lookup().resize_nodes(grid.get_num_layers(), grid.width(), grid.height(), rr_type, NUM_2D_SIDES); } } @@ -1459,7 +1677,6 @@ bool verify_rr_node_indices(const DeviceGrid& grid, if (rr_graph.node_type(inode) == CHANX) { VTR_ASSERT_MSG(rr_graph.node_ylow(inode) == rr_graph.node_yhigh(inode), "CHANX should be horizontal"); - if (y != rr_graph.node_ylow(inode)) { VPR_ERROR(VPR_ERROR_ROUTE, "RR node y position does not agree between rr_nodes (%d) and rr_node_indices (%d): %s", rr_graph.node_ylow(inode), @@ -1533,8 +1750,8 @@ bool verify_rr_node_indices(const DeviceGrid& grid, * This check code should be invalid * if (rr_node.side() != side) { * VPR_ERROR(VPR_ERROR_ROUTE, "RR node xlow does not match between rr_nodes and rr_node_indices (%s/%s): %s", - * SIDE_STRING[rr_node.side()], - * SIDE_STRING[side], + * TOTAL_2D_SIDE_STRINGS[rr_node.side()], + * TOTAL_2D_SIDE_STRINGS[side], * describe_rr_node(rr_graph, grid, rr_indexed_data, inode).c_str()); * } else { * VTR_ASSERT(rr_node.side() == side); @@ -1702,12 +1919,16 @@ int get_track_to_tracks(RRGraphBuilder& rr_graph_builder, const DeviceGrid& grid, const int Fs_per_side, t_sblock_pattern& sblock_pattern, + vtr::NdMatrix& num_of_3d_conns_custom_SB, RRNodeId from_rr_node, t_rr_edge_info_set& rr_edges_to_create, + t_rr_edge_info_set& des_3d_rr_edges_to_create, const t_chan_seg_details* from_seg_details, const t_chan_seg_details* to_seg_details, const t_chan_details& to_chan_details, const enum e_directionality directionality, + const int custom_3d_sb_fanin_fanout, + const int delayless_switch, const vtr::NdMatrix, 3>& switch_block_conn, t_sb_connection_map* sb_conn_map) { int to_chan, to_sb; @@ -1837,10 +2058,10 @@ int get_track_to_tracks(RRGraphBuilder& rr_graph_builder, if (sb_seg < end_sb_seg) { if (custom_switch_block) { if (Direction::DEC == from_seg_details[from_track].direction() || BI_DIRECTIONAL == directionality) { - num_conn += get_track_to_chan_seg(rr_graph_builder, layer, from_track, to_chan, to_seg, + num_conn += get_track_to_chan_seg(rr_graph_builder, layer, max_chan_width, from_track, to_chan, to_seg, to_type, from_side_a, to_side, - switch_override, - sb_conn_map, from_rr_node, rr_edges_to_create); + switch_override, custom_3d_sb_fanin_fanout, delayless_switch, + sb_conn_map, num_of_3d_conns_custom_SB, from_rr_node, rr_edges_to_create, des_3d_rr_edges_to_create); } } else { if (BI_DIRECTIONAL == directionality) { @@ -1875,10 +2096,10 @@ int get_track_to_tracks(RRGraphBuilder& rr_graph_builder, if (sb_seg > start_sb_seg) { if (custom_switch_block) { if (Direction::INC == from_seg_details[from_track].direction() || BI_DIRECTIONAL == directionality) { - num_conn += get_track_to_chan_seg(rr_graph_builder, layer, from_track, to_chan, to_seg, + num_conn += get_track_to_chan_seg(rr_graph_builder, layer, max_chan_width, from_track, to_chan, to_seg, to_type, from_side_b, to_side, - switch_override, - sb_conn_map, from_rr_node, rr_edges_to_create); + switch_override,custom_3d_sb_fanin_fanout, delayless_switch, + sb_conn_map, num_of_3d_conns_custom_SB, from_rr_node, rr_edges_to_create, des_3d_rr_edges_to_create); } } else { if (BI_DIRECTIONAL == directionality) { @@ -2010,13 +2231,127 @@ static int get_bidir_track_to_chan_seg(RRGraphBuilder& rr_graph_builder, return num_conn; } -/* Figures out the edges that should connect the given wire segment to the given - * channel segment, adds these edges to 'edge_list' and returns the number of - * edges added . - * See route/build_switchblocks.c for a detailed description of how the switch block - * connection map sb_conn_map is generated. */ +static void get_switchblocks_edges(RRGraphBuilder& rr_graph_builder, + const int tile_x, + const int tile_y, + const int layer, + const int max_chan_width, + const e_side from_side, + const int from_wire, + RRNodeId from_rr_node, + const e_side to_side, + const int to_x, + const int to_y, + const t_rr_type to_chan_type, + const int switch_override, + const int custom_3d_sb_fanin_fanout, + const int delayless_switch, + t_sb_connection_map* sb_conn_map, + vtr::NdMatrix& num_of_3d_conns_custom_SB, + t_rr_edge_info_set& rr_edges_to_create, + t_rr_edge_info_set& des_3d_rr_edges_to_create, + int& edge_count) { + auto& device_ctx = g_vpr_ctx.device(); + + /* get coordinate to index into the SB map */ + Switchblock_Lookup sb_coord(tile_x, tile_y, layer, from_side, to_side); + if (sb_conn_map->count(sb_coord) > 0) { + /* get reference to the connections vector which lists all destination wires for a given source wire + * at a specific coordinate sb_coord */ + std::vector& conn_vector = (*sb_conn_map)[sb_coord]; + + /* go through the connections... */ + for (int iconn = 0; iconn < (int)conn_vector.size(); ++iconn) { + if (conn_vector.at(iconn).from_wire != from_wire) continue; + + int to_wire = conn_vector.at(iconn).to_wire; + int to_layer = conn_vector.at(iconn).to_wire_layer; + /* Get the index of the switch connecting the two wires */ + int src_switch = conn_vector[iconn].switch_ind; + /* Get the index of the switch connecting the two wires in two layers */ + int src_switch_betwen_layers = conn_vector[iconn].switch_ind_between_layers; + + if (to_layer == layer) { //track-to-track connection within the same layer + RRNodeId to_node = rr_graph_builder.node_lookup().find_node(to_layer, to_x, to_y, to_chan_type, to_wire); + + if (!to_node) { + continue; + } + //Apply any switch overrides + if (should_apply_switch_override(switch_override)) { + src_switch = switch_override; + } + + rr_edges_to_create.emplace_back(from_rr_node, to_node, src_switch, false); + ++edge_count; + + if (device_ctx.arch_switch_inf[src_switch].directionality() == BI_DIRECTIONAL) { + //Add reverse edge since bi-directional + rr_edges_to_create.emplace_back(to_node, from_rr_node, src_switch, false); + ++edge_count; + } + } else { //track-to_track connection crossing layer + VTR_ASSERT(to_layer != layer); + //check if current connection is valid, since switch block pattern is very general, + //we might see invalid layer in connection, so we just skip those + if ((layer < 0 || layer >= device_ctx.grid.get_num_layers()) || (to_layer < 0 || to_layer >= device_ctx.grid.get_num_layers())) { + continue; + } + + if (tile_x != to_x || tile_y != to_y) { + continue; + } + + /* + * In order to connect two tracks in different layers, we need to follow these three steps: + * 1) connect "from_tracks" to extra "chanx" node in the same switch blocks + * 2) connect extra "chanx" node located in from_layer to another extra "chanx" node located in to_layer + * 3) connect "chanx" node located in to_layer to "to_track" + * + * +-------------+ +-------------+ +--------------+ +--------------+ + * | from_wire | -----> | extra_chanx | ------> | extra_chanx | ------> | to_wire | + * | (src_layer) | | (src_layer) | | (dest_layer) | | (dest_layer) | + * +-------------+ +-------------+ +--------------+ +--------------+ + * + * */ + int offset = num_of_3d_conns_custom_SB[tile_x][tile_y] / custom_3d_sb_fanin_fanout; + RRNodeId track_to_chanx_node = rr_graph_builder.node_lookup().find_node(layer, tile_x, tile_y, CHANX, max_chan_width + offset); + RRNodeId diff_layer_chanx_node = rr_graph_builder.node_lookup().find_node(to_layer, tile_x, tile_y, CHANX, max_chan_width + offset); + RRNodeId chanx_to_track_node = rr_graph_builder.node_lookup().find_node(to_layer, to_x, to_y, to_chan_type, to_wire); + + if (!track_to_chanx_node || !diff_layer_chanx_node || !chanx_to_track_node) { + continue; + } + + //Apply any switch overrides + if (should_apply_switch_override(switch_override)) { + src_switch = switch_override; + } + + //add edge between source node at from layer to intermediate node + rr_edges_to_create.emplace_back(from_rr_node, track_to_chanx_node, delayless_switch, false); + ++edge_count; + + //add edge between intermediate node to destination node at to layer + //might add the same edge more than once + des_3d_rr_edges_to_create.emplace_back(diff_layer_chanx_node, chanx_to_track_node, src_switch_betwen_layers, false); + ++edge_count; + + //we only add the following edge between intermediate nodes once for the first 3D connection for each pair of intermediate nodes + if (num_of_3d_conns_custom_SB[tile_x][tile_y] % custom_3d_sb_fanin_fanout == 0) { + rr_edges_to_create.emplace_back(track_to_chanx_node, diff_layer_chanx_node, delayless_switch, false); + ++edge_count; + } + + num_of_3d_conns_custom_SB[tile_x][tile_y]++; + } + } + } +} + static int get_track_to_chan_seg(RRGraphBuilder& rr_graph_builder, const int layer, + const int max_chan_width, const int from_wire, const int to_chan, const int to_seg, @@ -2024,9 +2359,13 @@ static int get_track_to_chan_seg(RRGraphBuilder& rr_graph_builder, const e_side from_side, const e_side to_side, const int switch_override, + const int custom_3d_sb_fanin_fanout, + const int delayless_switch, t_sb_connection_map* sb_conn_map, + vtr::NdMatrix& num_of_3d_conns_custom_SB, RRNodeId from_rr_node, - t_rr_edge_info_set& rr_edges_to_create) { + t_rr_edge_info_set& rr_edges_to_create, + t_rr_edge_info_set& des_3d_rr_edges_to_create) { int edge_count = 0; int to_x, to_y; int tile_x, tile_y; @@ -2047,45 +2386,49 @@ static int get_track_to_chan_seg(RRGraphBuilder& rr_graph_builder, } } - /* get coordinate to index into the SB map */ - Switchblock_Lookup sb_coord(tile_x, tile_y, from_side, to_side); - if (sb_conn_map->count(sb_coord) > 0) { - /* get reference to the connections vector which lists all destination wires for a given source wire - * at a specific coordinate sb_coord */ - std::vector& conn_vector = (*sb_conn_map)[sb_coord]; - - /* go through the connections... */ - for (int iconn = 0; iconn < (int)conn_vector.size(); ++iconn) { - if (conn_vector.at(iconn).from_wire != from_wire) continue; - - int to_wire = conn_vector.at(iconn).to_wire; - RRNodeId to_node = rr_graph_builder.node_lookup().find_node(layer, to_x, to_y, to_chan_type, to_wire); - - if (!to_node) { - continue; - } - - /* Get the index of the switch connecting the two wires */ - int src_switch = conn_vector[iconn].switch_ind; - - //Apply any switch overrides - if (should_apply_switch_override(switch_override)) { - src_switch = switch_override; - } - - rr_edges_to_create.emplace_back(from_rr_node, to_node, src_switch, false); - ++edge_count; - - auto& device_ctx = g_vpr_ctx.device(); - - if (device_ctx.arch_switch_inf[src_switch].directionality() == BI_DIRECTIONAL) { - //Add reverse edge since bi-directional - rr_edges_to_create.emplace_back(to_node, from_rr_node, src_switch, false); - ++edge_count; - } - } - } else { - /* specified sb_conn_map entry does not exist -- do nothing */ + get_switchblocks_edges(rr_graph_builder, + tile_x, + tile_y, + layer, + max_chan_width, + from_side, + from_wire, + from_rr_node, + to_side, + to_x, + to_y, + to_chan_type, + switch_override, + custom_3d_sb_fanin_fanout, + delayless_switch, + sb_conn_map, + num_of_3d_conns_custom_SB, + rr_edges_to_create, + des_3d_rr_edges_to_create, + edge_count); + + //check sb_conn_map for connections between two layers + for (e_side to_another_die_side : {ABOVE, UNDER}) { + get_switchblocks_edges(rr_graph_builder, + tile_x, + tile_y, + layer, + max_chan_width, + from_side, + from_wire, + from_rr_node, + to_another_die_side, + to_x, + to_y, + to_chan_type, + switch_override, + custom_3d_sb_fanin_fanout, + delayless_switch, + sb_conn_map, + num_of_3d_conns_custom_SB, + rr_edges_to_create, + des_3d_rr_edges_to_create, + edge_count); } return edge_count; } @@ -2411,15 +2754,15 @@ void load_sblock_pattern_lookup(const int i, /* SB's range from (0, 0) to (grid.width() - 2, grid.height() - 2) */ /* First find all four sides' incoming wires */ - static_assert(NUM_SIDES == 4, "Should be 4 sides"); - std::array, NUM_SIDES> wire_mux_on_track; - std::array, NUM_SIDES> incoming_wire_label; - int num_incoming_wires[NUM_SIDES]; - int num_ending_wires[NUM_SIDES]; - int num_wire_muxes[NUM_SIDES]; + static_assert(NUM_2D_SIDES == 4, "Should be 4 sides"); + std::array, NUM_2D_SIDES> wire_mux_on_track; + std::array, NUM_2D_SIDES> incoming_wire_label; + int num_incoming_wires[NUM_2D_SIDES]; + int num_ending_wires[NUM_2D_SIDES]; + int num_wire_muxes[NUM_2D_SIDES]; /* "Label" the wires around the switch block by connectivity. */ - for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { + for (e_side side : TOTAL_2D_SIDES) { /* Assume the channel segment doesn't exist. */ wire_mux_on_track[side].clear(); incoming_wire_label[side].clear(); @@ -2493,7 +2836,7 @@ void load_sblock_pattern_lookup(const int i, false, wire_mux_on_track[side], &num_wire_muxes[side], &dummy); } - for (e_side to_side : {TOP, RIGHT, BOTTOM, LEFT}) { + for (e_side to_side : TOTAL_2D_SIDES) { /* Can't do anything if no muxes on this side. */ if (num_wire_muxes[to_side] == 0) continue; @@ -2756,7 +3099,7 @@ static void label_incoming_wires(const int chan_num, static int find_label_of_track(const std::vector& wire_mux_on_track, int num_wire_muxes, int from_track) { - /* Returns the index/label in array wire_mux_on_track whose entry equals from_track. If none are + /* Returns the index/label in array wire_mux_on_track whose entry equals from_tracks. If none are * found, then returns the index of the entry whose value is the largest */ int i_label = -1; int max_track = -1; diff --git a/vpr/src/route/rr_graph2.h b/vpr/src/route/rr_graph2.h index b1651f6ccda..348e5633708 100644 --- a/vpr/src/route/rr_graph2.h +++ b/vpr/src/route/rr_graph2.h @@ -29,6 +29,22 @@ void alloc_and_load_rr_node_indices(RRGraphBuilder& rr_graph_builder, const t_chan_details& chan_details_y, bool is_flat); +/** + * @brief allocates extra nodes within the RR graph to support 3D custom switch blocks for multi-die FPGAs + * + * @param rr_graph_builder RRGraphBuilder data structure which allows data modification on a routing resource graph + * @param nodes_per_chan number of tracks per channel (x, y) + * @param grid device grid + * @param extra_nodes_per_switchblock keeps how many extra length-0 CHANX node is required for each unique (x,y) location within the grid. + * Number of these extra nodes are exactly the same for all layers. Hence, we only keep it for one layer. ([0..grid.width-1][0..grid.height-1) + * @param index RRNodeId that should be assigned to add a new RR node to the RR graph + */ +void alloc_and_load_inter_die_rr_node_indices(RRGraphBuilder& rr_graph_builder, + const t_chan_width* nodes_per_chan, + const DeviceGrid& grid, + const vtr::NdMatrix& extra_nodes_per_switchblock, + int* index); + void alloc_and_load_tile_rr_node_indices(RRGraphBuilder& rr_graph_builder, t_physical_tile_type_ptr physical_tile, int layer, @@ -61,7 +77,18 @@ int get_rr_node_index(const t_rr_node_indices& L_rr_node_indices, int y, t_rr_type rr_type, int ptc, - e_side side = NUM_SIDES); + e_side side = NUM_2D_SIDES); +/** + * @brief goes through 3D custom switch blocks and counts how many connections are crossing dice for each switch block. + * + * @param sb_conn_map switch block permutation map + * @param rr_graph_builder RRGraphBuilder data structure which allows data modification on a routing resource graph + * + * @return number of die-crossing connection for each unique (x, y) location within the grid ([0..grid.width-1][0..grid.height-1]) + */ +vtr::NdMatrix get_number_track_to_track_inter_die_conn(t_sb_connection_map* sb_conn_map, + const int custom_3d_sb_fanin_fanout, + RRGraphBuilder& rr_graph_builder); int find_average_rr_node_index(int device_width, int device_height, @@ -182,12 +209,16 @@ int get_track_to_tracks(RRGraphBuilder& rr_graph_builder, const DeviceGrid& grid, const int Fs_per_side, t_sblock_pattern& sblock_pattern, + vtr::NdMatrix& num_of_3d_conns_custom_SB, RRNodeId from_rr_node, t_rr_edge_info_set& rr_edges_to_create, + t_rr_edge_info_set& des_3d_rr_edges_to_create, const t_chan_seg_details* from_seg_details, const t_chan_seg_details* to_seg_details, const t_chan_details& to_chan_details, const enum e_directionality directionality, + const int custom_3d_sb_fanin_fanout, + const int delayless_switch, const vtr::NdMatrix, 3>& switch_block_conn, t_sb_connection_map* sb_conn_map); diff --git a/vpr/src/route/rr_graph_sbox.cpp b/vpr/src/route/rr_graph_sbox.cpp index 22a4db0c4be..0e57727f056 100644 --- a/vpr/src/route/rr_graph_sbox.cpp +++ b/vpr/src/route/rr_graph_sbox.cpp @@ -24,8 +24,8 @@ * BOTTOM (CHANY) */ /* [0..3][0..3][0..nodes_per_chan-1]. Structure below is indexed as: * - * [from_side][to_side][from_track]. That yields an integer vector (ivec) * - * of the tracks to which from_track connects in the proper to_location. * + * [from_side][to_side][from_tracks]. That yields an integer vector (ivec) * + * of the tracks to which from_tracks connects in the proper to_location. * * For simple switch boxes this is overkill, but it will allow complicated * * switch boxes with Fs > 3, etc. without trouble. */ @@ -40,8 +40,8 @@ vtr::NdMatrix, 3> alloc_and_load_switch_block_conn(t_chan_width vtr::NdMatrix, 3> switch_block_conn({4, 4, (size_t)nodes_per_chan->max}); - for (e_side from_side : {TOP, RIGHT, BOTTOM, LEFT}) { - for (e_side to_side : {TOP, RIGHT, BOTTOM, LEFT}) { + for (e_side from_side : TOTAL_2D_SIDES) { + for (e_side to_side : TOTAL_2D_SIDES) { int from_chan_width = (from_side == TOP || from_side == BOTTOM) ? nodes_per_chan->y_max : nodes_per_chan->x_max; int to_chan_width = (to_side == TOP || to_side == BOTTOM) ? nodes_per_chan->y_max : nodes_per_chan->x_max; for (int from_track = 0; from_track < from_chan_width; from_track++) { @@ -95,7 +95,7 @@ int get_simple_switch_block_track(const enum e_side from_side, const enum e_switch_block_type switch_block_type, const int from_chan_width, const int to_chan_width) { - /* This routine returns the track number to which the from_track should * + /* This routine returns the track number to which the from_tracks should * * connect. It supports three simple, Fs = 3, switch blocks. */ int to_track = SBOX_ERROR; /* Can check to see if it's not set later. */ diff --git a/vpr/src/route/rr_graph_timing_params.cpp b/vpr/src/route/rr_graph_timing_params.cpp index f89b8aba9a0..ae316cef2c7 100644 --- a/vpr/src/route/rr_graph_timing_params.cpp +++ b/vpr/src/route/rr_graph_timing_params.cpp @@ -58,7 +58,7 @@ void add_rr_graph_C_from_switches(float C_ipin_cblock) { from_rr_type = rr_graph.node_type(rr_id); - if (from_rr_type == CHANX || from_rr_type == CHANY) { + if ((from_rr_type == CHANX || from_rr_type == CHANY)) { for (t_edge_size iedge = 0; iedge < rr_graph.num_edges(rr_id); iedge++) { to_node = size_t(rr_graph.edge_sink_node(rr_id, iedge)); to_rr_type = rr_graph.node_type(RRNodeId(to_node)); diff --git a/vpr/src/util/vpr_utils.cpp b/vpr/src/util/vpr_utils.cpp index 0285a42a5da..e4ae8e996e5 100644 --- a/vpr/src/util/vpr_utils.cpp +++ b/vpr/src/util/vpr_utils.cpp @@ -2201,7 +2201,7 @@ RRNodeId get_pin_rr_node_id(const RRSpatialLookup& rr_spatial_lookup, std::vector x_offset; std::vector y_offset; std::vector pin_sides; - std::tie(x_offset, y_offset, pin_sides) = get_pin_coordinates(physical_tile, pin_physical_num, std::vector(SIDES.begin(), SIDES.end())); + std::tie(x_offset, y_offset, pin_sides) = get_pin_coordinates(physical_tile, pin_physical_num, std::vector(TOTAL_2D_SIDES.begin(), TOTAL_2D_SIDES.end())); VTR_ASSERT(!x_offset.empty()); RRNodeId node_id = RRNodeId::INVALID(); for (int coord_idx = 0; coord_idx < (int)pin_sides.size(); coord_idx++) { diff --git a/vtr_flow/arch/multi_die/stratixiv_3d/3d_SB_inter_die_stratixiv_arch.timing.xml b/vtr_flow/arch/multi_die/stratixiv_3d/3d_SB_inter_die_stratixiv_arch.timing.xml new file mode 100644 index 00000000000..d671996b6f4 --- /dev/null +++ b/vtr_flow/arch/multi_die/stratixiv_3d/3d_SB_inter_die_stratixiv_arch.timing.xml @@ -0,0 +1,48370 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.core_out[0:19] io.core_in[0:24] io.clk[0:2] + io.core_out[20:39] io.core_in[24:49] io.clk[3:4] + io.core_out[0:19] io.core_in[0:24] io.clk[0:2] + io.core_in[24:49] io.clk[3:4] io.core_out[20:39] + + + + + + + + + + + + + + + PLL.out_clock PLL.out_signal PLL.in_signal PLL.in_clock + PLL.out_clock PLL.out_signal PLL.in_signal PLL.in_clock + PLL.out_clock PLL.out_signal PLL.in_signal PLL.in_clock + PLL.out_clock PLL.out_signal PLL.in_signal PLL.in_clock + + + + + + + + + + + + + + + + + + + + + + + + + + + LAB.data_out[0:19] LAB.data_in[0:39] LAB.control_in[0:3] LAB.clk[0] + LAB.data_out[20:39] LAB.data_in[40:79] LAB.control_in[4:6] LAB.clk[1] + LAB.data_out LAB.data_in LAB.control_in LAB.clk LAB.cin LAB.sharein + LAB.cout LAB.shareout + + + + + + + + + + + + + + + + + + + + + + + + + + + DSP.data_out_top[17:0] DSP.scan_a_out[1:0] DSP.data_in[35:0] DSP.control_in[2:0] DSP.scan_a_in[1:0] DSP.clk[0] + DSP.data_out_top[35:18] DSP.scan_a_out[3:2] DSP.signal_out[0:0] DSP.data_in[71:36] DSP.control_in[4:3] DSP.scan_a_in[4:2] + DSP.data_out_top[53:36] DSP.scan_a_out[5:4] DSP.signal_out[1:1] DSP.data_in[107:72] DSP.control_in[6:5] DSP.scan_a_in[6:5] DSP.clk[1] + DSP.data_out_top[71:54] DSP.scan_a_out[7:6] DSP.signal_out[2:2] DSP.data_in[144:108] DSP.control_in[8:6] DSP.scan_a_in[8:7] + DSP.data_out_top[35:0] DSP.scan_a_out[3:0] DSP.signal_out[0:0] DSP.data_in[143:72] DSP.control_in[3:0] DSP.scan_a_in[3:0] DSP.clk[0] DSP.chain_in + DSP.data_out_top[71:36] DSP.scan_a_out[9:4] DSP.signal_out[2:1] DSP.data_in[71:0] DSP.control_in[8:4] DSP.scan_a_in[8:4] DSP.clk[1] + + + + DSP.data_out_bot[17:0] DSP.scan_a_out[9:8] DSP.data_in[179:145] DSP.control_in[11:9] DSP.scan_a_in[10:9] DSP.clk[2] + DSP.data_out_bot[35:18] DSP.scan_a_out[11:10] DSP.signal_out[3:3] DSP.data_in[215:180] DSP.control_in[13:12] DSP.scan_a_in[12:11] + DSP.data_in[251:216] DSP.control_in[15:14] DSP.scan_a_in[14:13] DSP.data_out_bot[53:36] DSP.scan_a_out[13:12] DSP.signal_out[4:4] + DSP.data_in[287:252] DSP.control_in[20:16] DSP.scan_a_in[17:15] DSP.clk[3] DSP.data_out_bot[71:54] DSP.scan_a_out[15:14] DSP.signal_out[5:5] + DSP.data_in[287:215] DSP.control_in[13:9] DSP.scan_a_in[13:9] DSP.clk[2] DSP.data_out_bot[35:0] DSP.scan_a_out[12:10] DSP.signal_out[3:3] + DSP.data_in[214:144] DSP.control_in[20:16] DSP.scan_a_in[17:14] DSP.clk[3] DSP.data_out_bot[71:36] DSP.scan_a_out[17:13] DSP.signal_out[5:4] + + DSP.chain_out + + + + + + + + + + + + + + + + + + M9K.data_out[17:0] M9K.control_out[1:0] M9K.data_addr_control_in[51:0] M9K.clk_in[0] + M9K.data_out[35:18] M9K.control_out[2:2] M9K.data_addr_control_in[103:52] M9K.clk_in[1] + M9K.data_out M9K.control_out M9K.data_addr_control_in M9K.clk_in + + + + + + + + + + + + + + + + + + + M144K.data_out[7:0] M144K.data_addr_control_in[24:0] + M144K.data_out[15:8] M144K.data_addr_control_in[50:25] + M144K.data_out[22:16] M144K.data_addr_control_in[76:51] + M144K.data_out[29:23] + M144K.data_out[36:30] M144K.control_out[0] M144K.data_addr_control_in[128:103] M144K.clk_in[0] + M144K.data_out[43:37] M144K.control_out[1] M144K.data_addr_control_in[154:129] + M144K.data_out[51:44] M144K.data_addr_control_in[180:155] + M144K.data_out[59:52] M144K.data_addr_control_in[206:181] + M144K.data_out[67:60] M144K.data_addr_control_in[232:207] + M144K.data_out[75:68] M144K.data_addr_control_in[258:233] + M144K.data_out[82:76] M144K.data_addr_control_in[284:259] + M144K.data_out[89:83] M144K.control_out[2] M144K.data_addr_control_in[310:285] + M144K.data_out[96:90] M144K.data_addr_control_in[336:311] M144K.clk_in[1] + M144K.data_out[103:97] M144K.data_addr_control_in[362:337] + M144K.data_out[111:104] M144K.data_addr_control_in[388:363] + M144K.data_out[119:112] M144K.data_addr_control_in[415:389] + M144K.data_out[14:0] M144K.data_addr_control_in[51:0] + M144K.data_out[29:15] M144K.data_addr_control_in[103:52] + M144K.data_out[44:30] M144K.control_out[0] M144K.data_addr_control_in[155:104] + M144K.data_out[59:45] M144K.control_out[1] M144K.data_addr_control_in[207:156] M144K.clk_in[0] + M144K.data_addr_control_in[259:208] M144K.clk_in[1] M144K.data_out[74:60] M144K.control_out[2] + M144K.data_out[89:75] M144K.data_addr_control_in[311:260] + M144K.data_out[104:90] M144K.data_addr_control_in[363:312] + M144K.data_out[119:105] M144K.data_addr_control_in[415:364] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 + + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + 193e-12 + 193e-12 + 193e-12 + 193e-12 + 193e-12 + 193e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_verify_rr_graph_3d/config/config.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_verify_rr_graph_3d/config/config.txt new file mode 100644 index 00000000000..3897e7c8b98 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_verify_rr_graph_3d/config/config.txt @@ -0,0 +1,28 @@ +############################################## +# Configuration file for running experiments +############################################## + +# Path to directory of circuits to use +circuits_dir=benchmarks/titan_blif/other_benchmarks/stratixiv + +# Path to directory of architectures to use +archs_dir=arch/multi_die/simple_arch + +# Add circuits to list to sweep +circuit_list_add=ucsb_152_tap_fir_stratixiv_arch_timing.blif + +# Add architectures to list to sweep +arch_list_add=k6_frac_N10_40nm.xml + +# Parse info and how to parse +parse_file=vpr_fixed_chan_width.txt +parse_file=vpr_parse_second_file.txt + +# How to parse QoR info +qor_parse_file=qor_rr_graph.txt + +# Pass requirements +pass_requirements_file=pass_requirements_verify_rr_graph.txt + +# Script parameters +script_params = -verify_rr_graph --route_chan_width 100 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_verify_rr_graph_3d/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_verify_rr_graph_3d/config/golden_results.txt new file mode 100644 index 00000000000..3d6020c77de --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_verify_rr_graph_3d/config/golden_results.txt @@ -0,0 +1,3 @@ + 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 routed_wirelength total_nets_routed total_connections_routed total_heap_pushes total_heap_pops logic_block_area_total logic_block_area_used routing_area_total routing_area_per_tile crit_path_route_success_iteration critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS route_mem crit_path_route_time crit_path_total_timing_analysis_time crit_path_total_sta_time router_lookahead_mem router_lookahead_computation_time + k4_N4_90nm.xml stereovision3.v common 1.67 vpr 56.43 MiB -1 -1 0.45 25384 6 0.13 -1 -1 35724 -1 -1 26 10 -1 -1 success v8.0.0-6989-g4a9293e1e-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 11.3.0 on Linux-5.15.0-58-generic x86_64 2023-02-04T01:37:29 dev /home/dev/Desktop/CAS-Atlantic/vtr-verilog-to-routing 57788 10 2 186 188 1 49 38 8 8 64 clb auto 18.2 MiB 0.02 224 56.4 MiB 0.01 0.00 2.40278 -103.067 -2.40278 2.40278 0.00 0.000119899 9.7708e-05 0.00253947 0.0021251 203 186 417 22870 4288 80255.5 57962.3 276194. 4315.53 12 2.46522 2.46522 -111.211 -2.46522 -0.0734 -0.0734 56.4 MiB 0.01 0.00769657 0.00673067 56.4 MiB 0.03 + k6_frac_N10_40nm.xml stereovision3.v common 1.49 vpr 57.75 MiB -1 -1 0.45 25728 5 0.13 -1 -1 35988 -1 -1 7 10 -1 -1 success v8.0.0-6989-g4a9293e1e-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 11.3.0 on Linux-5.15.0-58-generic x86_64 2023-02-04T01:37:29 dev /home/dev/Desktop/CAS-Atlantic/vtr-verilog-to-routing 59136 10 2 181 183 1 37 19 5 5 25 clb auto 19.5 MiB 0.05 109 57.8 MiB 0.01 0.00 1.93928 -79.4364 -1.93928 1.93928 0.00 0.000117405 9.5811e-05 0.00377247 0.00328288 95 62 85 1742 528 485046 377258 99699.4 3987.98 5 2.07705 2.07705 -87.1807 -2.07705 0 0 57.8 MiB 0.01 0.00934793 0.00856098 57.8 MiB 0.01