| #include <cstdio> |
| |
| #include "vtr_util.h" |
| #include "vtr_assert.h" |
| #include "vtr_log.h" |
| #include "vtr_memory.h" |
| |
| #include "vpr_types.h" |
| #include "vpr_error.h" |
| |
| #include "globals.h" |
| #include "rr_graph_util.h" |
| #include "rr_graph2.h" |
| #include "rr_graph_sbox.h" |
| #include "read_xml_arch_file.h" |
| #include "rr_types.h" |
| |
| constexpr short UN_SET = -1; |
| |
| /************************** Subroutines local to this module ****************/ |
| |
| static void get_switch_type(bool is_from_sb, |
| bool is_to_sb, |
| short from_node_switch, |
| short to_node_switch, |
| const int switch_override, |
| short switch_types[2]); |
| |
| static void load_chan_rr_indices(const int max_chan_width, |
| const int chan_len, |
| const int num_chans, |
| const t_rr_type type, |
| const t_chan_details& chan_details, |
| t_rr_node_indices& indices, |
| int* index); |
| |
| static void load_block_rr_indices(const DeviceGrid& grid, |
| t_rr_node_indices& indices, |
| int* index); |
| |
| static int get_bidir_track_to_chan_seg(const std::vector<int> conn_tracks, |
| const t_rr_node_indices& L_rr_node_indices, |
| const int to_chan, |
| const int to_seg, |
| const int to_sb, |
| const t_rr_type to_type, |
| const t_chan_seg_details* seg_details, |
| const bool from_is_sblock, |
| const int from_switch, |
| const int switch_override, |
| const enum e_directionality directionality, |
| const int from_rr_node, |
| t_rr_edge_info_set& rr_edges_to_create); |
| |
| static int get_unidir_track_to_chan_seg(const int from_track, |
| const int to_chan, |
| const int to_seg, |
| const int to_sb, |
| const t_rr_type to_type, |
| const int max_chan_width, |
| const DeviceGrid& grid, |
| const enum e_side from_side, |
| const enum e_side to_side, |
| const int Fs_per_side, |
| t_sblock_pattern& sblock_pattern, |
| const int switch_override, |
| const t_rr_node_indices& L_rr_node_indices, |
| const t_chan_seg_details* seg_details, |
| bool* Fs_clipped, |
| const int from_rr_node, |
| t_rr_edge_info_set& rr_edges_to_create); |
| |
| static int get_track_to_chan_seg(const int from_track, |
| const int to_chan, |
| const int to_seg, |
| const t_rr_type to_chan_type, |
| const e_side from_side, |
| const e_side to_side, |
| const int swtich_override, |
| const t_rr_node_indices& L_rr_node_indices, |
| t_sb_connection_map* sb_conn_map, |
| const int from_rr_node, |
| t_rr_edge_info_set& rr_edges_to_create); |
| |
| static int vpr_to_phy_track(const int itrack, |
| const int chan_num, |
| const int seg_num, |
| const t_chan_seg_details* seg_details, |
| const enum e_directionality directionality); |
| |
| static int* label_wire_muxes(const int chan_num, |
| const int seg_num, |
| const t_chan_seg_details* seg_details, |
| const int seg_type_index, |
| const int max_len, |
| const enum e_direction dir, |
| const int max_chan_width, |
| const bool check_cb, |
| int* num_wire_muxes, |
| int* num_wire_muxes_cb_restricted); |
| |
| static int* label_incoming_wires(const int chan_num, |
| const int seg_num, |
| const int sb_seg, |
| const t_chan_seg_details* seg_details, |
| const int max_len, |
| const enum e_direction dir, |
| const int max_chan_width, |
| int* num_incoming_wires, |
| int* num_ending_wires); |
| |
| static int find_label_of_track(int* wire_mux_on_track, |
| int num_wire_muxes, |
| int from_track); |
| |
| void dump_seg_details(t_seg_details* seg_details, |
| int max_chan_width, |
| const char* fname); |
| |
| //Returns how the switch type for the switch block at the specified location should be created |
| // grid: The device grid |
| // from_chan_coord: The horizontal or vertical channel index (i.e. x-coord for CHANY, y-coord for CHANX) |
| // from_seg_coord: The horizontal or vertical location along the channel (i.e. y-coord for CHANY, x-coord for CHANX) |
| // from_chan_type: The from channel type |
| // to_chan_type: The to channel type |
| static int should_create_switchblock(const DeviceGrid& grid, int from_chan_coord, int from_seg_coord, t_rr_type from_chan_type, t_rr_type to_chan_type); |
| |
| static bool should_apply_switch_override(int switch_override); |
| |
| /******************** Subroutine definitions *******************************/ |
| |
| /* This assigns tracks (individually or pairs) to segment types. |
| * It tries to match requested ratio. If use_full_seg_groups is |
| * true, then segments are assigned only in multiples of their |
| * length. This is primarily used for making a tileable unidir |
| * layout. The effect of using this is that the number of tracks |
| * requested will not always be met and the result will sometimes |
| * be over and sometimes under. |
| * The pattern when using use_full_seg_groups is to keep adding |
| * one group of the track type that wants the largest number of |
| * groups of tracks. Each time a group is assigned, the types |
| * demand is reduced by 1 unit. The process stops when we are |
| * no longer less than the requested number of tracks. As a final |
| * step, if we were closer to target before last more, undo it |
| * and end up with a result that uses fewer tracks than given. */ |
| int* get_seg_track_counts(const int num_sets, |
| const std::vector<t_segment_inf>& segment_inf, |
| const bool use_full_seg_groups) { |
| int* result; |
| double* demand; |
| int imax, freq_sum, assigned, size; |
| double scale, max, reduce; |
| |
| result = (int*)vtr::malloc(sizeof(int) * segment_inf.size()); |
| demand = (double*)vtr::malloc(sizeof(double) * segment_inf.size()); |
| |
| /* Scale factor so we can divide by any length |
| * and still use integers */ |
| scale = 1; |
| freq_sum = 0; |
| for (size_t i = 0; i < segment_inf.size(); ++i) { |
| scale *= segment_inf[i].length; |
| freq_sum += segment_inf[i].frequency; |
| } |
| reduce = scale * freq_sum; |
| |
| /* Init assignments to 0 and set the demand values */ |
| for (size_t i = 0; i < segment_inf.size(); ++i) { |
| result[i] = 0; |
| demand[i] = scale * num_sets * segment_inf[i].frequency; |
| if (use_full_seg_groups) { |
| demand[i] /= segment_inf[i].length; |
| } |
| } |
| |
| /* Keep assigning tracks until we use them up */ |
| assigned = 0; |
| size = 0; |
| imax = 0; |
| while (assigned < num_sets) { |
| /* Find current maximum demand */ |
| max = 0; |
| for (size_t i = 0; i < segment_inf.size(); ++i) { |
| if (demand[i] > max) { |
| imax = i; |
| max = demand[i]; |
| } |
| } |
| |
| /* Assign tracks to the type and reduce the types demand */ |
| size = (use_full_seg_groups ? segment_inf[imax].length : 1); |
| demand[imax] -= reduce; |
| result[imax] += size; |
| assigned += size; |
| } |
| |
| /* Undo last assignment if we were closer to goal without it */ |
| if ((assigned - num_sets) > (size / 2)) { |
| result[imax] -= size; |
| } |
| |
| /* Free temps */ |
| if (demand) { |
| vtr::free(demand); |
| demand = nullptr; |
| } |
| |
| /* This must be freed by caller */ |
| return result; |
| } |
| |
| t_seg_details* alloc_and_load_seg_details(int* max_chan_width, |
| const int max_len, |
| const std::vector<t_segment_inf>& segment_inf, |
| const bool use_full_seg_groups, |
| const bool is_global_graph, |
| const enum e_directionality directionality, |
| int* num_seg_details) { |
| /* Allocates and loads the seg_details data structure. Max_len gives the * |
| * maximum length of a segment (dimension of array). The code below tries * |
| * to: * |
| * (1) stagger the start points of segments of the same type evenly; * |
| * (2) spread out the limited number of connection boxes or switch boxes * |
| * evenly along the length of a segment, starting at the segment ends; * |
| * (3) stagger the connection and switch boxes on different long lines, * |
| * as they will not be staggered by different segment start points. */ |
| |
| int cur_track, ntracks, itrack, length, j, index; |
| int arch_wire_switch, arch_opin_switch, fac, num_sets, tmp; |
| int group_start, first_track; |
| int* sets_per_seg_type = nullptr; |
| t_seg_details* seg_details = nullptr; |
| bool longline; |
| |
| /* Unidir tracks are assigned in pairs, and bidir tracks individually */ |
| if (directionality == BI_DIRECTIONAL) { |
| fac = 1; |
| } else { |
| VTR_ASSERT(directionality == UNI_DIRECTIONAL); |
| fac = 2; |
| } |
| |
| if (*max_chan_width % fac != 0) { |
| VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Routing channel width must be divisible by %d (channel width was %d)", fac, *max_chan_width); |
| } |
| |
| /* Map segment type fractions and groupings to counts of tracks */ |
| sets_per_seg_type = get_seg_track_counts((*max_chan_width / fac), |
| segment_inf, use_full_seg_groups); |
| |
| /* Count the number tracks actually assigned. */ |
| tmp = 0; |
| for (size_t i = 0; i < segment_inf.size(); ++i) { |
| tmp += sets_per_seg_type[i] * fac; |
| } |
| VTR_ASSERT(use_full_seg_groups || (tmp == *max_chan_width)); |
| *max_chan_width = tmp; |
| |
| seg_details = new t_seg_details[*max_chan_width]; |
| |
| /* Setup the seg_details data */ |
| cur_track = 0; |
| for (size_t i = 0; i < segment_inf.size(); ++i) { |
| first_track = cur_track; |
| |
| num_sets = sets_per_seg_type[i]; |
| ntracks = fac * num_sets; |
| if (ntracks < 1) { |
| continue; |
| } |
| /* Avoid divide by 0 if ntracks */ |
| longline = segment_inf[i].longline; |
| length = segment_inf[i].length; |
| if (longline) { |
| length = max_len; |
| } |
| |
| arch_wire_switch = segment_inf[i].arch_wire_switch; |
| arch_opin_switch = segment_inf[i].arch_opin_switch; |
| VTR_ASSERT((arch_wire_switch == arch_opin_switch) || (directionality != UNI_DIRECTIONAL)); |
| |
| /* Set up the tracks of same type */ |
| group_start = 0; |
| for (itrack = 0; itrack < ntracks; itrack++) { |
| /* set the name of the segment type this track belongs to */ |
| seg_details[cur_track].type_name = segment_inf[i].name; |
| |
| /* Remember the start track of the current wire group */ |
| if ((itrack / fac) % length == 0 && (itrack % fac) == 0) { |
| group_start = cur_track; |
| } |
| |
| seg_details[cur_track].length = length; |
| seg_details[cur_track].longline = longline; |
| |
| /* Stagger the start points in for each track set. The |
| * pin mappings should be aware of this when chosing an |
| * intelligent way of connecting pins and tracks. |
| * cur_track is used as an offset so that extra tracks |
| * from different segment types are hopefully better |
| * balanced. */ |
| seg_details[cur_track].start = (cur_track / fac) % length + 1; |
| |
| /* These properties are used for vpr_to_phy_track to determine |
| * * twisting of wires. */ |
| seg_details[cur_track].group_start = group_start; |
| seg_details[cur_track].group_size = std::min(ntracks + first_track - group_start, length * fac); |
| VTR_ASSERT(0 == seg_details[cur_track].group_size % fac); |
| if (0 == seg_details[cur_track].group_size) { |
| seg_details[cur_track].group_size = length * fac; |
| } |
| |
| seg_details[cur_track].seg_start = -1; |
| seg_details[cur_track].seg_end = -1; |
| |
| /* Setup the cb and sb patterns. Global route graphs can't depopulate cb and sb |
| * since this is a property of a detailed route. */ |
| seg_details[cur_track].cb = std::make_unique<bool[]>(length); |
| seg_details[cur_track].sb = std::make_unique<bool[]>(length + 1); |
| for (j = 0; j < length; ++j) { |
| if (is_global_graph || seg_details[cur_track].longline) { |
| seg_details[cur_track].cb[j] = true; |
| } else { |
| /* Use the segment's pattern. */ |
| index = j % segment_inf[i].cb.size(); |
| seg_details[cur_track].cb[j] = segment_inf[i].cb[index]; |
| } |
| } |
| for (j = 0; j < (length + 1); ++j) { |
| if (is_global_graph || seg_details[cur_track].longline) { |
| seg_details[cur_track].sb[j] = true; |
| } else { |
| /* Use the segment's pattern. */ |
| index = j % segment_inf[i].sb.size(); |
| seg_details[cur_track].sb[j] = segment_inf[i].sb[index]; |
| } |
| } |
| |
| seg_details[cur_track].Rmetal = segment_inf[i].Rmetal; |
| seg_details[cur_track].Cmetal = segment_inf[i].Cmetal; |
| //seg_details[cur_track].Cmetal_per_m = segment_inf[i].Cmetal_per_m; |
| |
| seg_details[cur_track].arch_wire_switch = arch_wire_switch; |
| seg_details[cur_track].arch_opin_switch = arch_opin_switch; |
| |
| if (BI_DIRECTIONAL == directionality) { |
| seg_details[cur_track].direction = BI_DIRECTION; |
| } else { |
| VTR_ASSERT(UNI_DIRECTIONAL == directionality); |
| seg_details[cur_track].direction = (itrack % 2) ? DEC_DIRECTION : INC_DIRECTION; |
| } |
| |
| seg_details[cur_track].index = i; |
| |
| ++cur_track; |
| } |
| } /* End for each segment type. */ |
| |
| /* free variables */ |
| vtr::free(sets_per_seg_type); |
| |
| if (num_seg_details) { |
| *num_seg_details = cur_track; |
| } |
| return seg_details; |
| } |
| |
| /* Allocates and loads the chan_details data structure, a 2D array of |
| * seg_details structures. This array is used to handle unique seg_details |
| * (ie. channel segments) for each horizontal and vertical channel. */ |
| |
| void alloc_and_load_chan_details(const DeviceGrid& grid, |
| const t_chan_width* nodes_per_chan, |
| const bool trim_empty_channels, |
| const bool trim_obs_channels, |
| const int num_seg_details, |
| const t_seg_details* seg_details, |
| t_chan_details& chan_details_x, |
| t_chan_details& chan_details_y) { |
| chan_details_x = init_chan_details(grid, nodes_per_chan, |
| num_seg_details, seg_details, SEG_DETAILS_X); |
| chan_details_y = init_chan_details(grid, nodes_per_chan, |
| num_seg_details, seg_details, SEG_DETAILS_Y); |
| |
| /* Obstruct channel segment details based on grid block widths/heights */ |
| obstruct_chan_details(grid, nodes_per_chan, |
| trim_empty_channels, trim_obs_channels, |
| chan_details_x, chan_details_y); |
| |
| /* Adjust segment start/end based on obstructed channels, if any */ |
| adjust_chan_details(grid, nodes_per_chan, |
| chan_details_x, chan_details_y); |
| } |
| |
| t_chan_details init_chan_details(const DeviceGrid& grid, |
| const t_chan_width* nodes_per_chan, |
| const int num_seg_details, |
| const t_seg_details* seg_details, |
| const enum e_seg_details_type seg_details_type) { |
| VTR_ASSERT(num_seg_details <= nodes_per_chan->max); |
| |
| t_chan_details chan_details({grid.width(), grid.height(), size_t(num_seg_details)}); |
| |
| for (size_t x = 0; x < grid.width(); ++x) { |
| for (size_t y = 0; y < grid.height(); ++y) { |
| t_chan_seg_details* p_seg_details = chan_details[x][y].data(); |
| for (int i = 0; i < num_seg_details; ++i) { |
| p_seg_details[i] = t_chan_seg_details(&seg_details[i]); |
| |
| int seg_start = -1; |
| int seg_end = -1; |
| |
| if (seg_details_type == SEG_DETAILS_X) { |
| seg_start = get_seg_start(p_seg_details, i, y, x); |
| seg_end = get_seg_end(p_seg_details, i, seg_start, y, grid.width() - 2); //-2 for no perim channels |
| } |
| if (seg_details_type == SEG_DETAILS_Y) { |
| seg_start = get_seg_start(p_seg_details, i, x, y); |
| seg_end = get_seg_end(p_seg_details, i, seg_start, x, grid.height() - 2); //-2 for no perim channels |
| } |
| |
| p_seg_details[i].set_seg_start(seg_start); |
| p_seg_details[i].set_seg_end(seg_end); |
| |
| if (seg_details_type == SEG_DETAILS_X) { |
| if (i >= nodes_per_chan->x_list[y]) { |
| p_seg_details[i].set_length(0); |
| } |
| } |
| if (seg_details_type == SEG_DETAILS_Y) { |
| if (i >= nodes_per_chan->y_list[x]) { |
| p_seg_details[i].set_length(0); |
| } |
| } |
| } |
| } |
| } |
| return chan_details; |
| } |
| |
| void obstruct_chan_details(const DeviceGrid& grid, |
| const t_chan_width* nodes_per_chan, |
| const bool trim_empty_channels, |
| const bool trim_obs_channels, |
| t_chan_details& chan_details_x, |
| t_chan_details& chan_details_y) { |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| /* Iterate grid to find and obstruct based on multi-width/height blocks */ |
| for (size_t x = 0; x < grid.width() - 1; ++x) { |
| for (size_t y = 0; y < grid.height() - 1; ++y) { |
| if (!trim_obs_channels) |
| continue; |
| |
| if (grid[x][y].type == device_ctx.EMPTY_TYPE) |
| continue; |
| if (grid[x][y].width_offset > 0 || grid[x][y].height_offset > 0) |
| continue; |
| if (grid[x][y].type->width == 1 && grid[x][y].type->height == 1) |
| continue; |
| |
| if (grid[x][y].type->height > 1) { |
| for (int dx = 0; dx <= grid[x][y].type->width - 1; ++dx) { |
| for (int dy = 0; dy < grid[x][y].type->height - 1; ++dy) { |
| for (int track = 0; track < nodes_per_chan->max; ++track) { |
| chan_details_x[x + dx][y + dy][track].set_length(0); |
| } |
| } |
| } |
| } |
| if (grid[x][y].type->width > 1) { |
| for (int dy = 0; dy <= grid[x][y].type->height - 1; ++dy) { |
| for (int dx = 0; dx < grid[x][y].type->width - 1; ++dx) { |
| for (int track = 0; track < nodes_per_chan->max; ++track) { |
| chan_details_y[x + dx][y + dy][track].set_length(0); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /* Iterate grid again to find and obstruct based on neighboring EMPTY and/or IO types */ |
| for (size_t x = 0; x <= grid.width() - 2; ++x) { //-2 for no perim channels |
| for (size_t y = 0; y <= grid.height() - 2; ++y) { //-2 for no perim channels |
| |
| if (!trim_empty_channels) |
| continue; |
| |
| if (is_io_type(grid[x][y].type)) { |
| if ((x == 0) || (y == 0)) |
| continue; |
| } |
| if (grid[x][y].type == device_ctx.EMPTY_TYPE) { |
| if ((x == grid.width() - 2) && is_io_type(grid[x + 1][y].type)) //-2 for no perim channels |
| continue; |
| if ((y == grid.height() - 2) && is_io_type(grid[x][y + 1].type)) //-2 for no perim channels |
| continue; |
| } |
| |
| if (is_io_type(grid[x][y].type) || (grid[x][y].type == device_ctx.EMPTY_TYPE)) { |
| if (is_io_type(grid[x][y + 1].type) || (grid[x][y + 1].type == device_ctx.EMPTY_TYPE)) { |
| for (int track = 0; track < nodes_per_chan->max; ++track) { |
| chan_details_x[x][y][track].set_length(0); |
| } |
| } |
| } |
| if (is_io_type(grid[x][y].type) || (grid[x][y].type == device_ctx.EMPTY_TYPE)) { |
| if (is_io_type(grid[x + 1][y].type) || (grid[x + 1][y].type == device_ctx.EMPTY_TYPE)) { |
| for (int track = 0; track < nodes_per_chan->max; ++track) { |
| chan_details_y[x][y][track].set_length(0); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void adjust_chan_details(const DeviceGrid& grid, |
| const t_chan_width* nodes_per_chan, |
| t_chan_details& chan_details_x, |
| t_chan_details& chan_details_y) { |
| for (size_t y = 0; y <= grid.height() - 2; ++y) { //-2 for no perim channels |
| for (size_t x = 0; x <= grid.width() - 2; ++x) { //-2 for no perim channels |
| |
| /* Ignore any non-obstructed channel seg_detail structures */ |
| if (chan_details_x[x][y][0].length() > 0) |
| continue; |
| |
| adjust_seg_details(x, y, grid, nodes_per_chan, |
| chan_details_x, SEG_DETAILS_X); |
| } |
| } |
| |
| for (size_t x = 0; x <= grid.width() - 2; ++x) { //-2 for no perim channels |
| for (size_t y = 0; y <= grid.height() - 2; ++y) { //-2 for no perim channels |
| |
| /* Ignore any non-obstructed channel seg_detail structures */ |
| if (chan_details_y[x][y][0].length() > 0) |
| continue; |
| |
| adjust_seg_details(x, y, grid, nodes_per_chan, |
| chan_details_y, SEG_DETAILS_Y); |
| } |
| } |
| } |
| |
| void adjust_seg_details(const int x, |
| const int y, |
| const DeviceGrid& grid, |
| const t_chan_width* nodes_per_chan, |
| t_chan_details& chan_details, |
| const enum e_seg_details_type seg_details_type) { |
| int seg_index = (seg_details_type == SEG_DETAILS_X ? x : y); |
| |
| for (int track = 0; track < nodes_per_chan->max; ++track) { |
| int lx = (seg_details_type == SEG_DETAILS_X ? x - 1 : x); |
| int ly = (seg_details_type == SEG_DETAILS_X ? y : y - 1); |
| if (lx < 0 || ly < 0 || chan_details[lx][ly][track].length() == 0) |
| continue; |
| |
| while (chan_details[lx][ly][track].seg_end() >= seg_index) { |
| chan_details[lx][ly][track].set_seg_end(seg_index - 1); |
| lx = (seg_details_type == SEG_DETAILS_X ? lx - 1 : lx); |
| ly = (seg_details_type == SEG_DETAILS_X ? ly : ly - 1); |
| if (lx < 0 || ly < 0 || chan_details[lx][ly][track].length() == 0) |
| break; |
| } |
| } |
| |
| for (int track = 0; track < nodes_per_chan->max; ++track) { |
| size_t lx = (seg_details_type == SEG_DETAILS_X ? x + 1 : x); |
| size_t ly = (seg_details_type == SEG_DETAILS_X ? y : y + 1); |
| if (lx > grid.width() - 2 || ly > grid.height() - 2 || chan_details[lx][ly][track].length() == 0) //-2 for no perim channels |
| continue; |
| |
| while (chan_details[lx][ly][track].seg_start() <= seg_index) { |
| chan_details[lx][ly][track].set_seg_start(seg_index + 1); |
| lx = (seg_details_type == SEG_DETAILS_X ? lx + 1 : lx); |
| ly = (seg_details_type == SEG_DETAILS_X ? ly : ly + 1); |
| if (lx > grid.width() - 2 || ly > grid.height() - 2 || chan_details[lx][ly][track].length() == 0) //-2 for no perim channels |
| break; |
| } |
| } |
| } |
| |
| void free_chan_details(t_chan_details& chan_details_x, |
| t_chan_details& chan_details_y) { |
| chan_details_x.clear(); |
| chan_details_y.clear(); |
| } |
| |
| /* Returns the segment number at which the segment this track lies on * |
| * started. */ |
| int get_seg_start(const t_chan_seg_details* seg_details, |
| const int itrack, |
| const int chan_num, |
| const int seg_num) { |
| int seg_start = 0; |
| if (seg_details[itrack].seg_start() >= 0) { |
| seg_start = seg_details[itrack].seg_start(); |
| |
| } else { |
| seg_start = 1; |
| if (false == seg_details[itrack].longline()) { |
| int length = seg_details[itrack].length(); |
| int start = seg_details[itrack].start(); |
| |
| /* Start is guaranteed to be between 1 and length. Hence adding length to * |
| * the quantity in brackets below guarantees it will be nonnegative. */ |
| |
| VTR_ASSERT(start > 0); |
| VTR_ASSERT(start <= length); |
| |
| /* NOTE: Start points are staggered between different channels. |
| * The start point must stagger backwards as chan_num increases. |
| * Unidirectional routing expects this to allow the N-to-N |
| * assumption to be made with respect to ending wires in the core. */ |
| seg_start = seg_num - (seg_num + length + chan_num - start) % length; |
| if (seg_start < 1) { |
| seg_start = 1; |
| } |
| } |
| } |
| return seg_start; |
| } |
| |
| int get_seg_end(const t_chan_seg_details* seg_details, const int itrack, const int istart, const int chan_num, const int seg_max) { |
| if (seg_details[itrack].longline()) { |
| return seg_max; |
| } |
| |
| if (seg_details[itrack].seg_end() >= 0) { |
| return seg_details[itrack].seg_end(); |
| } |
| |
| int len = seg_details[itrack].length(); |
| int ofs = seg_details[itrack].start(); |
| |
| /* Normal endpoint */ |
| int seg_end = istart + len - 1; |
| |
| /* If start is against edge it may have been clipped */ |
| if (1 == istart) { |
| /* If the (staggered) startpoint of first full wire wasn't |
| * also 1, we must be the clipped wire */ |
| int first_full = (len - (chan_num % len) + ofs - 1) % len + 1; |
| if (first_full > 1) { |
| /* then we stop just before the first full seg */ |
| seg_end = first_full - 1; |
| } |
| } |
| |
| /* Clip against far edge */ |
| if (seg_end > seg_max) { |
| seg_end = seg_max; |
| } |
| |
| return seg_end; |
| } |
| |
| /* Returns the number of tracks to which clb opin #ipin at (i,j) connects. * |
| * Also stores the nodes to which this pin connects in rr_edges_to_create */ |
| int get_bidir_opin_connections(const int i, |
| const int j, |
| const int ipin, |
| const int from_rr_node, |
| t_rr_edge_info_set& rr_edges_to_create, |
| const t_pin_to_track_lookup& opin_to_track_map, |
| const t_rr_node_indices& L_rr_node_indices, |
| const t_chan_details& chan_details_x, |
| const t_chan_details& chan_details_y) { |
| int num_conn, tr_i, tr_j, chan, seg; |
| int to_switch, to_node; |
| int is_connected_track; |
| t_physical_tile_type_ptr type; |
| t_rr_type to_type; |
| |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| type = device_ctx.grid[i][j].type; |
| int width_offset = device_ctx.grid[i][j].width_offset; |
| int height_offset = device_ctx.grid[i][j].height_offset; |
| |
| 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) { |
| /* Figure out coords of channel segment based on side */ |
| tr_i = ((side == LEFT) ? (i - 1) : i); |
| tr_j = ((side == BOTTOM) ? (j - 1) : j); |
| |
| to_type = ((side == LEFT) || (side == RIGHT)) ? CHANY : CHANX; |
| |
| chan = ((to_type == CHANX) ? tr_j : tr_i); |
| seg = ((to_type == CHANX) ? tr_i : tr_j); |
| |
| bool vert = !((side == TOP) || (side == BOTTOM)); |
| |
| /* Don't connect where no tracks on fringes */ |
| if ((tr_i < 0) || (tr_i > int(device_ctx.grid.width() - 2))) { //-2 for no perimeter channels |
| continue; |
| } |
| if ((tr_j < 0) || (tr_j > int(device_ctx.grid.height() - 2))) { //-2 for no perimeter channels |
| continue; |
| } |
| if ((CHANX == to_type) && (tr_i < 1)) { |
| continue; |
| } |
| if ((CHANY == to_type) && (tr_j < 1)) { |
| continue; |
| } |
| if (opin_to_track_map[type->index].empty()) { |
| continue; |
| } |
| |
| is_connected_track = false; |
| |
| const t_chan_seg_details* seg_details = (vert ? chan_details_y[chan][seg] : chan_details_x[seg][chan]).data(); |
| |
| /* Iterate of the opin to track connections */ |
| for (int to_track : opin_to_track_map[type->index][ipin][width_offset][height_offset][side]) { |
| /* Skip unconnected connections */ |
| if (OPEN == to_track || is_connected_track) { |
| is_connected_track = true; |
| VTR_ASSERT(OPEN == opin_to_track_map[type->index][ipin][width_offset][height_offset][side][0]); |
| continue; |
| } |
| |
| /* Only connect to wire if there is a CB */ |
| if (is_cblock(chan, seg, to_track, seg_details)) { |
| to_switch = seg_details[to_track].arch_wire_switch(); |
| to_node = get_rr_node_index(L_rr_node_indices, tr_i, tr_j, to_type, to_track); |
| |
| if (to_node == OPEN) { |
| continue; |
| } |
| |
| rr_edges_to_create.emplace_back(from_rr_node, to_node, to_switch); |
| ++num_conn; |
| } |
| } |
| } |
| |
| return num_conn; |
| } |
| |
| int get_unidir_opin_connections(const int chan, |
| const int seg, |
| int Fc, |
| const int seg_type_index, |
| const t_rr_type chan_type, |
| const t_chan_seg_details* seg_details, |
| const int from_rr_node, |
| t_rr_edge_info_set& rr_edges_to_create, |
| vtr::NdMatrix<int, 3>& Fc_ofs, |
| const int max_len, |
| const int max_chan_width, |
| const t_rr_node_indices& L_rr_node_indices, |
| bool* Fc_clipped) { |
| /* Gets a linked list of Fc nodes of specified seg_type_index to connect |
| * to in given chan seg. Fc_ofs is used for the opin staggering pattern. */ |
| |
| int* inc_muxes = nullptr; |
| int* dec_muxes = nullptr; |
| int num_inc_muxes, num_dec_muxes, iconn; |
| int inc_inode_index, dec_inode_index; |
| int inc_mux, dec_mux; |
| int inc_track, dec_track; |
| int x, y; |
| int num_edges; |
| |
| *Fc_clipped = false; |
| |
| /* Fc is assigned in pairs so check it is even. */ |
| VTR_ASSERT(Fc % 2 == 0); |
| |
| /* get_rr_node_indices needs x and y coords. */ |
| x = ((CHANX == chan_type) ? seg : chan); |
| y = ((CHANX == chan_type) ? chan : seg); |
| |
| /* Get the lists of possible muxes. */ |
| int dummy; |
| inc_muxes = label_wire_muxes(chan, seg, seg_details, seg_type_index, max_len, |
| INC_DIRECTION, max_chan_width, true, &num_inc_muxes, &dummy); |
| dec_muxes = label_wire_muxes(chan, seg, seg_details, seg_type_index, max_len, |
| DEC_DIRECTION, max_chan_width, true, &num_dec_muxes, &dummy); |
| |
| /* Clip Fc to the number of muxes. */ |
| if (((Fc / 2) > num_inc_muxes) || ((Fc / 2) > num_dec_muxes)) { |
| *Fc_clipped = true; |
| Fc = 2 * std::min(num_inc_muxes, num_dec_muxes); |
| } |
| |
| /* Assign tracks to meet Fc demand */ |
| num_edges = 0; |
| for (iconn = 0; iconn < (Fc / 2); ++iconn) { |
| /* Figure of the next mux to use for the 'inc' and 'dec' connections */ |
| inc_mux = Fc_ofs[chan][seg][seg_type_index] % num_inc_muxes; |
| dec_mux = Fc_ofs[chan][seg][seg_type_index] % num_dec_muxes; |
| ++Fc_ofs[chan][seg][seg_type_index]; |
| |
| /* Figure out the track it corresponds to. */ |
| VTR_ASSERT(inc_muxes != nullptr); |
| inc_track = inc_muxes[inc_mux]; |
| |
| VTR_ASSERT(dec_muxes != nullptr); |
| dec_track = dec_muxes[dec_mux]; |
| |
| /* Figure the inodes of those muxes */ |
| inc_inode_index = get_rr_node_index(L_rr_node_indices, x, y, chan_type, inc_track); |
| dec_inode_index = get_rr_node_index(L_rr_node_indices, x, y, chan_type, dec_track); |
| |
| if (inc_inode_index == OPEN || dec_inode_index == OPEN) { |
| continue; |
| } |
| |
| /* Add to the list. */ |
| rr_edges_to_create.emplace_back(from_rr_node, inc_inode_index, seg_details[inc_track].arch_opin_switch()); |
| ++num_edges; |
| |
| rr_edges_to_create.emplace_back(from_rr_node, dec_inode_index, seg_details[dec_track].arch_opin_switch()); |
| ++num_edges; |
| } |
| |
| if (inc_muxes) { |
| vtr::free(inc_muxes); |
| inc_muxes = nullptr; |
| } |
| if (dec_muxes) { |
| vtr::free(dec_muxes); |
| dec_muxes = nullptr; |
| } |
| |
| return num_edges; |
| } |
| |
| bool is_cblock(const int chan, const int seg, const int track, const t_chan_seg_details* seg_details) { |
| int length, ofs, start_seg; |
| |
| length = seg_details[track].length(); |
| |
| /* Make sure they gave us correct start */ |
| start_seg = get_seg_start(seg_details, track, chan, seg); |
| |
| ofs = seg - start_seg; |
| |
| VTR_ASSERT(ofs >= 0); |
| VTR_ASSERT(ofs < length); |
| |
| /* If unidir segment that is going backwards, we need to flip the ofs */ |
| if (DEC_DIRECTION == seg_details[track].direction()) { |
| ofs = (length - 1) - ofs; |
| } |
| |
| return seg_details[track].cb(ofs); |
| } |
| |
| void dump_seg_details(const t_chan_seg_details* seg_details, |
| int max_chan_width, |
| FILE* fp) { |
| for (int i = 0; i < max_chan_width; i++) { |
| fprintf(fp, "track: %d\n", i); |
| fprintf(fp, "length: %d start: %d", |
| seg_details[i].length(), seg_details[i].start()); |
| |
| if (seg_details[i].length() > 0) { |
| if (seg_details[i].seg_start() >= 0 && seg_details[i].seg_end() >= 0) { |
| fprintf(fp, " [%d,%d]", |
| seg_details[i].seg_start(), seg_details[i].seg_end()); |
| } |
| fprintf(fp, " longline: %d arch_wire_switch: %d arch_opin_switch: %d", |
| seg_details[i].longline(), |
| seg_details[i].arch_wire_switch(), seg_details[i].arch_opin_switch()); |
| } |
| fprintf(fp, "\n"); |
| |
| fprintf(fp, "Rmetal: %g Cmetal: %g\n", |
| seg_details[i].Rmetal(), seg_details[i].Cmetal()); |
| |
| fprintf(fp, "direction: %s\n", |
| DIRECTION_STRING[seg_details[i].direction()]); |
| |
| fprintf(fp, "cb list: "); |
| for (int j = 0; j < seg_details[i].length(); j++) |
| fprintf(fp, "%d ", seg_details[i].cb(j)); |
| fprintf(fp, "\n"); |
| |
| fprintf(fp, "sb list: "); |
| for (int j = 0; j <= seg_details[i].length(); j++) |
| fprintf(fp, "%d ", seg_details[i].sb(j)); |
| fprintf(fp, "\n"); |
| |
| fprintf(fp, "\n"); |
| } |
| } |
| |
| /* Dumps out an array of seg_details structures to file fname. Used only * |
| * for debugging. */ |
| void dump_seg_details(const t_chan_seg_details* seg_details, |
| int max_chan_width, |
| const char* fname) { |
| FILE* fp = vtr::fopen(fname, "w"); |
| dump_seg_details(seg_details, max_chan_width, fp); |
| fclose(fp); |
| } |
| |
| /* Dumps out a 2D array of chan_details structures to file fname. Used * |
| * only for debugging. */ |
| void dump_chan_details(const t_chan_details& chan_details_x, |
| const t_chan_details& chan_details_y, |
| int max_chan_width, |
| const DeviceGrid& grid, |
| const char* fname) { |
| FILE* fp = vtr::fopen(fname, "w"); |
| if (fp) { |
| for (size_t y = 0; y <= grid.height() - 2; ++y) { //-2 for no perim channels |
| for (size_t x = 0; x <= grid.width() - 2; ++x) { //-2 for no perim channels |
| |
| fprintf(fp, "========================\n"); |
| fprintf(fp, "chan_details_x: [%zu][%zu]\n", x, y); |
| fprintf(fp, "========================\n"); |
| |
| const t_chan_seg_details* seg_details = chan_details_x[x][y].data(); |
| dump_seg_details(seg_details, max_chan_width, fp); |
| } |
| } |
| for (size_t x = 0; x <= grid.width() - 2; ++x) { //-2 for no perim channels |
| for (size_t y = 0; y <= grid.height() - 2; ++y) { //-2 for no perim channels |
| |
| fprintf(fp, "========================\n"); |
| fprintf(fp, "chan_details_y: [%zu][%zu]\n", x, y); |
| fprintf(fp, "========================\n"); |
| |
| const t_chan_seg_details* seg_details = chan_details_y[x][y].data(); |
| dump_seg_details(seg_details, max_chan_width, fp); |
| } |
| } |
| } |
| fclose(fp); |
| } |
| |
| /* Dumps out a 2D array of switch block pattern structures to file fname. * |
| * Used for debugging purposes only. */ |
| void dump_sblock_pattern(const t_sblock_pattern& sblock_pattern, |
| int max_chan_width, |
| const DeviceGrid& grid, |
| const char* fname) { |
| FILE* fp = vtr::fopen(fname, "w"); |
| if (fp) { |
| for (size_t y = 0; y <= grid.height() - 2; ++y) { |
| for (size_t x = 0; x <= grid.width() - 2; ++x) { |
| fprintf(fp, "==========================\n"); |
| fprintf(fp, "sblock_pattern: [%zu][%zu]\n", x, y); |
| fprintf(fp, "==========================\n"); |
| |
| for (int from_side = 0; from_side < 4; ++from_side) { |
| for (int to_side = 0; to_side < 4; ++to_side) { |
| if (from_side == to_side) |
| continue; |
| |
| const char* psz_from_side = "?"; |
| switch (from_side) { |
| case 0: |
| psz_from_side = "T"; |
| break; |
| case 1: |
| psz_from_side = "R"; |
| break; |
| case 2: |
| psz_from_side = "B"; |
| break; |
| case 3: |
| psz_from_side = "L"; |
| break; |
| default: |
| VTR_ASSERT_MSG(false, "Unrecognized from side"); |
| break; |
| } |
| const char* psz_to_side = "?"; |
| switch (to_side) { |
| case 0: |
| psz_to_side = "T"; |
| break; |
| case 1: |
| psz_to_side = "R"; |
| break; |
| case 2: |
| psz_to_side = "B"; |
| break; |
| case 3: |
| psz_to_side = "L"; |
| break; |
| default: |
| VTR_ASSERT_MSG(false, "Unrecognized to side"); |
| break; |
| } |
| |
| for (int from_track = 0; from_track < max_chan_width; ++from_track) { |
| short to_mux = sblock_pattern[x][y][from_side][to_side][from_track][0]; |
| short to_track = sblock_pattern[x][y][from_side][to_side][from_track][1]; |
| short alt_mux = sblock_pattern[x][y][from_side][to_side][from_track][2]; |
| short alt_track = sblock_pattern[x][y][from_side][to_side][from_track][3]; |
| |
| if (to_mux == UN_SET && to_track == UN_SET) |
| continue; |
| |
| if (alt_mux == UN_SET && alt_track == UN_SET) { |
| fprintf(fp, "%s %d => %s [%d][%d]\n", |
| psz_from_side, from_track, psz_to_side, |
| to_mux, to_track); |
| } else { |
| fprintf(fp, "%s %d => %s [%d][%d] [%d][%d]\n", |
| psz_from_side, from_track, psz_to_side, |
| to_mux, to_track, alt_mux, alt_track); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| fclose(fp); |
| } |
| |
| static void load_chan_rr_indices(const int max_chan_width, |
| const int chan_len, |
| const int num_chans, |
| const t_rr_type type, |
| const t_chan_details& chan_details, |
| t_rr_node_indices& indices, |
| int* index) { |
| VTR_ASSERT(indices[type].size() == size_t(num_chans)); |
| for (int chan = 0; chan < num_chans - 1; ++chan) { |
| VTR_ASSERT(indices[type][chan].size() == size_t(chan_len)); |
| |
| for (int seg = 1; seg < chan_len - 1; ++seg) { |
| VTR_ASSERT(indices[type][chan][seg].size() == NUM_SIDES); |
| |
| /* Alloc the track inode lookup list */ |
| //Since channels have no side, we just use the first side |
| indices[type][chan][seg][0].resize(max_chan_width, OPEN); |
| } |
| } |
| |
| for (int chan = 0; chan < num_chans - 1; ++chan) { |
| for (int seg = 1; seg < chan_len - 1; ++seg) { |
| /* Assign an inode to the starts of tracks */ |
| int x = (type == CHANX ? seg : chan); |
| int y = (type == CHANX ? chan : seg); |
| const t_chan_seg_details* seg_details = chan_details[x][y].data(); |
| |
| for (unsigned track = 0; track < indices[type][chan][seg][0].size(); ++track) { |
| if (seg_details[track].length() <= 0) |
| continue; |
| |
| int start = get_seg_start(seg_details, track, chan, seg); |
| |
| /* If the start of the wire doesn't have a inode, |
| * assign one to it. */ |
| int inode = indices[type][chan][start][0][track]; |
| if (OPEN == inode) { |
| inode = *index; |
| ++(*index); |
| |
| indices[type][chan][start][0][track] = inode; |
| } |
| |
| /* Assign inode of start of wire to current position */ |
| indices[type][chan][seg][0][track] = inode; |
| } |
| } |
| } |
| } |
| |
| static void load_block_rr_indices(const DeviceGrid& grid, |
| t_rr_node_indices& indices, |
| int* index) { |
| //Walk through the grid assigning indices to SOURCE/SINK IPIN/OPIN |
| for (size_t x = 0; x < grid.width(); x++) { |
| for (size_t y = 0; y < grid.height(); y++) { |
| if (grid[x][y].width_offset == 0 && grid[x][y].height_offset == 0) { |
| //Process each block from it's root location |
| auto type = grid[x][y].type; |
| |
| //Assign indicies for SINKs and SOURCEs |
| // Note that SINKS/SOURCES have no side, so we always use side 0 |
| for (int iclass = 0; iclass < type->num_class; ++iclass) { |
| auto class_type = type->class_inf[iclass].type; |
| if (class_type == DRIVER) { |
| indices[SOURCE][x][y][0].push_back(*index); |
| indices[SINK][x][y][0].push_back(OPEN); |
| } else { |
| VTR_ASSERT(class_type == RECEIVER); |
| indices[SINK][x][y][0].push_back(*index); |
| indices[SOURCE][x][y][0].push_back(OPEN); |
| } |
| ++(*index); |
| } |
| VTR_ASSERT(indices[SOURCE][x][y][0].size() == size_t(type->num_class)); |
| VTR_ASSERT(indices[SINK][x][y][0].size() == size_t(type->num_class)); |
| |
| //Assign indicies for IPINs and OPINs at all offsets from root |
| for (int ipin = 0; ipin < type->num_pins; ++ipin) { |
| for (int width_offset = 0; width_offset < type->width; ++width_offset) { |
| int x_tile = x + width_offset; |
| for (int height_offset = 0; height_offset < type->height; ++height_offset) { |
| int y_tile = y + height_offset; |
| for (e_side side : SIDES) { |
| if (type->pinloc[width_offset][height_offset][side][ipin]) { |
| int iclass = type->pin_class[ipin]; |
| auto class_type = type->class_inf[iclass].type; |
| |
| if (class_type == DRIVER) { |
| indices[OPIN][x_tile][y_tile][side].push_back(*index); |
| indices[IPIN][x_tile][y_tile][side].push_back(OPEN); |
| } else { |
| VTR_ASSERT(class_type == RECEIVER); |
| indices[IPIN][x_tile][y_tile][side].push_back(*index); |
| indices[OPIN][x_tile][y_tile][side].push_back(OPEN); |
| } |
| ++(*index); |
| } else { |
| indices[IPIN][x_tile][y_tile][side].push_back(OPEN); |
| indices[OPIN][x_tile][y_tile][side].push_back(OPEN); |
| } |
| } |
| } |
| } |
| } |
| |
| //Sanity check |
| for (int width_offset = 0; width_offset < type->width; ++width_offset) { |
| int x_tile = x + width_offset; |
| for (int height_offset = 0; height_offset < type->height; ++height_offset) { |
| int y_tile = y + height_offset; |
| for (e_side side : SIDES) { |
| VTR_ASSERT(indices[IPIN][x_tile][y_tile][side].size() == size_t(type->num_pins)); |
| VTR_ASSERT(indices[OPIN][x_tile][y_tile][side].size() == size_t(type->num_pins)); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| //Copy the SOURCE/SINK nodes to all offset positions for blocks with width > 1 and/or height > 1 |
| // This ensures that look-ups on non-root locations will still find the correct SOURCE/SINK |
| for (size_t x = 0; x < grid.width(); x++) { |
| for (size_t y = 0; y < grid.height(); y++) { |
| int width_offset = grid[x][y].width_offset; |
| int height_offset = grid[x][y].height_offset; |
| if (width_offset != 0 || height_offset != 0) { |
| int root_x = x - width_offset; |
| int root_y = y - height_offset; |
| |
| indices[SOURCE][x][y] = indices[SOURCE][root_x][root_y]; |
| indices[SINK][x][y] = indices[SINK][root_x][root_y]; |
| } |
| } |
| } |
| } |
| |
| t_rr_node_indices alloc_and_load_rr_node_indices(const int max_chan_width, |
| const DeviceGrid& grid, |
| int* index, |
| const t_chan_details& chan_details_x, |
| const t_chan_details& chan_details_y) { |
| /* Allocates and loads all the structures needed for fast lookups of the * |
| * index of an rr_node. rr_node_indices is a matrix containing the index * |
| * of the *first* rr_node at a given (i,j) location. */ |
| |
| t_rr_node_indices indices; |
| |
| /* Alloc the lookup table */ |
| indices.resize(NUM_RR_TYPES); |
| for (t_rr_type rr_type : RR_TYPES) { |
| if (rr_type == CHANX) { |
| indices[rr_type].resize(grid.height()); |
| for (size_t y = 0; y < grid.height(); ++y) { |
| indices[rr_type][y].resize(grid.width()); |
| for (size_t x = 0; x < grid.width(); ++x) { |
| indices[rr_type][y][x].resize(NUM_SIDES); |
| } |
| } |
| } else { |
| indices[rr_type].resize(grid.width()); |
| for (size_t x = 0; x < grid.width(); ++x) { |
| indices[rr_type][x].resize(grid.height()); |
| for (size_t y = 0; y < grid.height(); ++y) { |
| indices[rr_type][x][y].resize(NUM_SIDES); |
| } |
| } |
| } |
| } |
| |
| /* Assign indices for block nodes */ |
| load_block_rr_indices(grid, indices, index); |
| |
| /* Load the data for x and y channels */ |
| load_chan_rr_indices(max_chan_width, grid.width(), grid.height(), |
| CHANX, chan_details_x, indices, index); |
| load_chan_rr_indices(max_chan_width, grid.height(), grid.width(), |
| CHANY, chan_details_y, indices, index); |
| return indices; |
| } |
| |
| std::vector<int> get_rr_node_chan_wires_at_location(const t_rr_node_indices& L_rr_node_indices, |
| t_rr_type rr_type, |
| int x, |
| int y) { |
| VTR_ASSERT(rr_type == CHANX || rr_type == CHANY); |
| |
| /* Currently need to swap x and y for CHANX because of chan, seg convention */ |
| if (CHANX == rr_type) { |
| std::swap(x, y); |
| } |
| |
| return L_rr_node_indices[rr_type][x][y][SIDES[0]]; |
| } |
| |
| std::vector<int> get_rr_node_indices(const t_rr_node_indices& L_rr_node_indices, |
| int x, |
| int y, |
| t_rr_type rr_type, |
| int ptc) { |
| /* |
| * Like get_rr_node_index() but returns all matching nodes, |
| * rather than just the first. This is particularly useful for getting all instances |
| * of a specific IPIN/OPIN at a specific gird tile (x,y) location. |
| */ |
| std::vector<int> indices; |
| |
| if (rr_type == IPIN || rr_type == OPIN) { |
| //For pins we need to look at all the sides of the current grid tile |
| |
| for (e_side side : SIDES) { |
| int rr_node_index = get_rr_node_index(L_rr_node_indices, x, y, rr_type, ptc, side); |
| |
| if (rr_node_index >= 0) { |
| indices.push_back(rr_node_index); |
| } |
| } |
| } else { |
| //Sides do not effect non-pins so there should only be one per ptc |
| int rr_node_index = get_rr_node_index(L_rr_node_indices, x, y, rr_type, ptc); |
| |
| if (rr_node_index != OPEN) { |
| indices.push_back(rr_node_index); |
| } |
| } |
| |
| return indices; |
| } |
| |
| int get_rr_node_index(const t_rr_node_indices& L_rr_node_indices, |
| int x, |
| int y, |
| t_rr_type rr_type, |
| int ptc, |
| e_side side) { |
| /* |
| * Returns the index of the specified routing resource node. (x,y) are |
| * the location within the FPGA, rr_type specifies the type of resource, |
| * and ptc gives the number of this resource. ptc is the class number, |
| * pin number or track number, depending on what type of resource this |
| * is. All ptcs start at 0 and go up to pins_per_clb-1 or the equivalent. |
| * There are type->num_class SOURCEs + SINKs, type->num_pins IPINs + OPINs, |
| * and max_chan_width CHANX and CHANY (each). |
| * |
| * Note that for segments (CHANX and CHANY) of length > 1, the segment is |
| * given an rr_index based on the (x,y) location at which it starts (i.e. |
| * lowest (x,y) location at which this segment exists). |
| * This routine also performs error checking to make sure the node in |
| * question exists. |
| * |
| * The 'side' argument only applies to IPIN/OPIN types, and specifies which |
| * side of the grid tile the node should be located on. The value is ignored |
| * for non-IPIN/OPIN types |
| */ |
| if (rr_type == IPIN || rr_type == OPIN) { |
| VTR_ASSERT_MSG(side != NUM_SIDES, "IPIN/OPIN must specify desired side (can not be default NUM_SIDES)"); |
| } else { |
| VTR_ASSERT(rr_type != IPIN && rr_type != OPIN); |
| side = SIDES[0]; |
| } |
| |
| int iclass; |
| |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| VTR_ASSERT(ptc >= 0); |
| VTR_ASSERT(x >= 0 && x < int(device_ctx.grid.width())); |
| VTR_ASSERT(y >= 0 && y < int(device_ctx.grid.height())); |
| |
| auto type = device_ctx.grid[x][y].type; |
| |
| /* Currently need to swap x and y for CHANX because of chan, seg convention */ |
| if (CHANX == rr_type) { |
| std::swap(x, y); |
| } |
| |
| /* Start of that block. */ |
| const std::vector<int>& lookup = L_rr_node_indices[rr_type][x][y][side]; |
| |
| /* Check valid ptc num */ |
| VTR_ASSERT(ptc >= 0); |
| |
| switch (rr_type) { |
| case SOURCE: |
| VTR_ASSERT(ptc < type->num_class); |
| VTR_ASSERT(type->class_inf[ptc].type == DRIVER); |
| break; |
| |
| case SINK: |
| VTR_ASSERT(ptc < type->num_class); |
| VTR_ASSERT(type->class_inf[ptc].type == RECEIVER); |
| break; |
| |
| case OPIN: |
| VTR_ASSERT(ptc < type->num_pins); |
| iclass = type->pin_class[ptc]; |
| VTR_ASSERT(type->class_inf[iclass].type == DRIVER); |
| break; |
| |
| case IPIN: |
| VTR_ASSERT(ptc < type->num_pins); |
| iclass = type->pin_class[ptc]; |
| VTR_ASSERT(type->class_inf[iclass].type == RECEIVER); |
| break; |
| |
| case CHANX: |
| case CHANY: |
| break; |
| |
| default: |
| VPR_FATAL_ERROR(VPR_ERROR_ROUTE, |
| "Bad rr_node passed to get_rr_node_index.\n" |
| "Request for type=%d ptc=%d at (%d, %d).\n", |
| rr_type, ptc, x, y); |
| } |
| |
| return ((unsigned)ptc < lookup.size() ? lookup[ptc] : -1); |
| } |
| |
| int find_average_rr_node_index(int device_width, |
| int device_height, |
| t_rr_type rr_type, |
| int ptc, |
| const t_rr_node_indices& L_rr_node_indices) { |
| /* Find and return the index to a rr_node that is located at the "center" * |
| * of the current grid array, if possible. In the event the "center" of * |
| * the grid array is an EMPTY or IO node, then retry alterate locations. * |
| * Worst case, this function will simply return the 1st non-EMPTY and * |
| * non-IO node. */ |
| |
| int inode = get_rr_node_index(L_rr_node_indices, (device_width) / 2, (device_height) / 2, |
| rr_type, ptc); |
| |
| if (inode == OPEN) { |
| inode = get_rr_node_index(L_rr_node_indices, (device_width) / 4, (device_height) / 4, |
| rr_type, ptc); |
| } |
| if (inode == OPEN) { |
| inode = get_rr_node_index(L_rr_node_indices, (device_width) / 4 * 3, (device_height) / 4 * 3, |
| rr_type, ptc); |
| } |
| if (inode == OPEN) { |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| for (int x = 0; x < device_width; ++x) { |
| for (int y = 0; y < device_height; ++y) { |
| if (device_ctx.grid[x][y].type == device_ctx.EMPTY_TYPE) |
| continue; |
| if (is_io_type(device_ctx.grid[x][y].type)) |
| continue; |
| |
| inode = get_rr_node_index(L_rr_node_indices, x, y, rr_type, ptc); |
| if (inode != OPEN) |
| break; |
| } |
| if (inode != OPEN) |
| break; |
| } |
| } |
| return (inode); |
| } |
| |
| int get_track_to_pins(int seg, |
| int chan, |
| int track, |
| int tracks_per_chan, |
| int from_rr_node, |
| t_rr_edge_info_set& rr_edges_to_create, |
| const t_rr_node_indices& L_rr_node_indices, |
| const t_track_to_pin_lookup& track_to_pin_lookup, |
| const t_chan_seg_details* seg_details, |
| enum e_rr_type chan_type, |
| int chan_length, |
| int wire_to_ipin_switch, |
| enum e_directionality directionality) { |
| /* |
| * Adds the fan-out edges from wire segment at (chan, seg, track) to adjacent |
| * blocks along the wire's length |
| */ |
| |
| int j, pass, iconn, phy_track, end, max_conn, ipin, x, y, num_conn; |
| |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| /* End of this wire */ |
| end = get_seg_end(seg_details, track, seg, chan, chan_length); |
| |
| num_conn = 0; |
| |
| for (j = seg; j <= end; j++) { |
| if (is_cblock(chan, j, track, seg_details)) { |
| for (pass = 0; pass < 2; ++pass) { //pass == 0 => TOP/RIGHT, pass == 1 => BOTTOM/LEFT |
| e_side side; |
| if (CHANX == chan_type) { |
| x = j; |
| y = chan + pass; |
| side = (0 == pass ? TOP : BOTTOM); |
| } else { |
| VTR_ASSERT(CHANY == chan_type); |
| x = chan + pass; |
| y = j; |
| side = (0 == pass ? RIGHT : LEFT); |
| } |
| |
| /* PAJ - if the pointed to is an EMPTY then shouldn't look for ipins */ |
| if (device_ctx.grid[x][y].type == device_ctx.EMPTY_TYPE) |
| continue; |
| |
| /* Move from logical (straight) to physical (twisted) track index |
| * - algorithm assigns ipin connections to same physical track index |
| * so that the logical track gets distributed uniformly */ |
| |
| phy_track = vpr_to_phy_track(track, chan, j, seg_details, directionality); |
| phy_track %= tracks_per_chan; |
| |
| /* We need the type to find the ipin map for this type */ |
| auto type = device_ctx.grid[x][y].type; |
| int width_offset = device_ctx.grid[x][y].width_offset; |
| int height_offset = device_ctx.grid[x][y].height_offset; |
| |
| max_conn = track_to_pin_lookup[type->index][phy_track][width_offset][height_offset][side].size(); |
| for (iconn = 0; iconn < max_conn; iconn++) { |
| ipin = track_to_pin_lookup[type->index][phy_track][width_offset][height_offset][side][iconn]; |
| |
| /* Check there is a connection and Fc map isn't wrong */ |
| /*int to_node = get_rr_node_index(L_rr_node_indices, x + width_offset, y + height_offset, IPIN, ipin, side);*/ |
| int to_node = get_rr_node_index(L_rr_node_indices, x, y, IPIN, ipin, side); |
| if (to_node >= 0) { |
| rr_edges_to_create.emplace_back(from_rr_node, to_node, wire_to_ipin_switch); |
| ++num_conn; |
| } |
| } |
| } |
| } |
| } |
| return (num_conn); |
| } |
| |
| /* |
| * Collects the edges fanning-out of the 'from' track which connect to the 'to' |
| * tracks, according to the switch block pattern. |
| * |
| * It returns the number of connections added, and updates edge_list_ptr to |
| * point at the head of the (extended) linked list giving the nodes to which |
| * this segment connects and the switch type used to connect to each. |
| * |
| * An edge is added from this segment to a y-segment if: |
| * (1) this segment should have a switch box at that location, or |
| * (2) the y-segment to which it would connect has a switch box, and the switch |
| * type of that y-segment is unbuffered (bidirectional pass transistor). |
| * |
| * For bidirectional: |
| * If the switch in each direction is a pass transistor (unbuffered), both |
| * switches are marked as being of the types of the larger (lower R) pass |
| * transistor. |
| */ |
| int get_track_to_tracks(const int from_chan, |
| const int from_seg, |
| const int from_track, |
| const t_rr_type from_type, |
| const int to_seg, |
| const t_rr_type to_type, |
| const int chan_len, |
| const int max_chan_width, |
| const DeviceGrid& grid, |
| const int Fs_per_side, |
| t_sblock_pattern& sblock_pattern, |
| const int from_rr_node, |
| t_rr_edge_info_set& 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 t_rr_node_indices& L_rr_node_indices, |
| const vtr::NdMatrix<std::vector<int>, 3>& switch_block_conn, |
| t_sb_connection_map* sb_conn_map) { |
| int to_chan, to_sb; |
| std::vector<int> conn_tracks; |
| bool from_is_sblock, is_behind, Fs_clipped; |
| enum e_side from_side_a, from_side_b, to_side; |
| bool custom_switch_block; |
| |
| /* check whether a custom switch block will be used */ |
| custom_switch_block = false; |
| if (sb_conn_map) { |
| custom_switch_block = true; |
| VTR_ASSERT(switch_block_conn.empty()); |
| } |
| |
| VTR_ASSERT_MSG(from_seg == get_seg_start(from_seg_details, from_track, from_chan, from_seg), "From segment location must be a the wire start point"); |
| |
| int from_switch = from_seg_details[from_track].arch_wire_switch(); |
| |
| //The absolute coordinate along the channel where the switch block at the |
| //beginning of the current wire segment is located |
| int start_sb_seg = from_seg - 1; |
| |
| //The absolute coordinate along the channel where the switch block at the |
| //end of the current wire segment is lcoated |
| int end_sb_seg = get_seg_end(from_seg_details, from_track, from_seg, from_chan, chan_len); |
| |
| /* Figure out the sides of SB the from_wire will use */ |
| if (CHANX == from_type) { |
| from_side_a = RIGHT; |
| from_side_b = LEFT; |
| } else { |
| VTR_ASSERT(CHANY == from_type); |
| from_side_a = TOP; |
| from_side_b = BOTTOM; |
| } |
| |
| //Set the loop bounds so we iterate over the whole wire segment |
| int start = start_sb_seg; |
| int end = end_sb_seg; |
| |
| //If source and destination segments both lie along the same channel |
| //we clip the loop bounds to the switch blocks of interest and proceed |
| //normally |
| if (to_type == from_type) { |
| start = to_seg - 1; |
| end = to_seg; |
| } |
| |
| //Walk along the 'from' wire segment identifying if a switchblock is located |
| //at each coordinate and add any related fan-out connections to the 'from' wire segment |
| int num_conn = 0; |
| for (int sb_seg = start; sb_seg <= end; ++sb_seg) { |
| if (sb_seg < start_sb_seg || sb_seg > end_sb_seg) { |
| continue; |
| } |
| |
| /* Figure out if we are at a sblock */ |
| from_is_sblock = is_sblock(from_chan, from_seg, sb_seg, from_track, |
| from_seg_details, directionality); |
| if (sb_seg == end_sb_seg || sb_seg == start_sb_seg) { |
| /* end of wire must be an sblock */ |
| from_is_sblock = true; |
| } |
| |
| auto switch_override = should_create_switchblock(grid, from_chan, sb_seg, from_type, to_type); |
| if (switch_override == NO_SWITCH) { |
| continue; //Do not create an SB here |
| } |
| |
| /* Get the coordinates of the current SB from the perspective of the destination channel. |
| * i.e. for segments laid in the x-direction, sb_seg corresponds to the x coordinate and from_chan to the y, |
| * but for segments in the y-direction, from_chan is the x coordinate and sb_seg is the y. So here we reverse |
| * the coordinates if necessary */ |
| if (from_type == to_type) { |
| //Same channel |
| to_chan = from_chan; |
| to_sb = sb_seg; |
| } else { |
| VTR_ASSERT(from_type != to_type); |
| //Different channels |
| to_chan = sb_seg; |
| to_sb = from_chan; |
| } |
| |
| /* to_chan_details may correspond to an x-directed or y-directed channel, depending for which |
| * channel type this function is used; so coordinates are reversed as necessary */ |
| if (to_type == CHANX) { |
| to_seg_details = to_chan_details[to_seg][to_chan].data(); |
| } else { |
| to_seg_details = to_chan_details[to_chan][to_seg].data(); |
| } |
| |
| if (to_seg_details[0].length() == 0) |
| continue; |
| |
| /* Figure out whether the switch block at the current sb_seg coordinate is *behind* |
| * the target channel segment (with respect to VPR coordinate system) */ |
| is_behind = false; |
| if (to_type == from_type) { |
| if (sb_seg == start) { |
| is_behind = true; |
| } else { |
| is_behind = false; |
| } |
| } else { |
| VTR_ASSERT((to_seg == from_chan) || (to_seg == (from_chan + 1))); |
| if (to_seg > from_chan) { |
| is_behind = true; |
| } |
| } |
| |
| /* Figure out which side of the SB the destination segment lies on */ |
| if (CHANX == to_type) { |
| to_side = (is_behind ? RIGHT : LEFT); |
| } else { |
| VTR_ASSERT(CHANY == to_type); |
| to_side = (is_behind ? TOP : BOTTOM); |
| } |
| |
| /* To get to the destination seg/chan, the source track can connect to the SB from |
| * one of two directions. If we're in CHANX, we can connect to it from the left or |
| * right, provided we're not at a track endpoint. And similarly for a source track |
| * in CHANY. */ |
| /* Do edges going from the right SB side (if we're in CHANX) or top (if we're in CHANY). |
| * However, can't connect to right (top) if already at rightmost (topmost) track end */ |
| if (sb_seg < end_sb_seg) { |
| if (custom_switch_block) { |
| if (DEC_DIRECTION == from_seg_details[from_track].direction() || BI_DIRECTIONAL == directionality) { |
| num_conn += get_track_to_chan_seg(from_track, to_chan, to_seg, |
| to_type, from_side_a, to_side, |
| switch_override, |
| L_rr_node_indices, |
| sb_conn_map, from_rr_node, rr_edges_to_create); |
| } |
| } else { |
| if (BI_DIRECTIONAL == directionality) { |
| /* For bidir, the target segment might have an unbuffered (bidir pass transistor) |
| * switchbox, so we follow through regardless of whether the current segment has an SB */ |
| conn_tracks = switch_block_conn[from_side_a][to_side][from_track]; |
| num_conn += get_bidir_track_to_chan_seg(conn_tracks, |
| L_rr_node_indices, to_chan, to_seg, to_sb, to_type, |
| to_seg_details, from_is_sblock, from_switch, |
| switch_override, |
| directionality, from_rr_node, rr_edges_to_create); |
| } |
| if (UNI_DIRECTIONAL == directionality) { |
| /* No fanout if no SB. */ |
| /* Also, we are connecting from the top or right of SB so it |
| * makes the most sense to only get there from DEC_DIRECTION wires. */ |
| if ((from_is_sblock) && (DEC_DIRECTION == from_seg_details[from_track].direction())) { |
| num_conn += get_unidir_track_to_chan_seg(from_track, to_chan, |
| to_seg, to_sb, to_type, max_chan_width, grid, |
| from_side_a, to_side, Fs_per_side, |
| sblock_pattern, |
| switch_override, |
| L_rr_node_indices, to_seg_details, |
| &Fs_clipped, from_rr_node, rr_edges_to_create); |
| } |
| } |
| } |
| } |
| |
| /* Do the edges going from the left SB side (if we're in CHANX) or bottom (if we're in CHANY) |
| * However, can't connect to left (bottom) if already at leftmost (bottommost) track end */ |
| if (sb_seg > start_sb_seg) { |
| if (custom_switch_block) { |
| if (INC_DIRECTION == from_seg_details[from_track].direction() || BI_DIRECTIONAL == directionality) { |
| num_conn += get_track_to_chan_seg(from_track, to_chan, to_seg, |
| to_type, from_side_b, to_side, |
| switch_override, |
| L_rr_node_indices, |
| sb_conn_map, from_rr_node, rr_edges_to_create); |
| } |
| } else { |
| if (BI_DIRECTIONAL == directionality) { |
| /* For bidir, the target segment might have an unbuffered (bidir pass transistor) |
| * switchbox, so we follow through regardless of whether the current segment has an SB */ |
| conn_tracks = switch_block_conn[from_side_b][to_side][from_track]; |
| num_conn += get_bidir_track_to_chan_seg(conn_tracks, |
| L_rr_node_indices, to_chan, to_seg, to_sb, to_type, |
| to_seg_details, from_is_sblock, from_switch, |
| switch_override, |
| directionality, from_rr_node, rr_edges_to_create); |
| } |
| if (UNI_DIRECTIONAL == directionality) { |
| /* No fanout if no SB. */ |
| /* Also, we are connecting from the bottom or left of SB so it |
| * makes the most sense to only get there from INC_DIRECTION wires. */ |
| if ((from_is_sblock) |
| && (INC_DIRECTION == from_seg_details[from_track].direction())) { |
| num_conn += get_unidir_track_to_chan_seg(from_track, to_chan, |
| to_seg, to_sb, to_type, max_chan_width, grid, |
| from_side_b, to_side, Fs_per_side, |
| sblock_pattern, |
| switch_override, |
| L_rr_node_indices, to_seg_details, |
| &Fs_clipped, from_rr_node, rr_edges_to_create); |
| } |
| } |
| } |
| } |
| } |
| |
| return num_conn; |
| } |
| |
| static int get_bidir_track_to_chan_seg(const std::vector<int> conn_tracks, |
| const t_rr_node_indices& L_rr_node_indices, |
| const int to_chan, |
| const int to_seg, |
| const int to_sb, |
| const t_rr_type to_type, |
| const t_chan_seg_details* seg_details, |
| const bool from_is_sblock, |
| const int from_switch, |
| const int switch_override, |
| const enum e_directionality directionality, |
| const int from_rr_node, |
| t_rr_edge_info_set& rr_edges_to_create) { |
| unsigned iconn; |
| int to_track, to_node, to_switch, num_conn, to_x, to_y, i; |
| bool to_is_sblock; |
| short switch_types[2]; |
| |
| /* x, y coords for get_rr_node lookups */ |
| if (CHANX == to_type) { |
| to_x = to_seg; |
| to_y = to_chan; |
| } else { |
| VTR_ASSERT(CHANY == to_type); |
| to_x = to_chan; |
| to_y = to_seg; |
| } |
| |
| /* Go through the list of tracks we can connect to */ |
| num_conn = 0; |
| for (iconn = 0; iconn < conn_tracks.size(); ++iconn) { |
| to_track = conn_tracks[iconn]; |
| to_node = get_rr_node_index(L_rr_node_indices, to_x, to_y, to_type, to_track); |
| |
| if (to_node == OPEN) { |
| continue; |
| } |
| |
| /* Get the switches for any edges between the two tracks */ |
| to_switch = seg_details[to_track].arch_wire_switch(); |
| |
| to_is_sblock = is_sblock(to_chan, to_seg, to_sb, to_track, seg_details, |
| directionality); |
| get_switch_type(from_is_sblock, to_is_sblock, from_switch, to_switch, |
| switch_override, |
| switch_types); |
| |
| /* There are up to two switch edges allowed from track to track */ |
| for (i = 0; i < 2; ++i) { |
| /* If the switch_type entry is empty, skip it */ |
| if (OPEN == switch_types[i]) { |
| continue; |
| } |
| |
| /* Add the edge to the list */ |
| rr_edges_to_create.emplace_back(from_rr_node, to_node, switch_types[i]); |
| ++num_conn; |
| } |
| } |
| |
| 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 int get_track_to_chan_seg(const int from_wire, |
| const int to_chan, |
| const int to_seg, |
| const t_rr_type to_chan_type, |
| const e_side from_side, |
| const e_side to_side, |
| const int switch_override, |
| const t_rr_node_indices& L_rr_node_indices, |
| t_sb_connection_map* sb_conn_map, |
| const int from_rr_node, |
| t_rr_edge_info_set& rr_edges_to_create) { |
| int edge_count = 0; |
| int to_x, to_y; |
| int tile_x, tile_y; |
| |
| /* get x/y coordinates from seg/chan coordinates */ |
| if (CHANX == to_chan_type) { |
| to_x = tile_x = to_seg; |
| to_y = tile_y = to_chan; |
| if (RIGHT == to_side) { |
| tile_x--; |
| } |
| } else { |
| VTR_ASSERT(CHANY == to_chan_type); |
| to_x = tile_x = to_chan; |
| to_y = tile_y = to_seg; |
| if (TOP == to_side) { |
| tile_y--; |
| } |
| } |
| |
| /* 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<t_switchblock_edge>& 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_node = get_rr_node_index(L_rr_node_indices, to_x, to_y, to_chan_type, to_wire); |
| |
| if (to_node == OPEN) { |
| 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); |
| ++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); |
| ++edge_count; |
| } |
| } |
| } else { |
| /* specified sb_conn_map entry does not exist -- do nothing */ |
| } |
| return edge_count; |
| } |
| |
| static int get_unidir_track_to_chan_seg(const int from_track, |
| const int to_chan, |
| const int to_seg, |
| const int to_sb, |
| const t_rr_type to_type, |
| const int max_chan_width, |
| const DeviceGrid& grid, |
| const enum e_side from_side, |
| const enum e_side to_side, |
| const int Fs_per_side, |
| t_sblock_pattern& sblock_pattern, |
| const int switch_override, |
| const t_rr_node_indices& L_rr_node_indices, |
| const t_chan_seg_details* seg_details, |
| bool* Fs_clipped, |
| const int from_rr_node, |
| t_rr_edge_info_set& rr_edges_to_create) { |
| int num_labels = 0; |
| int* mux_labels = nullptr; |
| |
| /* x, y coords for get_rr_node lookups */ |
| int to_x = (CHANX == to_type ? to_seg : to_chan); |
| int to_y = (CHANX == to_type ? to_chan : to_seg); |
| int sb_x = (CHANX == to_type ? to_sb : to_chan); |
| int sb_y = (CHANX == to_type ? to_chan : to_sb); |
| int max_len = (CHANX == to_type ? grid.width() : grid.height()) - 2; //-2 for no perimeter channels |
| |
| enum e_direction to_dir = DEC_DIRECTION; |
| if (to_sb < to_seg) { |
| to_dir = INC_DIRECTION; |
| } |
| |
| *Fs_clipped = false; |
| |
| /* get list of muxes to which we can connect */ |
| int dummy; |
| mux_labels = label_wire_muxes(to_chan, to_seg, seg_details, UNDEFINED, max_len, |
| to_dir, max_chan_width, false, &num_labels, &dummy); |
| |
| /* Can't connect if no muxes. */ |
| if (num_labels < 1) { |
| if (mux_labels) { |
| vtr::free(mux_labels); |
| mux_labels = nullptr; |
| } |
| return 0; |
| } |
| |
| /* Check if Fs demand was too high. */ |
| if (Fs_per_side > num_labels) { |
| *Fs_clipped = true; |
| } |
| |
| /* Handle Fs > 3 by assigning consecutive muxes. */ |
| int count = 0; |
| for (int i = 0; i < Fs_per_side; ++i) { |
| /* Get the target label */ |
| for (int j = 0; j < 4; j = j + 2) { |
| /* Use the balanced labeling for passing and fringe wires */ |
| int to_mux = sblock_pattern[sb_x][sb_y][from_side][to_side][from_track][j]; |
| if (to_mux == UN_SET) |
| continue; |
| |
| int to_track = sblock_pattern[sb_x][sb_y][from_side][to_side][from_track][j + 1]; |
| if (to_track == UN_SET) { |
| to_track = mux_labels[(to_mux + i) % num_labels]; |
| sblock_pattern[sb_x][sb_y][from_side][to_side][from_track][j + 1] = to_track; |
| } |
| |
| int to_node = get_rr_node_index(L_rr_node_indices, to_x, to_y, to_type, to_track); |
| |
| if (to_node == OPEN) { |
| continue; |
| } |
| |
| //Determine which switch to use |
| int iswitch = seg_details[to_track].arch_wire_switch(); |
| |
| //Apply any switch overrides |
| if (should_apply_switch_override(switch_override)) { |
| iswitch = switch_override; |
| } |
| VTR_ASSERT(iswitch != OPEN); |
| |
| /* Add edge to list. */ |
| rr_edges_to_create.emplace_back(from_rr_node, to_node, iswitch); |
| ++count; |
| |
| auto& device_ctx = g_vpr_ctx.device(); |
| if (device_ctx.arch_switch_inf[iswitch].directionality() == BI_DIRECTIONAL) { |
| //Add reverse edge since bi-directional |
| rr_edges_to_create.emplace_back(to_node, from_rr_node, iswitch); |
| ++count; |
| } |
| } |
| } |
| |
| if (mux_labels) { |
| vtr::free(mux_labels); |
| mux_labels = nullptr; |
| } |
| return count; |
| } |
| |
| bool is_sblock(const int chan, int wire_seg, const int sb_seg, const int track, const t_chan_seg_details* seg_details, const enum e_directionality directionality) { |
| int length, ofs, fac; |
| |
| fac = 1; |
| if (UNI_DIRECTIONAL == directionality) { |
| fac = 2; |
| } |
| |
| length = seg_details[track].length(); |
| |
| /* Make sure they gave us correct start */ |
| wire_seg = get_seg_start(seg_details, track, chan, wire_seg); |
| |
| ofs = sb_seg - wire_seg + 1; /* Ofset 0 is behind us, so add 1 */ |
| |
| VTR_ASSERT(ofs >= 0); |
| VTR_ASSERT(ofs < (length + 1)); |
| |
| /* If unidir segment that is going backwards, we need to flip the ofs */ |
| if ((ofs % fac) > 0) { |
| ofs = length - ofs; |
| } |
| |
| return seg_details[track].sb(ofs); |
| } |
| |
| static void get_switch_type(bool is_from_sblock, |
| bool is_to_sblock, |
| short from_node_switch, |
| short to_node_switch, |
| const int switch_override, |
| short switch_types[2]) { |
| /* This routine looks at whether the from_node and to_node want a switch, * |
| * and what type of switch is used to connect *to* each type of node * |
| * (from_node_switch and to_node_switch). It decides what type of switch, * |
| * if any, should be used to go from from_node to to_node. If no switch * |
| * should be inserted (i.e. no connection), it returns OPEN. Its returned * |
| * values are in the switch_types array. It needs to return an array * |
| * because some topologies (e.g. bi-dir pass gates) result in two switches.*/ |
| |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| switch_types[0] = NO_SWITCH; |
| switch_types[1] = NO_SWITCH; |
| |
| if (switch_override == NO_SWITCH) { |
| return; //No switches |
| } |
| |
| if (should_apply_switch_override(switch_override)) { |
| //Use the override switches instead |
| from_node_switch = switch_override; |
| to_node_switch = switch_override; |
| } |
| |
| int used = 0; |
| bool forward_switch = false; |
| bool backward_switch = false; |
| |
| /* Connect forward if we are a sblock */ |
| if (is_from_sblock) { |
| switch_types[used] = to_node_switch; |
| ++used; |
| |
| forward_switch = true; |
| } |
| |
| /* Check for reverse switch */ |
| if (is_to_sblock) { |
| if (device_ctx.arch_switch_inf[from_node_switch].directionality() == e_directionality::BI_DIRECTIONAL) { |
| switch_types[used] = from_node_switch; |
| ++used; |
| |
| backward_switch = true; |
| } |
| } |
| |
| /* Take the larger switch if there are two of the same type */ |
| if (forward_switch |
| && backward_switch |
| && (device_ctx.arch_switch_inf[from_node_switch].type() == device_ctx.arch_switch_inf[to_node_switch].type())) { |
| //Sanity checks |
| VTR_ASSERT_SAFE_MSG(device_ctx.arch_switch_inf[from_node_switch].type() == device_ctx.arch_switch_inf[to_node_switch].type(), "Same switch type"); |
| VTR_ASSERT_MSG(device_ctx.arch_switch_inf[to_node_switch].directionality() == e_directionality::BI_DIRECTIONAL, "Bi-dir to switch"); |
| VTR_ASSERT_MSG(device_ctx.arch_switch_inf[from_node_switch].directionality() == e_directionality::BI_DIRECTIONAL, "Bi-dir from switch"); |
| |
| /* Take the smaller index unless the other |
| * switch is bigger (smaller R). */ |
| |
| int first_switch = std::min(to_node_switch, from_node_switch); |
| int second_switch = std::max(to_node_switch, from_node_switch); |
| |
| if (used < 2) { |
| VPR_FATAL_ERROR(VPR_ERROR_ROUTE, |
| "Expected 2 switches (forward and back) between RR nodes (found %d switches, min switch index: %d max switch index: %d)", |
| used, first_switch, second_switch); |
| } |
| |
| int switch_to_use = first_switch; |
| if (device_ctx.arch_switch_inf[second_switch].R < device_ctx.arch_switch_inf[first_switch].R) { |
| switch_to_use = second_switch; |
| } |
| |
| for (int i = 0; i < used; ++i) { |
| switch_types[i] = switch_to_use; |
| } |
| } |
| } |
| |
| static int vpr_to_phy_track(const int itrack, |
| const int chan_num, |
| const int seg_num, |
| const t_chan_seg_details* seg_details, |
| const enum e_directionality directionality) { |
| int group_start, group_size; |
| int vpr_offset_for_first_phy_track; |
| int vpr_offset, phy_offset; |
| int phy_track; |
| int fac; |
| |
| /* Assign in pairs if unidir. */ |
| fac = 1; |
| if (UNI_DIRECTIONAL == directionality) { |
| fac = 2; |
| } |
| |
| group_start = seg_details[itrack].group_start(); |
| group_size = seg_details[itrack].group_size(); |
| |
| vpr_offset_for_first_phy_track = (chan_num + seg_num - 1) |
| % (group_size / fac); |
| vpr_offset = (itrack - group_start) / fac; |
| phy_offset = (vpr_offset_for_first_phy_track + vpr_offset) |
| % (group_size / fac); |
| phy_track = group_start + (fac * phy_offset) + (itrack - group_start) % fac; |
| |
| return phy_track; |
| } |
| |
| t_sblock_pattern alloc_sblock_pattern_lookup(const DeviceGrid& grid, |
| const int max_chan_width) { |
| /* loading up the sblock connection pattern matrix. It's a huge matrix because |
| * for nonquantized W, it's impossible to make simple permutations to figure out |
| * where muxes are and how to connect to them such that their sizes are balanced */ |
| |
| /* Do chunked allocations to make freeing easier, speed up malloc and free, and |
| * reduce some of the memory overhead. Could use fewer malloc's but this way |
| * avoids all considerations of pointer sizes and allignment. */ |
| |
| /* Alloc each list of pointers in one go. items is a running product that increases |
| * with each new dimension of the matrix. */ |
| |
| VTR_ASSERT(grid.width() > 0); |
| VTR_ASSERT(grid.height() > 0); |
| VTR_ASSERT(max_chan_width >= 0); |
| |
| t_sblock_pattern sblock_pattern({{ |
| grid.width() - 1, |
| grid.height() - 1, |
| 4, //From side |
| 4, //To side |
| size_t(max_chan_width), |
| 4 //to_mux, to_trac, alt_mux, alt_track |
| }}, |
| UN_SET); |
| |
| /* This is the outer pointer to the full matrix */ |
| return sblock_pattern; |
| } |
| |
| void load_sblock_pattern_lookup(const int i, |
| const int j, |
| const DeviceGrid& grid, |
| const t_chan_width* nodes_per_chan, |
| const t_chan_details& chan_details_x, |
| const t_chan_details& chan_details_y, |
| const int /*Fs*/, |
| const enum e_switch_block_type switch_block_type, |
| t_sblock_pattern& sblock_pattern) { |
| /* This routine loads a lookup table for sblock topology. The lookup table is huge |
| * because the sblock varies from location to location. The i, j means the owning |
| * location of the sblock under investigation. */ |
| |
| /* SB's have coords from (0, 0) to (grid.width()-2, grid.height()-2) */ |
| VTR_ASSERT(i >= 0); |
| VTR_ASSERT(i <= int(grid.width()) - 2); |
| VTR_ASSERT(j >= 0); |
| VTR_ASSERT(j <= int(grid.height()) - 2); |
| |
| /* May 12 - 15, 2007 |
| * |
| * I identify three types of sblocks in the chip: 1) The core sblock, whose special |
| * property is that the number of muxes (and ending wires) on each side is the same (very useful |
| * property, since it leads to a N-to-N assignment problem with ending wires). 2) The corner sblock |
| * which is same as a L=1 core sblock with 2 sides only (again N-to-N assignment problem). 3) The |
| * fringe / chip edge sblock which is most troublesome, as balance in each side of muxes is |
| * attainable but balance in the entire sblock is not. The following code first identifies the |
| * incoming wires, which can be classified into incoming passing wires with sblock and incoming |
| * ending wires (the word "incoming" is sometimes dropped for ease of discussion). It appropriately |
| * labels all the wires on each side by the following order: By the call to label_incoming_wires, |
| * which labels for one side, the order is such that the incoming ending wires (always with sblock) |
| * are labelled first 0,1,2,... p-1, then the incoming passing wires with sblock are labelled |
| * p,p+1,p+2,... k-1 (for total of k). By this convention, one can easily distinguish the ending |
| * wires from the passing wires by checking a label against num_ending_wires variable. |
| * |
| * After labelling all the incoming wires, this routine labels the muxes on the side we're currently |
| * connecting to (iterated for four sides of the sblock), called the to_side. The label scheme is |
| * the natural order of the muxes by their track #. Also we find the number of muxes. |
| * |
| * For each to_side, the total incoming wires that connect to the muxes on to_side |
| * come from three sides: side_1 (to_side's right), side_2 (to_side's left) and opp_side. |
| * The problem of balancing mux size is then: considering all incoming passing wires |
| * with sblock on side_1, side_2 and opp_side, how to assign them to the muxes on to_side |
| * (with specified Fs) in a way that mux size is imbalanced by at most 1. I solve this |
| * problem by this approach: the first incoming passing wire will connect to 0, 1, 2, |
| * ..., Fs_per_side - 1, then the next incoming passing wire will connect to |
| * Fs_per_side, Fs_per_side+1, ..., Fs_per_side*2-1, and so on. This consistent STAGGERING |
| * ensures N-to-N assignment is perfectly balanced and M-to-N assignment is imbalanced by no |
| * more than 1. |
| */ |
| |
| /* SB's range from (0, 0) to (grid.width() - 2, grid.height() - 2) */ |
| /* First find all four sides' incoming wires */ |
| int* wire_mux_on_track[4]; |
| int* incoming_wire_label[4]; |
| int num_incoming_wires[4]; |
| int num_ending_wires[4]; |
| int num_wire_muxes[4]; |
| |
| /* "Label" the wires around the switch block by connectivity. */ |
| for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { |
| /* Assume the channel segment doesn't exist. */ |
| wire_mux_on_track[side] = nullptr; |
| incoming_wire_label[side] = nullptr; |
| num_incoming_wires[side] = 0; |
| num_ending_wires[side] = 0; |
| num_wire_muxes[side] = 0; |
| |
| /* Skip the side and leave the zero'd value if the |
| * channel segment doesn't exist. */ |
| bool skip = true; |
| switch (side) { |
| case TOP: |
| if (j < int(grid.height()) - 2) { |
| skip = false; |
| } |
| break; |
| case RIGHT: |
| if (i < int(grid.width()) - 2) { |
| skip = false; |
| } |
| break; |
| case BOTTOM: |
| if (j > 0) { |
| skip = false; |
| } |
| break; |
| case LEFT: |
| if (i > 0) { |
| skip = false; |
| } |
| break; |
| default: |
| VTR_ASSERT_MSG(false, "Unrecognzied side"); |
| break; |
| } |
| if (skip) { |
| continue; |
| } |
| |
| /* Figure out the channel and segment for a certain direction */ |
| bool vert = ((side == TOP) || (side == BOTTOM)); |
| bool pos_dir = ((side == TOP) || (side == RIGHT)); |
| int chan_len = (vert ? grid.height() : grid.width()) - 2; //-2 for no perim channels |
| int chan = (vert ? i : j); |
| int sb_seg = (vert ? j : i); |
| int seg = (pos_dir ? (sb_seg + 1) : sb_seg); |
| |
| const t_chan_seg_details* seg_details = (vert ? chan_details_y[chan][seg] : chan_details_x[seg][chan]).data(); |
| if (seg_details[0].length() <= 0) |
| continue; |
| |
| /* Figure out all the tracks on a side that are ending and the |
| * ones that are passing through and have a SB. */ |
| enum e_direction end_dir = (pos_dir ? DEC_DIRECTION : INC_DIRECTION); |
| incoming_wire_label[side] = label_incoming_wires(chan, seg, sb_seg, |
| seg_details, chan_len, end_dir, nodes_per_chan->max, |
| &num_incoming_wires[side], &num_ending_wires[side]); |
| |
| /* Figure out all the tracks on a side that are starting. */ |
| int dummy; |
| enum e_direction start_dir = (pos_dir ? INC_DIRECTION : DEC_DIRECTION); |
| wire_mux_on_track[side] = label_wire_muxes(chan, seg, |
| seg_details, UNDEFINED, chan_len, start_dir, nodes_per_chan->max, |
| false, &num_wire_muxes[side], &dummy); |
| } |
| |
| for (e_side to_side : {TOP, RIGHT, BOTTOM, LEFT}) { |
| /* Can't do anything if no muxes on this side. */ |
| if (num_wire_muxes[to_side] == 0) |
| continue; |
| |
| /* Figure out side rotations */ |
| VTR_ASSERT((TOP == 0) && (RIGHT == 1) && (BOTTOM == 2) && (LEFT == 3)); |
| int side_cw = (to_side + 1) % 4; |
| int side_opp = (to_side + 2) % 4; |
| int side_ccw = (to_side + 3) % 4; |
| |
| /* For the core sblock: |
| * The new order for passing wires should appear as |
| * 0,1,2..,scw-1, for passing wires with sblock on side_cw |
| * scw,scw+1,...,sccw-1, for passing wires with sblock on side_ccw |
| * sccw,sccw+1,... for passing wires with sblock on side_opp. |
| * This way, I can keep the imbalance to at most 1. |
| * |
| * For the fringe sblocks, I don't distinguish between |
| * passing and ending wires so the above statement still holds |
| * if you replace "passing" by "incoming" */ |
| |
| if (incoming_wire_label[side_cw]) { |
| for (int ichan = 0; ichan < nodes_per_chan->max; ichan++) { |
| int itrack = ichan; |
| if (side_cw == TOP || side_cw == BOTTOM) { |
| itrack = ichan % nodes_per_chan->y_list[i]; |
| } else if (side_cw == RIGHT || side_cw == LEFT) { |
| itrack = ichan % nodes_per_chan->x_list[j]; |
| } |
| |
| if (incoming_wire_label[side_cw][itrack] != UN_SET) { |
| int mux = get_simple_switch_block_track((enum e_side)side_cw, |
| (enum e_side)to_side, |
| incoming_wire_label[side_cw][ichan], |
| switch_block_type, |
| num_wire_muxes[to_side]); |
| |
| if (sblock_pattern[i][j][side_cw][to_side][itrack][0] == UN_SET) { |
| sblock_pattern[i][j][side_cw][to_side][itrack][0] = mux; |
| } else if (sblock_pattern[i][j][side_cw][to_side][itrack][2] == UN_SET) { |
| sblock_pattern[i][j][side_cw][to_side][itrack][2] = mux; |
| } |
| } |
| } |
| } |
| |
| if (incoming_wire_label[side_ccw]) { |
| for (int ichan = 0; ichan < nodes_per_chan->max; ichan++) { |
| int itrack = ichan; |
| if (side_ccw == TOP || side_ccw == BOTTOM) { |
| itrack = ichan % nodes_per_chan->y_list[i]; |
| } else if (side_ccw == RIGHT || side_ccw == LEFT) { |
| itrack = ichan % nodes_per_chan->x_list[j]; |
| } |
| |
| if (incoming_wire_label[side_ccw][itrack] != UN_SET) { |
| int mux = get_simple_switch_block_track((enum e_side)side_ccw, |
| (enum e_side)to_side, |
| incoming_wire_label[side_ccw][ichan], |
| switch_block_type, num_wire_muxes[to_side]); |
| |
| if (sblock_pattern[i][j][side_ccw][to_side][itrack][0] == UN_SET) { |
| sblock_pattern[i][j][side_ccw][to_side][itrack][0] = mux; |
| } else if (sblock_pattern[i][j][side_ccw][to_side][itrack][2] == UN_SET) { |
| sblock_pattern[i][j][side_ccw][to_side][itrack][2] = mux; |
| } |
| } |
| } |
| } |
| |
| if (incoming_wire_label[side_opp]) { |
| for (int itrack = 0; itrack < nodes_per_chan->max; itrack++) { |
| /* not ending wire nor passing wire with sblock */ |
| if (incoming_wire_label[side_opp][itrack] != UN_SET) { |
| /* corner sblocks for sure have no opposite channel segments so don't care about them */ |
| if (incoming_wire_label[side_opp][itrack] < num_ending_wires[side_opp]) { |
| /* The ending wires in core sblocks form N-to-N assignment problem, so can |
| * use any pattern such as Wilton */ |
| /* In the direct connect case, I know for sure the init mux is at the same track # |
| * as this ending wire, but still need to find the init mux label for Fs > 3 */ |
| int mux = find_label_of_track(wire_mux_on_track[to_side], |
| num_wire_muxes[to_side], itrack); |
| sblock_pattern[i][j][side_opp][to_side][itrack][0] = mux; |
| } else { |
| /* These are wire segments that pass through the switch block. |
| * |
| * There is no connection from wire segment midpoints to the opposite switch block |
| * side, so there's nothing to be done here (at Fs=3, this connection is implicit for passing |
| * wires and at Fs>3 the code in this function seems to create heavily unbalanced |
| * switch patterns). Additionally, the code in build_rr_chan() explicitly skips |
| * connections from wire segment midpoints to the opposide sb side (for switch block patterns |
| * generated with this function) so any such assignment to sblock_pattern will be ignored anyway. */ |
| } |
| } |
| } |
| } |
| } |
| |
| for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { |
| if (incoming_wire_label[side]) { |
| vtr::free(incoming_wire_label[side]); |
| } |
| if (wire_mux_on_track[side]) { |
| vtr::free(wire_mux_on_track[side]); |
| } |
| } |
| } |
| |
| static int* label_wire_muxes(const int chan_num, |
| const int seg_num, |
| const t_chan_seg_details* seg_details, |
| const int seg_type_index, |
| const int max_len, |
| const enum e_direction dir, |
| const int max_chan_width, |
| const bool check_cb, |
| int* num_wire_muxes, |
| int* num_wire_muxes_cb_restricted) { |
| /* Labels the muxes on that side (seg_num, chan_num, direction). The returned array |
| * maps a label to the actual track #: array[0] = <the track number of the first/lowest mux> |
| * This routine orders wire muxes by their natural order, i.e. track # |
| * If seg_type_index == UNDEFINED, all segments in the channel are considered. Otherwise this routine |
| * only looks at segments that belong to the specified segment type. */ |
| |
| int itrack, start, end, num_labels, num_labels_restricted, pass; |
| int* labels = nullptr; |
| bool is_endpoint; |
| |
| /* COUNT pass then a LOAD pass */ |
| num_labels = 0; |
| num_labels_restricted = 0; |
| for (pass = 0; pass < 2; ++pass) { |
| /* Alloc the list on LOAD pass */ |
| if (pass > 0) { |
| labels = (int*)vtr::malloc(sizeof(int) * num_labels); |
| num_labels = 0; |
| } |
| |
| /* Find the tracks that are starting. */ |
| for (itrack = 0; itrack < max_chan_width; ++itrack) { |
| start = get_seg_start(seg_details, itrack, chan_num, seg_num); |
| end = get_seg_end(seg_details, itrack, start, chan_num, max_len); |
| |
| /* Skip tracks that are undefined */ |
| if (seg_details[itrack].length() == 0) { |
| continue; |
| } |
| |
| /* Skip tracks going the wrong way */ |
| if (seg_details[itrack].direction() != dir) { |
| continue; |
| } |
| |
| if (seg_type_index != UNDEFINED) { |
| /* skip tracks that don't belong to the specified segment type */ |
| if (seg_details[itrack].index() != seg_type_index) { |
| continue; |
| } |
| } |
| |
| /* Determine if we are a wire startpoint */ |
| is_endpoint = (seg_num == start); |
| if (DEC_DIRECTION == seg_details[itrack].direction()) { |
| is_endpoint = (seg_num == end); |
| } |
| |
| /* Count the labels and load if LOAD pass */ |
| if (is_endpoint) { |
| /* |
| * not all wire endpoints can be driven by OPIN (depending on the <cb> pattern in the arch file) |
| * the check_cb is targeting this arch specification: |
| * if this function is called by get_unidir_opin_connections(), |
| * then we need to check if mux connections can be added to this type of wire, |
| * otherwise, this function should not consider <cb> specification. |
| */ |
| if ((!check_cb) || (seg_details[itrack].cb(0) == true)) { |
| if (pass > 0) { |
| labels[num_labels] = itrack; |
| } |
| ++num_labels; |
| } |
| if (pass > 0) |
| num_labels_restricted += (seg_details[itrack].cb(0) == true) ? 1 : 0; |
| } |
| } |
| } |
| |
| *num_wire_muxes = num_labels; |
| *num_wire_muxes_cb_restricted = num_labels_restricted; |
| |
| return labels; |
| } |
| |
| static int* label_incoming_wires(const int chan_num, |
| const int seg_num, |
| const int sb_seg, |
| const t_chan_seg_details* seg_details, |
| const int max_len, |
| const enum e_direction dir, |
| const int max_chan_width, |
| int* num_incoming_wires, |
| int* num_ending_wires) { |
| /* Labels the incoming wires on that side (seg_num, chan_num, direction). |
| * The returned array maps a track # to a label: array[0] = <the new hash value/label for track 0>, |
| * the labels 0,1,2,.. identify consecutive incoming wires that have sblock (passing wires with sblock and ending wires) */ |
| |
| int itrack, start, end, i, num_passing, num_ending, pass; |
| int* labels; |
| bool sblock_exists, is_endpoint; |
| |
| /* Alloc the list of labels for the tracks */ |
| labels = (int*)vtr::malloc(max_chan_width * sizeof(int)); |
| for (i = 0; i < max_chan_width; ++i) { |
| labels[i] = UN_SET; /* crash hard if unset */ |
| } |
| |
| num_ending = 0; |
| num_passing = 0; |
| for (pass = 0; pass < 2; ++pass) { |
| for (itrack = 0; itrack < max_chan_width; ++itrack) { |
| /* Skip tracks that are undefined */ |
| if (seg_details[itrack].length() == 0) { |
| continue; |
| } |
| |
| if (seg_details[itrack].direction() == dir) { |
| start = get_seg_start(seg_details, itrack, chan_num, seg_num); |
| end = get_seg_end(seg_details, itrack, start, chan_num, max_len); |
| |
| /* Determine if we are a wire endpoint */ |
| is_endpoint = (seg_num == end); |
| if (DEC_DIRECTION == seg_details[itrack].direction()) { |
| is_endpoint = (seg_num == start); |
| } |
| |
| /* Determine if we have a sblock on the wire */ |
| sblock_exists = is_sblock(chan_num, seg_num, sb_seg, itrack, |
| seg_details, UNI_DIRECTIONAL); |
| |
| switch (pass) { |
| /* On first pass, only load ending wire labels. */ |
| case 0: |
| if (is_endpoint) { |
| labels[itrack] = num_ending; |
| ++num_ending; |
| } |
| break; |
| |
| /* On second pass, load the passing wire labels. They |
| * will follow after the ending wire labels. */ |
| case 1: |
| if ((false == is_endpoint) && sblock_exists) { |
| labels[itrack] = num_ending + num_passing; |
| ++num_passing; |
| } |
| break; |
| default: |
| VTR_ASSERT_MSG(false, "Unrecognized pass"); |
| break; |
| } |
| } |
| } |
| } |
| |
| *num_incoming_wires = num_passing + num_ending; |
| *num_ending_wires = num_ending; |
| return labels; |
| } |
| |
| static int find_label_of_track(int* 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 |
| * found, then returns the index of the entry whose value is the largest */ |
| int i_label = -1; |
| int max_track = -1; |
| |
| for (int i = 0; i < num_wire_muxes; i++) { |
| if (wire_mux_on_track[i] == from_track) { |
| i_label = i; |
| break; |
| } else if (wire_mux_on_track[i] > max_track) { |
| i_label = i; |
| max_track = wire_mux_on_track[i]; |
| } |
| } |
| return i_label; |
| } |
| |
| static int should_create_switchblock(const DeviceGrid& grid, int from_chan_coord, int from_seg_coord, t_rr_type from_chan_type, t_rr_type to_chan_type) { |
| //Convert the chan/seg indicies to real x/y coordinates |
| int y_coord; |
| int x_coord; |
| if (from_chan_type == CHANX) { |
| y_coord = from_chan_coord; |
| x_coord = from_seg_coord; |
| } else { |
| VTR_ASSERT(from_chan_type == CHANY); |
| y_coord = from_seg_coord; |
| x_coord = from_chan_coord; |
| } |
| |
| auto blk_type = grid[x_coord][y_coord].type; |
| int width_offset = grid[x_coord][y_coord].width_offset; |
| int height_offset = grid[x_coord][y_coord].height_offset; |
| |
| e_sb_type sb_type = blk_type->switchblock_locations[width_offset][height_offset]; |
| auto switch_override = blk_type->switchblock_switch_overrides[width_offset][height_offset]; |
| |
| if (sb_type == e_sb_type::FULL) { |
| return switch_override; |
| } else if (sb_type == e_sb_type::STRAIGHT && from_chan_type == to_chan_type) { |
| return switch_override; |
| } else if (sb_type == e_sb_type::TURNS && from_chan_type != to_chan_type) { |
| return switch_override; |
| } else if (sb_type == e_sb_type::HORIZONTAL && from_chan_type == CHANX && to_chan_type == CHANX) { |
| return switch_override; |
| } else if (sb_type == e_sb_type::VERTICAL && from_chan_type == CHANY && to_chan_type == CHANY) { |
| return switch_override; |
| } |
| |
| return NO_SWITCH; |
| } |
| |
| static bool should_apply_switch_override(int switch_override) { |
| if (switch_override != NO_SWITCH && switch_override != DEFAULT_SWITCH) { |
| VTR_ASSERT(switch_override >= 0); |
| return true; |
| } |
| return false; |
| } |
| |
| void partition_rr_graph_edges(DeviceContext& device_ctx) { |
| for (size_t inode = 0; inode < device_ctx.rr_nodes.size(); ++inode) { |
| device_ctx.rr_nodes[inode].partition_edges(); |
| |
| VTR_ASSERT_SAFE(device_ctx.rr_nodes[inode].validate()); |
| } |
| } |