Skip to content

[Analysis] Printing Nets timing Information #3023

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 34 commits into from
May 16, 2025
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e275a82
[vpr][CLI] add generate_net_timing_report
amin1377 May 2, 2025
333de67
[vpr][analysis] add generate_net_timing_report
amin1377 May 2, 2025
8aebd6a
[vpr][analysis] add comments
amin1377 May 5, 2025
010144e
make format
amin1377 May 5, 2025
b8289db
[vpr][CLI] remove generate net timing from CLI parameters and generat…
amin1377 May 5, 2025
717ab21
Merge branch 'master' of https://github.com/verilog-to-routing/vtr-ve…
amin1377 May 5, 2025
25bcd43
[vpr][analysis] add header for net timing report
amin1377 May 6, 2025
e7df9ea
[vpr][analysis] add timing format to comments
amin1377 May 6, 2025
1d50ca1
formatting fix
amin1377 May 6, 2025
310ecac
Revert "[vpr][CLI] remove generate net timing from CLI parameters and…
amin1377 May 6, 2025
546961b
Merge branch 'master' of https://github.com/verilog-to-routing/vtr-ve…
amin1377 May 6, 2025
2265ce8
make format
amin1377 May 6, 2025
1df1770
Merge branch 'master' of https://github.com/verilog-to-routing/vtr-ve…
amin1377 May 12, 2025
909e3f2
[task] add generate_net_timing_report to timing report strong test
amin1377 May 12, 2025
616cc8d
[doc] add doc for generating _net_timing_report command line option
amin1377 May 12, 2025
ebcf74b
[vpr][timing] update generate_net_timing_report comment
amin1377 May 12, 2025
dcbe1b3
[vpr][timing] add get_net_bounding_box
amin1377 May 12, 2025
548d53a
[vpr][timing] add net bounding box to the report
amin1377 May 12, 2025
c4b781e
[test] add test for net timing report
amin1377 May 12, 2025
7558005
[doc] update doc with new format to net timing report
amin1377 May 12, 2025
b2e1530
[vpr][analysis] fix net timing report bugs + including layer min/max …
amin1377 May 12, 2025
b625d58
make format
amin1377 May 12, 2025
40522e5
[vpr][analysis] capture vars by reference in lambda
amin1377 May 12, 2025
b171544
Merge branch 'master' of https://github.com/verilog-to-routing/vtr-ve…
amin1377 May 15, 2025
403b945
[doc] update the doc with new report format
amin1377 May 15, 2025
526e953
Merge branch 'master' of https://github.com/verilog-to-routing/vtr-ve…
amin1377 May 16, 2025
77799b6
[vpr][analysis] use std::min/max instead of if condition
amin1377 May 16, 2025
e03f90c
[vpr][analysis] change report_net_timing format to csv
amin1377 May 16, 2025
b8a60ea
[vpr][analysis] update comments
amin1377 May 16, 2025
87d161c
[vpr][analysis] print constant nets in the net timing report
amin1377 May 16, 2025
7649fdd
[vpr][analysis] apply comments
amin1377 May 16, 2025
f699b30
[vpr][analysis] fix function name
amin1377 May 16, 2025
aaf2569
[doc] add net timing report use case
amin1377 May 16, 2025
33291d3
fix a typo
amin1377 May 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions doc/src/vpr/command_line_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1517,6 +1517,21 @@ VPR uses a negotiated congestion algorithm (based on Pathfinder) to perform rout
* `swns` - setup Worst Negative Slack (sWNS) [ns]
* `stns` - Setup Total Negative Slack (sTNS) [ns]


.. option:: --generate_net_timing_report {on | off}

Generates a net timing report for each net in the design. For each net, the timing information written in the following format:

.. code-block:: none

netname : Fanout :
(bounding_box_xmin,bounding_box_ymin,bounding_box_layer_min),(bounding_box_xmax,bounding_box_ymax,bounding_box_layer_max) :
source_instance <slack_on source pin> :
<load pin name1> <slack on load pin name1> <net delay for this net> :
<load pin name2> <slack on load pin name2> <net delay for this net> : ...
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for this particular format? A .csv file would be easier to load into a spreadsheet an examine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My initial intention was to maintain compatibility with the format expected by Synopsys Synplify. However, since it can be post-processed later, I changed the report format to CSV.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I would leave it compatible with Synplify (that is useful) but just comment that is the reason for this format.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially thought the file would be directly consumed by Synplify, but apparently the synthesis team will be doing post-processing on it anyway, so it can be consumed by Synplify.


**Default:** ``off``

.. option:: --route_verbosity <int>

