diff --git a/utils/route_diag/src/main.cpp b/utils/route_diag/src/main.cpp index 2598c8de50f..a30bec137b1 100644 --- a/utils/route_diag/src/main.cpp +++ b/utils/route_diag/src/main.cpp @@ -112,7 +112,7 @@ static void do_one_route(int source_node, int sink_node, if (found_path) { VTR_ASSERT(cheapest.index == sink_node); - t_rt_node* rt_node_of_sink = update_route_tree(&cheapest, nullptr); + t_rt_node* rt_node_of_sink = update_route_tree(&cheapest, OPEN, nullptr); //find delay float net_delay = rt_node_of_sink->Tdel; diff --git a/vpr/src/base/read_route.cpp b/vpr/src/base/read_route.cpp index 042baffcd92..5a16f4d7c48 100644 --- a/vpr/src/base/read_route.cpp +++ b/vpr/src/base/read_route.cpp @@ -216,7 +216,7 @@ static void process_nodes(std::ifstream& fp, ClusterNetId inet, const char* file /*remember the position of the last line in order to go back*/ std::streampos oldpos = fp.tellg(); - int inode, x, y, x2, y2, ptc, switch_id, offset; + int inode, x, y, x2, y2, ptc, switch_id, net_pin_index, offset; std::string prev_type; int node_count = 0; std::string input; @@ -348,10 +348,26 @@ static void process_nodes(std::ifstream& fp, ClusterNetId inet, const char* file switch_id = atoi(tokens[7 + offset].c_str()); } + /* Process net pin index for sinks * + * If you have an old .route file, it may not have this information * + * Please check your .route file to see if it contains Net_pin_index * + * information for sinks. If not, plrase re-generate the routing. */ + if (tokens[2] == "SINK") { + if (tokens[8 + offset] == "Net_pin_index:") { + net_pin_index = atoi(tokens[9 + offset].c_str()); + } else { + vpr_throw(VPR_ERROR_ROUTE, filename, lineno, + "%d (sink) node does not have net pin index. If you are using an old .route file without this information, please re-generate the routing.", inode); + } + } else { + net_pin_index = OPEN; //net pin index is invalid for non-SINKs + } + /* Allocate and load correct values to trace.head*/ if (node_count == 0) { route_ctx.trace[inet].head = alloc_trace_data(); route_ctx.trace[inet].head->index = inode; + route_ctx.trace[inet].head->net_pin_index = net_pin_index; route_ctx.trace[inet].head->iswitch = switch_id; route_ctx.trace[inet].head->next = nullptr; tptr = route_ctx.trace[inet].head; @@ -360,6 +376,7 @@ static void process_nodes(std::ifstream& fp, ClusterNetId inet, const char* file tptr->next = alloc_trace_data(); tptr = tptr->next; tptr->index = inode; + tptr->net_pin_index = net_pin_index; tptr->iswitch = switch_id; tptr->next = nullptr; node_count++; diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index dbf5a274cef..b88e9f36184 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1315,6 +1315,16 @@ typedef std::array, 3>, NUM_RR_TYPES> t_rr_node_i * @brief Basic element used to store the traceback (routing) of each net. * * @param index Array index (ID) of this routing resource node. + * @param net_pin_index: Net pin index associated with the node. This value + * ranges from 1 to fanout [1..num_pins-1]. For cases when + * different speed paths are taken to the same SINK for + * different pins, node index cannot uniquely identify + * each SINK, so the net pin index guarantees an unique + * identification for each SINK node. For non-SINK nodes + * and for SINK nodes with no associated net pin index + * (i.e. special SINKs like the source of a clock tree + * which do not correspond to an actual netlist connection), + * the value for this member should be set to OPEN (-1). * @param iswitch Index of the switch type used to go from this rr_node to * the next one in the routing. OPEN if there is no next node * (i.e. this node is the last one (a SINK) in a branch of the @@ -1324,6 +1334,7 @@ typedef std::array, 3>, NUM_RR_TYPES> t_rr_node_i struct t_trace { t_trace* next; int index; + int net_pin_index = OPEN; short iswitch; }; diff --git a/vpr/src/route/check_route.cpp b/vpr/src/route/check_route.cpp index bc244c3b519..a57b81eaa5d 100644 --- a/vpr/src/route/check_route.cpp +++ b/vpr/src/route/check_route.cpp @@ -190,8 +190,7 @@ static void check_sink(int inode, ClusterNetId net_id, bool* pin_done) { int ptc_num = device_ctx.rr_nodes[inode].ptc_num(); int ifound = 0; - for (int iblk = 0; iblk < type->capacity; iblk++) { - ClusterBlockId bnum = place_ctx.grid_blocks[i][j].blocks[iblk]; /* Hardcoded to one cluster_ctx block*/ + for (auto bnum : place_ctx.grid_blocks[i][j].blocks) { unsigned int ipin = 1; for (auto pin_id : cluster_ctx.clb_nlist.net_sinks(net_id)) { if (cluster_ctx.clb_nlist.pin_block(pin_id) == bnum) { @@ -203,6 +202,7 @@ static void check_sink(int inode, ClusterNetId net_id, bool* pin_done) { if (pin_done[ipin] == false) { ifound++; pin_done[ipin] = true; + break; } } } @@ -210,10 +210,7 @@ static void check_sink(int inode, ClusterNetId net_id, bool* pin_done) { } } - if (ifound > 1 && is_io_type(type)) { - VPR_FATAL_ERROR(VPR_ERROR_ROUTE, - "in check_sink: found %d terminals of net %d of pad %d at location (%d, %d).\n", ifound, size_t(net_id), ptc_num, i, j); - } + VTR_ASSERT(ifound <= 1); if (ifound < 1) { VPR_FATAL_ERROR(VPR_ERROR_ROUTE, diff --git a/vpr/src/route/connection_based_routing.cpp b/vpr/src/route/connection_based_routing.cpp index ffe27280066..4d442462f92 100644 --- a/vpr/src/route/connection_based_routing.cpp +++ b/vpr/src/route/connection_based_routing.cpp @@ -12,8 +12,6 @@ Connection_based_routing_resources::Connection_based_routing_resources() , connection_criticality_tolerance{0.9f} , connection_delay_optimality_tolerance{1.1f} { /* Initialize the persistent data structures for incremental rerouting - * this includes rr_sink_node_to_pin, which provides pin lookup given a - * sink node for a specific net. * * remaining_targets will reserve enough space to ensure it won't need * to grow while storing the sinks that still need routing after pruning @@ -31,18 +29,14 @@ Connection_based_routing_resources::Connection_based_routing_resources() reached_rt_sinks.reserve(max_sink_pins_per_net); size_t routing_num_nets = cluster_ctx.clb_nlist.nets().size(); - rr_sink_node_to_pin.resize(routing_num_nets); lower_bound_connection_delay.resize(routing_num_nets); forcible_reroute_connection_flag.resize(routing_num_nets); for (auto net_id : cluster_ctx.clb_nlist.nets()) { - // unordered_map net_node_to_pin; - auto& net_node_to_pin = rr_sink_node_to_pin[net_id]; auto& net_lower_bound_connection_delay = lower_bound_connection_delay[net_id]; auto& net_forcible_reroute_connection_flag = forcible_reroute_connection_flag[net_id]; - unsigned int num_pins = cluster_ctx.clb_nlist.net_pins(net_id).size(); - net_node_to_pin.reserve(num_pins); // not looking up on the SOURCE pin + unsigned int num_pins = cluster_ctx.clb_nlist.net_pins(net_id).size(); // not looking up on the SOURCE pin net_lower_bound_connection_delay.resize(num_pins, std::numeric_limits::infinity()); // will be filled in after the 1st iteration's net_forcible_reroute_connection_flag.reserve(num_pins); // all false to begin with @@ -50,70 +44,11 @@ Connection_based_routing_resources::Connection_based_routing_resources() // rr sink node index corresponding to this connection terminal auto rr_sink_node = route_ctx.net_rr_terminals[net_id][ipin]; - net_node_to_pin.insert({rr_sink_node, ipin}); net_forcible_reroute_connection_flag.insert({rr_sink_node, false}); } } } -void Connection_based_routing_resources::convert_sink_nodes_to_net_pins(std::vector& rr_sink_nodes) const { - /* Turn a vector of device_ctx.rr_nodes indices, assumed to be of sinks for a net * - * into the pin indices of the same net. */ - - VTR_ASSERT(current_inet != ClusterNetId::INVALID()); // not uninitialized - - const auto& node_to_pin_mapping = rr_sink_node_to_pin[current_inet]; - - for (size_t s = 0; s < rr_sink_nodes.size(); ++s) { - auto mapping = node_to_pin_mapping.find(rr_sink_nodes[s]); - if (mapping != node_to_pin_mapping.end()) { - rr_sink_nodes[s] = mapping->second; - } else { - VTR_ASSERT_SAFE_MSG(false, "Should always expect it find a pin mapping for its own net"); - } - } -} - -void Connection_based_routing_resources::put_sink_rt_nodes_in_net_pins_lookup(const std::vector& sink_rt_nodes, - t_rt_node** rt_node_of_sink) const { - /* Load rt_node_of_sink (which maps a PIN index to a route tree node) - * with a vector of route tree sink nodes. */ - - VTR_ASSERT(current_inet != ClusterNetId::INVALID()); - - // a net specific mapping from node index to pin index - const auto& node_to_pin_mapping = rr_sink_node_to_pin[current_inet]; - - for (t_rt_node* rt_node : sink_rt_nodes) { - auto mapping = node_to_pin_mapping.find(rt_node->inode); - - if (mapping != node_to_pin_mapping.end()) { - rt_node_of_sink[mapping->second] = rt_node; - } else { - VTR_ASSERT_SAFE_MSG(false, "element should be able to find itself"); - } - } -} - -bool Connection_based_routing_resources::sanity_check_lookup() const { - auto& cluster_ctx = g_vpr_ctx.clustering(); - auto& route_ctx = g_vpr_ctx.routing(); - - for (auto net_id : cluster_ctx.clb_nlist.nets()) { - const auto& net_node_to_pin = rr_sink_node_to_pin[net_id]; - - for (auto mapping : net_node_to_pin) { - auto sanity = net_node_to_pin.find(mapping.first); - if (sanity == net_node_to_pin.end()) { - VTR_LOG("%d cannot find itself (net %lu)\n", mapping.first, size_t(net_id)); - return false; - } - VTR_ASSERT(route_ctx.net_rr_terminals[net_id][mapping.second] == mapping.first); - } - } - return true; -} - void Connection_based_routing_resources::set_lower_bound_connection_delays(ClbNetPinsMatrix& net_delay) { /* Set the lower bound connection delays after first iteration, which only optimizes for timing delay. * This will be used later to judge the optimality of a connection, with suboptimal ones being candidates diff --git a/vpr/src/route/connection_based_routing.h b/vpr/src/route/connection_based_routing.h index 2ca63cda261..1179e044daf 100644 --- a/vpr/src/route/connection_based_routing.h +++ b/vpr/src/route/connection_based_routing.h @@ -15,11 +15,6 @@ // reroute only the connections to the ones that did not have a legal connection the previous time class Connection_based_routing_resources { // Incremental reroute resources -------------- - // conceptually works like rr_sink_node_to_pin[inet][sink_rr_node_index] to get the pin index for that net - // each net maps SINK node index -> PIN index for net - // only need to be built once at the start since the SINK nodes never change - // the reverse lookup of route_ctx.net_rr_terminals - vtr::vector> rr_sink_node_to_pin; // a property of each net, but only valid after pruning the previous route tree // the "targets" in question can be either rr_node indices or pin indices, the @@ -43,11 +38,6 @@ class Connection_based_routing_resources { std::vector& get_remaining_targets() { return remaining_targets; } std::vector& get_reached_rt_sinks() { return reached_rt_sinks; } - void convert_sink_nodes_to_net_pins(std::vector& rr_sink_nodes) const; - - void put_sink_rt_nodes_in_net_pins_lookup(const std::vector& sink_rt_nodes, - t_rt_node** rt_node_of_sink) const; - bool sanity_check_lookup() const; void set_connection_criticality_tolerance(float val) { connection_criticality_tolerance = val; } diff --git a/vpr/src/route/route_breadth_first.cpp b/vpr/src/route/route_breadth_first.cpp index 3fc32cba0e7..e115ba9d5da 100644 --- a/vpr/src/route/route_breadth_first.cpp +++ b/vpr/src/route/route_breadth_first.cpp @@ -265,7 +265,8 @@ static bool breadth_first_route_net(BinaryHeap& heap, ClusterNetId net_id, float route_ctx.rr_node_route_inf[inode].target_flag--; /* Connected to this SINK. */ remaining_connections_to_sink = route_ctx.rr_node_route_inf[inode].target_flag; - tptr = update_traceback(current, net_id); + size_t ipin = cluster_ctx.clb_nlist.pin_net_index(pin_id); + tptr = update_traceback(current, ipin, net_id); heap.free(current); } diff --git a/vpr/src/route/route_common.cpp b/vpr/src/route/route_common.cpp index 7855ae82ad3..9547a09b543 100644 --- a/vpr/src/route/route_common.cpp +++ b/vpr/src/route/route_common.cpp @@ -86,7 +86,7 @@ static int num_linked_f_pointer_allocated = 0; * */ /******************** Subroutines local to route_common.c *******************/ -static t_trace_branch traceback_branch(int node, std::unordered_set& main_branch_visited); +static t_trace_branch traceback_branch(int node, int target_net_pin_index, std::unordered_set& main_branch_visited); static std::pair add_trace_non_configurable(t_trace* head, t_trace* tail, int node, std::unordered_set& visited); static std::pair add_trace_non_configurable_recurr(int node, std::unordered_set& visited, int depth = 0); @@ -494,26 +494,28 @@ void init_route_structs(int bb_factor) { route_ctx.net_status.resize(cluster_ctx.clb_nlist.nets().size()); } -t_trace* update_traceback(t_heap* hptr, ClusterNetId net_id) { - /* This routine adds the most recently finished wire segment to the * - * traceback linked list. The first connection starts with the net SOURCE * - * and begins at the structure pointed to by route_ctx.trace[net_id].head. * - * Each connection ends with a SINK. After each SINK, the next connection * - * begins (if the net has more than 2 pins). The first element after the * - * SINK gives the routing node on a previous piece of the routing, which is * - * the link from the existing net to this new piece of the net. * - * In each traceback I start at the end of a path and trace back through * - * its predecessors to the beginning. I have stored information on the * - * predecesser of each node to make traceback easy -- this sacrificies some * - * memory for easier code maintenance. This routine returns a pointer to * - * the first "new" node in the traceback (node not previously in trace). */ +/* This routine adds the most recently finished wire segment to the * + * traceback linked list. The first connection starts with the net SOURCE * + * and begins at the structure pointed to by route_ctx.trace[net_id].head. * + * Each connection ends with a SINK. After each SINK, the next connection * + * begins (if the net has more than 2 pins). The first element after the * + * SINK gives the routing node on a previous piece of the routing, which is * + * the link from the existing net to this new piece of the net. * + * In each traceback I start at the end of a path, which is a SINK with * + * target_net_pin_index (net pin index corresponding to the SINK, ranging * + * from 1 to fanout), and trace back through its predecessors to the * + * beginning. I have stored information on the predecesser of each node to * + * make traceback easy -- this sacrificies some memory for easier code * + * maintenance. This routine returns a pointer to the first "new" node in * + * the traceback (node not previously in trace). */ +t_trace* update_traceback(t_heap* hptr, int target_net_pin_index, ClusterNetId net_id) { auto& route_ctx = g_vpr_ctx.mutable_routing(); auto& trace_nodes = route_ctx.trace_nodes[net_id]; VTR_ASSERT_SAFE(validate_trace_nodes(route_ctx.trace[net_id].head, trace_nodes)); - t_trace_branch branch = traceback_branch(hptr->index, trace_nodes); + t_trace_branch branch = traceback_branch(hptr->index, target_net_pin_index, trace_nodes); VTR_ASSERT_SAFE(validate_trace_nodes(branch.head, trace_nodes)); @@ -530,9 +532,10 @@ t_trace* update_traceback(t_heap* hptr, ClusterNetId net_id) { return (ret_ptr); } -//Traces back a new routing branch starting from the specified 'node' and working backwards to any existing routing. +//Traces back a new routing branch starting from the specified SINK 'node' with target_net_pin_index, which is the +//net pin index corresponding to the SINK (ranging from 1 to fanout), and working backwards to any existing routing. //Returns the new branch, and also updates trace_nodes for any new nodes which are included in the branches traceback. -static t_trace_branch traceback_branch(int node, std::unordered_set& trace_nodes) { +static t_trace_branch traceback_branch(int node, int target_net_pin_index, std::unordered_set& trace_nodes) { auto& device_ctx = g_vpr_ctx.device(); auto& route_ctx = g_vpr_ctx.routing(); @@ -547,6 +550,7 @@ static t_trace_branch traceback_branch(int node, std::unordered_set& trace_ t_trace* branch_head = alloc_trace_data(); t_trace* branch_tail = branch_head; branch_head->index = node; + branch_head->net_pin_index = target_net_pin_index; //The first node is the SINK node, so store its net pin index branch_head->iswitch = OPEN; branch_head->next = nullptr; @@ -561,6 +565,7 @@ static t_trace_branch traceback_branch(int node, std::unordered_set& trace_ //Add the current node to the head of traceback t_trace* prev_ptr = alloc_trace_data(); prev_ptr->index = inode; + prev_ptr->net_pin_index = OPEN; //Net pin index is invalid for Non-SINK nodes prev_ptr->iswitch = device_ctx.rr_nodes.edge_switch(iedge); prev_ptr->next = branch_head; branch_head = prev_ptr; @@ -741,11 +746,16 @@ void mark_ends(ClusterNetId net_id) { } } -void mark_remaining_ends(const std::vector& remaining_sinks) { +void mark_remaining_ends(ClusterNetId net_id, const std::vector& remaining_sinks) { // like mark_ends, but only performs it for the remaining sinks of a net + int inode; + auto& route_ctx = g_vpr_ctx.mutable_routing(); - for (int sink_node : remaining_sinks) - ++route_ctx.rr_node_route_inf[sink_node].target_flag; + + for (int sink_pin : remaining_sinks) { + inode = route_ctx.net_rr_terminals[net_id][sink_pin]; + ++route_ctx.rr_node_route_inf[inode].target_flag; + } } void drop_traceback_tail(ClusterNetId net_id) { @@ -1192,6 +1202,7 @@ alloc_trace_data() { trace_free_head->next = nullptr; } temp_ptr = trace_free_head; + temp_ptr->net_pin_index = OPEN; //default trace_free_head = trace_free_head->next; num_trace_allocated++; return (temp_ptr); @@ -1286,6 +1297,11 @@ void print_route(FILE* fp, const vtr::vector& traceba * used in the routing. */ fprintf(fp, "Switch: %d", tptr->iswitch); + //Save net pin index for sinks + if (rr_type == SINK) { + fprintf(fp, " Net_pin_index: %d", tptr->net_pin_index); + } + fprintf(fp, "\n"); tptr = tptr->next; diff --git a/vpr/src/route/route_common.h b/vpr/src/route/route_common.h index 003f10002d4..f8898616dcf 100644 --- a/vpr/src/route/route_common.h +++ b/vpr/src/route/route_common.h @@ -22,7 +22,10 @@ void pathfinder_update_acc_cost_and_overuse_info(float acc_fac, OveruseInfo& ove float update_pres_fac(float new_pres_fac); -t_trace* update_traceback(t_heap* hptr, ClusterNetId net_id); +/* Pass in the hptr starting at a SINK with target_net_pin_index, which is the net pin index corresonding * + * to the sink (ranging from 1 to fanout). Returns a pointer to the first "new" node in the traceback * + * (node not previously in trace). */ +t_trace* update_traceback(t_heap* hptr, int target_net_pin_index, ClusterNetId net_id); void reset_path_costs(const std::vector& visited_rr_nodes); @@ -85,7 +88,7 @@ inline float get_single_rr_cong_cost(int inode, float pres_fac) { } void mark_ends(ClusterNetId net_id); -void mark_remaining_ends(const std::vector& remaining_sinks); +void mark_remaining_ends(ClusterNetId net_id, const std::vector& remaining_sinks); void free_traceback(ClusterNetId net_id); void drop_traceback_tail(ClusterNetId net_id); diff --git a/vpr/src/route/route_timing.cpp b/vpr/src/route/route_timing.cpp index 26dba9e97dd..38f2b7fd50b 100644 --- a/vpr/src/route/route_timing.cpp +++ b/vpr/src/route/route_timing.cpp @@ -273,7 +273,6 @@ bool try_timing_driven_route_tmpl(const t_router_opts& router_opts, } CBRR connections_inf{}; - VTR_ASSERT_SAFE(connections_inf.sanity_check_lookup()); route_budgets budgeting_inf; @@ -1083,6 +1082,7 @@ bool timing_driven_route_net(ConnectionRouter& router, // route tree is not kept persistent since building it from the traceback the next iteration takes almost 0 time VTR_LOGV_DEBUG(f_router_debug, "Routed Net %zu (%zu sinks)\n", size_t(net_id), num_sinks); + free_route_tree(rt_root); return (true); } @@ -1144,9 +1144,14 @@ static bool timing_driven_pre_route_to_clock_root( * lets me reuse all the routines written for breadth-first routing, which * * all take a traceback structure as input. */ - t_trace* new_route_start_tptr = update_traceback(&cheapest, net_id); + /* This is a special pre-route to a sink that does not correspond to any * + * netlist pin, but which can be reached from the global clock root drive * + * points. Therefore, we can set the net pin index of the sink node to * + * OPEN (meaning illegal) as it is not meaningful for this sink. */ + + t_trace* new_route_start_tptr = update_traceback(&cheapest, OPEN, net_id); VTR_ASSERT_DEBUG(validate_traceback(route_ctx.trace[net_id].head)); - update_route_tree(&cheapest, ((high_fanout) ? &spatial_rt_lookup : nullptr)); + update_route_tree(&cheapest, OPEN, ((high_fanout) ? &spatial_rt_lookup : nullptr)); VTR_ASSERT_DEBUG(verify_route_tree(rt_root)); VTR_ASSERT_DEBUG(verify_traceback_route_tree_equivalent(route_ctx.trace[net_id].head, rt_root)); VTR_ASSERT_DEBUG(!high_fanout || validate_route_tree_spatial_lookup(rt_root, spatial_rt_lookup)); @@ -1196,7 +1201,6 @@ static bool timing_driven_route_sink( profiling::sink_criticality_start(); int sink_node = route_ctx.net_rr_terminals[net_id][target_pin]; - VTR_LOGV_DEBUG(f_router_debug, "Net %zu Target %d (%s)\n", size_t(net_id), itarget, describe_rr_node(sink_node).c_str()); VTR_ASSERT_DEBUG(verify_traceback_route_tree_equivalent(route_ctx.trace[net_id].head, rt_root)); @@ -1255,10 +1259,11 @@ static bool timing_driven_route_sink( int inode = cheapest.index; route_ctx.rr_node_route_inf[inode].target_flag--; /* Connected to this SINK. */ - t_trace* new_route_start_tptr = update_traceback(&cheapest, net_id); + t_trace* new_route_start_tptr = update_traceback(&cheapest, target_pin, net_id); + VTR_ASSERT_DEBUG(validate_traceback(route_ctx.trace[net_id].head)); - rt_node_of_sink[target_pin] = update_route_tree(&cheapest, ((high_fanout) ? &spatial_rt_lookup : nullptr)); + rt_node_of_sink[target_pin] = update_route_tree(&cheapest, target_pin, ((high_fanout) ? &spatial_rt_lookup : nullptr)); VTR_ASSERT_DEBUG(verify_route_tree(rt_root)); VTR_ASSERT_DEBUG(verify_traceback_route_tree_equivalent(route_ctx.trace[net_id].head, rt_root)); VTR_ASSERT_DEBUG(!high_fanout || validate_route_tree_spatial_lookup(rt_root, spatial_rt_lookup)); @@ -1379,7 +1384,9 @@ static t_rt_node* setup_routing_resources(int itry, add_route_tree_to_rr_node_lookup(rt_root); // give lookup on the reached sinks - connections_inf.put_sink_rt_nodes_in_net_pins_lookup(reached_rt_sinks, rt_node_of_sink); + for (t_rt_node* sink_node : reached_rt_sinks) { + rt_node_of_sink[sink_node->net_pin_index] = sink_node; + } profiling::net_rebuild_end(num_sinks, remaining_targets.size()); @@ -1388,11 +1395,8 @@ static t_rt_node* setup_routing_resources(int itry, // congestion should've been pruned away VTR_ASSERT_SAFE(is_uncongested_route_tree(rt_root)); - // use the nodes to directly mark ends before they get converted to pins - mark_remaining_ends(remaining_targets); - - // everything dealing with a net works with it in terms of its sink pins; need to convert its sink nodes to sink pins - connections_inf.convert_sink_nodes_to_net_pins(remaining_targets); + // mark remaining ends + mark_remaining_ends(net_id, remaining_targets); // still need to calculate the tree's time delay (0 Tarrival means from SOURCE) load_route_tree_Tdel(rt_root, 0); diff --git a/vpr/src/route/route_traceback.cpp b/vpr/src/route/route_traceback.cpp index 0a1badf8c10..7f4ad67f72b 100644 --- a/vpr/src/route/route_traceback.cpp +++ b/vpr/src/route/route_traceback.cpp @@ -8,10 +8,11 @@ t_traceback::t_traceback(const t_traceback& other) { //Deep-copy of traceback t_trace* prev = nullptr; for (t_trace* other_curr = other.head; other_curr; other_curr = other_curr->next) { - //VTR_LOG("Copying trace %p node: %d switch: %d\n", other_curr, other_curr->index, other_curr->iswitch); + //VTR_LOG("Copying trace %p node: %d switch: %d pin(for sink): %d\n", other_curr, other_curr->index, other_curr->iswitch. other_curr->net_pin_index); t_trace* curr = alloc_trace_data(); curr->index = other_curr->index; + curr->net_pin_index = other_curr->net_pin_index; curr->iswitch = other_curr->iswitch; if (prev) { diff --git a/vpr/src/route/route_tree_timing.cpp b/vpr/src/route/route_tree_timing.cpp index 5e98fba420f..a3121a63167 100644 --- a/vpr/src/route/route_tree_timing.cpp +++ b/vpr/src/route/route_tree_timing.cpp @@ -27,6 +27,13 @@ /* Array below allows mapping from any rr_node to any rt_node currently in * the rt_tree. */ +/* In some cases the same SINK node is put into the tree multiple times in a * + * single route. To model this, we are putting in separate rt_nodes in the route * + * tree if we go to the same SINK more than once. rr_node_to_rt_node[inode] will * + * therefore store the last rt_node created of all the SINK nodes with the same * + * index "inode". This is okay because the mapping is only used in this file to * + * quickly figure out where rt_nodes that we are branching off of (for nets with * + * fanout > 1) are, and we will never branch off a SINK. */ static std::vector rr_node_to_rt_node; /* [0..device_ctx.rr_nodes.size()-1] */ /* Frees lists for fast addition and deletion of nodes and edges. */ @@ -45,6 +52,7 @@ static t_linked_rt_edge* alloc_linked_rt_edge(); static void free_linked_rt_edge(t_linked_rt_edge* rt_edge); static t_rt_node* add_subtree_to_route_tree(t_heap* hptr, + int target_net_pin_index, t_rt_node** sink_rt_node_ptr); static t_rt_node* add_non_configurable_to_route_tree(const int rr_node, const bool reached_by_non_configurable_edge, std::unordered_set& visited); @@ -59,7 +67,7 @@ static t_trace* traceback_to_route_tree_branch(t_trace* trace, std::map traceback_from_route_tree_recurr(t_trace* head, t_trace* tail, const t_rt_node* node); -void collect_route_tree_connections(const t_rt_node* node, std::set>& connections); +void collect_route_tree_connections(const t_rt_node* node, std::multiset>& connections); /************************** Subroutine definitions ***************************/ @@ -187,6 +195,7 @@ t_rt_node* init_route_tree_to_source(ClusterNetId inet) { inode = route_ctx.net_rr_terminals[inet][0]; /* Net source */ rt_root->inode = inode; + rt_root->net_pin_index = OPEN; rt_root->C_downstream = device_ctx.rr_nodes[inode].C(); rt_root->R_upstream = device_ctx.rr_nodes[inode].R(); rt_root->Tdel = 0.5 * device_ctx.rr_nodes[inode].R() * device_ctx.rr_nodes[inode].C(); @@ -197,9 +206,10 @@ t_rt_node* init_route_tree_to_source(ClusterNetId inet) { /* Adds the most recently finished wire segment to the routing tree, and * updates the Tdel, etc. numbers for the rest of the routing tree. hptr - * is the heap pointer of the SINK that was reached. This routine returns - * a pointer to the rt_node of the SINK that it adds to the routing. */ -t_rt_node* update_route_tree(t_heap* hptr, SpatialRouteTreeLookup* spatial_rt_lookup) { + * is the heap pointer of the SINK that was reached, and target_net_pin_index + * is the net pin index corresponding to the SINK that was reached. This routine + * returns a pointer to the rt_node of the SINK that it adds to the routing. */ +t_rt_node* update_route_tree(t_heap* hptr, int target_net_pin_index, SpatialRouteTreeLookup* spatial_rt_lookup) { t_rt_node *start_of_new_subtree_rt_node, *sink_rt_node; t_rt_node *unbuffered_subtree_rt_root, *subtree_parent_rt_node; float Tdel_start; @@ -208,7 +218,7 @@ t_rt_node* update_route_tree(t_heap* hptr, SpatialRouteTreeLookup* spatial_rt_lo auto& device_ctx = g_vpr_ctx.device(); //Create a new subtree from the target in hptr to existing routing - start_of_new_subtree_rt_node = add_subtree_to_route_tree(hptr, &sink_rt_node); + start_of_new_subtree_rt_node = add_subtree_to_route_tree(hptr, target_net_pin_index, &sink_rt_node); //Propagate R_upstream down into the new subtree load_new_subtree_R_upstream(start_of_new_subtree_rt_node); @@ -239,9 +249,26 @@ t_rt_node* update_route_tree(t_heap* hptr, SpatialRouteTreeLookup* spatial_rt_lo return (sink_rt_node); } +/* Records all nodes from the current routing (rt_tree) into the rr_node_to_rt_node + * lookup, which maps the node's corresponding rr_node index (inode) to the node + * itself. This is done recursively, starting from the root of the tree to its leafs + * (SINKs) in a depth-first traversal. The rt_node we are currently processing has + * either not been added to the routing for this net before or if it was added, the + * rr_node_to_rt_node mapping structure should point back at the rt_node itself so + * we are just branching off that point. Exceptions are the SINK nodes, some + * netlists and input pin equivalence can lead to us routing to the same SINK more + * than once on a net (resulting in different rt_nodes sharing the same rr_node index). + * Hence for SINKs we assert on a weaker condition that if this SINK is already in the + * rt_tree, the rr_node_to_rt_node mapping structure points to a legal rt_node (but + * not necessarily the only one) containing the SINK */ void add_route_tree_to_rr_node_lookup(t_rt_node* node) { if (node) { - VTR_ASSERT(rr_node_to_rt_node[node->inode] == nullptr || rr_node_to_rt_node[node->inode] == node); + auto& device_ctx = g_vpr_ctx.device(); + if (device_ctx.rr_nodes[node->inode].type() == SINK) { + VTR_ASSERT(rr_node_to_rt_node[node->inode] == nullptr || rr_node_to_rt_node[node->inode]->inode == node->inode); + } else { + VTR_ASSERT(rr_node_to_rt_node[node->inode] == nullptr || rr_node_to_rt_node[node->inode] == node); + } rr_node_to_rt_node[node->inode] = node; @@ -251,12 +278,12 @@ void add_route_tree_to_rr_node_lookup(t_rt_node* node) { } } +/* Adds the most recent wire segment, ending at the SINK indicated by hptr, + * to the routing tree. target_net_pin_index is the net pin index correspinding + * to the SINK indicated by hptr. Returns the first (most upstream) new rt_node, + * and (via a pointer) the rt_node of the new SINK. Traverses up from SINK */ static t_rt_node* -add_subtree_to_route_tree(t_heap* hptr, t_rt_node** sink_rt_node_ptr) { - /* Adds the most recent wire segment, ending at the SINK indicated by hptr, - * to the routing tree. It returns the first (most upstream) new rt_node, - * and (via a pointer) the rt_node of the new SINK. Traverses up from SINK */ - +add_subtree_to_route_tree(t_heap* hptr, int target_net_pin_index, t_rt_node** sink_rt_node_ptr) { t_rt_node *rt_node, *downstream_rt_node, *sink_rt_node; t_linked_rt_edge* linked_rt_edge; @@ -274,6 +301,7 @@ add_subtree_to_route_tree(t_heap* hptr, t_rt_node** sink_rt_node_ptr) { sink_rt_node = alloc_rt_node(); sink_rt_node->u.child_list = nullptr; sink_rt_node->inode = inode; + sink_rt_node->net_pin_index = target_net_pin_index; //hptr is the heap pointer of the SINK that was reached, which corresponds to the target pin rr_node_to_rt_node[inode] = sink_rt_node; /* In the code below I'm marking SINKs and IPINs as not to be re-expanded. @@ -286,8 +314,8 @@ add_subtree_to_route_tree(t_heap* hptr, t_rt_node** sink_rt_node_ptr) { downstream_rt_node = sink_rt_node; - std::unordered_set main_branch_visited; - std::unordered_set all_visited; + std::unordered_set main_branch_visited; //does not include sink + std::unordered_set all_visited; //does not include sink inode = hptr->prev_node(); RREdgeId edge = hptr->prev_edge(); short iswitch = device_ctx.rr_nodes.edge_switch(edge); @@ -316,6 +344,7 @@ add_subtree_to_route_tree(t_heap* hptr, t_rt_node** sink_rt_node_ptr) { rt_node->u.child_list = linked_rt_edge; rt_node->inode = inode; + rt_node->net_pin_index = OPEN; //net pin index is invalid for non-SINK nodes rr_node_to_rt_node[inode] = rt_node; @@ -347,6 +376,7 @@ add_subtree_to_route_tree(t_heap* hptr, t_rt_node** sink_rt_node_ptr) { //Expand (recursively) each of the main-branch nodes adding any //non-configurably connected nodes + //Sink is not included, so no need to pass in the node's ipin value. for (int rr_node : main_branch_visited) { add_non_configurable_to_route_tree(rr_node, false, all_visited); } @@ -374,6 +404,7 @@ static t_rt_node* add_non_configurable_to_route_tree(const int rr_node, const bo rt_node = alloc_rt_node(); rt_node->u.child_list = nullptr; rt_node->inode = rr_node; + rt_node->net_pin_index = OPEN; if (device_ctx.rr_nodes[rr_node].type() == IPIN) { rt_node->re_expand = false; @@ -657,7 +688,8 @@ void print_route_tree(const t_rt_node* rt_node, int depth) { } auto& device_ctx = g_vpr_ctx.device(); - VTR_LOG("%srt_node: %d (%s)", indent.c_str(), rt_node->inode, device_ctx.rr_nodes[rt_node->inode].type_string()); + VTR_LOG("%srt_node: %d (%s) \t ipin: %d \t R: %g \t C: %g \t delay: %g", + indent.c_str(), rt_node->inode, device_ctx.rr_nodes[rt_node->inode].type_string(), rt_node->net_pin_index, rt_node->R_upstream, rt_node->C_downstream, rt_node->Tdel); if (rt_node->parent_switch != OPEN) { bool parent_edge_configurable = device_ctx.rr_switch_inf[rt_node->parent_switch].configurable(); @@ -760,22 +792,28 @@ static t_trace* traceback_to_route_tree_branch(t_trace* trace, t_rt_node* node = nullptr; int inode = trace->index; + int ipin = trace->net_pin_index; int iswitch = trace->iswitch; + auto& device_ctx = g_vpr_ctx.device(); auto itr = rr_node_to_rt.find(trace->index); - if (itr == rr_node_to_rt.end()) { + + // In some cases, the same sink node is put into the tree multiple times in a single route. + // So it is possible to hit the same node index multiple times during traceback. Create a + // separate rt_node for each sink with the same node index. + if (itr == rr_node_to_rt.end() || device_ctx.rr_nodes[inode].type() == SINK) { //Create //Initialize route tree node node = alloc_rt_node(); node->inode = inode; + node->net_pin_index = ipin; node->u.child_list = nullptr; node->R_upstream = std::numeric_limits::quiet_NaN(); node->C_downstream = std::numeric_limits::quiet_NaN(); node->Tdel = std::numeric_limits::quiet_NaN(); - auto& device_ctx = g_vpr_ctx.device(); auto node_type = device_ctx.rr_nodes[inode].type(); if (node_type == IPIN || node_type == SINK) node->re_expand = false; @@ -808,7 +846,6 @@ static t_trace* traceback_to_route_tree_branch(t_trace* trace, // // Each configurable edges from the non-configurable set is a // usage of the set. - auto& device_ctx = g_vpr_ctx.device(); auto set_itr = device_ctx.rr_node_to_non_config_node_set.find(inode); if (non_config_node_set_usage != nullptr && set_itr != device_ctx.rr_node_to_non_config_node_set.end()) { if (device_ctx.rr_switch_inf[iswitch].configurable()) { @@ -853,6 +890,7 @@ static std::pair traceback_from_route_tree_recurr(t_trace* h for (t_linked_rt_edge* edge = node->u.child_list; edge != nullptr; edge = edge->next) { t_trace* curr = alloc_trace_data(); curr->index = node->inode; + curr->net_pin_index = node->net_pin_index; curr->iswitch = edge->iswitch; curr->next = nullptr; @@ -873,6 +911,7 @@ static std::pair traceback_from_route_tree_recurr(t_trace* h //Leaf t_trace* curr = alloc_trace_data(); curr->index = node->inode; + curr->net_pin_index = node->net_pin_index; curr->iswitch = OPEN; curr->next = nullptr; @@ -1010,7 +1049,7 @@ static t_rt_node* prune_route_tree_recurr(t_rt_node* node, CBRR& connections_inf VTR_ASSERT(force_prune); //Record as not reached - connections_inf.toreach_rr_sink(node->inode); + connections_inf.toreach_rr_sink(node->net_pin_index); free_rt_node(node); return nullptr; //Pruned @@ -1431,6 +1470,7 @@ init_route_tree_to_source_no_net(int inode) { rt_root->parent_switch = OPEN; rt_root->re_expand = true; rt_root->inode = inode; + rt_root->net_pin_index = OPEN; rt_root->C_downstream = device_ctx.rr_nodes[inode].C(); rt_root->R_upstream = device_ctx.rr_nodes[inode].R(); rt_root->Tdel = 0.5 * device_ctx.rr_nodes[inode].R() * device_ctx.rr_nodes[inode].C(); @@ -1441,7 +1481,7 @@ init_route_tree_to_source_no_net(int inode) { bool verify_traceback_route_tree_equivalent(const t_trace* head, const t_rt_node* rt_root) { //Walk the route tree saving all the used connections - std::set> route_tree_connections; + std::multiset> route_tree_connections; collect_route_tree_connections(rt_root, route_tree_connections); //Remove the extra parent connection to root (not included in traceback) @@ -1461,7 +1501,7 @@ bool verify_traceback_route_tree_equivalent(const t_trace* head, const t_rt_node VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Route tree missing traceback connection: node %d -> %d (switch %d)\n", prev_node, to_node, prev_switch); } else { - route_tree_connections.erase(conn); //Remove found connections + route_tree_connections.erase(route_tree_connections.lower_bound(conn)); //Remove the first found connections } } @@ -1482,7 +1522,7 @@ bool verify_traceback_route_tree_equivalent(const t_trace* head, const t_rt_node return true; } -void collect_route_tree_connections(const t_rt_node* node, std::set>& connections) { +void collect_route_tree_connections(const t_rt_node* node, std::multiset>& connections) { if (node) { //Record reaching connection int prev_node = OPEN; diff --git a/vpr/src/route/route_tree_timing.h b/vpr/src/route/route_tree_timing.h index ac10b52459b..b6ca587d86b 100644 --- a/vpr/src/route/route_tree_timing.h +++ b/vpr/src/route/route_tree_timing.h @@ -19,7 +19,7 @@ void free_route_tree(t_rt_node* rt_node); void print_route_tree(const t_rt_node* rt_node); void print_route_tree(const t_rt_node* rt_node, int depth); -t_rt_node* update_route_tree(t_heap* hptr, SpatialRouteTreeLookup* spatial_rt_lookup); +t_rt_node* update_route_tree(t_heap* hptr, int target_net_pin_index, SpatialRouteTreeLookup* spatial_rt_lookup); void update_net_delays_from_route_tree(float* net_delay, const t_rt_node* const* rt_node_of_sink, diff --git a/vpr/src/route/route_tree_type.h b/vpr/src/route/route_tree_type.h index 7d663aafb9f..edf2cebb393 100644 --- a/vpr/src/route/route_tree_type.h +++ b/vpr/src/route/route_tree_type.h @@ -27,6 +27,16 @@ struct t_linked_rt_edge { * parent_switch: Index of the switch type driving this node (by its * * parent). * * inode: index (ID) of the rr_node that corresponds to this rt_node. * + * net_pin_index: Net pin index associated with the rt_node. This value + * ranges from 1 to fanout [1..num_pins-1]. For cases when * + * different speed paths are taken to the same SINK for * + * different pins, inode cannot uniquely identify each SINK, * + * so the net pin index guarantees an unique identification * + * for each SINK rt_node. For non-SINK nodes and for SINK * + * nodes with no associated net pin index, (i.e. special * + * SINKs like the source of a clock tree which do not * + * correspond to an actual netlist connection), the value * + * for this member should be set to OPEN (-1). * * C_downstream: Total downstream capacitance from this rt_node. That is, * * the total C of the subtree rooted at the current node, * * including the C of the current node. * @@ -43,6 +53,7 @@ struct t_rt_node { short parent_switch; bool re_expand; int inode; + int net_pin_index; float C_downstream; float R_upstream; float Tdel; diff --git a/vpr/src/route/router_delay_profiling.cpp b/vpr/src/route/router_delay_profiling.cpp index 008aa46b7de..79307342a40 100644 --- a/vpr/src/route/router_delay_profiling.cpp +++ b/vpr/src/route/router_delay_profiling.cpp @@ -74,7 +74,7 @@ bool RouterDelayProfiler::calculate_delay(int source_node, int sink_node, const if (found_path) { VTR_ASSERT(cheapest.index == sink_node); - t_rt_node* rt_node_of_sink = update_route_tree(&cheapest, nullptr); + t_rt_node* rt_node_of_sink = update_route_tree(&cheapest, OPEN, nullptr); //find delay *net_delay = rt_node_of_sink->Tdel; @@ -143,7 +143,7 @@ std::vector calculate_all_path_delays_from_rr_node(int src_rr_node, const //Build the routing tree to get the delay rt_root = setup_routing_resources_no_net(src_rr_node); - t_rt_node* rt_node_of_sink = update_route_tree(&shortest_paths[sink_rr_node], nullptr); + t_rt_node* rt_node_of_sink = update_route_tree(&shortest_paths[sink_rr_node], OPEN, nullptr); VTR_ASSERT(rt_node_of_sink->inode == sink_rr_node); diff --git a/vpr/src/timing/net_delay.cpp b/vpr/src/timing/net_delay.cpp index c97a1a88325..6bf3f9bbe0d 100644 --- a/vpr/src/timing/net_delay.cpp +++ b/vpr/src/timing/net_delay.cpp @@ -24,12 +24,13 @@ /********************** Variables local to this module ***********************/ -/* Unordered map below stores the pair whose key is the index of the rr_node * - * that corresponds to the rt_node, and whose value is the time delay * - * associated with that node. The map will be used to store delays while * - * traversing the nodes of the route tree in load_one_net_delay_recurr. */ +/* Unordered map below stores the pair whose key is the pin index (ranging * + * from 1 to net fan-out) that corresponds to the rt_node, and whose value * + * is the time delay associated with that node. The map will be used to * + * store delays while traversing the nodes of the route tree in * + * load_one_net_delay_recurr. */ -static std::unordered_map inode_to_Tdel_map; +static std::unordered_map ipin_to_Tdel_map; /*********************** Subroutines local to this module ********************/ @@ -63,11 +64,10 @@ static void load_one_net_delay(ClbNetPinsMatrix& net_delay, ClusterNetId * net_delay[net_id][1..num_pins-1]. First, from the traceback, it * * constructs the route tree and computes its values for R, C, and Tdel. * * Next, it walks the route tree recursively, storing the time delays for * - * each node into the map inode_to_Tdel. Then, while looping through the * - * net_delay array we search for the inode corresponding to the pin * - * identifiers, and correspondingly update the entry in net_delay. * - * Finally, it frees the route tree and clears the inode_to_Tdel_map * - * associated with that net. */ + * each sink into the map ipin_to_Tdel. Then, while looping through the * + * net_delay array we search for the pin index in the map, and * + * correspondingly update the entry in net_delay. Finally, it frees the * + * route tree and clears the ipin_to_Tdel_map associated with that net. */ auto& route_ctx = g_vpr_ctx.routing(); @@ -77,29 +77,29 @@ static void load_one_net_delay(ClbNetPinsMatrix& net_delay, ClusterNetId } auto& cluster_ctx = g_vpr_ctx.clustering(); - int inode; t_rt_node* rt_root = traceback_to_route_tree(net_id); // obtain the root of the tree constructed from the traceback load_new_subtree_R_upstream(rt_root); // load in the resistance values for the route tree load_new_subtree_C_downstream(rt_root); // load in the capacitance values for the route tree load_route_tree_Tdel(rt_root, 0.); // load the time delay values for the route tree - load_one_net_delay_recurr(rt_root, net_id); // recursively traverse the tree and load entries into the inode_to_Tdel map + load_one_net_delay_recurr(rt_root, net_id); // recursively traverse the tree and load entries into the ipin_to_Tdel map for (unsigned int ipin = 1; ipin < cluster_ctx.clb_nlist.net_pins(net_id).size(); ipin++) { - inode = route_ctx.net_rr_terminals[net_id][ipin]; // look for the index of the rr node that corresponds to the sink that was used to route a certain connection. - auto itr = inode_to_Tdel_map.find(inode); - VTR_ASSERT(itr != inode_to_Tdel_map.end()); + auto itr = ipin_to_Tdel_map.find(ipin); + VTR_ASSERT(itr != ipin_to_Tdel_map.end()); - net_delay[net_id][ipin] = itr->second; // search for the value of Tdel in the inode map and load into net_delay + net_delay[net_id][ipin] = itr->second; // search for the value of Tdel in the ipin map and load into net_delay } - free_route_tree(rt_root); // free the route tree - inode_to_Tdel_map.clear(); // clear the map + free_route_tree(rt_root); // free the route tree + ipin_to_Tdel_map.clear(); // clear the map } static void load_one_net_delay_recurr(t_rt_node* node, ClusterNetId net_id) { - /* This routine recursively traverses the route tree, and copies the Tdel of the node into the map. */ - - inode_to_Tdel_map[node->inode] = node->Tdel; // add to the map, process current node + /* This routine recursively traverses the route tree, and copies the Tdel of the sink_type nodes * + * into the map. */ + if (node->net_pin_index != OPEN) { // value of OPEN indicates a non-SINK + ipin_to_Tdel_map[node->net_pin_index] = node->Tdel; // add to the map, process current sink-type node + } for (t_linked_rt_edge* edge = node->u.child_list; edge != nullptr; edge = edge->next) { // process children load_one_net_delay_recurr(edge->child, net_id);