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.
This commit is contained in:
Ross Schlaikjer 2020-08-30 17:43:29 -04:00
parent f6d436d58b
commit a8c110b045
No known key found for this signature in database
GPG Key ID: 3C4CAA5FEDEF28DA
3 changed files with 94 additions and 0 deletions

View File

@ -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;
}

View File

@ -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) {}
// --------------------------------------------------------------

View File

@ -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;