2363 lines
75 KiB
C
Executable File
2363 lines
75 KiB
C
Executable File
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include "util.h"
|
|
#include "vpr_types.h"
|
|
#include "globals.h"
|
|
#include "rr_graph_util.h"
|
|
#include "rr_graph2.h"
|
|
#include "rr_graph_sbox.h"
|
|
#include "read_xml_arch_file.h"
|
|
|
|
/*mrFPGA: Xifan TANG */
|
|
#include "mrfpga_globals.h"
|
|
#include "mrfpga_api.h"
|
|
/* end */
|
|
|
|
#define ALLOW_SWITCH_OFF
|
|
|
|
/* WMF: May 07 I put this feature in, but on May 09 in my testing phase
|
|
* I found that for Wilton, this feature is bad, since Wilton is already doing
|
|
* a reverse. */
|
|
#define ENABLE_REVERSE 0
|
|
|
|
#define SAME_TRACK -5
|
|
#define UN_SET -1
|
|
/* Variables below are the global variables shared only amongst the rr_graph *
|
|
************************************ routines. ******************************/
|
|
|
|
/* Used to keep my own list of free linked integers, for speed reasons. */
|
|
|
|
t_linked_edge *free_edge_list_head = NULL;
|
|
|
|
/*************************** Variables local to this module *****************/
|
|
|
|
/* Two arrays below give the rr_node_index of the channel segment at *
|
|
* (i,j,track) for fast index lookup. */
|
|
|
|
/* UDSD Modifications by WMF Begin */
|
|
|
|
/* The sblock_pattern_init_mux_lookup contains the assignment of incoming
|
|
* wires to muxes. More specifically, it only contains the assignment of
|
|
* M-to-N cases. WMF_BETTER_COMMENTS */
|
|
|
|
/* UDSD MOdifications by WMF End */
|
|
|
|
/************************** Subroutines local to this module ****************/
|
|
|
|
static void get_switch_type(boolean is_from_sbox, boolean is_to_sbox,
|
|
short from_node_switch, short to_node_switch, short switch_types[2]);
|
|
|
|
static void load_chan_rr_indices(INP int nodes_per_chan, INP int chan_len,
|
|
INP int num_chans, INP t_rr_type type, INP t_seg_details * seg_details,
|
|
INOUTP int *index, INOUTP t_ivec *** indices);
|
|
|
|
static int get_bidir_track_to_chan_seg(INP struct s_ivec conn_tracks,
|
|
INP t_ivec *** L_rr_node_indices, INP int to_chan, INP int to_seg,
|
|
INP int to_sb, INP t_rr_type to_type, INP t_seg_details * seg_details,
|
|
INP boolean from_is_sbox, INP int from_switch,
|
|
INOUTP boolean * L_rr_edge_done,
|
|
INP enum e_directionality directionality,
|
|
INOUTP struct s_linked_edge **edge_list);
|
|
|
|
static int get_unidir_track_to_chan_seg(INP boolean is_end_sb,
|
|
INP int from_track, INP int to_chan, INP int to_seg, INP int to_sb,
|
|
INP t_rr_type to_type, INP int nodes_per_chan, INP int L_nx,
|
|
INP int L_ny, INP enum e_side from_side, INP enum e_side to_side,
|
|
INP int Fs_per_side, INP int *opin_mux_size,
|
|
INP short *****sblock_pattern, INP t_ivec *** L_rr_node_indices,
|
|
INP t_seg_details * seg_details, INOUTP boolean * L_rr_edge_done,
|
|
OUTP boolean * Fs_clipped, INOUTP struct s_linked_edge **edge_list);
|
|
|
|
static int vpr_to_phy_track(INP int itrack, INP int chan_num, INP int seg_num,
|
|
INP t_seg_details * seg_details,
|
|
INP enum e_directionality directionality);
|
|
|
|
static int *get_seg_track_counts(INP int num_sets, INP int num_seg_types,
|
|
INP t_segment_inf * segment_inf, INP boolean use_full_seg_groups);
|
|
|
|
static int *label_wire_muxes(INP int chan_num, INP int seg_num,
|
|
INP t_seg_details * seg_details, INP int max_len,
|
|
INP enum e_direction dir, INP int nodes_per_chan,
|
|
OUTP int *num_wire_muxes);
|
|
|
|
static int *label_wire_muxes_for_balance(INP int chan_num, INP int seg_num,
|
|
INP t_seg_details * seg_details, INP int max_len,
|
|
INP enum e_direction direction, INP int nodes_per_chan,
|
|
INP int *num_wire_muxes, INP t_rr_type chan_type,
|
|
INP int *opin_mux_size, INP t_ivec *** L_rr_node_indices);
|
|
|
|
static int *label_incoming_wires(INP int chan_num, INP int seg_num,
|
|
INP int sb_seg, INP t_seg_details * seg_details, INP int max_len,
|
|
INP enum e_direction dir, INP int nodes_per_chan,
|
|
OUTP int *num_incoming_wires, OUTP int *num_ending_wires);
|
|
|
|
static int find_label_of_track(int *wire_mux_on_track, int num_wire_muxes,
|
|
int from_track);
|
|
|
|
/* mrFPGA : Xifan TANG*/
|
|
/*
|
|
static int get_seg_to_seg(INP t_ivec*** L_rr_node_indices,
|
|
INP int to_chan,
|
|
INP int to_seg,
|
|
INP int track,
|
|
INP t_rr_type type,
|
|
INP t_seg_details* seg_details,
|
|
INOUTP struct s_linked_edge** edge_list);
|
|
*/
|
|
/* end */
|
|
|
|
/******************** 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. */
|
|
static int *
|
|
get_seg_track_counts(INP int num_sets, INP int num_seg_types,
|
|
INP t_segment_inf * segment_inf, INP boolean use_full_seg_groups) {
|
|
int *result;
|
|
double *demand;
|
|
int i, imax, freq_sum, assigned, size;
|
|
double scale, max, reduce;
|
|
|
|
result = (int *) my_malloc(sizeof(int) * num_seg_types);
|
|
demand = (double *) my_malloc(sizeof(double) * num_seg_types);
|
|
|
|
/* Scale factor so we can divide by any length
|
|
* and still use integers */
|
|
scale = 1;
|
|
freq_sum = 0;
|
|
for (i = 0; i < num_seg_types; ++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 (i = 0; i < num_seg_types; ++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 (i = 0; i < num_seg_types; ++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) {
|
|
free(demand);
|
|
demand = NULL;
|
|
}
|
|
|
|
/* This must be freed by caller */
|
|
return result;
|
|
}
|
|
|
|
t_seg_details *
|
|
alloc_and_load_seg_details(INOUTP int *nodes_per_chan, INP int max_len,
|
|
INP int num_seg_types, INP t_segment_inf * segment_inf,
|
|
INP boolean use_full_seg_groups, INP boolean is_global_graph,
|
|
INP enum e_directionality directionality) {
|
|
|
|
/* 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 i, cur_track, ntracks, itrack, length, j, index;
|
|
int wire_switch, opin_switch, fac, num_sets, tmp;
|
|
int group_start, first_track;
|
|
int *sets_per_seg_type = NULL;
|
|
t_seg_details *seg_details = NULL;
|
|
boolean longline;
|
|
|
|
/* Unidir tracks are assigned in pairs, and bidir tracks individually */
|
|
if (directionality == BI_DIRECTIONAL) {
|
|
fac = 1;
|
|
} else {
|
|
assert(directionality == UNI_DIRECTIONAL);
|
|
fac = 2;
|
|
}
|
|
assert(*nodes_per_chan % fac == 0);
|
|
|
|
/* Map segment type fractions and groupings to counts of tracks */
|
|
sets_per_seg_type = get_seg_track_counts((*nodes_per_chan / fac),
|
|
num_seg_types, segment_inf, use_full_seg_groups);
|
|
|
|
/* Count the number tracks actually assigned. */
|
|
tmp = 0;
|
|
for (i = 0; i < num_seg_types; ++i) {
|
|
tmp += sets_per_seg_type[i] * fac;
|
|
}
|
|
assert(use_full_seg_groups || (tmp == *nodes_per_chan));
|
|
*nodes_per_chan = tmp;
|
|
// Xifan TANG Note: Alloc segments
|
|
seg_details = (t_seg_details *) my_malloc(
|
|
*nodes_per_chan * sizeof(t_seg_details));
|
|
|
|
/* Setup the seg_details data */
|
|
cur_track = 0;
|
|
for (i = 0; i < num_seg_types; ++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;
|
|
}
|
|
// Xifan TANG Note: Set the input and output switches
|
|
wire_switch = segment_inf[i].wire_switch;
|
|
opin_switch = segment_inf[i].opin_switch;
|
|
/* Xifan TANG Note: I think it should be && not || in this assert,
|
|
* Uni-direcitonal routing architecture, wire_switch and opin_switch should be the same...
|
|
*/
|
|
//assert(
|
|
// (wire_switch == opin_switch) || (directionality != UNI_DIRECTIONAL));
|
|
/* END */
|
|
|
|
/* mrFPGA: Xifan TANG */
|
|
assert((wire_switch == opin_switch)
|
|
|| (directionality != UNI_DIRECTIONAL) || (is_mrFPGA) );
|
|
/* END */
|
|
|
|
/* Set up the tracks of same type */
|
|
group_start = 0;
|
|
for (itrack = 0; itrack < ntracks; itrack++) {
|
|
|
|
/* 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. */
|
|
/* Original VPR */
|
|
/* seg_details[cur_track].start = (cur_track / fac) % length + 1; */
|
|
/* end */
|
|
/* mrFPGA: Xifan TANG */
|
|
seg_details[cur_track].start = (cur_track / fac) % length + (is_stack ? 0 : 1);
|
|
/* end */
|
|
|
|
/* 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);
|
|
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;
|
|
}
|
|
|
|
/* 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 = (boolean *) my_malloc(
|
|
length * sizeof(boolean));
|
|
seg_details[cur_track].sb = (boolean *) my_malloc(
|
|
(length + 1) * sizeof(boolean));
|
|
for (j = 0; j < length; ++j) { /* apply connection block existence */
|
|
if (is_global_graph) {
|
|
seg_details[cur_track].cb[j] = TRUE;
|
|
} else {
|
|
index = j;
|
|
|
|
/* Rotate longline's so they vary across the FPGA */
|
|
if (longline) {
|
|
index = (index + itrack) % length;
|
|
}
|
|
|
|
/* Reverse the order for tracks going in DEC_DIRECTION */
|
|
if (itrack % fac == 1) {
|
|
index = (length - 1) - j;
|
|
}
|
|
|
|
/* Use the segment's pattern. */
|
|
index = j % segment_inf[i].cb_len;
|
|
seg_details[cur_track].cb[j] = segment_inf[i].cb[index];
|
|
}
|
|
}
|
|
for (j = 0; j < (length + 1); ++j) { /* apply switch block existence */
|
|
if (is_global_graph) {
|
|
seg_details[cur_track].sb[j] = TRUE;
|
|
} else {
|
|
index = j;
|
|
|
|
/* Rotate longline's so they vary across the FPGA */
|
|
if (longline) {
|
|
index = (index + itrack) % (length + 1);
|
|
}
|
|
|
|
/* Reverse the order for tracks going in DEC_DIRECTION */
|
|
if (itrack % fac == 1) {
|
|
index = ((length + 1) - 1) - j;
|
|
}
|
|
|
|
/* Use the segment's pattern. */
|
|
index = j % segment_inf[i].sb_len;
|
|
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].wire_switch = wire_switch;
|
|
seg_details[cur_track].opin_switch = opin_switch;
|
|
|
|
if (BI_DIRECTIONAL == directionality) {
|
|
seg_details[cur_track].direction = BI_DIRECTION;
|
|
} else {
|
|
assert(UNI_DIRECTIONAL == directionality);
|
|
seg_details[cur_track].direction =
|
|
(itrack % 2) ? DEC_DIRECTION : INC_DIRECTION;
|
|
}
|
|
|
|
switch (segment_inf[i].directionality) {
|
|
case UNI_DIRECTIONAL:
|
|
seg_details[cur_track].drivers = SINGLE;
|
|
break;
|
|
case BI_DIRECTIONAL:
|
|
seg_details[cur_track].drivers = MULTI_BUFFERED;
|
|
break;
|
|
}
|
|
|
|
seg_details[cur_track].index = i;
|
|
|
|
++cur_track;
|
|
}
|
|
} /* End for each segment type. */
|
|
|
|
/* free variables */
|
|
free(sets_per_seg_type);
|
|
|
|
return seg_details;
|
|
}
|
|
|
|
void free_seg_details(t_seg_details * seg_details, int nodes_per_chan) {
|
|
|
|
/* Frees all the memory allocated to an array of seg_details structures. */
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < nodes_per_chan; i++) {
|
|
free(seg_details[i].cb);
|
|
free(seg_details[i].sb);
|
|
}
|
|
free(seg_details);
|
|
}
|
|
|
|
/* Dumps out an array of seg_details structures to file fname. Used only *
|
|
* for debugging. */
|
|
void dump_seg_details(t_seg_details * seg_details, int nodes_per_chan,
|
|
const char *fname) {
|
|
|
|
FILE *fp;
|
|
int i, j;
|
|
const char *drivers_names[] = { "multi_buffered", "single" };
|
|
const char *direction_names[] = { "inc_direction", "dec_direction",
|
|
"bi_direction" };
|
|
|
|
fp = my_fopen(fname, "w", 0);
|
|
|
|
for (i = 0; i < nodes_per_chan; i++) {
|
|
fprintf(fp, "Track: %d.\n", i);
|
|
fprintf(fp, "Length: %d, Start: %d, Long line: %d "
|
|
"wire_switch: %d opin_switch: %d.\n", seg_details[i].length,
|
|
seg_details[i].start, seg_details[i].longline,
|
|
seg_details[i].wire_switch, seg_details[i].opin_switch);
|
|
|
|
fprintf(fp, "Rmetal: %g Cmetal: %g\n", seg_details[i].Rmetal,
|
|
seg_details[i].Cmetal);
|
|
|
|
fprintf(fp, "Direction: %s Drivers: %s\n",
|
|
direction_names[seg_details[i].direction],
|
|
drivers_names[seg_details[i].drivers]);
|
|
|
|
fprintf(fp, "cb list: ");
|
|
for (j = 0; j < seg_details[i].length; j++)
|
|
fprintf(fp, "%d ", seg_details[i].cb[j]);
|
|
fprintf(fp, "\n");
|
|
|
|
fprintf(fp, "sb list: ");
|
|
for (j = 0; j <= seg_details[i].length; j++)
|
|
fprintf(fp, "%d ", seg_details[i].sb[j]);
|
|
fprintf(fp, "\n");
|
|
|
|
fprintf(fp, "\n");
|
|
}
|
|
|
|
fclose(fp);
|
|
}
|
|
|
|
/* Returns the segment number at which the segment this track lies on *
|
|
* started. */
|
|
int get_seg_start(INP t_seg_details * seg_details, INP int itrack,
|
|
INP int chan_num, INP int seg_num) {
|
|
|
|
int seg_start, length, start;
|
|
|
|
seg_start = 1;
|
|
if (FALSE == seg_details[itrack].longline) {
|
|
|
|
length = seg_details[itrack].length;
|
|
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. */
|
|
/* Original VPR */
|
|
/* assert(start > 0); */
|
|
/* end */
|
|
/* mrFPGA: Xifan TANG */
|
|
assert(is_stack ? start >= 0: start > 0);
|
|
/* end */
|
|
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. */
|
|
/* mrFPGA: Xifan TANG */
|
|
seg_start = seg_num - (seg_num - (is_stack ? 1 : 0) + length + chan_num - start) % length;
|
|
seg_start = std::max(seg_start, is_stack ? 0 : 1);
|
|
/* end */
|
|
/* Original VPR */
|
|
/*
|
|
seg_start = seg_num - (seg_num + length + chan_num - start) % length;
|
|
if (seg_start < 1) {
|
|
seg_start = 1;
|
|
}
|
|
*/
|
|
/* end */
|
|
}
|
|
|
|
return seg_start;
|
|
}
|
|
|
|
int get_seg_end(INP t_seg_details * seg_details, INP int itrack, INP int istart,
|
|
INP int chan_num, INP int seg_max) {
|
|
int len, ofs, end, first_full;
|
|
|
|
len = seg_details[itrack].length;
|
|
ofs = seg_details[itrack].start;
|
|
|
|
/* Normal endpoint */
|
|
end = istart + len - 1;
|
|
|
|
/* If start is against edge it may have been clipped */
|
|
/* Original VPR*/
|
|
/* if (1 == istart) { */
|
|
/* end */
|
|
/* mrFPGA: Xifan TANG */
|
|
if ((is_stack ? 0 : 1) == istart) {
|
|
/* end */
|
|
/* If the (staggered) startpoint of first full wire wasn't
|
|
* also 1, we must be the clipped wire */
|
|
/* Original VPR*/
|
|
/* first_full = (len - (chan_num % len) + ofs - 1) % len + 1; */
|
|
/* end */
|
|
/* mrFPGA: Xifan TANG */
|
|
first_full = (len - (chan_num - (is_stack ? 1 : 0)) % len + ofs + (is_stack ? 1: 0) - 1) % len
|
|
+ (is_stack ? 0 : 1);
|
|
/* end */
|
|
/* Original VPR*/
|
|
/* if (first_full > 1) { */
|
|
/* end */
|
|
/* mrFPGA: Xifan TANG */
|
|
if (first_full > (is_stack ? 0 : 1)) {
|
|
/* end */
|
|
/* then we stop just before the first full seg */
|
|
end = first_full - 1;
|
|
}
|
|
}
|
|
|
|
/* Clip against far edge */
|
|
if (end > seg_max) {
|
|
end = seg_max;
|
|
}
|
|
|
|
return 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 the linked list *
|
|
* pointed to by *edge_list_ptr. */
|
|
int get_bidir_opin_connections(INP int i, INP int j, INP int ipin,
|
|
INP struct s_linked_edge **edge_list, INP int *****opin_to_track_map,
|
|
INP int Fc, INP boolean * L_rr_edge_done,
|
|
INP t_ivec *** L_rr_node_indices, INP t_seg_details * seg_details) {
|
|
|
|
int iside, num_conn, ofs, tr_i, tr_j, chan, seg;
|
|
int to_track, to_switch, to_node, iconn;
|
|
int is_connected_track;
|
|
t_type_ptr type;
|
|
t_rr_type to_type;
|
|
|
|
type = grid[i][j].type;
|
|
ofs = grid[i][j].offset;
|
|
|
|
num_conn = 0;
|
|
|
|
/* [0..num_types-1][0..num_pins-1][0..height][0..3][0..Fc-1] */
|
|
for (iside = 0; iside < 4; iside++) {
|
|
|
|
/* Figure out coords of channel segment based on side */
|
|
tr_i = ((iside == LEFT) ? (i - 1) : i);
|
|
tr_j = ((iside == BOTTOM) ? (j - 1) : j);
|
|
|
|
to_type = ((iside == LEFT) || (iside == RIGHT)) ? CHANY : CHANX;
|
|
|
|
chan = ((to_type == CHANX) ? tr_j : tr_i);
|
|
seg = ((to_type == CHANX) ? tr_i : tr_j);
|
|
|
|
/* Don't connect where no tracks on fringes */
|
|
if ((tr_i < 0) || (tr_i > nx)) {
|
|
continue;
|
|
}
|
|
if ((tr_j < 0) || (tr_j > ny)) {
|
|
continue;
|
|
}
|
|
if ((CHANX == to_type) && (tr_i < 1)) {
|
|
continue;
|
|
}
|
|
if ((CHANY == to_type) && (tr_j < 1)) {
|
|
continue;
|
|
}
|
|
|
|
is_connected_track = FALSE;
|
|
|
|
/* Itterate of the opin to track connections */
|
|
for (iconn = 0; iconn < Fc; ++iconn) {
|
|
to_track = opin_to_track_map[type->index][ipin][ofs][iside][iconn];
|
|
|
|
/* Skip unconnected connections */
|
|
if (OPEN == to_track || is_connected_track) {
|
|
is_connected_track = TRUE;
|
|
assert(
|
|
OPEN == opin_to_track_map[type-> index][ipin][ofs][iside] [0]);
|
|
continue;
|
|
}
|
|
|
|
/* Only connect to wire if there is a CB */
|
|
if (is_cbox(chan, seg, to_track, seg_details, BI_DIRECTIONAL)) {
|
|
to_switch = seg_details[to_track].wire_switch;
|
|
to_node = get_rr_node_index(tr_i, tr_j, to_type, to_track,
|
|
L_rr_node_indices);
|
|
|
|
*edge_list = insert_in_edge_list(*edge_list, to_node,
|
|
to_switch);
|
|
L_rr_edge_done[to_node] = TRUE;
|
|
++num_conn;
|
|
}
|
|
}
|
|
}
|
|
|
|
return num_conn;
|
|
}
|
|
|
|
int get_unidir_opin_connections(INP int chan, INP int seg, INP int Fc,
|
|
INP t_rr_type chan_type, INP t_seg_details * seg_details,
|
|
INOUTP t_linked_edge ** edge_list_ptr, INOUTP int **Fc_ofs,
|
|
INOUTP boolean * L_rr_edge_done, INP int max_len,
|
|
INP int nodes_per_chan, INP t_ivec *** L_rr_node_indices,
|
|
OUTP boolean * Fc_clipped) {
|
|
/* Gets a linked list of Fc nodes to connect to in given
|
|
* chan seg. Fc_ofs is used for the for the opin staggering
|
|
* pattern. */
|
|
|
|
int *inc_muxes = NULL;
|
|
int *dec_muxes = NULL;
|
|
int num_inc_muxes, num_dec_muxes, iconn;
|
|
int inc_inode, dec_inode;
|
|
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. */
|
|
assert(Fc % 2 == 0);
|
|
|
|
/* get_rr_node_indices needs x and y coords. */
|
|
/* Original VPR */
|
|
//x = ((CHANX == chan_type) ? seg : chan);
|
|
//y = ((CHANX == chan_type) ? chan : seg);
|
|
/* end */
|
|
/* mrFPGA : Xifan TANG */
|
|
x = (((is_stack ? CHANY : CHANX) == chan_type) ? seg : chan);
|
|
y = (((is_stack ? CHANY : CHANX) == chan_type) ? chan : seg);
|
|
/* end */
|
|
|
|
/* Get the lists of possible muxes. */
|
|
inc_muxes = label_wire_muxes(chan, seg, seg_details, max_len, INC_DIRECTION,
|
|
nodes_per_chan, &num_inc_muxes);
|
|
dec_muxes = label_wire_muxes(chan, seg, seg_details, max_len, DEC_DIRECTION,
|
|
nodes_per_chan, &num_dec_muxes);
|
|
|
|
/* 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 */
|
|
inc_mux = Fc_ofs[chan][seg] % num_inc_muxes;
|
|
dec_mux = Fc_ofs[chan][seg] % num_dec_muxes;
|
|
++Fc_ofs[chan][seg];
|
|
|
|
/* Figure out the track it corresponds to. */
|
|
inc_track = inc_muxes[inc_mux];
|
|
dec_track = dec_muxes[dec_mux];
|
|
|
|
/* Figure the inodes of those muxes */
|
|
inc_inode = get_rr_node_index(x, y, chan_type, inc_track,
|
|
L_rr_node_indices);
|
|
dec_inode = get_rr_node_index(x, y, chan_type, dec_track,
|
|
L_rr_node_indices);
|
|
|
|
/* Add to the list. */
|
|
if (FALSE == L_rr_edge_done[inc_inode]) {
|
|
L_rr_edge_done[inc_inode] = TRUE;
|
|
*edge_list_ptr = insert_in_edge_list(*edge_list_ptr, inc_inode,
|
|
seg_details[inc_track].opin_switch);
|
|
++num_edges;
|
|
}
|
|
if (FALSE == L_rr_edge_done[dec_inode]) {
|
|
L_rr_edge_done[dec_inode] = TRUE;
|
|
*edge_list_ptr = insert_in_edge_list(*edge_list_ptr, dec_inode,
|
|
seg_details[dec_track].opin_switch);
|
|
++num_edges;
|
|
}
|
|
}
|
|
|
|
if (inc_muxes) {
|
|
free(inc_muxes);
|
|
inc_muxes = NULL;
|
|
}
|
|
if (dec_muxes) {
|
|
free(dec_muxes);
|
|
dec_muxes = NULL;
|
|
}
|
|
|
|
return num_edges;
|
|
}
|
|
|
|
boolean is_cbox(INP int chan, INP int seg, INP int track,
|
|
INP t_seg_details * seg_details,
|
|
INP enum e_directionality directionality) {
|
|
|
|
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;
|
|
|
|
assert(ofs >= 0);
|
|
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];
|
|
}
|
|
|
|
static void load_chan_rr_indices(INP int nodes_per_chan, INP int chan_len,
|
|
INP int num_chans, INP t_rr_type type, INP t_seg_details * seg_details,
|
|
INOUTP int *index, INOUTP t_ivec *** indices) {
|
|
int chan, seg, track, start, inode;
|
|
|
|
indices[type] = (t_ivec **) my_malloc(sizeof(t_ivec *) * num_chans);
|
|
for (chan = 0; chan < num_chans; ++chan) {
|
|
indices[type][chan] = (t_ivec *) my_malloc(sizeof(t_ivec) * chan_len);
|
|
|
|
indices[type][chan][0].nelem = 0;
|
|
indices[type][chan][0].list = NULL;
|
|
|
|
/* Original VPR */
|
|
/* for (seg = 1; seg < chan_len; ++seg) { */
|
|
/* end */
|
|
/* mrFPGA: Xifan TANG */
|
|
for (seg = (is_stack ? 0 : 1); seg < chan_len; ++seg) {
|
|
/* end */
|
|
/* Alloc the track inode lookup list */
|
|
indices[type][chan][seg].nelem = nodes_per_chan;
|
|
indices[type][chan][seg].list = (int *) my_malloc(
|
|
sizeof(int) * nodes_per_chan);
|
|
for (track = 0; track < nodes_per_chan; ++track) {
|
|
indices[type][chan][seg].list[track] = OPEN;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Original VPR */
|
|
/*
|
|
for (chan = 0; chan < num_chans; ++chan) {
|
|
for (seg = 1; seg < chan_len; ++seg) {
|
|
*/
|
|
/* end */
|
|
/* mrFPGA: Xifan TANG */
|
|
for (chan = (is_stack ? 1 : 0); chan < num_chans; ++chan) {
|
|
for (seg = (is_stack ? 0 : 1); seg < chan_len; ++seg) {
|
|
/* end */
|
|
/* Assign an inode to the starts of tracks */
|
|
for (track = 0; track < indices[type][chan][seg].nelem; ++track) {
|
|
start = get_seg_start(seg_details, track, chan, seg);
|
|
/* Original VPR */
|
|
/* If the start of the wire doesn't have a inode,
|
|
* assign one to it. */
|
|
inode = indices[type][chan][start].list[track];
|
|
if (OPEN == inode) {
|
|
inode = *index;
|
|
++(*index);
|
|
|
|
indices[type][chan][start].list[track] = inode;
|
|
}
|
|
/* end */
|
|
/* Assign inode of start of wire to current position */
|
|
indices[type][chan][seg].list[track] = inode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct s_ivec ***
|
|
alloc_and_load_rr_node_indices(INP int nodes_per_chan, INP int L_nx,
|
|
INP int L_ny, INOUTP int *index, INP t_seg_details * seg_details) {
|
|
|
|
/* 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. */
|
|
|
|
int i, j, k, ofs;
|
|
t_ivec ***indices;
|
|
t_ivec tmp;
|
|
t_type_ptr type;
|
|
|
|
/* Alloc the lookup table */
|
|
indices = (t_ivec ***) my_malloc(sizeof(t_ivec **) * NUM_RR_TYPES);
|
|
indices[IPIN] = (t_ivec **) my_malloc(sizeof(t_ivec *) * (L_nx + 2));
|
|
indices[SINK] = (t_ivec **) my_malloc(sizeof(t_ivec *) * (L_nx + 2));
|
|
for (i = 0; i <= (L_nx + 1); ++i) {
|
|
indices[IPIN][i] = (t_ivec *) my_malloc(sizeof(t_ivec) * (L_ny + 2));
|
|
indices[SINK][i] = (t_ivec *) my_malloc(sizeof(t_ivec) * (L_ny + 2));
|
|
for (j = 0; j <= (L_ny + 1); ++j) {
|
|
indices[IPIN][i][j].nelem = 0;
|
|
indices[IPIN][i][j].list = NULL;
|
|
|
|
indices[SINK][i][j].nelem = 0;
|
|
indices[SINK][i][j].list = NULL;
|
|
}
|
|
}
|
|
|
|
/* Count indices for block nodes */
|
|
for (i = 0; i <= (L_nx + 1); i++) {
|
|
for (j = 0; j <= (L_ny + 1); j++) {
|
|
ofs = grid[i][j].offset;
|
|
if (0 == ofs) {
|
|
type = grid[i][j].type;
|
|
|
|
/* Load the pin class lookups. The ptc nums for SINK and SOURCE
|
|
* are disjoint so they can share the list. */
|
|
tmp.nelem = type->num_class;
|
|
tmp.list = NULL;
|
|
if (tmp.nelem > 0) {
|
|
tmp.list = (int *) my_malloc(sizeof(int) * tmp.nelem);
|
|
for (k = 0; k < tmp.nelem; ++k) {
|
|
tmp.list[k] = *index;
|
|
++(*index);
|
|
}
|
|
}
|
|
indices[SINK][i][j] = tmp;
|
|
|
|
/* Load the pin lookups. The ptc nums for IPIN and OPIN
|
|
* are disjoint so they can share the list. */
|
|
tmp.nelem = type->num_pins;
|
|
tmp.list = NULL;
|
|
if (tmp.nelem > 0) {
|
|
tmp.list = (int *) my_malloc(sizeof(int) * tmp.nelem);
|
|
for (k = 0; k < tmp.nelem; ++k) {
|
|
tmp.list[k] = *index;
|
|
++(*index);
|
|
}
|
|
}
|
|
indices[IPIN][i][j] = tmp;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Point offset blocks of a large block to base block */
|
|
for (i = 0; i <= (L_nx + 1); i++) {
|
|
for (j = 0; j <= (L_ny + 1); j++) {
|
|
ofs = grid[i][j].offset;
|
|
if (ofs > 0) {
|
|
/* NOTE: this only supports vertical large blocks */
|
|
indices[SINK][i][j] = indices[SINK][i][j - ofs];
|
|
indices[IPIN][i][j] = indices[IPIN][i][j - ofs];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* SOURCE and SINK have unique ptc values so their data can be shared.
|
|
* IPIN and OPIN have unique ptc values so their data can be shared. */
|
|
indices[SOURCE] = indices[SINK];
|
|
indices[OPIN] = indices[IPIN];
|
|
|
|
/* Original VPR */
|
|
/* Load the data for x and y channels */
|
|
/*
|
|
load_chan_rr_indices(nodes_per_chan, L_nx + 1, L_ny + 1, CHANX, seg_details,
|
|
index, indices);
|
|
load_chan_rr_indices(nodes_per_chan, L_ny + 1, L_nx + 1, CHANY, seg_details,
|
|
index, indices);
|
|
*/
|
|
/* end */
|
|
/* mrFPGA : Xifan TANG */
|
|
load_chan_rr_indices(nodes_per_chan, (is_stack ? L_ny + 1 : L_nx + 1), (is_stack ? L_nx + 1 : L_ny + 1),
|
|
CHANX, seg_details, index, indices);
|
|
load_chan_rr_indices(nodes_per_chan, (is_stack ? L_nx + 1 : L_ny + 1), (is_stack ? L_ny + 1 : L_nx + 1),
|
|
CHANY, seg_details, index, indices);
|
|
/* end */
|
|
|
|
return indices;
|
|
}
|
|
|
|
void free_rr_node_indices(INP t_ivec *** L_rr_node_indices) {
|
|
int i, j, ofs;
|
|
/* This function must unallocate the structure allocated in
|
|
* alloc_and_load_rr_node_indices. */
|
|
for (i = 0; i <= (nx + 1); ++i) {
|
|
for (j = 0; j <= (ny + 1); ++j) {
|
|
ofs = grid[i][j].offset;
|
|
if (ofs > 0) {
|
|
/* Vertical large blocks reference is same as offset 0 */
|
|
L_rr_node_indices[SINK][i][j].list = NULL;
|
|
L_rr_node_indices[IPIN][i][j].list = NULL;
|
|
continue;
|
|
}
|
|
if (L_rr_node_indices[SINK][i][j].list != NULL) {
|
|
free(L_rr_node_indices[SINK][i][j].list);
|
|
}
|
|
if (L_rr_node_indices[IPIN][i][j].list != NULL) {
|
|
free(L_rr_node_indices[IPIN][i][j].list);
|
|
}
|
|
}
|
|
free(L_rr_node_indices[SINK][i]);
|
|
free(L_rr_node_indices[IPIN][i]);
|
|
}
|
|
free(L_rr_node_indices[SINK]);
|
|
free(L_rr_node_indices[IPIN]);
|
|
|
|
for (i = 0; i < (nx + 1); ++i) {
|
|
for (j = 0; j < (ny + 1); ++j) {
|
|
if (L_rr_node_indices[CHANY][i][j].list != NULL) {
|
|
free(L_rr_node_indices[CHANY][i][j].list);
|
|
}
|
|
}
|
|
free(L_rr_node_indices[CHANY][i]);
|
|
}
|
|
free(L_rr_node_indices[CHANY]);
|
|
|
|
for (i = 0; i < (ny + 1); ++i) {
|
|
for (j = 0; j < (nx + 1); ++j) {
|
|
if (L_rr_node_indices[CHANX][i][j].list != NULL) {
|
|
free(L_rr_node_indices[CHANX][i][j].list);
|
|
}
|
|
}
|
|
free(L_rr_node_indices[CHANX][i]);
|
|
}
|
|
free(L_rr_node_indices[CHANX]);
|
|
|
|
free(L_rr_node_indices);
|
|
}
|
|
|
|
int get_rr_node_index(int x, int y, t_rr_type rr_type, int ptc,
|
|
t_ivec *** L_rr_node_indices) {
|
|
/* 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. *
|
|
* The order within a clb is: SOURCEs + SINKs (type->num_class of them); IPINs, *
|
|
* and OPINs (pins_per_clb of them); CHANX; and CHANY (nodes_per_chan of *
|
|
* each). For (x,y) locations that point at pads the order is: type->capacity *
|
|
* occurances of SOURCE, SINK, OPIN, IPIN (one for each pad), then one *
|
|
* associated channel (if there is a channel at (x,y)). All IO pads are *
|
|
* bidirectional, so while each will be used only as an INPAD or as an *
|
|
* OUTPAD, all the switches necessary to do both must be in each pad. *
|
|
* *
|
|
* 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. */
|
|
|
|
int iclass, tmp;
|
|
t_type_ptr type;
|
|
t_ivec lookup;
|
|
|
|
assert(ptc >= 0);
|
|
assert(x >= 0 && x <= (nx + 1));
|
|
assert(y >= 0 && y <= (ny + 1));
|
|
|
|
type = grid[x][y].type;
|
|
|
|
/* Currently need to swap x and y for CHANX because of chan, seg convention */
|
|
/* Original VPR */
|
|
/*
|
|
if (CHANX == rr_type) {
|
|
tmp = x;
|
|
x = y;
|
|
y = tmp;
|
|
}
|
|
*/
|
|
/* end */
|
|
/* mrFPGA: Xifan TANG*/
|
|
if ((is_stack ? CHANY : CHANX) == rr_type) {
|
|
tmp = x;
|
|
x = y;
|
|
y = tmp;
|
|
}
|
|
/* end */
|
|
|
|
/* Start of that block. */
|
|
lookup = L_rr_node_indices[rr_type][x][y];
|
|
|
|
/* Check valid ptc num */
|
|
assert(ptc >= 0);
|
|
assert(ptc < lookup.nelem);
|
|
|
|
#ifdef DEBUG
|
|
switch (rr_type) {
|
|
case SOURCE:
|
|
assert(ptc < type->num_class);
|
|
assert(type->class_inf[ptc].type == DRIVER);
|
|
break;
|
|
|
|
case SINK:
|
|
assert(ptc < type->num_class);
|
|
assert(type->class_inf[ptc].type == RECEIVER);
|
|
break;
|
|
|
|
case OPIN:
|
|
assert(ptc < type->num_pins);
|
|
iclass = type->pin_class[ptc];
|
|
assert(type->class_inf[iclass].type == DRIVER);
|
|
break;
|
|
|
|
case IPIN:
|
|
assert(ptc < type->num_pins);
|
|
iclass = type->pin_class[ptc];
|
|
assert(type->class_inf[iclass].type == RECEIVER);
|
|
break;
|
|
|
|
case CHANX:
|
|
case CHANY:
|
|
break;
|
|
|
|
default:
|
|
vpr_printf(TIO_MESSAGE_ERROR, "Bad rr_node passed to get_rr_node_index.\n");
|
|
vpr_printf(TIO_MESSAGE_ERROR, "Request for type=%d ptc=%d at (%d, %d).\n", rr_type, ptc, x, y);
|
|
exit(1);
|
|
}
|
|
#endif
|
|
|
|
return lookup.list[ptc];
|
|
}
|
|
|
|
int get_track_to_ipins(int seg, int chan, int track,
|
|
t_linked_edge ** edge_list_ptr, t_ivec *** L_rr_node_indices,
|
|
struct s_ivec ****track_to_ipin_lookup, t_seg_details * seg_details,
|
|
enum e_rr_type chan_type, int chan_length, int wire_to_ipin_switch,
|
|
enum e_directionality directionality) {
|
|
|
|
/* This counts the fan-out from wire segment (chan, seg, track) to blocks on either side */
|
|
|
|
t_linked_edge *edge_list_head;
|
|
int j, pass, iconn, phy_track, end, to_node, max_conn, ipin, side, x, y,
|
|
num_conn;
|
|
t_type_ptr type;
|
|
int off;
|
|
|
|
/* End of this wire */
|
|
/* Original VPR */
|
|
/* end = get_seg_end(seg_details, track, seg, chan, chan_length); */
|
|
/* end */
|
|
/* mrFPGA: Xifan TANG */
|
|
end = get_seg_end(seg_details, track, seg, chan, chan_length);
|
|
/* end */
|
|
|
|
edge_list_head = *edge_list_ptr;
|
|
num_conn = 0;
|
|
|
|
for (j = seg; j <= end; j++) {
|
|
if (is_cbox(chan, j, track, seg_details, directionality)) {
|
|
for (pass = 0; pass < 2; ++pass) {
|
|
/* mrFPGA: Xifan TANG */
|
|
if (is_stack) {
|
|
if (CHANX == chan_type) {
|
|
x = chan;
|
|
y = j + pass;
|
|
side = (0 == pass ? TOP : BOTTOM);
|
|
} else {
|
|
assert(CHANY == chan_type);
|
|
x = j + pass;
|
|
y = chan;
|
|
side = (0 == pass ? RIGHT : LEFT);
|
|
}
|
|
/* end */
|
|
} else {
|
|
/* Original VPR */
|
|
if (CHANX == chan_type) {
|
|
x = j;
|
|
y = chan + pass;
|
|
side = (0 == pass ? TOP : BOTTOM);
|
|
} else {
|
|
assert(CHANY == chan_type);
|
|
x = chan + pass;
|
|
y = j;
|
|
side = (0 == pass ? RIGHT : LEFT);
|
|
}
|
|
/* end */
|
|
}
|
|
/* PAJ - if the pointed to is an EMPTY then shouldn't look for ipins */
|
|
if (grid[x][y].type == 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);
|
|
|
|
/* We need the type to find the ipin map for this type */
|
|
type = grid[x][y].type;
|
|
off = grid[x][y].offset;
|
|
|
|
max_conn =
|
|
track_to_ipin_lookup[type->index][phy_track][off][side].nelem;
|
|
for (iconn = 0; iconn < max_conn; iconn++) {
|
|
ipin =
|
|
track_to_ipin_lookup[type->index][phy_track][off][side].list[iconn];
|
|
|
|
/* Check there is a connection and Fc map isn't wrong */
|
|
assert(type->pinloc[off][side][ipin]);
|
|
assert(type->is_global_pin[ipin] == FALSE);
|
|
|
|
to_node = get_rr_node_index(x, y, IPIN, ipin,
|
|
L_rr_node_indices);
|
|
edge_list_head = insert_in_edge_list(edge_list_head,
|
|
to_node, wire_to_ipin_switch);
|
|
}
|
|
num_conn += max_conn;
|
|
}
|
|
}
|
|
}
|
|
|
|
*edge_list_ptr = edge_list_head;
|
|
return (num_conn);
|
|
}
|
|
|
|
/* Counts how many connections should be made from this segment to the y- *
|
|
* segments in the adjacent channels at to_j. It returns the number of *
|
|
* connections, 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(INP int from_chan, INP int from_seg, INP int from_track,
|
|
INP t_rr_type from_type, INP int to_seg, INP t_rr_type to_type,
|
|
INP int chan_len, INP int nodes_per_chan, INP int *opin_mux_size,
|
|
INP int Fs_per_side, INP short *****sblock_pattern,
|
|
INOUTP struct s_linked_edge **edge_list,
|
|
INP t_seg_details * seg_details,
|
|
INP enum e_directionality directionality,
|
|
INP t_ivec *** L_rr_node_indices, INOUTP boolean * L_rr_edge_done,
|
|
INP struct s_ivec ***switch_block_conn) {
|
|
int num_conn;
|
|
int from_switch, from_end, from_sb, from_first;
|
|
int to_chan, to_sb;
|
|
int start, end;
|
|
struct s_ivec conn_tracks;
|
|
boolean from_is_sbox, is_behind, Fs_clipped;
|
|
enum e_side from_side_a, from_side_b, to_side;
|
|
|
|
/* mrFPGA: Xifan TANG */
|
|
//int true_from_start, true_from_end;
|
|
|
|
/* Original VPR */
|
|
assert(
|
|
from_seg == get_seg_start(seg_details, from_track, from_chan, from_seg));
|
|
|
|
from_switch = seg_details[from_track].wire_switch;
|
|
from_end = get_seg_end(seg_details, from_track, from_seg, from_chan,
|
|
chan_len);
|
|
/* end */
|
|
|
|
/* mrFPGA : Xifan TANG*/
|
|
if (is_stack) {
|
|
from_end = from_end + 1;
|
|
from_first = from_seg;
|
|
/* end */
|
|
} else {
|
|
/* Original VPR */
|
|
from_first = from_seg - 1;
|
|
}
|
|
/* end */
|
|
|
|
/* mrFPGA : Xifan TANG*/
|
|
if (is_stack ? (CHANY == from_type) : (CHANX == from_type)) {
|
|
from_side_a = RIGHT;
|
|
from_side_b = LEFT;
|
|
} else {
|
|
assert(is_stack ? (CHANX == from_type) : (CHANY == from_type));
|
|
from_side_a = TOP;
|
|
from_side_b = BOTTOM;
|
|
}
|
|
/* end */
|
|
|
|
/* Original VPR */
|
|
/* Figure out the sides of SB the from_wire will use */
|
|
/*
|
|
if (CHANX == from_type) {
|
|
from_side_a = RIGHT;
|
|
from_side_b = LEFT;
|
|
} else {
|
|
assert(CHANY == from_type);
|
|
from_side_a = TOP;
|
|
from_side_b = BOTTOM;
|
|
}
|
|
*/
|
|
/* end */
|
|
|
|
/* Figure out if the to_wire is connecting to a SB
|
|
* that is behind it. */
|
|
is_behind = FALSE;
|
|
if (to_type == from_type) {
|
|
/* Original VPR */
|
|
/* If inline, check that they only are trying
|
|
* to connect at endpoints. */
|
|
/*
|
|
assert((to_seg == (from_end + 1)) || (to_seg == (from_seg - 1)));
|
|
if (to_seg > from_end) {
|
|
is_behind = TRUE;
|
|
}
|
|
*/
|
|
/* end */
|
|
/* mrFPGA: Xifan TANG */
|
|
assert((to_seg == (from_end + (is_stack ? 0 : 1))) || (to_seg == (from_seg - 1)));
|
|
if (is_stack ? to_seg >= from_end : to_seg > from_end) {
|
|
is_behind = TRUE;
|
|
}
|
|
/* end */
|
|
} else {
|
|
/* Original VPR */
|
|
/* If bending, check that they are adjacent to
|
|
* our channel. */
|
|
/*
|
|
assert((to_seg == from_chan) || (to_seg == (from_chan + 1)));
|
|
if (to_seg > from_chan) {
|
|
is_behind = TRUE;
|
|
}
|
|
*/
|
|
/* end */
|
|
/* mrFPGA: Xifan TANG */
|
|
if (is_stack) {
|
|
assert((to_seg == (from_chan - 1)) || (to_seg == from_chan));
|
|
} else {
|
|
assert((to_seg == from_chan) || (to_seg == (from_chan + 1)));
|
|
}
|
|
if (is_stack ? (to_seg >= from_chan) : (to_seg > from_chan)) {
|
|
is_behind = TRUE;
|
|
}
|
|
/* end */
|
|
}
|
|
|
|
/* Original VPR */
|
|
/* Figure out the side of SB the to_wires will use.
|
|
* The to_seg and from_chan are in same direction. */
|
|
/*
|
|
if (CHANX == to_type) {
|
|
to_side = (is_behind ? RIGHT : LEFT);
|
|
} else {
|
|
assert(CHANY == to_type);
|
|
to_side = (is_behind ? TOP : BOTTOM);
|
|
}
|
|
*/
|
|
/* end */
|
|
|
|
/* mrFPGA: Xifan TANG */
|
|
if (is_stack ? (CHANY == to_type) : (CHANX == to_type)) {
|
|
to_side = (is_behind ? RIGHT : LEFT);
|
|
} else {
|
|
assert(is_stack ? (CHANX == to_type) : (CHANY == to_type));
|
|
to_side = (is_behind ? TOP : BOTTOM);
|
|
}
|
|
/* end */
|
|
|
|
/* Set the loop bounds */
|
|
start = from_first;
|
|
end = from_end;
|
|
|
|
/* If we are connecting in same direction the connection is
|
|
* on one of the two sides so clip the bounds to the SB of
|
|
* interest and proceed normally. */
|
|
if (to_type == from_type) {
|
|
start = (is_behind ? end : start);
|
|
end = start;
|
|
}
|
|
|
|
/* Iterate over the SBs */
|
|
num_conn = 0;
|
|
for (from_sb = start; from_sb <= end; ++from_sb) {
|
|
/* mrFPGA: Xifan TANG */
|
|
if (is_stack) {
|
|
if (CHANX == from_type) {
|
|
if ((0 == from_sb)||((ny + 1) == from_sb)) {
|
|
continue;
|
|
}
|
|
} else {
|
|
assert(CHANY == from_type);
|
|
if ((0 == from_sb)||((nx + 1) == from_sb)) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
/* end */
|
|
|
|
/* Figure out if we are at a sbox */
|
|
from_is_sbox = is_sbox(from_chan, from_seg, from_sb, from_track,
|
|
seg_details, directionality);
|
|
/* end of wire must be an sbox */
|
|
if (from_sb == from_end || from_sb == from_first) {
|
|
from_is_sbox = TRUE; /* Endpoints always default to true */
|
|
}
|
|
|
|
/* to_chan is the current segment if different directions,
|
|
* otherwise to_chan is the from_chan */
|
|
to_chan = from_sb;
|
|
to_sb = from_chan;
|
|
if (from_type == to_type) {
|
|
to_chan = from_chan;
|
|
to_sb = from_sb;
|
|
}
|
|
|
|
/* Do the edges going to the left or down */
|
|
if (from_sb < from_end) {
|
|
if (BI_DIRECTIONAL == directionality) {
|
|
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,
|
|
seg_details, from_is_sbox, from_switch, L_rr_edge_done,
|
|
directionality, edge_list);
|
|
}
|
|
if (UNI_DIRECTIONAL == directionality) {
|
|
/* No fanout if no SB. */
|
|
/* We are connecting from the top or right of SB so it
|
|
* makes the most sense to only there from DEC_DIRECTION wires. */
|
|
if ((from_is_sbox)
|
|
&& (DEC_DIRECTION == seg_details[from_track].direction)) {
|
|
num_conn += get_unidir_track_to_chan_seg(
|
|
(boolean)(from_sb == from_first), from_track, to_chan,
|
|
to_seg, to_sb, to_type, nodes_per_chan, nx, ny,
|
|
from_side_a, to_side, Fs_per_side, opin_mux_size,
|
|
sblock_pattern, L_rr_node_indices, seg_details,
|
|
L_rr_edge_done, &Fs_clipped, edge_list);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Do the edges going to the right or up */
|
|
if (from_sb > from_first) {
|
|
if (BI_DIRECTIONAL == directionality) {
|
|
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,
|
|
seg_details, from_is_sbox, from_switch, L_rr_edge_done,
|
|
directionality, edge_list);
|
|
}
|
|
if (UNI_DIRECTIONAL == directionality) {
|
|
/* No fanout if no SB. */
|
|
/* We are connecting from the bottom or left of SB so it
|
|
* makes the most sense to only there from INC_DIRECTION wires. */
|
|
if ((from_is_sbox)
|
|
&& (INC_DIRECTION == seg_details[from_track].direction)) {
|
|
num_conn += get_unidir_track_to_chan_seg(
|
|
(boolean)(from_sb == from_end), from_track, to_chan, to_seg,
|
|
to_sb, to_type, nodes_per_chan, nx, ny, from_side_b,
|
|
to_side, Fs_per_side, opin_mux_size, sblock_pattern,
|
|
L_rr_node_indices, seg_details, L_rr_edge_done,
|
|
&Fs_clipped, edge_list);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return num_conn;
|
|
}
|
|
|
|
/* mrFPGA : Xifan TANG, Abolish */
|
|
/*
|
|
static int get_seg_to_seg(INP t_ivec*** L_rr_node_indices,
|
|
INP int to_chan,
|
|
INP int to_seg,
|
|
INP int track,
|
|
INP t_rr_type type,
|
|
INP t_seg_details* seg_details,
|
|
INOUTP struct s_linked_edge** edge_list) {
|
|
int to_x, to_y, to_node;
|
|
|
|
if ((is_stack ? CHANY : CHANX) == type) {
|
|
to_x = to_seg;
|
|
to_y = to_chan;
|
|
} else {
|
|
assert((is_stack ? CHANX : CHANY) == type);
|
|
to_x = to_chan;
|
|
to_y = to_seg;
|
|
}
|
|
to_node = get_rr_node_index(to_x, to_y, type, track, L_rr_node_indices);
|
|
*edge_list = insert_in_edge_list(*edge_list, to_node, seg_details[track].seg_switch);
|
|
|
|
return 1;
|
|
}
|
|
*/
|
|
/* END */
|
|
|
|
|
|
static int get_bidir_track_to_chan_seg(INP struct s_ivec conn_tracks,
|
|
INP t_ivec *** L_rr_node_indices, INP int to_chan, INP int to_seg,
|
|
INP int to_sb, INP t_rr_type to_type, INP t_seg_details * seg_details,
|
|
INP boolean from_is_sbox, INP int from_switch,
|
|
INOUTP boolean * L_rr_edge_done,
|
|
INP enum e_directionality directionality,
|
|
INOUTP struct s_linked_edge **edge_list) {
|
|
int iconn, to_track, to_node, to_switch, num_conn, to_x, to_y, i;
|
|
boolean to_is_sbox;
|
|
short switch_types[2];
|
|
|
|
/* Original VPR */
|
|
/* x, y coords for get_rr_node lookups */
|
|
/*
|
|
if (CHANX == to_type) {
|
|
to_x = to_seg;
|
|
to_y = to_chan;
|
|
} else {
|
|
assert(CHANY == to_type);
|
|
to_x = to_chan;
|
|
to_y = to_seg;
|
|
}
|
|
*/
|
|
/* END */
|
|
/* mrFPGA : Xifan TANG*/
|
|
if ((is_stack ? CHANY : CHANX) == to_type) {
|
|
to_x = to_seg;
|
|
to_y = to_chan;
|
|
} else {
|
|
assert((is_stack ? CHANX : CHANY) == to_type);
|
|
to_x = to_chan;
|
|
to_y = to_seg;
|
|
}
|
|
/* END */
|
|
|
|
/* Go through the list of tracks we can connect to */
|
|
num_conn = 0;
|
|
for (iconn = 0; iconn < conn_tracks.nelem; ++iconn) {
|
|
to_track = conn_tracks.list[iconn];
|
|
to_node = get_rr_node_index(to_x, to_y, to_type, to_track,
|
|
L_rr_node_indices);
|
|
|
|
/* Skip edge if already done */
|
|
if (L_rr_edge_done[to_node]) {
|
|
continue;
|
|
}
|
|
|
|
/* Get the switches for any edges between the two tracks */
|
|
to_switch = seg_details[to_track].wire_switch;
|
|
|
|
to_is_sbox = is_sbox(to_chan, to_seg, to_sb, to_track, seg_details,
|
|
directionality);
|
|
get_switch_type(from_is_sbox, to_is_sbox, from_switch, to_switch,
|
|
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 */
|
|
*edge_list = insert_in_edge_list(*edge_list, to_node,
|
|
switch_types[i]);
|
|
/* Mark the edge as now done */
|
|
L_rr_edge_done[to_node] = TRUE;
|
|
++num_conn;
|
|
}
|
|
}
|
|
|
|
return num_conn;
|
|
}
|
|
|
|
static int get_unidir_track_to_chan_seg(INP boolean is_end_sb,
|
|
INP int from_track, INP int to_chan, INP int to_seg, INP int to_sb,
|
|
INP t_rr_type to_type, INP int nodes_per_chan, INP int L_nx,
|
|
INP int L_ny, INP enum e_side from_side, INP enum e_side to_side,
|
|
INP int Fs_per_side, INP int *opin_mux_size,
|
|
INP short *****sblock_pattern, INP t_ivec *** L_rr_node_indices,
|
|
INP t_seg_details * seg_details, INOUTP boolean * L_rr_edge_done,
|
|
OUTP boolean * Fs_clipped, INOUTP struct s_linked_edge **edge_list) {
|
|
int to_track, to_mux, to_node, to_x, to_y, i, max_len, num_labels;
|
|
int sb_x, sb_y, count;
|
|
int *mux_labels = NULL;
|
|
enum e_direction to_dir;
|
|
boolean is_fringe, is_core, is_corner, is_straight;
|
|
|
|
/* x, y coords for get_rr_node lookups */
|
|
/* Original VPR */
|
|
/*
|
|
if (CHANX == to_type) {
|
|
to_x = to_seg;
|
|
to_y = to_chan;
|
|
sb_x = to_sb;
|
|
sb_y = to_chan;
|
|
max_len = L_nx;
|
|
} else {
|
|
assert(CHANY == to_type);
|
|
to_x = to_chan;
|
|
to_y = to_seg;
|
|
sb_x = to_chan;
|
|
sb_y = to_sb;
|
|
max_len = L_ny;
|
|
}
|
|
*/
|
|
/* end */
|
|
/* mrFPGA: Xifan TANG*/
|
|
if ((is_stack ? CHANY : CHANX) == to_type) {
|
|
to_x = to_seg;
|
|
to_y = to_chan;
|
|
sb_x = to_sb;
|
|
sb_y = to_chan;
|
|
max_len = L_nx;
|
|
} else {
|
|
assert((is_stack ? CHANX : CHANY) == to_type);
|
|
to_x = to_chan;
|
|
to_y = to_seg;
|
|
sb_x = to_chan;
|
|
sb_y = to_sb;
|
|
max_len = L_ny;
|
|
}
|
|
/* end */
|
|
|
|
to_dir = DEC_DIRECTION;
|
|
if (to_sb < to_seg) {
|
|
to_dir = INC_DIRECTION;
|
|
}
|
|
|
|
*Fs_clipped = FALSE;
|
|
|
|
/* SBs go from (0, 0) to (nx, ny) */
|
|
is_corner = (boolean)(((sb_x < 1) || (sb_x >= L_nx))
|
|
&& ((sb_y < 1) || (sb_y >= L_ny)));
|
|
is_fringe = (boolean)((FALSE == is_corner)
|
|
&& ((sb_x < 1) || (sb_y < 1) || (sb_x >= L_nx) || (sb_y >= L_ny)));
|
|
is_core = (boolean)((FALSE == is_corner) && (FALSE == is_fringe));
|
|
is_straight = (boolean)((from_side == RIGHT && to_side == LEFT)
|
|
|| (from_side == LEFT && to_side == RIGHT)
|
|
|| (from_side == TOP && to_side == BOTTOM)
|
|
|| (from_side == BOTTOM && to_side == TOP));
|
|
|
|
/* Ending wires use N-to-N mapping if not fringe or if goes straight */
|
|
if (is_end_sb && (is_core || is_corner || is_straight)) {
|
|
/* Get the list of possible muxes for the N-to-N mapping. */
|
|
mux_labels = label_wire_muxes(to_chan, to_seg, seg_details, max_len,
|
|
to_dir, nodes_per_chan, &num_labels);
|
|
} else {
|
|
assert(is_fringe || !is_end_sb);
|
|
|
|
mux_labels = label_wire_muxes_for_balance(to_chan, to_seg, seg_details,
|
|
max_len, to_dir, nodes_per_chan, &num_labels, to_type,
|
|
opin_mux_size, L_rr_node_indices);
|
|
}
|
|
|
|
/* Can't connect if no muxes. */
|
|
if (num_labels < 1) {
|
|
if (mux_labels) {
|
|
free(mux_labels);
|
|
mux_labels = NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Check if Fs demand was too high. */
|
|
if (Fs_per_side > num_labels) {
|
|
*Fs_clipped = TRUE;
|
|
}
|
|
|
|
/* Get the target label */
|
|
to_mux = sblock_pattern[sb_x][sb_y][from_side][to_side][from_track];
|
|
assert(to_mux != UN_SET);
|
|
|
|
/* Handle Fs > 3 but assigning consecutive muxes. */
|
|
count = 0;
|
|
for (i = 0; i < Fs_per_side; ++i) {
|
|
/* Use the balanced labeling for passing and fringe wires */
|
|
to_track = mux_labels[(to_mux + i) % num_labels];
|
|
to_node = get_rr_node_index(to_x, to_y, to_type, to_track,
|
|
L_rr_node_indices);
|
|
|
|
/* Add edge to list. */
|
|
if (FALSE == L_rr_edge_done[to_node]) {
|
|
L_rr_edge_done[to_node] = TRUE;
|
|
*edge_list = insert_in_edge_list(*edge_list, to_node,
|
|
seg_details[to_track].wire_switch);
|
|
++count;
|
|
}
|
|
}
|
|
|
|
if (mux_labels) {
|
|
free(mux_labels);
|
|
mux_labels = NULL;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
boolean is_sbox(INP int chan, INP int wire_seg, INP int sb_seg, INP int track,
|
|
INP t_seg_details * seg_details,
|
|
INP 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);
|
|
|
|
/* original VPR */
|
|
/* Ofset 0 is behind us, so add 1 */
|
|
/* ofs = sb_seg - wire_seg + 1; */
|
|
/* end */
|
|
|
|
/* mrFPGA: Xifan TANG */
|
|
ofs = sb_seg - wire_seg + (is_stack ? 0 : 1);
|
|
/* end */
|
|
|
|
assert(ofs >= 0);
|
|
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(boolean is_from_sbox, boolean is_to_sbox,
|
|
short from_node_switch, short to_node_switch, 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 one topology (a buffer in the forward direction and a pass *
|
|
* transistor in the backward direction) results in *two* switches. */
|
|
|
|
boolean forward_pass_trans;
|
|
boolean backward_pass_trans;
|
|
int used, min_switch, max_switch;
|
|
|
|
/* mrFPGA: Xifan TANG */
|
|
if (is_mrFPGA) {
|
|
get_mrfpga_switch_type(is_from_sbox, is_to_sbox, from_node_switch,
|
|
to_node_switch, switch_types);
|
|
return;
|
|
}
|
|
/* end */
|
|
|
|
switch_types[0] = OPEN; /* No switch */
|
|
switch_types[1] = OPEN;
|
|
used = 0;
|
|
forward_pass_trans = FALSE;
|
|
backward_pass_trans = FALSE;
|
|
|
|
/* Connect forward if we are a sbox */
|
|
if (is_from_sbox) {
|
|
switch_types[used] = to_node_switch;
|
|
if (FALSE == switch_inf[to_node_switch].buffered) {
|
|
forward_pass_trans = TRUE;
|
|
}
|
|
++used;
|
|
}
|
|
|
|
/* Check for pass_trans coming backwards */
|
|
if (is_to_sbox) {
|
|
if (FALSE == switch_inf[from_node_switch].buffered) {
|
|
switch_types[used] = from_node_switch;
|
|
backward_pass_trans = TRUE;
|
|
++used;
|
|
}
|
|
}
|
|
|
|
/* Take the larger pass trans if there are two */
|
|
if (forward_pass_trans && backward_pass_trans) {
|
|
min_switch = std::min(to_node_switch, from_node_switch);
|
|
max_switch = std::max(to_node_switch, from_node_switch);
|
|
|
|
/* Take the smaller index unless the other
|
|
* pass_trans is bigger (smaller R). */
|
|
switch_types[used] = min_switch;
|
|
if (switch_inf[max_switch].R < switch_inf[min_switch].R) {
|
|
switch_types[used] = max_switch;
|
|
}
|
|
++used;
|
|
}
|
|
}
|
|
|
|
static int vpr_to_phy_track(INP int itrack, INP int chan_num, INP int seg_num,
|
|
INP t_seg_details * seg_details,
|
|
INP 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;
|
|
}
|
|
|
|
short *****
|
|
alloc_sblock_pattern_lookup(INP int L_nx, INP int L_ny, INP int nodes_per_chan) {
|
|
int i, j, from_side, to_side, itrack, items;
|
|
short *****result;
|
|
short *****i_list;
|
|
short ****j_list;
|
|
short ***from_list;
|
|
short **to_list;
|
|
short *track_list;
|
|
|
|
/* 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. */
|
|
items = 1;
|
|
items *= (L_nx + 1);
|
|
i_list = (short *****) my_malloc(sizeof(short ****) * items);
|
|
items *= (L_ny + 1);
|
|
j_list = (short ****) my_malloc(sizeof(short ***) * items);
|
|
items *= (4);
|
|
from_list = (short ***) my_malloc(sizeof(short **) * items);
|
|
items *= (4);
|
|
to_list = (short **) my_malloc(sizeof(short *) * items);
|
|
items *= (nodes_per_chan);
|
|
track_list = (short *) my_malloc(sizeof(short) * items);
|
|
|
|
/* Build the pointer lists to form the multidimensional array */
|
|
result = i_list;
|
|
i_list += (L_nx + 1); /* Skip forward nx+1 items */
|
|
for (i = 0; i < (L_nx + 1); ++i) {
|
|
|
|
result[i] = j_list;
|
|
j_list += (L_ny + 1); /* Skip forward ny+1 items */
|
|
for (j = 0; j < (L_ny + 1); ++j) {
|
|
|
|
result[i][j] = from_list;
|
|
from_list += (4); /* Skip forward 4 items */
|
|
for (from_side = 0; from_side < 4; ++from_side) {
|
|
|
|
result[i][j][from_side] = to_list;
|
|
to_list += (4); /* Skip forward 4 items */
|
|
for (to_side = 0; to_side < 4; ++to_side) {
|
|
|
|
result[i][j][from_side][to_side] = track_list;
|
|
track_list += (nodes_per_chan); /* Skip forward nodes_per_chan items */
|
|
for (itrack = 0; itrack < nodes_per_chan; itrack++) {
|
|
|
|
/* Set initial value to be unset */
|
|
result[i][j][from_side][to_side][itrack] = UN_SET;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* This is the outer pointer to the full matrix */
|
|
return result;
|
|
}
|
|
|
|
void free_sblock_pattern_lookup(INOUTP short *****sblock_pattern) {
|
|
/* This free function corresponds to the chunked matrix
|
|
* allocation above and there should only be one free
|
|
* call for each dimension. */
|
|
|
|
/* Free dimensions from the inner one, outwards so
|
|
* we can still access them. The comments beside
|
|
* each one indicate the corresponding name used when
|
|
* allocating them. */
|
|
free(****sblock_pattern); /* track_list */
|
|
free(***sblock_pattern); /* to_list */
|
|
free(**sblock_pattern); /* from_list */
|
|
free(*sblock_pattern); /* j_list */
|
|
free(sblock_pattern); /* i_list */
|
|
}
|
|
|
|
void load_sblock_pattern_lookup(INP int i, INP int j, INP int nodes_per_chan,
|
|
INP t_seg_details * seg_details, INP int Fs,
|
|
INP enum e_switch_block_type switch_block_type,
|
|
INOUTP short *****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. */
|
|
|
|
int side_cw_incoming_wire_count, side_ccw_incoming_wire_count,
|
|
opp_incoming_wire_count;
|
|
int to_side, side, side_cw, side_ccw, side_opp, itrack;
|
|
int Fs_per_side, chan, seg, chan_len, sb_seg;
|
|
boolean is_core_sblock, is_corner_sblock, x_edge, y_edge;
|
|
int *incoming_wire_label[4];
|
|
int *wire_mux_on_track[4];
|
|
int num_incoming_wires[4];
|
|
int num_ending_wires[4];
|
|
int num_wire_muxes[4];
|
|
boolean skip, vert, pos_dir;
|
|
enum e_direction dir;
|
|
|
|
Fs_per_side = 1;
|
|
if (Fs != -1) {
|
|
Fs_per_side = Fs / 3;
|
|
}
|
|
|
|
/* SB's have coords from (0, 0) to (nx, ny) */
|
|
assert(i >= 0);
|
|
assert(i <= nx);
|
|
assert(j >= 0);
|
|
assert(j <= ny);
|
|
|
|
/* 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 sbox 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 sbox)
|
|
* are labelled first 0,1,2,... p-1, then the incoming passing wires with sbox 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 sbox 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.
|
|
*
|
|
* For the sblock_pattern_init_mux_lookup lookup table, I will only need the lookup
|
|
* table to remember the first/init mux to connect, since the convention is Fs_per_side consecutive
|
|
* muxes to connect. Then how do I determine the order of the incoming wires? I use the labels
|
|
* on side_1, then labels on side_2, then labels on opp_side. Effectively I listed all
|
|
* incoming passing wires from the three sides, and order them to each make Fs_per_side
|
|
* consecutive connections to muxes, and use % to rotate to keep imbalance at most 1.
|
|
*/
|
|
|
|
/* SB's range from (0, 0) to (nx, ny) */
|
|
/* First find all four sides' incoming wires */
|
|
x_edge = (boolean)((i < 1) || (i >= nx));
|
|
y_edge = (boolean)((j < 1) || (j >= ny));
|
|
|
|
is_corner_sblock = (boolean)(x_edge && y_edge);
|
|
is_core_sblock = (boolean)(!x_edge && !y_edge);
|
|
|
|
/* "Label" the wires around the switch block by connectivity. */
|
|
for (side = 0; side < 4; ++side) {
|
|
/* Assume the channel segment doesn't exist. */
|
|
wire_mux_on_track[side] = NULL;
|
|
incoming_wire_label[side] = NULL;
|
|
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. */
|
|
skip = TRUE;
|
|
switch (side) {
|
|
/* Original VPR */
|
|
/*
|
|
case TOP:
|
|
if (j < ny) {
|
|
skip = FALSE;
|
|
}
|
|
;
|
|
break;
|
|
case RIGHT:
|
|
if (i < nx) {
|
|
skip = FALSE;
|
|
}
|
|
break;
|
|
case BOTTOM:
|
|
if (j > 0) {
|
|
skip = FALSE;
|
|
}
|
|
break;
|
|
case LEFT:
|
|
if (i > 0) {
|
|
skip = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
*/
|
|
/* end */
|
|
/* mrFPGA : Xifan TANG*/
|
|
case TOP:
|
|
if (j < (is_stack ? ny+1 : ny)) {
|
|
skip = FALSE;
|
|
}
|
|
;
|
|
break;
|
|
case RIGHT:
|
|
if (i < (is_stack ? nx+1 : nx)) {
|
|
skip = FALSE;
|
|
}
|
|
break;
|
|
case BOTTOM:
|
|
if (j > 0) {
|
|
skip = FALSE;
|
|
}
|
|
break;
|
|
case LEFT:
|
|
if (i > 0) {
|
|
skip = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
/* end */
|
|
if (skip) {
|
|
continue;
|
|
}
|
|
|
|
/* Figure out the channel and segment for a certain direction */
|
|
vert = (boolean) ((side == TOP) || (side == BOTTOM));
|
|
pos_dir = (boolean) ((side == TOP) || (side == RIGHT));
|
|
chan = (vert ? i : j);
|
|
sb_seg = (vert ? j : i);
|
|
/* Original VPR */
|
|
/* seg = (pos_dir ? (sb_seg + 1) : sb_seg); */
|
|
/* end */
|
|
/* mrFPGA : Xifan TANG*/
|
|
seg = (pos_dir ? (sb_seg + 1) : sb_seg) - (is_stack ? 1 : 0);
|
|
/* end */
|
|
chan_len = (vert ? ny : nx);
|
|
|
|
/* Figure out all the tracks on a side that are ending and the
|
|
* ones that are passing through and have a SB. */
|
|
dir = (pos_dir ? DEC_DIRECTION : INC_DIRECTION);
|
|
incoming_wire_label[side] = label_incoming_wires(chan, seg, sb_seg,
|
|
seg_details, chan_len, dir, nodes_per_chan,
|
|
&num_incoming_wires[side], &num_ending_wires[side]);
|
|
|
|
/* Figure out all the tracks on a side that are starting. */
|
|
dir = (pos_dir ? INC_DIRECTION : DEC_DIRECTION);
|
|
wire_mux_on_track[side] = label_wire_muxes(chan, seg, seg_details,
|
|
chan_len, dir, nodes_per_chan, &num_wire_muxes[side]);
|
|
}
|
|
|
|
for (to_side = 0; to_side < 4; to_side++) {
|
|
/* Can't do anything if no muxes on this side. */
|
|
if (0 == num_wire_muxes[to_side]) {
|
|
continue;
|
|
}
|
|
|
|
/* Figure out side rotations */
|
|
assert((TOP == 0) && (RIGHT == 1) && (BOTTOM == 2) && (LEFT == 3));
|
|
side_cw = (to_side + 1) % 4;
|
|
side_opp = (to_side + 2) % 4;
|
|
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 sbox on side_cw
|
|
* scw,scw+1,...,sccw-1, for passing wires with sbox on side_ccw
|
|
* sccw,sccw+1,... for passing wires with sbox 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" */
|
|
|
|
side_cw_incoming_wire_count = 0;
|
|
if (incoming_wire_label[side_cw]) {
|
|
for (itrack = 0; itrack < nodes_per_chan; itrack++) {
|
|
/* Ending wire, or passing wire with sbox. */
|
|
if (incoming_wire_label[side_cw][itrack] != UN_SET) {
|
|
|
|
if ((is_corner_sblock || is_core_sblock)
|
|
&& (incoming_wire_label[side_cw][itrack]
|
|
< num_ending_wires[side_cw])) {
|
|
/* The ending wires in core sblocks form N-to-N assignment
|
|
* problem, so can use any pattern such as Wilton. This N-to-N
|
|
* mapping depends on the fact that start points stagger across
|
|
* channels. */
|
|
assert(
|
|
num_ending_wires[side_cw] == num_wire_muxes[to_side]);
|
|
sblock_pattern[i][j][side_cw][to_side][itrack] =
|
|
get_simple_switch_block_track((enum e_side)side_cw, (enum e_side)to_side,
|
|
incoming_wire_label[side_cw][itrack],
|
|
switch_block_type,
|
|
num_wire_muxes[to_side]);
|
|
|
|
} else {
|
|
|
|
/* These are passing wires with sbox only for core sblocks
|
|
* or passing and ending wires (for fringe cases). */
|
|
sblock_pattern[i][j][side_cw][to_side][itrack] =
|
|
(side_cw_incoming_wire_count * Fs_per_side)
|
|
% num_wire_muxes[to_side];
|
|
side_cw_incoming_wire_count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
side_ccw_incoming_wire_count = 0;
|
|
for (itrack = 0; itrack < nodes_per_chan; itrack++) {
|
|
|
|
/* if that side has no channel segment skip it */
|
|
if (incoming_wire_label[side_ccw] == NULL)
|
|
break;
|
|
|
|
/* not ending wire nor passing wire with sbox */
|
|
if (incoming_wire_label[side_ccw][itrack] != UN_SET) {
|
|
|
|
if ((is_corner_sblock || is_core_sblock)
|
|
&& (incoming_wire_label[side_ccw][itrack]
|
|
< num_ending_wires[side_ccw])) {
|
|
/* The ending wires in core sblocks form N-to-N assignment problem, so can
|
|
* use any pattern such as Wilton */
|
|
assert(
|
|
incoming_wire_label[side_ccw] [itrack] < num_wire_muxes[to_side]);
|
|
sblock_pattern[i][j][side_ccw][to_side][itrack] =
|
|
get_simple_switch_block_track((enum e_side)side_ccw, (enum e_side)to_side,
|
|
incoming_wire_label[side_ccw][itrack],
|
|
switch_block_type, num_wire_muxes[to_side]);
|
|
} else {
|
|
|
|
/* These are passing wires with sbox only for core sblocks
|
|
* or passing and ending wires (for fringe cases). */
|
|
sblock_pattern[i][j][side_ccw][to_side][itrack] =
|
|
((side_ccw_incoming_wire_count
|
|
+ side_cw_incoming_wire_count) * Fs_per_side)
|
|
% num_wire_muxes[to_side];
|
|
side_ccw_incoming_wire_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
opp_incoming_wire_count = 0;
|
|
if (incoming_wire_label[side_opp]) {
|
|
for (itrack = 0; itrack < nodes_per_chan; itrack++) {
|
|
/* not ending wire nor passing wire with sbox */
|
|
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 (is_core_sblock) {
|
|
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 */
|
|
sblock_pattern[i][j][side_opp][to_side][itrack] =
|
|
find_label_of_track(
|
|
wire_mux_on_track[to_side],
|
|
num_wire_muxes[to_side], itrack);
|
|
} else {
|
|
/* These are passing wires with sbox for core sblocks */
|
|
sblock_pattern[i][j][side_opp][to_side][itrack] =
|
|
((side_ccw_incoming_wire_count
|
|
+ side_cw_incoming_wire_count)
|
|
* Fs_per_side
|
|
+ opp_incoming_wire_count
|
|
* (Fs_per_side - 1))
|
|
% num_wire_muxes[to_side];
|
|
opp_incoming_wire_count++;
|
|
}
|
|
} else {
|
|
if (incoming_wire_label[side_opp][itrack]
|
|
< num_ending_wires[side_opp]) {
|
|
sblock_pattern[i][j][side_opp][to_side][itrack] =
|
|
find_label_of_track(
|
|
wire_mux_on_track[to_side],
|
|
num_wire_muxes[to_side], itrack);
|
|
} else {
|
|
/* These are passing wires with sbox for fringe sblocks */
|
|
sblock_pattern[i][j][side_opp][to_side][itrack] =
|
|
((side_ccw_incoming_wire_count
|
|
+ side_cw_incoming_wire_count)
|
|
* Fs_per_side
|
|
+ opp_incoming_wire_count
|
|
* (Fs_per_side - 1))
|
|
% num_wire_muxes[to_side];
|
|
opp_incoming_wire_count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (side = 0; side < 4; ++side) {
|
|
if (incoming_wire_label[side]) {
|
|
free(incoming_wire_label[side]);
|
|
}
|
|
if (wire_mux_on_track[side]) {
|
|
free(wire_mux_on_track[side]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int *
|
|
label_wire_muxes_for_balance(INP int chan_num, INP int seg_num,
|
|
INP t_seg_details * seg_details, INP int max_len,
|
|
INP enum e_direction direction, INP int nodes_per_chan,
|
|
INP int *num_wire_muxes, INP t_rr_type chan_type,
|
|
INP int *opin_mux_size, INP t_ivec *** L_rr_node_indices) {
|
|
|
|
/* 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 mux> */
|
|
|
|
/* Sblock (aka wire2mux) pattern generation occurs after opin2mux connections have been
|
|
* made. Since opin2muxes are done with a pattern with which I guarantee imbalance of at most 1 due
|
|
* to them, we will observe that, for each side of an sblock some muxes have one fewer size
|
|
* than the others, considering only the contribution from opins. I refer to these muxes as "holes"
|
|
* as they have one fewer opin connection going to them than the rest (like missing one electron)*/
|
|
|
|
/* Before May 14, I was labelling wire muxes in the natural order of their track # (lowest first).
|
|
* Now I want to label wire muxes like this: first label the holes in order of their track #,
|
|
* then label the non-holes in order of their track #. This way the wire2mux generation will
|
|
* not overlap its own "holes" with the opin "holes", thus creating imbalance greater than 1. */
|
|
|
|
/* The best approach in sblock generation is do one assignment of all incoming wires from 3 other
|
|
* sides to the muxes on the fourth side, connecting the "opin hole" muxes first (i.e. filling
|
|
* the holes) then the rest -> this means after all opin2mux and wire2mux connections the
|
|
* mux size imbalance on one side is at most 1. The mux size imbalance in one sblock is thus
|
|
* also one, since the number of muxes per side is identical for all four sides, and they number
|
|
* of incoming wires per side is identical for full pop, and almost the same for depop (due to
|
|
* staggering) within +1 or -1. For different tiles (different sblocks) the imbalance is irrelevant,
|
|
* since if the tiles are different in mux count then they have to be designed with a different
|
|
* physical tile. */
|
|
|
|
int num_labels, max_opin_mux_size, min_opin_mux_size;
|
|
int inode, i, j, x, y;
|
|
int *pre_labels, *final_labels;
|
|
|
|
if (chan_type == CHANX) {
|
|
x = seg_num;
|
|
y = chan_num;
|
|
} else if (chan_type == CHANY) {
|
|
x = chan_num;
|
|
y = seg_num;
|
|
} else {
|
|
vpr_printf(TIO_MESSAGE_ERROR, "Bad channel type (%d).\n", chan_type);
|
|
exit(1);
|
|
}
|
|
|
|
/* Generate the normal labels list as the baseline. */
|
|
pre_labels = label_wire_muxes(chan_num, seg_num, seg_details, max_len,
|
|
direction, nodes_per_chan, &num_labels);
|
|
|
|
/* Find the min and max mux size. */
|
|
min_opin_mux_size = MAX_SHORT;
|
|
max_opin_mux_size = 0;
|
|
for (i = 0; i < num_labels; ++i) {
|
|
inode = get_rr_node_index(x, y, chan_type, pre_labels[i],
|
|
L_rr_node_indices);
|
|
if (opin_mux_size[inode] < min_opin_mux_size) {
|
|
min_opin_mux_size = opin_mux_size[inode];
|
|
}
|
|
if (opin_mux_size[inode] > max_opin_mux_size) {
|
|
max_opin_mux_size = opin_mux_size[inode];
|
|
}
|
|
}
|
|
if (max_opin_mux_size > (min_opin_mux_size + 1)) {
|
|
vpr_printf(TIO_MESSAGE_ERROR, "opin muxes are not balanced!\n");
|
|
vpr_printf(TIO_MESSAGE_ERROR, "max_opin_mux_size %d min_opin_mux_size %d chan_type %d x %d y %d\n",
|
|
max_opin_mux_size, min_opin_mux_size, chan_type, x, y);
|
|
exit(1);
|
|
}
|
|
|
|
/* Create a new list that we will move the muxes with 'holes' to the start of list. */
|
|
final_labels = (int *) my_malloc(sizeof(int) * num_labels);
|
|
j = 0;
|
|
for (i = 0; i < num_labels; ++i) {
|
|
inode = pre_labels[i];
|
|
if (opin_mux_size[inode] < max_opin_mux_size) {
|
|
final_labels[j] = inode;
|
|
++j;
|
|
}
|
|
}
|
|
for (i = 0; i < num_labels; ++i) {
|
|
inode = pre_labels[i];
|
|
if (opin_mux_size[inode] >= max_opin_mux_size) {
|
|
final_labels[j] = inode;
|
|
++j;
|
|
}
|
|
}
|
|
|
|
/* Free the baseline labelling. */
|
|
if (pre_labels) {
|
|
free(pre_labels);
|
|
pre_labels = NULL;
|
|
}
|
|
|
|
*num_wire_muxes = num_labels;
|
|
return final_labels;
|
|
}
|
|
|
|
static int *
|
|
label_wire_muxes(INP int chan_num, INP int seg_num,
|
|
INP t_seg_details * seg_details, INP int max_len,
|
|
INP enum e_direction dir, INP int nodes_per_chan,
|
|
OUTP int *num_wire_muxes) {
|
|
|
|
/* 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 # */
|
|
|
|
int itrack, start, end, num_labels, pass;
|
|
int *labels = NULL;
|
|
boolean is_endpoint;
|
|
|
|
/* COUNT pass then a LOAD pass */
|
|
num_labels = 0;
|
|
for (pass = 0; pass < 2; ++pass) {
|
|
/* Alloc the list on LOAD pass */
|
|
if (pass > 0) {
|
|
labels = (int *) my_malloc(sizeof(int) * num_labels);
|
|
num_labels = 0;
|
|
}
|
|
|
|
/* Find the tracks that are starting. */
|
|
for (itrack = 0; itrack < nodes_per_chan; ++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 going the wrong way */
|
|
if (seg_details[itrack].direction != dir) {
|
|
continue;
|
|
}
|
|
|
|
/* Determine if we are a wire startpoint */
|
|
is_endpoint = (boolean)(seg_num == start);
|
|
if (DEC_DIRECTION == seg_details[itrack].direction) {
|
|
is_endpoint = (boolean)(seg_num == end);
|
|
}
|
|
|
|
/* Count the labels and load if LOAD pass */
|
|
if (is_endpoint) {
|
|
if (pass > 0) {
|
|
labels[num_labels] = itrack;
|
|
}
|
|
++num_labels;
|
|
}
|
|
}
|
|
}
|
|
|
|
*num_wire_muxes = num_labels;
|
|
return labels;
|
|
}
|
|
|
|
static int *
|
|
label_incoming_wires(INP int chan_num, INP int seg_num, INP int sb_seg,
|
|
INP t_seg_details * seg_details, INP int max_len,
|
|
INP enum e_direction dir, INP int nodes_per_chan,
|
|
OUTP int *num_incoming_wires, OUTP 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 sbox (passing wires with sbox and ending wires) */
|
|
|
|
int itrack, start, end, i, num_passing, num_ending, pass;
|
|
int *labels;
|
|
boolean sbox_exists, is_endpoint;
|
|
|
|
/* Alloc the list of labels for the tracks */
|
|
labels = (int *) my_malloc(nodes_per_chan * sizeof(int));
|
|
for (i = 0; i < nodes_per_chan; ++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 < nodes_per_chan; ++itrack) {
|
|
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 = (boolean)(seg_num == end);
|
|
if (DEC_DIRECTION == seg_details[itrack].direction) {
|
|
is_endpoint = (boolean)(seg_num == start);
|
|
}
|
|
|
|
/* Determine if we have a sbox on the wire */
|
|
sbox_exists = is_sbox(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) && sbox_exists) {
|
|
labels[itrack] = num_ending + num_passing;
|
|
++num_passing;
|
|
}
|
|
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) {
|
|
int i;
|
|
|
|
/* 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 */
|
|
|
|
for (i = 0; i < num_wire_muxes; i++) {
|
|
if (wire_mux_on_track[i] == from_track) {
|
|
return i; /* matched, return now */
|
|
}
|
|
}
|
|
|
|
vpr_printf(TIO_MESSAGE_ERROR, "Expected mux not found.\n");
|
|
exit(1);
|
|
}
|