Controls the verbosity of routing output.
Expand Down
195 changes: 195 additions & 0 deletions vpr/src/analysis/timing_reports.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
#include "timing_reports.h"

#include <fstream>
#include <sstream>

#include "timing_reports.h"
#include "rr_graph.h"

#include "tatum/TimingReporter.hpp"

#include "vtr_version.h"
#include "vpr_types.h"
#include "globals.h"

Expand All @@ -10,6 +17,121 @@

#include "VprTimingGraphResolver.h"

/**
* @brief Get the bounding box of a net.
* If the net is completely absorbed into a cluster block, return the bounding box of the cluster block.
* Otherwise, return the bounding box of the net's route tree.
* If a net is not routed, bounding box is returned with default values (OPEN).
*
* @param atom_net_id The id of the atom net to get the bounding box of.
*/
static t_bb get_net_bounding_box(const AtomNetId atom_net_id) {
const auto& route_trees = g_vpr_ctx.routing().route_trees;
const auto& rr_graph = g_vpr_ctx.device().rr_graph;

// Lambda to get the bounding box of a route tree
auto route_tree_bb = [&](const RouteTree& route_tree) {
t_bb bb;

// Set the initial bounding box to the root node's location
RRNodeId route_tree_root = route_tree.root().inode;
bb.xmin = rr_graph.node_xlow(route_tree_root);
bb.xmax = rr_graph.node_xhigh(route_tree_root);
bb.ymin = rr_graph.node_ylow(route_tree_root);
bb.ymax = rr_graph.node_yhigh(route_tree_root);
bb.layer_min = rr_graph.node_layer(route_tree_root);
bb.layer_max = rr_graph.node_layer(route_tree_root);

// Iterate over all nodes in the route tree and update the bounding box
for (auto& rt_node : route_tree.all_nodes()) {
RRNodeId inode = rt_node.inode;

if (rr_graph.node_xlow(inode) < bb.xmin)
bb.xmin = rr_graph.node_xlow(inode);
if (rr_graph.node_xhigh(inode) > bb.xmax)
bb.xmax = rr_graph.node_xhigh(inode);

if (rr_graph.node_ylow(inode) < bb.ymin)
bb.ymin = rr_graph.node_ylow(inode);
if (rr_graph.node_yhigh(inode) > bb.ymax)
bb.ymax = rr_graph.node_yhigh(inode);

if (rr_graph.node_layer(inode) < bb.layer_min)
bb.layer_min = rr_graph.node_layer(inode);
if (rr_graph.node_layer(inode) > bb.layer_max)
bb.layer_max = rr_graph.node_layer(inode);
}
return bb;
};

if (g_vpr_ctx.routing().is_flat) {
// If flat router is used, route tree data structure can be used
// directly to get the bounding box of the net
const auto& route_tree = route_trees[atom_net_id];
if (!route_tree)
return t_bb();
return route_tree_bb(*route_tree);
} else {
// If two-stage router is used, we need to first get the cluster net id
// corresponding to the atom net and then get the bounding box of the net
// from the route tree. If the net is completely absorbed into a cluster block,
const auto& atom_lookup = g_vpr_ctx.atom().lookup();
const auto& cluster_net_id = atom_lookup.clb_nets(atom_net_id);
std::vector<t_bb> bbs;
t_bb max_bb;
// There maybe multiple cluster nets corresponding to a single atom net.
// We iterate over all cluster nets and the final bounding box is the union
// of all cluster net bounding boxes
if (cluster_net_id != vtr::nullopt) {
for (const auto& clb_net_id : *cluster_net_id) {
const auto& route_tree = route_trees[clb_net_id];
if (!route_tree)
continue;
bbs.push_back(route_tree_bb(*route_tree));
}
if (bbs.empty()) {
return t_bb();
}
// Assign the first cluster net's bounding box to the final bounding box
// and then iteratively update it with the union of bounding boxes of
// all cluster nets
max_bb = bbs[0];
for (size_t i = 1; i < bbs.size(); ++i) {
max_bb.xmin = std::min(bbs[i].xmin, max_bb.xmin);
max_bb.xmax = std::max(bbs[i].xmax, max_bb.xmax);
max_bb.ymin = std::min(bbs[i].ymin, max_bb.ymin);
max_bb.ymax = std::max(bbs[i].ymax, max_bb.ymax);
max_bb.layer_min = std::min(bbs[i].layer_min, max_bb.layer_min);
max_bb.layer_max = std::max(bbs[i].layer_max, max_bb.layer_max);
}
return max_bb;
} else {
// If there is no cluster net corresponding to the atom net,
// it means the net is completely absorbed into a cluster block.
// In that case, we set the bounding box the cluster block's bounding box
const auto& atom_ctx = g_vpr_ctx.atom();
const auto& atom_nlist = atom_ctx.netlist();
AtomPinId source_pin = atom_nlist.net_driver(atom_net_id);

AtomBlockId atom_block = atom_nlist.pin_block(source_pin);
VTR_ASSERT(atom_block != AtomBlockId::INVALID());
ClusterBlockId cluster_block = atom_lookup.atom_clb(atom_block);
VTR_ASSERT(cluster_block != ClusterBlockId::INVALID());

const t_pl_loc& cluster_block_loc = g_vpr_ctx.placement().block_locs()[cluster_block].loc;
const auto& grid = g_vpr_ctx.device().grid;
vtr::Rect<int> tile_bb = grid.get_tile_bb({cluster_block_loc.x, cluster_block_loc.y, cluster_block_loc.layer});
const int block_layer = cluster_block_loc.layer;
return t_bb(tile_bb.xmin(),
tile_bb.xmax(),
tile_bb.ymin(),
tile_bb.ymax(),
block_layer,
block_layer);
}
}
}

