534 lines
17 KiB
C
534 lines
17 KiB
C
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include "util.h"
|
|
#include "vpr_types.h"
|
|
#include "globals.h"
|
|
#include "rr_graph_area.h"
|
|
#include "segment_stats.h"
|
|
#include "stats.h"
|
|
#include "net_delay.h"
|
|
#include "path_delay.h"
|
|
#include "read_xml_arch_file.h"
|
|
#include "ReadOptions.h"
|
|
/* mrFPGA: Xifan TANG*/
|
|
#include "mrfpga_globals.h"
|
|
#include "buffer_insertion.h"
|
|
#include "cal_capacitance.h"
|
|
/* end */
|
|
/* Xifan TANG: pb_pin_eq_auto_detect */
|
|
void print_net_opin_occupancy();
|
|
/* end */
|
|
|
|
/********************** Subroutines local to this module *********************/
|
|
|
|
static void load_channel_occupancies(int **chanx_occ, int **chany_occ);
|
|
|
|
static void get_length_and_bends_stats(void);
|
|
|
|
static void get_channel_occupancy_stats(void);
|
|
|
|
/************************* Subroutine definitions ****************************/
|
|
|
|
void routing_stats(boolean full_stats, enum e_route_type route_type,
|
|
int num_switch, t_segment_inf * segment_inf, int num_segment,
|
|
float R_minW_nmos, float R_minW_pmos,
|
|
enum e_directionality directionality, boolean timing_analysis_enabled,
|
|
float **net_delay, t_slack * slacks, float sram_area) {
|
|
|
|
/* Prints out various statistics about the current routing. Both a routing *
|
|
* and an rr_graph must exist when you call this routine. */
|
|
|
|
float area, used_area;
|
|
int i, j;
|
|
|
|
/* mrFPGA: Xifan TANG */
|
|
static float buffer_size;
|
|
/* end */
|
|
|
|
get_length_and_bends_stats();
|
|
get_channel_occupancy_stats();
|
|
|
|
vpr_printf(TIO_MESSAGE_INFO, "Logic area (in minimum width transistor areas, excludes I/Os and empty grid tiles)...\n");
|
|
|
|
area = 0;
|
|
for (i = 1; i <= nx; i++) {
|
|
for (j = 1; j <= ny; j++) {
|
|
if (grid[i][j].offset == 0) {
|
|
if (grid[i][j].type->area == UNDEFINED) {
|
|
area += grid_logic_tile_area * grid[i][j].type->height;
|
|
} else {
|
|
area += grid[i][j].type->area;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* Todo: need to add pitch of routing to blocks with height > 3 */
|
|
vpr_printf(TIO_MESSAGE_INFO, "\tTotal logic block area (Warning, need to add pitch of routing to blocks with height > 3): %g\n", area);
|
|
|
|
used_area = 0;
|
|
for (i = 0; i < num_blocks; i++) {
|
|
if (block[i].type != IO_TYPE) {
|
|
if (block[i].type->area == UNDEFINED) {
|
|
used_area += grid_logic_tile_area * block[i].type->height;
|
|
} else {
|
|
used_area += block[i].type->area;
|
|
}
|
|
}
|
|
}
|
|
vpr_printf(TIO_MESSAGE_INFO, "\tTotal used logic block area: %g\n", used_area);
|
|
|
|
if (route_type == DETAILED) {
|
|
/* mrFPGA: Xifan TANG */
|
|
if (is_mrFPGA) {
|
|
if (is_wire_buffer) {
|
|
buffer_size = trans_per_buf(wire_buffer_inf.R - memristor_inf.R, R_minW_nmos, R_minW_pmos);
|
|
count_routing_memristor_buffer(print_stat_memristor_buffer("mrFPGA_buffer.echo", buffer_size),
|
|
buffer_size);
|
|
} else {
|
|
vpr_printf(TIO_MESSAGE_INFO, "Tile area: %#g\n", grid_logic_tile_area);
|
|
}
|
|
} else {
|
|
if (!is_mrFPGA && is_stack) {
|
|
for (i = 0; i < num_normal_switch; i++) {
|
|
switch_inf[i].R -= Rseg_global;
|
|
}
|
|
}
|
|
if (rram_pass_tran_value > 0.01) {
|
|
for (i = 0; i < num_normal_switch; i++) {
|
|
switch_inf[i].R = (switch_inf[i].R - rram_pass_tran_value)*2;
|
|
}
|
|
}
|
|
/* END */
|
|
count_routing_transistors(directionality, num_switch, segment_inf,
|
|
R_minW_nmos, R_minW_pmos, sram_area);
|
|
/* mrFPGA : Xifan TANG */
|
|
if (rram_pass_tran_value > 0.01) {
|
|
for (i = 0; i < num_normal_switch; i++) {
|
|
switch_inf[i].R = (switch_inf[i].R / 2. - rram_pass_tran_value);
|
|
}
|
|
}
|
|
if (!is_mrFPGA && is_stack) {
|
|
for (i = 0; i < num_normal_switch; i++) {
|
|
switch_inf[i].R += Rseg_global;
|
|
}
|
|
}
|
|
}
|
|
/* END */
|
|
get_segment_usage_stats(num_segment, segment_inf);
|
|
|
|
if (timing_analysis_enabled) {
|
|
load_net_delay_from_routing(net_delay, clb_net, num_nets);
|
|
|
|
/* mrFPGA: Xifan TANG */
|
|
cal_capacitance_from_routing();
|
|
/* END */
|
|
load_timing_graph_net_delays(net_delay);
|
|
|
|
#ifdef HACK_LUT_PIN_SWAPPING
|
|
do_timing_analysis(slacks, FALSE, TRUE, TRUE);
|
|
#else
|
|
do_timing_analysis(slacks, FALSE, FALSE, TRUE);
|
|
#endif
|
|
if (getEchoEnabled()) {
|
|
if(isEchoFileEnabled(E_ECHO_TIMING_GRAPH))
|
|
print_timing_graph(getEchoFileName(E_ECHO_TIMING_GRAPH));
|
|
if (isEchoFileEnabled(E_ECHO_NET_DELAY))
|
|
print_net_delay(net_delay, getEchoFileName(E_ECHO_NET_DELAY));
|
|
if(isEchoFileEnabled(E_ECHO_LUT_REMAPPING))
|
|
print_lut_remapping(getEchoFileName(E_ECHO_LUT_REMAPPING));
|
|
}
|
|
print_slack(slacks->slack, TRUE, getOutputFileName(E_SLACK_FILE));
|
|
print_criticality(slacks, TRUE, getOutputFileName(E_CRITICALITY_FILE));
|
|
print_critical_path(getOutputFileName(E_CRIT_PATH_FILE));
|
|
print_timing_stats();
|
|
}
|
|
}
|
|
|
|
if (full_stats == TRUE)
|
|
print_wirelen_prob_dist();
|
|
|
|
/* Xifan TANG: Statisitcs for each net which has occupied more than 1 OPIN */
|
|
print_net_opin_occupancy();
|
|
}
|
|
|
|
void get_length_and_bends_stats(void) {
|
|
|
|
/* Figures out maximum, minimum and average number of bends and net length *
|
|
* in the routing. */
|
|
|
|
int inet, bends, total_bends, max_bends;
|
|
int length, total_length, max_length;
|
|
int segments, total_segments, max_segments;
|
|
float av_bends, av_length, av_segments;
|
|
int num_global_nets, num_clb_opins_reserved;
|
|
|
|
max_bends = 0;
|
|
total_bends = 0;
|
|
max_length = 0;
|
|
total_length = 0;
|
|
max_segments = 0;
|
|
total_segments = 0;
|
|
num_global_nets = 0;
|
|
num_clb_opins_reserved = 0;
|
|
|
|
for (inet = 0; inet < num_nets; inet++) {
|
|
if (clb_net[inet].is_global == FALSE && clb_net[inet].num_sinks != 0) { /* Globals don't count. */
|
|
get_num_bends_and_length(inet, &bends, &length, &segments);
|
|
|
|
total_bends += bends;
|
|
max_bends = std::max(bends, max_bends);
|
|
|
|
total_length += length;
|
|
max_length = std::max(length, max_length);
|
|
|
|
total_segments += segments;
|
|
max_segments = std::max(segments, max_segments);
|
|
} else if (clb_net[inet].is_global) {
|
|
num_global_nets++;
|
|
} else {
|
|
num_clb_opins_reserved++;
|
|
}
|
|
}
|
|
|
|
av_bends = (float) total_bends / (float) (num_nets - num_global_nets);
|
|
vpr_printf(TIO_MESSAGE_INFO, "\n");
|
|
vpr_printf(TIO_MESSAGE_INFO, "Average number of bends per net: %#g Maximum # of bends: %d\n", av_bends, max_bends);
|
|
vpr_printf(TIO_MESSAGE_INFO, "\n");
|
|
|
|
av_length = (float) total_length / (float) (num_nets - num_global_nets);
|
|
vpr_printf(TIO_MESSAGE_INFO, "Number of routed nets (nonglobal): %d\n", num_nets - num_global_nets);
|
|
vpr_printf(TIO_MESSAGE_INFO, "Wirelength results (in units of 1 clb segments)...\n");
|
|
vpr_printf(TIO_MESSAGE_INFO, "\tTotal wirelength: %d, average net length: %#g\n", total_length, av_length);
|
|
vpr_printf(TIO_MESSAGE_INFO, "\tMaximum net length: %d\n", max_length);
|
|
vpr_printf(TIO_MESSAGE_INFO, "\n");
|
|
|
|
av_segments = (float) total_segments / (float) (num_nets - num_global_nets);
|
|
vpr_printf(TIO_MESSAGE_INFO, "Wirelength results in terms of physical segments...\n");
|
|
vpr_printf(TIO_MESSAGE_INFO, "\tTotal wiring segments used: %d, average wire segments per net: %#g\n", total_segments, av_segments);
|
|
vpr_printf(TIO_MESSAGE_INFO, "\tMaximum segments used by a net: %d\n", max_segments);
|
|
vpr_printf(TIO_MESSAGE_INFO, "\tTotal local nets with reserved CLB opins: %d\n", num_clb_opins_reserved);
|
|
}
|
|
|
|
static void get_channel_occupancy_stats(void) {
|
|
|
|
/* Determines how many tracks are used in each channel. */
|
|
|
|
int i, j, max_occ, total_x, total_y;
|
|
float av_occ;
|
|
int **chanx_occ; /* [1..nx][0..ny] */
|
|
int **chany_occ; /* [0..nx][1..ny] */
|
|
|
|
chanx_occ = (int **) alloc_matrix(1, nx, 0, ny, sizeof(int));
|
|
chany_occ = (int **) alloc_matrix(0, nx, 1, ny, sizeof(int));
|
|
load_channel_occupancies(chanx_occ, chany_occ);
|
|
|
|
vpr_printf(TIO_MESSAGE_INFO, "\n");
|
|
vpr_printf(TIO_MESSAGE_INFO, "X - Directed channels: j\tmax occ\tav_occ\t\tcapacity\n");
|
|
|
|
total_x = 0;
|
|
|
|
for (j = 0; j <= ny; j++) {
|
|
total_x += chan_width_x[j];
|
|
av_occ = 0.;
|
|
max_occ = -1;
|
|
|
|
for (i = 1; i <= nx; i++) {
|
|
max_occ = std::max(chanx_occ[i][j], max_occ);
|
|
av_occ += chanx_occ[i][j];
|
|
}
|
|
av_occ /= nx;
|
|
vpr_printf(TIO_MESSAGE_INFO, "%d\t%d\t%-#9g\t%d\n", j, max_occ, av_occ, chan_width_x[j]);
|
|
}
|
|
|
|
vpr_printf(TIO_MESSAGE_INFO, "\n");
|
|
vpr_printf(TIO_MESSAGE_INFO, "Y - Directed channels: i\tmax occ\tav_occ\t\tcapacity\n");
|
|
|
|
total_y = 0;
|
|
|
|
for (i = 0; i <= nx; i++) {
|
|
total_y += chan_width_y[i];
|
|
av_occ = 0.;
|
|
max_occ = -1;
|
|
|
|
for (j = 1; j <= ny; j++) {
|
|
max_occ = std::max(chany_occ[i][j], max_occ);
|
|
av_occ += chany_occ[i][j];
|
|
}
|
|
av_occ /= ny;
|
|
vpr_printf(TIO_MESSAGE_INFO, "%d\t%d\t%-#9g\t%d\n", i, max_occ, av_occ, chan_width_y[i]);
|
|
}
|
|
|
|
vpr_printf(TIO_MESSAGE_INFO, "\n");
|
|
vpr_printf(TIO_MESSAGE_INFO, "Total tracks in x-direction: %d, in y-direction: %d\n", total_x, total_y);
|
|
vpr_printf(TIO_MESSAGE_INFO, "\n");
|
|
|
|
free_matrix(chanx_occ, 1, nx, 0, sizeof(int));
|
|
free_matrix(chany_occ, 0, nx, 1, sizeof(int));
|
|
}
|
|
|
|
static void load_channel_occupancies(int **chanx_occ, int **chany_occ) {
|
|
|
|
/* Loads the two arrays passed in with the total occupancy at each of the *
|
|
* channel segments in the FPGA. */
|
|
|
|
int i, j, inode, inet;
|
|
struct s_trace *tptr;
|
|
t_rr_type rr_type;
|
|
|
|
/* First set the occupancy of everything to zero. */
|
|
|
|
for (i = 1; i <= nx; i++)
|
|
for (j = 0; j <= ny; j++)
|
|
chanx_occ[i][j] = 0;
|
|
|
|
for (i = 0; i <= nx; i++)
|
|
for (j = 1; j <= ny; j++)
|
|
chany_occ[i][j] = 0;
|
|
|
|
/* Now go through each net and count the tracks and pins used everywhere */
|
|
|
|
for (inet = 0; inet < num_nets; inet++) {
|
|
|
|
if (clb_net[inet].is_global && clb_net[inet].num_sinks != 0) /* Skip global and empty nets. */
|
|
continue;
|
|
|
|
tptr = trace_head[inet];
|
|
while (tptr != NULL) {
|
|
inode = tptr->index;
|
|
rr_type = rr_node[inode].type;
|
|
|
|
if (rr_type == SINK) {
|
|
tptr = tptr->next; /* Skip next segment. */
|
|
if (tptr == NULL)
|
|
break;
|
|
}
|
|
|
|
else if (rr_type == CHANX) {
|
|
j = rr_node[inode].ylow;
|
|
for (i = rr_node[inode].xlow; i <= rr_node[inode].xhigh; i++)
|
|
chanx_occ[i][j]++;
|
|
}
|
|
|
|
else if (rr_type == CHANY) {
|
|
i = rr_node[inode].xlow;
|
|
for (j = rr_node[inode].ylow; j <= rr_node[inode].yhigh; j++)
|
|
chany_occ[i][j]++;
|
|
}
|
|
|
|
tptr = tptr->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
void get_num_bends_and_length(int inet, int *bends_ptr, int *len_ptr,
|
|
int *segments_ptr) {
|
|
|
|
/* Counts and returns the number of bends, wirelength, and number of routing *
|
|
* resource segments in net inet's routing. */
|
|
|
|
struct s_trace *tptr, *prevptr;
|
|
int inode;
|
|
t_rr_type curr_type, prev_type;
|
|
int bends, length, segments;
|
|
|
|
bends = 0;
|
|
length = 0;
|
|
segments = 0;
|
|
|
|
prevptr = trace_head[inet]; /* Should always be SOURCE. */
|
|
if (prevptr == NULL) {
|
|
vpr_printf(TIO_MESSAGE_ERROR, "in get_num_bends_and_length: net #%d has no traceback.\n", inet);
|
|
exit(1);
|
|
}
|
|
inode = prevptr->index;
|
|
prev_type = rr_node[inode].type;
|
|
|
|
tptr = prevptr->next;
|
|
|
|
while (tptr != NULL) {
|
|
inode = tptr->index;
|
|
curr_type = rr_node[inode].type;
|
|
|
|
if (curr_type == SINK) { /* Starting a new segment */
|
|
tptr = tptr->next; /* Link to existing path - don't add to len. */
|
|
if (tptr == NULL)
|
|
break;
|
|
|
|
curr_type = rr_node[tptr->index].type;
|
|
}
|
|
|
|
else if (curr_type == CHANX || curr_type == CHANY) {
|
|
segments++;
|
|
length += 1 + rr_node[inode].xhigh - rr_node[inode].xlow
|
|
+ rr_node[inode].yhigh - rr_node[inode].ylow;
|
|
|
|
if (curr_type != prev_type
|
|
&& (prev_type == CHANX || prev_type == CHANY))
|
|
bends++;
|
|
}
|
|
|
|
prev_type = curr_type;
|
|
tptr = tptr->next;
|
|
}
|
|
|
|
*bends_ptr = bends;
|
|
*len_ptr = length;
|
|
*segments_ptr = segments;
|
|
}
|
|
|
|
void print_wirelen_prob_dist(void) {
|
|
|
|
/* Prints out the probability distribution of the wirelength / number *
|
|
* input pins on a net -- i.e. simulates 2-point net length probability *
|
|
* distribution. */
|
|
|
|
float *prob_dist;
|
|
float norm_fac, two_point_length;
|
|
int inet, bends, length, segments, index;
|
|
float av_length;
|
|
int prob_dist_size, i, incr;
|
|
|
|
prob_dist_size = nx + ny + 10;
|
|
prob_dist = (float *) my_calloc(prob_dist_size, sizeof(float));
|
|
norm_fac = 0.;
|
|
|
|
for (inet = 0; inet < num_nets; inet++) {
|
|
if (clb_net[inet].is_global == FALSE && clb_net[inet].num_sinks != 0) {
|
|
get_num_bends_and_length(inet, &bends, &length, &segments);
|
|
|
|
/* Assign probability to two integer lengths proportionately -- i.e. *
|
|
* if two_point_length = 1.9, add 0.9 of the pins to prob_dist[2] and *
|
|
* only 0.1 to prob_dist[1]. */
|
|
|
|
two_point_length = (float) length
|
|
/ (float) (clb_net[inet].num_sinks);
|
|
index = (int) two_point_length;
|
|
if (index >= prob_dist_size) {
|
|
|
|
vpr_printf(TIO_MESSAGE_WARNING, "index (%d) to prob_dist exceeds its allocated size (%d).\n",
|
|
index, prob_dist_size);
|
|
vpr_printf(TIO_MESSAGE_INFO, "Realloc'ing to increase 2-pin wirelen prob distribution array.\n");
|
|
incr = index - prob_dist_size + 2;
|
|
prob_dist_size += incr;
|
|
prob_dist = (float *)my_realloc(prob_dist,
|
|
prob_dist_size * sizeof(float));
|
|
for (i = prob_dist_size - incr; i < prob_dist_size; i++)
|
|
prob_dist[i] = 0.0;
|
|
}
|
|
prob_dist[index] += (clb_net[inet].num_sinks)
|
|
* (1 - two_point_length + index);
|
|
|
|
index++;
|
|
if (index >= prob_dist_size) {
|
|
|
|
vpr_printf(TIO_MESSAGE_WARNING, "Warning: index (%d) to prob_dist exceeds its allocated size (%d).\n",
|
|
index, prob_dist_size);
|
|
vpr_printf(TIO_MESSAGE_INFO, "Realloc'ing to increase 2-pin wirelen prob distribution array.\n");
|
|
incr = index - prob_dist_size + 2;
|
|
prob_dist_size += incr;
|
|
prob_dist = (float *)my_realloc(prob_dist,
|
|
prob_dist_size * sizeof(float));
|
|
for (i = prob_dist_size - incr; i < prob_dist_size; i++)
|
|
prob_dist[i] = 0.0;
|
|
}
|
|
prob_dist[index] += (clb_net[inet].num_sinks)
|
|
* (1 - index + two_point_length);
|
|
|
|
norm_fac += clb_net[inet].num_sinks;
|
|
}
|
|
}
|
|
|
|
/* Normalize so total probability is 1 and print out. */
|
|
|
|
vpr_printf(TIO_MESSAGE_INFO, "\n");
|
|
vpr_printf(TIO_MESSAGE_INFO, "Probability distribution of 2-pin net lengths:\n");
|
|
vpr_printf(TIO_MESSAGE_INFO, "\n");
|
|
vpr_printf(TIO_MESSAGE_INFO, "Length p(Lenth)\n");
|
|
|
|
av_length = 0;
|
|
|
|
for (index = 0; index < prob_dist_size; index++) {
|
|
prob_dist[index] /= norm_fac;
|
|
vpr_printf(TIO_MESSAGE_INFO, "%6d %10.6f\n", index, prob_dist[index]);
|
|
av_length += prob_dist[index] * index;
|
|
}
|
|
|
|
vpr_printf(TIO_MESSAGE_INFO, "\n");
|
|
vpr_printf(TIO_MESSAGE_INFO, "Number of 2-pin nets: ;%g;\n", norm_fac);
|
|
vpr_printf(TIO_MESSAGE_INFO, "Expected value of 2-pin net length (R): ;%g;\n", av_length);
|
|
vpr_printf(TIO_MESSAGE_INFO, "Total wire length: ;%g;\n", norm_fac * av_length);
|
|
|
|
free(prob_dist);
|
|
}
|
|
|
|
void print_lambda(void) {
|
|
|
|
/* Finds the average number of input pins used per clb. Does not *
|
|
* count inputs which are hooked to global nets (i.e. the clock *
|
|
* when it is marked global). */
|
|
|
|
int bnum, ipin;
|
|
int num_inputs_used = 0;
|
|
int iclass, inet;
|
|
float lambda;
|
|
t_type_ptr type;
|
|
|
|
for (bnum = 0; bnum < num_blocks; bnum++) {
|
|
type = block[bnum].type;
|
|
assert(type != NULL);
|
|
if (type != IO_TYPE) {
|
|
for (ipin = 0; ipin < type->num_pins; ipin++) {
|
|
iclass = type->pin_class[ipin];
|
|
if (type->class_inf[iclass].type == RECEIVER) {
|
|
inet = block[bnum].nets[ipin];
|
|
if (inet != OPEN) /* Pin is connected? */
|
|
if (clb_net[inet].is_global == FALSE) /* Not a global clock */
|
|
num_inputs_used++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
lambda = (float) num_inputs_used / (float) num_blocks;
|
|
vpr_printf(TIO_MESSAGE_INFO, "Average lambda (input pins used per clb) is: %g\n", lambda);
|
|
}
|
|
|
|
int count_netlist_clocks(void) {
|
|
|
|
/* Count how many clocks are in the netlist. */
|
|
|
|
int iblock, i, clock_net;
|
|
char * name;
|
|
boolean found;
|
|
int num_clocks = 0;
|
|
char ** clock_names = NULL;
|
|
|
|
for (iblock = 0; iblock < num_logical_blocks; iblock++) {
|
|
if (logical_block[iblock].clock_net != OPEN) {
|
|
clock_net = logical_block[iblock].clock_net;
|
|
assert(clock_net != OPEN);
|
|
name = logical_block[clock_net].name;
|
|
/* Now that we've found a clock, let's see if we've counted it already */
|
|
found = FALSE;
|
|
for (i = 0; !found && i < num_clocks; i++) {
|
|
if (strcmp(clock_names[i], name) == 0) {
|
|
found = TRUE;
|
|
}
|
|
}
|
|
if (!found) {
|
|
/* If we get here, the clock is new and so we dynamically grow the array netlist_clocks by one. */
|
|
clock_names = (char **) my_realloc (clock_names, ++num_clocks * sizeof(char *));
|
|
clock_names[num_clocks - 1] = name;
|
|
}
|
|
}
|
|
}
|
|
free (clock_names);
|
|
return num_clocks;
|
|
}
|
|
|
|
|
|
|