blob: 39be7cd0c359af8ea6b8f0f8d2231265610dedf3 [file] [log] [blame]
#include "VprTimingGraphResolver.h"
#include "atom_netlist.h"
#include "atom_lookup.h"
VprTimingGraphResolver::VprTimingGraphResolver(const AtomNetlist& netlist, const AtomLookup& netlist_lookup, const tatum::TimingGraph& timing_graph, const AnalysisDelayCalculator& delay_calc)
: netlist_(netlist)
, netlist_lookup_(netlist_lookup)
, timing_graph_(timing_graph)
, delay_calc_(delay_calc) {}
std::string VprTimingGraphResolver::node_name(tatum::NodeId node) const {
AtomPinId pin = netlist_lookup_.tnode_atom_pin(node);
return netlist_.pin_name(pin);
}
std::string VprTimingGraphResolver::node_type_name(tatum::NodeId node) const {
AtomPinId pin = netlist_lookup_.tnode_atom_pin(node);
AtomBlockId blk = netlist_.pin_block(pin);
std::string name = netlist_.block_model(blk)->name;
if (detail_level() == e_timing_report_detail::AGGREGATED || detail_level() == e_timing_report_detail::DETAILED_ROUTING) {
//Detailed report consist of the aggregated reported with a breakdown of inter-block routing
//Annotate primitive grid location, if known
auto& atom_ctx = g_vpr_ctx.atom();
auto& place_ctx = g_vpr_ctx.placement();
ClusterBlockId cb = atom_ctx.lookup.atom_clb(blk);
if (cb && place_ctx.block_locs.count(cb)) {
int x = place_ctx.block_locs[cb].loc.x;
int y = place_ctx.block_locs[cb].loc.y;
name += " at (" + std::to_string(x) + "," + std::to_string(y) + ")";
}
}
return name;
}
tatum::EdgeDelayBreakdown VprTimingGraphResolver::edge_delay_breakdown(tatum::EdgeId edge, tatum::DelayType tatum_delay_type) const {
tatum::EdgeDelayBreakdown delay_breakdown;
if (edge && (detail_level() == e_timing_report_detail::AGGREGATED || detail_level() == e_timing_report_detail::DETAILED_ROUTING)) {
auto edge_type = timing_graph_.edge_type(edge);
DelayType delay_type; //TODO: should unify vpr/tatum DelayType
if (tatum_delay_type == tatum::DelayType::MAX) {
delay_type = DelayType::MAX;
} else {
VTR_ASSERT(tatum_delay_type == tatum::DelayType::MIN);
delay_type = DelayType::MIN;
}
if (edge_type == tatum::EdgeType::INTERCONNECT) {
delay_breakdown.components = interconnect_delay_breakdown(edge, delay_type);
} else {
//Primtiive edge
//
tatum::DelayComponent component;
tatum::NodeId node = timing_graph_.edge_sink_node(edge);
AtomPinId atom_pin = netlist_lookup_.tnode_atom_pin(node);
AtomBlockId atom_blk = netlist_.pin_block(atom_pin);
//component.inst_name = netlist_.block_name(atom_blk);
component.type_name = "primitive '";
component.type_name += netlist_.block_model(atom_blk)->name;
component.type_name += "'";
if (edge_type == tatum::EdgeType::PRIMITIVE_COMBINATIONAL) {
component.type_name += " combinational delay";
if (delay_type == DelayType::MAX) {
component.delay = delay_calc_.max_edge_delay(timing_graph_, edge);
} else {
VTR_ASSERT(delay_type == DelayType::MIN);
component.delay = delay_calc_.min_edge_delay(timing_graph_, edge);
}
} else if (edge_type == tatum::EdgeType::PRIMITIVE_CLOCK_LAUNCH) {
if (delay_type == DelayType::MAX) {
component.type_name += " Tcq_max";
component.delay = delay_calc_.max_edge_delay(timing_graph_, edge);
} else {
VTR_ASSERT(delay_type == DelayType::MIN);
component.type_name += " Tcq_min";
component.delay = delay_calc_.min_edge_delay(timing_graph_, edge);
}
} else {
VTR_ASSERT(edge_type == tatum::EdgeType::PRIMITIVE_CLOCK_CAPTURE);
if (delay_type == DelayType::MAX) {
component.type_name += " Tsu";
component.delay = delay_calc_.setup_time(timing_graph_, edge);
} else {
component.type_name += " Thld";
component.delay = delay_calc_.hold_time(timing_graph_, edge);
}
}
delay_breakdown.components.push_back(component);
}
}
return delay_breakdown;
}
std::vector<tatum::DelayComponent> VprTimingGraphResolver::interconnect_delay_breakdown(tatum::EdgeId edge, DelayType delay_type) const {
VTR_ASSERT(timing_graph_.edge_type(edge) == tatum::EdgeType::INTERCONNECT);
auto& cluster_ctx = g_vpr_ctx.clustering();
auto& route_ctx = g_vpr_ctx.routing();
std::vector<tatum::DelayComponent> components;
//We assume that the delay calculator has already cached all of the relevant delays,
//we just retrieve the cached values. This assumption greatly simplifies the calculation
//process and avoids us duplicating the complex delay calculation logic from the delay
//calcualtor.
//
//However note that this does couple this code tightly with the delay calculator implementation.
//Force delay calculation to ensure results are cached (redundant if already up-to-date)
delay_calc_.atom_net_delay(timing_graph_, edge, delay_type);
ClusterPinId src_pin = ClusterPinId::INVALID();
ClusterPinId sink_pin = ClusterPinId::INVALID();
std::tie(src_pin, sink_pin) = delay_calc_.get_cached_pins(edge, delay_type);
if (!src_pin && !sink_pin) {
//Cluster internal
tatum::NodeId node = timing_graph_.edge_sink_node(edge);
AtomPinId atom_pin = netlist_lookup_.tnode_atom_pin(node);
AtomBlockId atom_blk = netlist_.pin_block(atom_pin);
ClusterBlockId clb_blk = netlist_lookup_.atom_clb(atom_blk);
tatum::DelayComponent internal_component;
//internal_component.inst_name = cluster_ctx.clb_nlist.block_name(clb_blk);
internal_component.type_name = "intra '";
internal_component.type_name += cluster_ctx.clb_nlist.block_type(clb_blk)->name;
internal_component.type_name += "' routing";
internal_component.delay = delay_calc_.get_cached_delay(edge, delay_type);
components.push_back(internal_component);
} else {
//Cluster external
VTR_ASSERT(src_pin);
VTR_ASSERT(sink_pin);
ClusterBlockId src_blk = cluster_ctx.clb_nlist.pin_block(src_pin);
ClusterBlockId sink_blk = cluster_ctx.clb_nlist.pin_block(sink_pin);
tatum::Time driver_clb_delay = delay_calc_.get_driver_clb_cached_delay(edge, delay_type);
tatum::Time sink_clb_delay = delay_calc_.get_sink_clb_cached_delay(edge, delay_type);
ClusterNetId src_net = cluster_ctx.clb_nlist.pin_net(src_pin);
VTR_ASSERT(src_net == cluster_ctx.clb_nlist.pin_net(sink_pin));
tatum::Time net_delay = tatum::Time(delay_calc_.inter_cluster_delay(src_net,
0,
cluster_ctx.clb_nlist.pin_net_index(sink_pin)));
VTR_ASSERT(driver_clb_delay.valid());
VTR_ASSERT(net_delay.valid());
VTR_ASSERT(sink_clb_delay.valid());
tatum::DelayComponent driver_component;
//driver_component.inst_name = cluster_ctx.clb_nlist.block_name(src_blk);
driver_component.type_name = "intra '";
driver_component.type_name += cluster_ctx.clb_nlist.block_type(src_blk)->name;
driver_component.type_name += "' routing";
driver_component.delay = driver_clb_delay;
components.push_back(driver_component);
// For detailed timing report, we would like to breakdown the inter-block routing into
// its constituent net components. As a pre-requisite, we would like to ensure these conditions:
// 1. detailed timing report is selected
// 2. the vector of tracebacks are not empty (important because this module is also called during
// placement, which occurs before routing and implies and empty vector of tracebacks).
// 3. the traceback is not a nullptr, for global routing, there are null tracebacks representing
// unrouted nets.
tatum::DelayComponent interblock_component;
interblock_component.type_name = "inter-block routing";
interblock_component.delay = net_delay;
if (detail_level() == e_timing_report_detail::DETAILED_ROUTING && !route_ctx.trace.empty()) {
//check if detailed timing report has been selected and that the vector of tracebacks
//is not empty.
if (route_ctx.trace[src_net].head != nullptr) {
//the traceback is not a nullptr, so we find the path of the net from source to sink.
//Note that the previously declared interblock_component will not be added to the
//vector of net components.
get_detailed_interconnect_components(components, src_net, sink_pin);
} else {
//the traceback is a nullptr which means this is an unrouted net as part of global routing.
//We add the tag global net, and push the interblock into the vector of net components.
interblock_component.type_name += ":global net";
components.push_back(interblock_component);
}
} else {
//for aggregated and netlist modes, we simply add the previously declared interblock_component
//to the vector.
components.push_back(interblock_component);
}
tatum::DelayComponent sink_component;
//sink_component.inst_name = cluster_ctx.clb_nlist.block_name(sink_blk);
sink_component.type_name = "intra '";
sink_component.type_name += cluster_ctx.clb_nlist.block_type(sink_blk)->name;
sink_component.type_name += "' routing";
sink_component.delay = sink_clb_delay;
components.push_back(sink_component);
}
return components;
}
e_timing_report_detail VprTimingGraphResolver::detail_level() const {
return detail_level_;
}
void VprTimingGraphResolver::set_detail_level(e_timing_report_detail report_detail) {
detail_level_ = report_detail;
}
void VprTimingGraphResolver::get_detailed_interconnect_components(std::vector<tatum::DelayComponent>& components, ClusterNetId net_id, ClusterPinId sink_pin) const {
/* This routine obtains the interconnect components such as: OPIN, CHANX, CHANY, IPIN which join
* two intra-block clusters in two parts. In part one, we construct the route tree
* from the traceback and computes its value for R, C, and Tdel. Next, we find the pointer to
* the route tree sink which corresponds to the sink_pin. In part two, we call the helper function,
* which walks the route tree from the sink to the source. Along the way, we process each node
* and construct net_components that are added to the vector of components. */
t_rt_node* rt_root = traceback_to_route_tree(net_id); //obtain the route tree from the traceback
load_new_subtree_R_upstream(rt_root); //load in the resistance values for the route
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
t_rt_node* rt_sink = find_sink_rt_node(rt_root, net_id, sink_pin); //find the sink matching sink_pin
get_detailed_interconnect_components_helper(components, rt_sink); //from sink, walk up to source and add net components
free_route_tree(rt_root);
}
void VprTimingGraphResolver::get_detailed_interconnect_components_helper(std::vector<tatum::DelayComponent>& components, t_rt_node* node) const {
/* This routine comprieses the second part of obtaining the interconnect components.
* We begin at the sink node and travel up the tree towards the source. For each node, we would
* like to retreive information such as the routing resource type, index, and incremental delay.
* If the type is a channel, then we retrieve the name of the segment as well as the coordinates
* of its start and endpoint. All of this information is stored in the object DelayComponent,
* which belong to the vector "components".
*/
auto& device_ctx = g_vpr_ctx.device();
//Declare a separate vector "interconnect_components" to hold the interconnect components. We need
//this because we walk from the sink to the source, we will need to add elements to the front of
//interconnect_components so that we maintain the correct order of net components.
//Illustration:
// INTRA
// |
// IPIN <-end
// |
// CHANX
// |
// OPIN <-start
// |
// INTRA - not seen yet
//Pushing a stack steps:
//1. OPIN, 2. CHANX, OPIN 3. IPIN, CHANX, OPIN (order is preserved)
//At this point of the code, the vector "components" contains intrablock components.
//At the end of the module, we will append "interconnect_components" to the end of vector "components".
std::vector<tatum::DelayComponent> interconnect_components;
std::string start_x; //start x-coordinate
std::string start_y; //start y-coordinate
std::string end_x; //end x-coordinate
std::string end_y; //end y-coordinate
std::string arrow; //direction arrow
while (node != nullptr) {
//Process the current interconnect component if it is of type OPIN, CHANX, CHANY, IPIN
if (device_ctx.rr_nodes[node->inode].type() == OPIN
|| device_ctx.rr_nodes[node->inode].type() == IPIN
|| device_ctx.rr_nodes[node->inode].type() == CHANX
|| device_ctx.rr_nodes[node->inode].type() == CHANY) {
tatum::DelayComponent net_component; //declare a new instance of DelayComponent
net_component.type_name = device_ctx.rr_nodes[node->inode].type_string(); //write the component's type as a routing resource node
net_component.type_name += ":" + std::to_string(node->inode) + " "; //add the index of the routing resource node
if (device_ctx.rr_nodes[node->inode].type() == OPIN || device_ctx.rr_nodes[node->inode].type() == IPIN) {
net_component.type_name += "side:"; //add the side of the routing resource node
net_component.type_name += device_ctx.rr_nodes[node->inode].side_string(); //add the side of the routing resource node
// For OPINs and IPINs the starting and ending coordinate are identical, so we can just arbitrarily assign the start to larger values
// and the end to the lower coordinate
start_x = " (" + std::to_string(device_ctx.rr_nodes[node->inode].xhigh()) + ","; //start and end coordinates are the same for OPINs and IPINs
start_y = std::to_string(device_ctx.rr_nodes[node->inode].yhigh()) + ")";
end_x = "";
end_y = "";
arrow = "";
}
if (device_ctx.rr_nodes[node->inode].type() == CHANX || device_ctx.rr_nodes[node->inode].type() == CHANY) { //for channels, we would like to describe the component with segment specific information
net_component.type_name += device_ctx.arch->Segments[device_ctx.rr_indexed_data[device_ctx.rr_nodes[node->inode].cost_index()].seg_index].name; //Write the segment name
net_component.type_name += " length:" + std::to_string(device_ctx.rr_nodes[node->inode].length()); //add the length of the segment
//Figure out the starting and ending coordinate of the segment depending on the direction
arrow = "->"; //we will point the coordinates from start to finish, left to right
if (device_ctx.rr_nodes[node->inode].direction() == DEC_DIRECTION) { //signal travels along decreasing direction
start_x = " (" + std::to_string(device_ctx.rr_nodes[node->inode].xhigh()) + ","; //start coordinates have large value
start_y = std::to_string(device_ctx.rr_nodes[node->inode].yhigh()) + ")";
end_x = "(" + std::to_string(device_ctx.rr_nodes[node->inode].xlow()) + ","; //end coordinates have smaller value
end_y = std::to_string(device_ctx.rr_nodes[node->inode].ylow()) + ")";
}
else { // signal travels in increasing direction, stays at same point, or can travel both directions
start_x = " (" + std::to_string(device_ctx.rr_nodes[node->inode].xlow()) + ","; //start coordinates have smaller value
start_y = std::to_string(device_ctx.rr_nodes[node->inode].ylow()) + ")";
end_x = "(" + std::to_string(device_ctx.rr_nodes[node->inode].xhigh()) + ","; //end coordinates have larger value
end_y = std::to_string(device_ctx.rr_nodes[node->inode].yhigh()) + ")";
if (device_ctx.rr_nodes[node->inode].direction() == BI_DIRECTION) {
arrow = "<->"; //indicate that signal can travel both direction
}
}
}
net_component.type_name += start_x + start_y; //Write the starting coordinates
net_component.type_name += arrow; //Indicate the direction
net_component.type_name += end_x + end_y; //Write the end coordiates
net_component.delay = tatum::Time(node->Tdel - node->parent_node->Tdel); // add the incremental delay
interconnect_components.push_back(net_component); //insert net_component into the front of vector interconnect_component
}
node = node->parent_node; //travel up the tree through the parent
}
components.insert(components.end(), interconnect_components.rbegin(), interconnect_components.rend()); //append the completed "interconnect_component" to "component_vector"
}