void generate_setup_timing_stats(const std::string& prefix,
const SetupTimingInfo& timing_info,
const AnalysisDelayCalculator& delay_calc,
Expand Down Expand Up @@ -61,3 +183,76 @@ void generate_hold_timing_stats(const std::string& prefix,

timing_reporter.report_unconstrained_hold(prefix + "report_unconstrained_timing.hold.rpt", *timing_info.hold_analyzer());
}

void generate_net_timing_report(const std::string& prefix,
const SetupHoldTimingInfo& timing_info,
const AnalysisDelayCalculator& delay_calc) {
/* Create a report file for net timing information */
std::ofstream os(prefix + "report_net_timing.rpt");
const auto& atom_netlist = g_vpr_ctx.atom().netlist();
const auto& atom_lookup = g_vpr_ctx.atom().lookup();

const auto& timing_ctx = g_vpr_ctx.timing();
const auto& timing_graph = timing_ctx.graph;

os << "# This file is generated by VTR" << std::endl;
os << "# Version: " << vtr::VERSION << std::endl;
os << "# Revision: " << vtr::VCS_REVISION << std::endl;
os << "# For each net, the timing information is reported in the following format:" << std::endl;
os << "# netname : Fanout : "
<< "(bounding_box_xmin,bounding_box_ymin,bounding_box_layermin),(bounding_box_xmax,bounding_box_ymax,bounding_box_layermax) : "
<< "source_instance <slack_on source pin> : "
<< "<load pin name1> <slack on load pin name1> <net delay for this net> : "
<< "<load pin name2> <slack on load pin name2> <net delay for this net> : ..."
<< std::endl;

os << std::endl;

for (const auto& net : atom_netlist.nets()) {
/* Skip constant nets */
if (atom_netlist.net_is_constant(net)) {
continue;
}

const auto& net_name = atom_netlist.net_name(net);

/* Get source pin and its timing information */
const auto& source_pin = *atom_netlist.net_pins(net).begin();
auto source_pin_slack = timing_info.setup_pin_slack(source_pin);
/* Timing graph node id corresponding to the net's source pin */
auto tg_source_node = atom_lookup.atom_pin_tnode(source_pin);
VTR_ASSERT(tg_source_node.is_valid());

const size_t fanout = atom_netlist.net_sinks(net).size();
const auto& net_bb = get_net_bounding_box(net);
os << net_name << " : "
<< fanout << " : "
<< "(" << net_bb.xmin << "," << net_bb.ymin << "," << net_bb.layer_min << "),("
<< net_bb.xmax << "," << net_bb.ymax << "," << net_bb.layer_max << ") : "
<< atom_netlist.pin_name(source_pin).c_str() << " " << source_pin_slack << " : ";

/* Iterate over all fanout pins and print their timing information */
for (size_t net_pin_index = 1; net_pin_index <= fanout; ++net_pin_index) {
const auto& pin = *(atom_netlist.net_pins(net).begin() + net_pin_index);

/* Get timing graph node id corresponding to the fanout pin */
const auto& tg_sink_node = atom_lookup.atom_pin_tnode(pin);
VTR_ASSERT(tg_sink_node.is_valid());

/* Get timing graph edge id between atom pins */
const auto& tg_edge_id = timing_graph->find_edge(tg_source_node, tg_sink_node);
VTR_ASSERT(tg_edge_id.is_valid());

/* Get timing information for the fanout pin */
const auto& pin_setup_slack = timing_info.setup_pin_slack(pin);
const auto& pin_delay = delay_calc.max_edge_delay(*timing_graph, tg_edge_id);

const auto& pin_name = atom_netlist.pin_name(pin);
os << pin_name << " " << std::scientific << pin_setup_slack << " " << pin_delay;
if (net_pin_index < fanout) {
os << " : ";
}
}
os << "," << std::endl;
}
}
17 changes: 17 additions & 0 deletions vpr/src/analysis/timing_reports.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,21 @@ void generate_hold_timing_stats(const std::string& prefix,
bool is_flat,
const BlkLocRegistry& blk_loc_registry);

/**
* @brief Generates timing information for each net in atom netlist. For each net, the timing information
* is reported in the following format:
* netname : Fanout :
* (bounding_box_xmin,bounding_box_ymin,bounding_box_layermin),(bounding_box_xmax,bounding_box_ymax,bounding_box_layermax) :
* source_instance <slack_on source pin> :
* <load pin name1> <slack on load pin name1> <net delay for this net> :
* <load pin name2> <slack on load pin name2> <net delay for this net> : ...
*
* @param prefix The prefix for the report file to be added to filename: report_net_timing.rpt
* @param timing_info Updated timing information
* @param delay_calc Delay calculator
*/
void generate_net_timing_report(const std::string& prefix,
const SetupHoldTimingInfo& timing_info,
const AnalysisDelayCalculator& delay_calc);

#endif
1 change: 1 addition & 0 deletions vpr/src/base/SetupVPR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,7 @@ static void SetupAnalysisOpts(const t_options& Options, t_analysis_opts& analysi

analysis_opts.timing_update_type = Options.timing_update_type;
analysis_opts.write_timing_summary = Options.write_timing_summary;
analysis_opts.generate_net_timing_report = Options.generate_net_timing_report;
}

static void SetupPowerOpts(const t_options& Options, t_power_opts* power_opts, t_arch* Arch) {
Expand Down
5 changes: 5 additions & 0 deletions vpr/src/base/read_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3088,6 +3088,11 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio
.help("Writes implemented design final timing summary to the specified JSON, XML or TXT file.")
.show_in(argparse::ShowIn::HELP_ONLY);

analysis_grp.add_argument<bool, ParseOnOff>(args.generate_net_timing_report, "--generate_net_timing_report")
.help("Generates a net timing report for each net in the design.")
.default_value("off")
.show_in(argparse::ShowIn::HELP_ONLY);

auto& power_grp = parser.add_argument_group("power analysis options");

power_grp.add_argument<bool, ParseOnOff>(args.do_power, "--power")
Expand Down
1 change: 1 addition & 0 deletions vpr/src/base/read_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ struct t_options {
argparse::ArgValue<e_post_synth_netlist_unconn_handling> post_synth_netlist_unconn_output_handling;
argparse::ArgValue<bool> post_synth_netlist_module_parameters;
argparse::ArgValue<std::string> write_timing_summary;
argparse::ArgValue<bool> generate_net_timing_report;
};

argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_options& args);
Expand Down
4 changes: 4 additions & 0 deletions vpr/src/base/vpr_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1475,6 +1475,10 @@ void vpr_analysis(const Netlist<>& net_list,
merged_netlist_writer(atom_ctx.netlist().netlist_name(), analysis_delay_calc, Arch.models, vpr_setup.AnalysisOpts);
}

if (vpr_setup.AnalysisOpts.generate_net_timing_report) {
generate_net_timing_report(/*prefix=*/"", *timing_info, *analysis_delay_calc);
}

//Do power analysis
// TODO: Still assumes that cluster net list is used
if (vpr_setup.PowerOpts.do_power) {
Expand Down
1 change: 1 addition & 0 deletions vpr/src/base/vpr_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -1356,6 +1356,7 @@ struct t_analysis_opts {
bool timing_report_skew;
std::string echo_dot_timing_graph_node;
std::string write_timing_summary;
bool generate_net_timing_report;

e_timing_update_type timing_update_type;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ qor_parse_file=qor_standard.txt
pass_requirements_file=pass_requirements.txt

# Script parameters
script_params_common = -starting_stage vpr
script_params_common = -starting_stage vpr --generate_net_timing_report on
script_params_list_add=--timing_report_detail netlist
script_params_list_add=--timing_report_detail aggregated
script_params_list_add=--timing_report_detail detailed
script_params_list_add=--timing_report_detail netlist --flat_routing on
script_params_list_add=--timing_report_detail aggregated --flat_routing on
script_params_list_add=--timing_report_detail detailed --flat_routing on
Loading