aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoss Schlaikjer <ross@schlaikjer.net>2020-08-30 17:43:29 -0400
committerRoss Schlaikjer <ross@schlaikjer.net>2020-08-30 17:43:29 -0400
commita8c110b045744626f882e2a31ed8b3364f9a65db (patch)
tree5c82e78b995de12b65f8e3cea27276673abd1631
parentf6d436d58b8206aabe287325fc80da5521ed2e75 (diff)
downloadnextpnr-a8c110b045744626f882e2a31ed8b3364f9a65db.tar.gz
nextpnr-a8c110b045744626f882e2a31ed8b3364f9a65db.tar.bz2
nextpnr-a8c110b045744626f882e2a31ed8b3364f9a65db.zip
Add option to print critical path source code
In order to make debugging the critical path easier, add an option that will log the location each net was defined, if known. If the file that contains the definition is known, and is readable, also print the part of the source HDL responsible for the signal definition.
-rw-r--r--common/command.cc11
-rw-r--r--common/nextpnr.h5
-rw-r--r--common/timing.cc78
3 files changed, 94 insertions, 0 deletions
diff --git a/common/command.cc b/common/command.cc
index 3dc0d968..d0087fc9 100644
--- a/common/command.cc
+++ b/common/command.cc
@@ -158,6 +158,10 @@ po::options_description CommandHandler::getGeneralOptions()
general.add_options()("no-tmdriv", "disable timing-driven placement");
general.add_options()("sdf", po::value<std::string>(), "SDF delay back-annotation file to write");
general.add_options()("sdf-cvc", "enable tweaks for SDF file compatibility with the CVC simulator");
+ general.add_options()("print-critical-path-source",
+ "print the source code associated with each net in the critical path");
+ general.add_options()("critical-path-source-max-lines", po::value<int>(),
+ "max number of source lines to print per critical path report entry");
general.add_options()("placed-svg", po::value<std::string>(), "write render of placement to SVG file");
general.add_options()("routed-svg", po::value<std::string>(), "write render of routing to SVG file");
@@ -179,6 +183,13 @@ void CommandHandler::setupContext(Context *ctx)
ctx->debug = true;
}
+ if (vm.count("print-critical-path-source")) {
+ ctx->print_critical_path_source = true;
+ }
+ if (vm.count("critical-path-source-max-lines")) {
+ ctx->critical_path_source_max_lines = vm["critical-path-source-max-lines"].as<int>();
+ }
+
if (vm.count("force")) {
ctx->force = true;
}
diff --git a/common/nextpnr.h b/common/nextpnr.h
index 4d481d06..fc5e7c46 100644
--- a/common/nextpnr.h
+++ b/common/nextpnr.h
@@ -861,6 +861,11 @@ struct Context : Arch, DeterministicRNG
bool debug = false;
bool force = false;
+ // Print verilog sources for nets in critical path?
+ bool print_critical_path_source = false;
+ // Max line count to print for critical path sources
+ int critical_path_source_max_lines = 8;
+
Context(ArchArgs args) : Arch(args) {}
// --------------------------------------------------------------
diff --git a/common/timing.cc b/common/timing.cc
index d4d33183..b67f2ef8 100644
--- a/common/timing.cc
+++ b/common/timing.cc
@@ -22,6 +22,7 @@
#include <algorithm>
#include <boost/range/adaptor/reversed.hpp>
#include <deque>
+#include <fstream>
#include <map>
#include <unordered_map>
#include <utility>
@@ -811,6 +812,80 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
}
if (print_path) {
+ static auto print_net_source = [](Context *ctx, NetInfo *net) {
+ auto sources = net->attrs.find(ctx->id("src"));
+ if (sources == net->attrs.end()) {
+ // No sources for this net, can't print anything
+ return;
+ }
+
+ // Sources are separated by pipe characters, deepest source last
+ auto sourcelist = sources->second.as_string();
+ std::vector<std::string> source_entries;
+ size_t current = 0, prev = 0;
+ while ((current = sourcelist.find("|", prev)) != std::string::npos) {
+ source_entries.emplace_back(sourcelist.substr(prev, current - prev));
+ prev = current + 1;
+ }
+ // Ensure we emplace the final entry
+ source_entries.emplace_back(sourcelist.substr(prev, current - prev));
+
+ // For each source entry, split iinto filename, start line/character
+ // and end line/character
+ const unsigned entry_count = source_entries.size();
+ log_info(" Defined in:\n");
+ for (unsigned i = 0; i < entry_count; i++) {
+ const std::string source_entry = source_entries[i];
+ const bool is_final_entry = i == entry_count - 1;
+
+ log_info(" %s\n", source_entry.c_str());
+
+ // Split the source entry to get the filename
+ const size_t filename_split = source_entry.find(":");
+ const std::string filename = source_entry.substr(0, filename_split);
+ const std::string location_tuple = source_entry.substr(filename_split + 1);
+
+ // Split the location tuple into start/end groups
+ const size_t start_end_split = location_tuple.find("-");
+ const std::string code_start = location_tuple.substr(0, start_end_split);
+ const std::string code_end = location_tuple.substr(start_end_split + 1);
+
+ // Extract just the line number from those tuples
+ const int code_start_line = std::atoi(code_start.substr(0, code_start.find(".")).c_str());
+ const int code_end_line = std::atoi(code_end.substr(0, code_end.find(".")).c_str());
+
+ // Try and stat the source file
+ std::ifstream in(filename);
+ if (!in) {
+ // Failed to find source file, can't print the actual source
+ return;
+ }
+
+ // Skip through to the start line
+ in.seekg(std::ios::beg);
+ for (int i = 0; i < code_start_line - 1; ++i) {
+ in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
+ }
+ // Log each line til we hit the end line / max line count
+ // For non-final entries, just print one line so we don't spam too heavily
+ int max_print_lines = ctx->critical_path_source_max_lines - 1;
+ if (!is_final_entry) {
+ max_print_lines = 0; // Compare is inclusive
+ }
+ const int print_end =
+ ((code_end_line < code_start_line + max_print_lines) ? code_end_line
+ : code_start_line + max_print_lines);
+ for (int i = code_start_line; i <= print_end; i++) {
+ std::string line;
+ getline(in, line);
+ // Strip any whitespace from the start of the line, since we are already aligning it
+ line.erase(line.begin(),
+ std::find_if(line.begin(), line.end(), [](char c) { return !(c == ' ' || c == '\t'); }));
+ log_info(" %s\n", line.c_str());
+ }
+ }
+ };
+
auto print_path_report = [ctx](ClockPair &clocks, PortRefVector &crit_path) {
delay_t total = 0, logic_total = 0, route_total = 0;
auto &front = crit_path.front();
@@ -888,6 +963,9 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
cursor = ctx->getPipSrcWire(pip);
}
}
+ if (ctx->print_critical_path_source) {
+ print_net_source(ctx, net);
+ }
last_port = sink->port;
}
int clockCount = 0;