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");
|
ctx_cls, "hierarchy");
|
||||||
readwrite_wrapper<Context, decltype(&Context::top_module), &Context::top_module, conv_to_str<IdString>,
|
readwrite_wrapper<Context, decltype(&Context::top_module), &Context::top_module, conv_to_str<IdString>,
|
||||||
conv_from_str<IdString>>::def_wrap(ctx_cls, "top_module");
|
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(
|
fn_wrapper_0a<Context, decltype(&Context::getNameDelimiter), &Context::getNameDelimiter, pass_through<char>>::def_wrap(
|
||||||
ctx_cls, "getNameDelimiter");
|
ctx_cls, "getNameDelimiter");
|
||||||
|
@ -84,6 +84,9 @@ struct BaseCtx
|
|||||||
// Context meta data
|
// Context meta data
|
||||||
dict<IdString, Property> attrs;
|
dict<IdString, Property> attrs;
|
||||||
|
|
||||||
|
// Fmax data post timing analysis
|
||||||
|
TimingResult timing_result;
|
||||||
|
|
||||||
Context *as_ctx = nullptr;
|
Context *as_ctx = nullptr;
|
||||||
|
|
||||||
// Has the frontend loaded a design?
|
// Has the frontend loaded a design?
|
||||||
|
@ -173,6 +173,9 @@ po::options_description CommandHandler::getGeneralOptions()
|
|||||||
general.add_options()("router2-heatmap", po::value<std::string>(),
|
general.add_options()("router2-heatmap", po::value<std::string>(),
|
||||||
"prefix for router2 resource congestion heatmaps");
|
"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()("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");
|
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"));
|
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
|
#ifndef NO_PYTHON
|
||||||
deinit_python();
|
deinit_python();
|
||||||
#endif
|
#endif
|
||||||
|
@ -76,6 +76,10 @@ struct Context : Arch, DeterministicRNG
|
|||||||
|
|
||||||
// --------------------------------------------------------------
|
// --------------------------------------------------------------
|
||||||
|
|
||||||
|
// provided by report.cc
|
||||||
|
void writeReport(std::ostream &out) const;
|
||||||
|
// --------------------------------------------------------------
|
||||||
|
|
||||||
uint32_t checksum() const;
|
uint32_t checksum() const;
|
||||||
|
|
||||||
void check() const;
|
void check() const;
|
||||||
|
@ -217,6 +217,18 @@ struct ClockConstraint
|
|||||||
DelayPair period;
|
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
|
// Represents the contents of a non-leaf cell in a design
|
||||||
// with hierarchy
|
// with hierarchy
|
||||||
|
|
||||||
|
@ -285,8 +285,16 @@ PYBIND11_EMBEDDED_MODULE(MODULE_NAME, m)
|
|||||||
WRAP_MAP(m, WireMap, wrap_context<PipMap &>, "WireMap");
|
WRAP_MAP(m, WireMap, wrap_context<PipMap &>, "WireMap");
|
||||||
WRAP_MAP_UPTR(m, RegionMap, "RegionMap");
|
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);
|
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) {
|
if (print_fmax) {
|
||||||
log_break();
|
log_break();
|
||||||
unsigned max_width = 0;
|
unsigned max_width = 0;
|
||||||
|
auto &result = ctx->timing_result;
|
||||||
|
result.clock_fmax.clear();
|
||||||
for (auto &clock : clock_reports)
|
for (auto &clock : clock_reports)
|
||||||
max_width = std::max<unsigned>(max_width, clock.first.str(ctx).size());
|
max_width = std::max<unsigned>(max_width, clock.first.str(ctx).size());
|
||||||
for (auto &clock : clock_reports) {
|
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)
|
if (ctx->nets.at(clock.first)->clkconstr)
|
||||||
target = 1000 / ctx->getDelayNS(ctx->nets.at(clock.first)->clkconstr->period.minDelay());
|
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];
|
bool passed = target < clock_fmax[clock.first];
|
||||||
if (!warn_on_failure || passed)
|
if (!warn_on_failure || passed)
|
||||||
log_info("Max frequency for clock %*s'%s': %.02f MHz (%s at %.02f MHz)\n", width, "",
|
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