blob: 0c8e095cd52a205b43858ecc9cc58fb40da81fb3 [file] [log] [blame]
// Tool to test routing from one node to another node.
//
// This tool is intended to allow extremely verbose router output on exactly
// one path to diagnose router behavior, e.g. look ahead and A* characteristics.
//
// Usage follows typically VPR invocation, except that no circuit is actually
// used.
//
// Tool can either perform one route between a source (--source_rr_node) and
// a sink (--sink_rr_node), or profile a source to all tiles (set
// --source_rr_node and "--profile_source true").
#include <cstdio>
#include <cstring>
#include <ctime>
#include <fstream>
#include "vtr_error.h"
#include "vtr_memory.h"
#include "vtr_log.h"
#include "vtr_time.h"
#include "tatum/error.hpp"
#include "vpr_error.h"
#include "vpr_api.h"
#include "vpr_signal_handler.h"
#include "globals.h"
#include "net_delay.h"
#include "RoutingDelayCalculator.h"
#include "place_and_route.h"
#include "router_delay_profiling.h"
#include "route_tree_type.h"
#include "route_common.h"
#include "route_timing.h"
#include "route_tree_timing.h"
#include "route_export.h"
#include "rr_graph.h"
#include "rr_graph2.h"
#include "timing_place_lookup.h"
struct t_route_util_options {
/* Router diag tool Options */
argparse::ArgValue<int> source_rr_node;
argparse::ArgValue<int> sink_rr_node;
argparse::ArgValue<bool> profile_source;
t_options options;
};
t_route_util_options read_route_util_options(int argc, const char** argv);
/*
* Exit codes to signal success/failure to scripts
* calling vpr
*/
constexpr int SUCCESS_EXIT_CODE = 0; //Everything OK
constexpr int ERROR_EXIT_CODE = 1; //Something went wrong internally
constexpr int INTERRUPTED_EXIT_CODE = 3; //VPR was interrupted by the user (e.g. SIGINT/ctr-C)
static void do_one_route(int source_node, int sink_node,
const t_router_opts& router_opts,
const std::vector<t_segment_inf>& segment_inf) {
/* Returns true as long as found some way to hook up this net, even if that *
* way resulted in overuse of resources (congestion). If there is no way *
* to route this net, even ignoring congestion, it returns false. In this *
* case the rr_graph is disconnected and you can give up. */
auto& device_ctx = g_vpr_ctx.device();
auto& route_ctx = g_vpr_ctx.routing();
t_rt_node* rt_root = init_route_tree_to_source_no_net(source_node);
enable_router_debug(router_opts, ClusterNetId(), sink_node);
/* Update base costs according to fanout and criticality rules */
update_rr_base_costs(1);
//maximum bounding box for placement
t_bb bounding_box;
bounding_box.xmin = 0;
bounding_box.xmax = device_ctx.grid.width() + 1;
bounding_box.ymin = 0;
bounding_box.ymax = device_ctx.grid.height() + 1;
t_conn_cost_params cost_params;
cost_params.criticality = 1.;
cost_params.astar_fac = router_opts.astar_fac;
cost_params.bend_cost = router_opts.bend_cost;
route_budgets budgeting_inf;
init_heap(device_ctx.grid);
std::vector<int> modified_rr_node_inf;
RouterStats router_stats;
auto router_lookahead = make_router_lookahead(
router_opts.lookahead_type,
router_opts.write_router_lookahead,
router_opts.read_router_lookahead,
segment_inf
);
t_heap* cheapest = timing_driven_route_connection_from_route_tree(rt_root, sink_node, cost_params, bounding_box, *router_lookahead, modified_rr_node_inf, router_stats);
bool found_path = (cheapest != nullptr);
if (found_path) {
VTR_ASSERT(cheapest->index == sink_node);
t_rt_node* rt_node_of_sink = update_route_tree(cheapest, nullptr);
free_heap_data(cheapest);
//find delay
float net_delay = rt_node_of_sink->Tdel;
VTR_LOG("Routed successfully, delay = %g!\n", net_delay);
VTR_LOG("\n");
print_route_tree_node(rt_root);
VTR_LOG("\n");
print_route_tree(rt_root);
VTR_LOG("\n");
VTR_ASSERT_MSG(route_ctx.rr_node_route_inf[rt_root->inode].occ() <= device_ctx.rr_nodes[rt_root->inode].capacity(), "SOURCE should never be congested");
free_route_tree(rt_root);
} else {
VTR_LOG("Routed failed");
}
//Reset for the next router call
empty_heap();
reset_path_costs(modified_rr_node_inf);
}
static void profile_source(int source_rr_node,
const t_router_opts& router_opts,
const std::vector<t_segment_inf>& segment_inf) {
vtr::ScopedStartFinishTimer timer("Profiling source");
const auto& device_ctx = g_vpr_ctx.device();
const auto& grid = device_ctx.grid;
auto router_lookahead = make_router_lookahead(
router_opts.lookahead_type,
router_opts.write_router_lookahead,
router_opts.read_router_lookahead,
segment_inf
);
RouterDelayProfiler profiler(router_lookahead.get());
int start_x = 0;
int end_x = grid.width() - 1;
int start_y = 0;
int end_y = grid.height() - 1;
vtr::Matrix<float> delays({grid.width(), grid.height()},
std::numeric_limits<float>::infinity());
vtr::Matrix<int> sink_nodes({grid.width(), grid.height()}, OPEN);
for (int sink_x = start_x; sink_x <= end_x; sink_x++) {
for (int sink_y = start_y; sink_y <= end_y; sink_y++) {
if(device_ctx.grid[sink_x][sink_y].type == device_ctx.EMPTY_TYPE) {
continue;
}
auto best_sink_ptcs = get_best_classes(RECEIVER,
device_ctx.grid[sink_x][sink_y].type);
bool successfully_routed;
for (int sink_ptc : best_sink_ptcs) {
VTR_ASSERT(sink_ptc != OPEN);
int sink_rr_node = get_rr_node_index(device_ctx.rr_node_indices, sink_x, sink_y, SINK, sink_ptc);
if (directconnect_exists(source_rr_node, sink_rr_node)) {
//Skip if we shouldn't measure direct connects and a direct connect exists
continue;
}
VTR_ASSERT(sink_rr_node != OPEN);
{
vtr::ScopedStartFinishTimer delay_timer(vtr::string_fmt(
"Routing Src: %d Sink: %d", source_rr_node,
sink_rr_node));
successfully_routed = profiler.calculate_delay(source_rr_node, sink_rr_node,
router_opts,
&delays[sink_x][sink_y]);
}
if (successfully_routed) {
sink_nodes[sink_x][sink_y] = sink_rr_node;
break;
}
}
}
}
VTR_LOG("Delay matrix from source_rr_node: %d\n", source_rr_node);
for(size_t iy = 0; iy < delays.dim_size(1); ++iy) {
for(size_t ix = 0; ix < delays.dim_size(0); ++ix) {
VTR_LOG("%g,", delays[ix][iy]);
}
VTR_LOG("\n");
}
VTR_LOG("\n");
VTR_LOG("Sink matrix used for delay matrix:\n");
for(size_t iy = 0; iy < sink_nodes.dim_size(1); ++iy) {
for(size_t ix = 0; ix < sink_nodes.dim_size(0); ++ix) {
VTR_LOG("%d,", sink_nodes[ix][iy]);
}
VTR_LOG("\n");
}
VTR_LOG("\n");
}
static t_chan_width setup_chan_width(t_router_opts router_opts,
t_chan_width_dist chan_width_dist) {
/*we give plenty of tracks, this increases routability for the */
/*lookup table generation */
int width_fac;
if (router_opts.fixed_channel_width == NO_FIXED_CHANNEL_WIDTH) {
auto& device_ctx = g_vpr_ctx.device();
auto type = physical_tile_type(find_most_common_block_type(device_ctx.grid));
width_fac = 4 * type->num_pins;
/*this is 2x the value that binary search starts */
/*this should be enough to allow most pins to */
/*connect to tracks in the architecture */
} else {
width_fac = router_opts.fixed_channel_width;
}
return init_chan(width_fac, chan_width_dist);
}
t_route_util_options read_route_util_options(int argc, const char** argv) {
//Explicitly initialize for zero initialization
t_route_util_options args = t_route_util_options();
auto parser = create_arg_parser(argv[0], args.options);
auto& route_diag_grp = parser.add_argument_group("route diagnostic options");
route_diag_grp.add_argument(args.sink_rr_node, "--sink_rr_node")
.help("Sink RR node to route for route_diag.")
.show_in(argparse::ShowIn::HELP_ONLY);
route_diag_grp.add_argument(args.source_rr_node, "--source_rr_node")
.help("Source RR node to route for route_diag.")
.show_in(argparse::ShowIn::HELP_ONLY);
route_diag_grp.add_argument(args.profile_source, "--profile_source")
.help(
"Profile routes from source to IPINs at all locations."
"This is similiar to the placer delay matrix construction.")
.show_in(argparse::ShowIn::HELP_ONLY);
parser.parse_args(argc, argv);
set_conditional_defaults(args.options);
verify_args(args.options);
return args;
}
int main(int argc, const char **argv) {
t_options Options = t_options();
t_arch Arch = t_arch();
t_vpr_setup vpr_setup = t_vpr_setup();
try {
vpr_install_signal_handler();
vpr_initialize_logging();
/* Print title message */
vpr_print_title();
t_route_util_options route_options = read_route_util_options(argc, argv);
Options = route_options.options;
/* Read architecture, and circuit netlist */
vpr_init_with_options(&Options, &vpr_setup, &Arch);
vpr_create_device_grid(vpr_setup, Arch);
vpr_setup_clock_networks(vpr_setup, Arch);
t_chan_width chan_width = setup_chan_width(
vpr_setup.RouterOpts,
Arch.Chans);
alloc_routing_structs(
chan_width,
vpr_setup.RouterOpts,
&vpr_setup.RoutingArch,
vpr_setup.Segments,
Arch.Directs,
Arch.num_directs
);
if(route_options.profile_source) {
profile_source(
route_options.source_rr_node,
vpr_setup.RouterOpts,
vpr_setup.Segments
);
} else {
do_one_route(
route_options.source_rr_node,
route_options.sink_rr_node,
vpr_setup.RouterOpts,
vpr_setup.Segments);
}
free_routing_structs();
/* free data structures */
vpr_free_all(Arch, vpr_setup);
} catch (const tatum::Error& tatum_error) {
vtr::printf_error(__FILE__, __LINE__, "STA Engine: %s\n", tatum_error.what());
return ERROR_EXIT_CODE;
} catch (const VprError& vpr_error) {
vpr_print_error(vpr_error);
if (vpr_error.type() == VPR_ERROR_INTERRUPTED) {
return INTERRUPTED_EXIT_CODE;
} else {
return ERROR_EXIT_CODE;
}
} catch (const vtr::VtrError& vtr_error) {
vtr::printf_error(__FILE__, __LINE__, "%s:%d %s\n", vtr_error.filename_c_str(), vtr_error.line(), vtr_error.what());
return ERROR_EXIT_CODE;
}
/* Signal success to scripts */
return SUCCESS_EXIT_CODE;
}