blob: 28eb70c45dbed0e46f47a909c57c119259189f0c [file] [log] [blame]
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include "vtr_assert.h"
#include "vtr_log.h"
#include "vtr_math.h"
#include "vtr_ndmatrix.h"
#include "vpr_types.h"
#include "vpr_error.h"
#include "globals.h"
#include "atom_netlist.h"
#include "rr_graph_area.h"
#include "segment_stats.h"
#include "channel_stats.h"
#include "stats.h"
#include "net_delay.h"
#include "read_xml_arch_file.h"
#include "echo_files.h"
#include "timing_info.h"
#include "RoutingDelayCalculator.h"
#include "timing_util.h"
#include "tatum/TimingReporter.hpp"
/********************** Subroutines local to this module *********************/
static void load_channel_occupancies(vtr::Matrix<int>& chanx_occ, vtr::Matrix<int>& chany_occ);
static void length_and_bends_stats();
static void get_channel_occupancy_stats();
/************************* Subroutine definitions ****************************/
void routing_stats(bool full_stats, enum e_route_type route_type, std::vector<t_segment_inf>& segment_inf, float R_minW_nmos, float R_minW_pmos, float grid_logic_tile_area, enum e_directionality directionality, int wire_to_ipin_switch) {
/* 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;
auto& device_ctx = g_vpr_ctx.device();
auto& cluster_ctx = g_vpr_ctx.clustering();
int num_rr_switch = device_ctx.rr_switch_inf.size();
length_and_bends_stats();
print_channel_stats();
get_channel_occupancy_stats();
VTR_LOG("Logic area (in minimum width transistor areas, excludes I/Os and empty grid tiles)...\n");
area = 0;
for (size_t i = 0; i < device_ctx.grid.width(); i++) {
for (size_t j = 0; j < device_ctx.grid.height(); j++) {
auto type = device_ctx.grid[i][j].type;
if (device_ctx.grid[i][j].width_offset == 0
&& device_ctx.grid[i][j].height_offset == 0
&& !is_io_type(type)
&& type != device_ctx.EMPTY_TYPE) {
if (type->area == UNDEFINED) {
area += grid_logic_tile_area * type->width * type->height;
} else {
area += type->area;
}
}
}
}
/* Todo: need to add pitch of routing to blocks with height > 3 */
VTR_LOG("\tTotal logic block area (Warning, need to add pitch of routing to blocks with height > 3): %g\n", area);
used_area = 0;
for (auto blk_id : cluster_ctx.clb_nlist.blocks()) {
auto type = physical_tile_type(blk_id);
if (!is_io_type(type)) {
if (type->area == UNDEFINED) {
used_area += grid_logic_tile_area * type->width * type->height;
} else {
used_area += type->area;
}
}
}
VTR_LOG("\tTotal used logic block area: %g\n", used_area);
if (route_type == DETAILED) {
count_routing_transistors(directionality, num_rr_switch, wire_to_ipin_switch,
segment_inf, R_minW_nmos, R_minW_pmos);
get_segment_usage_stats(segment_inf);
}
if (full_stats == true)
print_wirelen_prob_dist();
}
/* Figures out maximum, minimum and average number of bends and net length *
* in the routing. */
void length_and_bends_stats() {
int 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;
auto& cluster_ctx = g_vpr_ctx.clustering();
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 (auto net_id : cluster_ctx.clb_nlist.nets()) {
if (!cluster_ctx.clb_nlist.net_is_ignored(net_id) && cluster_ctx.clb_nlist.net_sinks(net_id).size() != 0) { /* Globals don't count. */
get_num_bends_and_length(net_id, &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 (cluster_ctx.clb_nlist.net_is_ignored(net_id)) {
num_global_nets++;
} else {
num_clb_opins_reserved++;
}
}
av_bends = (float)total_bends / (float)((int)cluster_ctx.clb_nlist.nets().size() - num_global_nets);
VTR_LOG("\n");
VTR_LOG("Average number of bends per net: %#g Maximum # of bends: %d\n", av_bends, max_bends);
VTR_LOG("\n");
av_length = (float)total_length / (float)((int)cluster_ctx.clb_nlist.nets().size() - num_global_nets);
VTR_LOG("Number of global nets: %d\n", num_global_nets);
VTR_LOG("Number of routed nets (nonglobal): %d\n", (int)cluster_ctx.clb_nlist.nets().size() - num_global_nets);
VTR_LOG("Wire length results (in units of 1 clb segments)...\n");
VTR_LOG("\tTotal wirelength: %d, average net length: %#g\n", total_length, av_length);
VTR_LOG("\tMaximum net length: %d\n", max_length);
VTR_LOG("\n");
av_segments = (float)total_segments / (float)((int)cluster_ctx.clb_nlist.nets().size() - num_global_nets);
VTR_LOG("Wire length results in terms of physical segments...\n");
VTR_LOG("\tTotal wiring segments used: %d, average wire segments per net: %#g\n", total_segments, av_segments);
VTR_LOG("\tMaximum segments used by a net: %d\n", max_segments);
VTR_LOG("\tTotal local nets with reserved CLB opins: %d\n", num_clb_opins_reserved);
}
static void get_channel_occupancy_stats() {
/* Determines how many tracks are used in each channel. */
auto& device_ctx = g_vpr_ctx.device();
auto chanx_occ = vtr::Matrix<int>({{
device_ctx.grid.width(), //[0 .. device_ctx.grid.width() - 1] (length of x channel)
device_ctx.grid.height() - 1 //[0 .. device_ctx.grid.height() - 2] (# x channels)
}},
0);
auto chany_occ = vtr::Matrix<int>({{
device_ctx.grid.width() - 1, //[0 .. device_ctx.grid.width() - 2] (# y channels)
device_ctx.grid.height() //[0 .. device_ctx.grid.height() - 1] (length of y channel)
}},
0);
load_channel_occupancies(chanx_occ, chany_occ);
VTR_LOG("\n");
VTR_LOG("X - Directed channels: j max occ ave occ capacity\n");
VTR_LOG(" ---- ------- ------- --------\n");
int total_x = 0;
for (size_t j = 0; j < device_ctx.grid.height() - 1; ++j) {
total_x += device_ctx.chan_width.x_list[j];
float ave_occ = 0.0;
int max_occ = -1;
for (size_t i = 1; i < device_ctx.grid.width(); ++i) {
max_occ = std::max(chanx_occ[i][j], max_occ);
ave_occ += chanx_occ[i][j];
}
ave_occ /= device_ctx.grid.width();
VTR_LOG(" %4d %7d %7.3f %8d\n", j, max_occ, ave_occ, device_ctx.chan_width.x_list[j]);
}
VTR_LOG("Y - Directed channels: i max occ ave occ capacity\n");
VTR_LOG(" ---- ------- ------- --------\n");
int total_y = 0;
for (size_t i = 0; i < device_ctx.grid.width() - 1; ++i) {
total_y += device_ctx.chan_width.y_list[i];
float ave_occ = 0.0;
int max_occ = -1;
for (size_t j = 1; j < device_ctx.grid.height(); ++j) {
max_occ = std::max(chany_occ[i][j], max_occ);
ave_occ += chany_occ[i][j];
}
ave_occ /= device_ctx.grid.height();
VTR_LOG(" %4d %7d %7.3f %8d\n", i, max_occ, ave_occ, device_ctx.chan_width.y_list[i]);
}
VTR_LOG("\n");
VTR_LOG("Total tracks in x-direction: %d, in y-direction: %d\n", total_x, total_y);
VTR_LOG("\n");
}
/* Loads the two arrays passed in with the total occupancy at each of the *
* channel segments in the FPGA. */
static void load_channel_occupancies(vtr::Matrix<int>& chanx_occ, vtr::Matrix<int>& chany_occ) {
int i, j, inode;
t_trace* tptr;
t_rr_type rr_type;
auto& device_ctx = g_vpr_ctx.device();
auto& cluster_ctx = g_vpr_ctx.clustering();
auto& route_ctx = g_vpr_ctx.routing();
/* First set the occupancy of everything to zero. */
chanx_occ.fill(0);
chany_occ.fill(0);
/* Now go through each net and count the tracks and pins used everywhere */
for (auto net_id : cluster_ctx.clb_nlist.nets()) {
/* Skip global and empty nets. */
if (cluster_ctx.clb_nlist.net_is_ignored(net_id) && cluster_ctx.clb_nlist.net_sinks(net_id).size() != 0)
continue;
tptr = route_ctx.trace[net_id].head;
while (tptr != nullptr) {
inode = tptr->index;
rr_type = device_ctx.rr_nodes[inode].type();
if (rr_type == SINK) {
tptr = tptr->next; /* Skip next segment. */
if (tptr == nullptr)
break;
}
else if (rr_type == CHANX) {
j = device_ctx.rr_nodes[inode].ylow();
for (i = device_ctx.rr_nodes[inode].xlow(); i <= device_ctx.rr_nodes[inode].xhigh(); i++)
chanx_occ[i][j]++;
}
else if (rr_type == CHANY) {
i = device_ctx.rr_nodes[inode].xlow();
for (j = device_ctx.rr_nodes[inode].ylow(); j <= device_ctx.rr_nodes[inode].yhigh(); j++)
chany_occ[i][j]++;
}
tptr = tptr->next;
}
}
}
void get_num_bends_and_length(ClusterNetId 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. */
auto& route_ctx = g_vpr_ctx.routing();
auto& device_ctx = g_vpr_ctx.device();
t_trace *tptr, *prevptr;
int inode;
t_rr_type curr_type, prev_type;
int bends, length, segments;
bends = 0;
length = 0;
segments = 0;
prevptr = route_ctx.trace[inet].head; /* Should always be SOURCE. */
if (prevptr == nullptr) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"in get_num_bends_and_length: net #%lu has no traceback.\n", size_t(inet));
}
inode = prevptr->index;
prev_type = device_ctx.rr_nodes[inode].type();
tptr = prevptr->next;
while (tptr != nullptr) {
inode = tptr->index;
curr_type = device_ctx.rr_nodes[inode].type();
if (curr_type == SINK) { /* Starting a new segment */
tptr = tptr->next; /* Link to existing path - don't add to len. */
if (tptr == nullptr)
break;
curr_type = device_ctx.rr_nodes[tptr->index].type();
}
else if (curr_type == CHANX || curr_type == CHANY) {
segments++;
length += 1 + device_ctx.rr_nodes[inode].xhigh() - device_ctx.rr_nodes[inode].xlow()
+ device_ctx.rr_nodes[inode].yhigh() - device_ctx.rr_nodes[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() {
/* Prints out the probability distribution of the wirelength / number *
* input pins on a net -- i.e. simulates 2-point net length probability *
* distribution. */
auto& device_ctx = g_vpr_ctx.device();
auto& cluster_ctx = g_vpr_ctx.clustering();
float* prob_dist;
float norm_fac, two_point_length;
int bends, length, segments, index;
float av_length;
int prob_dist_size, i, incr;
prob_dist_size = device_ctx.grid.width() + device_ctx.grid.height() + 10;
prob_dist = (float*)vtr::calloc(prob_dist_size, sizeof(float));
norm_fac = 0.;
for (auto net_id : cluster_ctx.clb_nlist.nets()) {
if (!cluster_ctx.clb_nlist.net_is_ignored(net_id) && cluster_ctx.clb_nlist.net_sinks(net_id).size() != 0) {
get_num_bends_and_length(net_id, &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]. */
int num_sinks = cluster_ctx.clb_nlist.net_sinks(net_id).size();
VTR_ASSERT(num_sinks > 0);
two_point_length = (float)length / (float)(num_sinks);
index = (int)two_point_length;
if (index >= prob_dist_size) {
VTR_LOG_WARN("Index (%d) to prob_dist exceeds its allocated size (%d).\n",
index, prob_dist_size);
VTR_LOG("Realloc'ing to increase 2-pin wirelen prob distribution array.\n");
incr = index - prob_dist_size + 2;
prob_dist_size += incr;
prob_dist = (float*)vtr::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] += (num_sinks) * (1 - two_point_length + index);
index++;
if (index >= prob_dist_size) {
VTR_LOG_WARN("Index (%d) to prob_dist exceeds its allocated size (%d).\n",
index, prob_dist_size);
VTR_LOG("Realloc'ing to increase 2-pin wirelen prob distribution array.\n");
incr = index - prob_dist_size + 2;
prob_dist_size += incr;
prob_dist = (float*)vtr::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] += (num_sinks) * (1 - index + two_point_length);
norm_fac += num_sinks;
}
}
/* Normalize so total probability is 1 and print out. */
VTR_LOG("\n");
VTR_LOG("Probability distribution of 2-pin net lengths:\n");
VTR_LOG("\n");
VTR_LOG("Length p(Lenth)\n");
av_length = 0;
for (index = 0; index < prob_dist_size; index++) {
prob_dist[index] /= norm_fac;
VTR_LOG("%6d %10.6f\n", index, prob_dist[index]);
av_length += prob_dist[index] * index;
}
VTR_LOG("\n");
VTR_LOG("Number of 2-pin nets: ;%g;\n", norm_fac);
VTR_LOG("Expected value of 2-pin net length (R): ;%g;\n", av_length);
VTR_LOG("Total wirelength: ;%g;\n", norm_fac * av_length);
free(prob_dist);
}
void print_lambda() {
/* 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 ipin, iclass;
int num_inputs_used = 0;
float lambda;
auto& cluster_ctx = g_vpr_ctx.clustering();
for (auto blk_id : cluster_ctx.clb_nlist.blocks()) {
auto type = physical_tile_type(blk_id);
VTR_ASSERT(type != nullptr);
if (!is_io_type(type)) {
for (ipin = 0; ipin < type->num_pins; ipin++) {
iclass = type->pin_class[ipin];
if (type->class_inf[iclass].type == RECEIVER) {
ClusterNetId net_id = cluster_ctx.clb_nlist.block_net(blk_id, ipin);
if (net_id != ClusterNetId::INVALID()) /* Pin is connected? */
if (!cluster_ctx.clb_nlist.net_is_ignored(net_id)) /* Not a global clock */
num_inputs_used++;
}
}
}
}
lambda = (float)num_inputs_used / (float)cluster_ctx.clb_nlist.blocks().size();
VTR_LOG("Average lambda (input pins used per clb) is: %g\n", lambda);
}
int count_netlist_clocks() {
/* Count how many clocks are in the netlist. */
auto& atom_ctx = g_vpr_ctx.atom();
std::set<std::string> clock_names;
//Loop through each clock pin and record the names in clock_names
for (auto blk_id : atom_ctx.nlist.blocks()) {
for (auto pin_id : atom_ctx.nlist.block_clock_pins(blk_id)) {
auto net_id = atom_ctx.nlist.pin_net(pin_id);
clock_names.insert(atom_ctx.nlist.net_name(net_id));
}
}
//Since std::set does not include duplicates, the number of clocks is the size of the set
return static_cast<int>(clock_names.size());
}