Merge pull request #787 from YosysHQ/gatecat/report
Add JSON utilisation and timing report
This commit is contained in:
commit
ef1fbfc651
@ -9,6 +9,8 @@ readonly_wrapper<Context, decltype(&Context::hierarchy), &Context::hierarchy, wr
|
||||
ctx_cls, "hierarchy");
|
||||
readwrite_wrapper<Context, decltype(&Context::top_module), &Context::top_module, conv_to_str<IdString>,
|
||||
conv_from_str<IdString>>::def_wrap(ctx_cls, "top_module");
|
||||
readonly_wrapper<Context, decltype(&Context::timing_result), &Context::timing_result,
|
||||
wrap_context<TimingResult &>>::def_wrap(ctx_cls, "timing_result");
|
||||
|
||||
fn_wrapper_0a<Context, decltype(&Context::getNameDelimiter), &Context::getNameDelimiter, pass_through<char>>::def_wrap(
|
||||
ctx_cls, "getNameDelimiter");
|
||||
|
@ -84,6 +84,9 @@ struct BaseCtx
|
||||
// Context meta data
|
||||
dict<IdString, Property> attrs;
|
||||
|
||||
// Fmax data post timing analysis
|
||||
TimingResult timing_result;
|
||||
|
||||
Context *as_ctx = nullptr;
|
||||
|
||||
// Has the frontend loaded a design?
|
||||
|
@ -173,6 +173,9 @@ po::options_description CommandHandler::getGeneralOptions()
|
||||
general.add_options()("router2-heatmap", po::value<std::string>(),
|
||||
"prefix for router2 resource congestion heatmaps");
|
||||
|
||||
general.add_options()("report", po::value<std::string>(),
|
||||
"write timing and utilization report in JSON format to file");
|
||||
|
||||
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");
|
||||
|
||||
@ -424,6 +427,14 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
|
||||
ctx->writeSDF(f, vm.count("sdf-cvc"));
|
||||
}
|
||||
|
||||
if (vm.count("report")) {
|
||||
std::string filename = vm["report"].as<std::string>();
|
||||
std::ofstream f(filename);
|
||||
if (!f)
|
||||
log_error("Failed to open report file '%s' for writing.\n", filename.c_str());
|
||||
ctx->writeReport(f);
|
||||
}
|
||||
|
||||
#ifndef NO_PYTHON
|
||||
deinit_python();
|
||||
#endif
|
||||
|
@ -76,6 +76,10 @@ struct Context : Arch, DeterministicRNG
|
||||
|
||||
// --------------------------------------------------------------
|
||||
|
||||
// provided by report.cc
|
||||
void writeReport(std::ostream &out) const;
|
||||
// --------------------------------------------------------------
|
||||
|
||||
uint32_t checksum() const;
|
||||
|
||||
void check() const;
|
||||
|
@ -217,6 +217,18 @@ struct ClockConstraint
|
||||
DelayPair period;
|
||||
};
|
||||
|
||||
struct ClockFmax
|
||||
{
|
||||
float achieved;
|
||||
float constraint;
|
||||
};
|
||||
|
||||
struct TimingResult
|
||||
{
|
||||
// Achieved and target Fmax for all clock domains
|
||||
dict<IdString, ClockFmax> clock_fmax;
|
||||
};
|
||||
|
||||
// Represents the contents of a non-leaf cell in a design
|
||||
// with hierarchy
|
||||
|
||||
|
@ -285,8 +285,16 @@ PYBIND11_EMBEDDED_MODULE(MODULE_NAME, m)
|
||||
WRAP_MAP(m, WireMap, wrap_context<PipMap &>, "WireMap");
|
||||
WRAP_MAP_UPTR(m, RegionMap, "RegionMap");
|
||||
|
||||
WRAP_VECTOR(m, PortRefVector, wrap_context<PortRef &>);
|
||||
typedef dict<IdString, ClockFmax> ClockFmaxMap;
|
||||
WRAP_MAP(m, ClockFmaxMap, pass_through<ClockFmax>, "ClockFmaxMap");
|
||||
|
||||
auto clk_fmax_cls = py::class_<ClockFmax>(m, "ClockFmax")
|
||||
.def_readonly("achieved", &ClockFmax::achieved)
|
||||
.def_readonly("constraint", &ClockFmax::constraint);
|
||||
|
||||
auto tmg_result_cls = py::class_<ContextualWrapper<TimingResult &>>(m, "TimingResult");
|
||||
readonly_wrapper<TimingResult &, decltype(&TimingResult::clock_fmax), &TimingResult::clock_fmax,
|
||||
wrap_context<ClockFmaxMap &>>::def_wrap(tmg_result_cls, "clock_fmax");
|
||||
arch_wrap_python(m);
|
||||
}
|
||||
|
||||
|
68
common/report.cc
Normal file
68
common/report.cc
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2021 gatecat <gatecat@ds0.me>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "json11.hpp"
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
using namespace json11;
|
||||
|
||||
namespace {
|
||||
dict<IdString, std::pair<int, int>> get_utilization(const Context *ctx)
|
||||
{
|
||||
// Sort by Bel type
|
||||
dict<IdString, std::pair<int, int>> result;
|
||||
for (auto &cell : ctx->cells) {
|
||||
result[ctx->getBelBucketName(ctx->getBelBucketForCellType(cell.second.get()->type))].first++;
|
||||
}
|
||||
for (auto bel : ctx->getBels()) {
|
||||
if (!ctx->getBelHidden(bel)) {
|
||||
result[ctx->getBelBucketName(ctx->getBelBucketForBel(bel))].second++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
void Context::writeReport(std::ostream &out) const
|
||||
{
|
||||
auto util = get_utilization(this);
|
||||
dict<std::string, Json> util_json;
|
||||
for (const auto &kv : util) {
|
||||
util_json[kv.first.str(this)] = Json::object{
|
||||
{"used", kv.second.first},
|
||||
{"available", kv.second.second},
|
||||
};
|
||||
}
|
||||
dict<std::string, Json> fmax_json;
|
||||
for (const auto &kv : timing_result.clock_fmax) {
|
||||
fmax_json[kv.first.str(this)] = Json::object{
|
||||
{"achieved", kv.second.achieved},
|
||||
{"constraint", kv.second.constraint},
|
||||
};
|
||||
}
|
||||
out << Json(Json::object{
|
||||
{"utilization", util_json},
|
||||
{"fmax", fmax_json},
|
||||
})
|
||||
.dump()
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
@ -1315,6 +1315,8 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
|
||||
if (print_fmax) {
|
||||
log_break();
|
||||
unsigned max_width = 0;
|
||||
auto &result = ctx->timing_result;
|
||||
result.clock_fmax.clear();
|
||||
for (auto &clock : clock_reports)
|
||||
max_width = std::max<unsigned>(max_width, clock.first.str(ctx).size());
|
||||
for (auto &clock : clock_reports) {
|
||||
@ -1324,6 +1326,9 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
|
||||
if (ctx->nets.at(clock.first)->clkconstr)
|
||||
target = 1000 / ctx->getDelayNS(ctx->nets.at(clock.first)->clkconstr->period.minDelay());
|
||||
|
||||
result.clock_fmax[clock.first].achieved = clock_fmax[clock.first];
|
||||
result.clock_fmax[clock.first].constraint = target;
|
||||
|
||||
bool passed = target < clock_fmax[clock.first];
|
||||
if (!warn_on_failure || passed)
|
||||
log_info("Max frequency for clock %*s'%s': %.02f MHz (%s at %.02f MHz)\n", width, "",
|
||||
|
22
docs/report.md
Normal file
22
docs/report.md
Normal file
@ -0,0 +1,22 @@
|
||||
# JSON Reports
|
||||
|
||||
nextpnr can write a JSON report using `--report` post-place-and-route for integration with other build systems. It contains information on post-pack utilization and maximum achieved frequency for each clock domain, and is of the following format:
|
||||
|
||||
```
|
||||
{
|
||||
"utilization": {
|
||||
<beltype>: {
|
||||
"used": <number of bels used in design>,
|
||||
"available": <total number of bels available in device>
|
||||
},
|
||||
...
|
||||
},
|
||||
"fmax": {
|
||||
<clock domain>: {
|
||||
"achieved": <computed Fmax of routed design for clock in MHz>,
|
||||
"constraint": <constraint for clock in MHz>
|
||||
},
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
Loading…
Reference in New Issue
Block a user