blob: d2bc49a4c009c2329fd86e385b14a34f3207b596 [file] [log] [blame]
#include "slack_evaluation.h"
#include "tatum/TimingGraph.hpp"
#include "timing_util.h"
#include "vpr_error.h"
#include "atom_netlist.h"
#include "vtr_log.h"
#if defined(VPR_USE_TBB)
# include <tbb/task_group.h>
# include <tbb/parallel_for_each.h>
#endif
/*
* SetupSlackCrit
*/
SetupSlackCrit::SetupSlackCrit(const AtomNetlist& netlist, const AtomLookup& netlist_lookup)
: netlist_(netlist)
, netlist_lookup_(netlist_lookup)
, pin_slacks_(netlist_.pins().size(), NAN)
, pin_criticalities_(netlist_.pins().size(), NAN) {
//pass
}
//Returns the worst (least) slack of connections through the specified pin
float SetupSlackCrit::setup_pin_slack(AtomPinId pin) const { return pin_slacks_[pin]; }
//Returns the worst (maximum) criticality of connections through the specified pin.
// Criticality (in [0., 1.]) represents how timing-critical something is,
// 0. is non-critical and 1. is most-critical.
float SetupSlackCrit::setup_pin_criticality(AtomPinId pin) const { return pin_criticalities_[pin]; }
void SetupSlackCrit::update_slacks_and_criticalities(const tatum::TimingGraph& timing_graph, const tatum::SetupTimingAnalyzer& analyzer) {
#if defined(VPR_USE_TBB)
tbb::task_group g;
g.run([&] { update_slacks(analyzer); });
g.run([&] { update_criticalities(timing_graph, analyzer); });
g.wait();
#else
update_slacks(analyzer);
update_criticalities(timing_graph, analyzer);
#endif
}
void SetupSlackCrit::update_slacks(const tatum::SetupTimingAnalyzer& analyzer) {
auto pins = netlist_.pins();
#if defined(VPR_USE_TBB)
tbb::parallel_for_each(pins.begin(), pins.end(), [&, this](auto pin) {
this->update_pin_slack(pin, analyzer);
});
#else
for (auto pin : pins) {
update_pin_slack(pin, analyzer);
}
#endif
}
void SetupSlackCrit::update_pin_slack(const AtomPinId pin, const tatum::SetupTimingAnalyzer& analyzer) {
//Find the timing node associated with the pin
tatum::NodeId node = netlist_lookup_.atom_pin_tnode(pin);
VTR_ASSERT(node);
//Find the worst (least) slack at this node
auto tags = analyzer.setup_slacks(node);
auto min_tag_iter = find_minimum_tag(tags);
if (min_tag_iter != tags.end()) {
pin_slacks_[pin] = min_tag_iter->time().value();
} else {
//No tags (e.g. driven by constant generator)
pin_slacks_[pin] = std::numeric_limits<float>::infinity();
}
}
void SetupSlackCrit::update_criticalities(const tatum::TimingGraph& timing_graph, const tatum::SetupTimingAnalyzer& analyzer) {
//Record the maximum required time, and wost slack per domain pair
std::map<DomainPair, float> max_req;
std::map<DomainPair, float> worst_slack;
for (tatum::NodeId node : timing_graph.logical_outputs()) {
for (auto& tag : analyzer.setup_tags(node, tatum::TagType::DATA_REQUIRED)) {
auto domain_pair = DomainPair(tag.launch_clock_domain(), tag.capture_clock_domain());
float req = tag.time().value();
if (!max_req.count(domain_pair) || max_req[domain_pair] < req) {
max_req[domain_pair] = req;
}
}
for (auto& tag : analyzer.setup_slacks(node)) {
auto domain_pair = DomainPair(tag.launch_clock_domain(), tag.capture_clock_domain());
float slack = tag.time().value();
if (!worst_slack.count(domain_pair) || slack < worst_slack[domain_pair]) {
worst_slack[domain_pair] = slack;
}
}
}
//Update the criticalities of each pin
auto pins = netlist_.pins();
#if defined(VPR_USE_TBB)
tbb::parallel_for_each(pins.begin(), pins.end(), [&, this](auto pin) {
this->pin_criticalities_[pin] = this->calc_pin_criticality(pin, analyzer, max_req, worst_slack);
});
#else
for (auto pin : pins) {
pin_criticalities_[pin] = calc_pin_criticality(pin, analyzer, max_req, worst_slack);
}
#endif
}
float SetupSlackCrit::calc_pin_criticality(AtomPinId pin,
const tatum::SetupTimingAnalyzer& analyzer,
const std::map<DomainPair, float>& max_req,
const std::map<DomainPair, float>& worst_slack) {
tatum::NodeId node = netlist_lookup_.atom_pin_tnode(pin);
VTR_ASSERT(node);
//Calculate maximum criticality over all domains
return calc_relaxed_criticality(max_req, worst_slack, analyzer.setup_slacks(node));
}
/*
* HoldSlackCrit
*/
HoldSlackCrit::HoldSlackCrit(const AtomNetlist& netlist, const AtomLookup& netlist_lookup)
: netlist_(netlist)
, netlist_lookup_(netlist_lookup)
, pin_slacks_(netlist_.pins().size(), NAN)
, pin_criticalities_(netlist_.pins().size(), NAN) {
//pass
}
//Returns the worst (least) slack of connections through the specified pin
float HoldSlackCrit::hold_pin_slack(AtomPinId pin) const { return pin_slacks_[pin]; }
//Returns the worst (maximum) criticality of connections through the specified pin.
// Criticality (in [0., 1.]) represents how timing-critical something is,
// 0. is non-critical and 1. is most-critical.
float HoldSlackCrit::hold_pin_criticality(AtomPinId pin) const { return pin_criticalities_[pin]; }
void HoldSlackCrit::update_slacks_and_criticalities(const tatum::TimingGraph& timing_graph, const tatum::HoldTimingAnalyzer& analyzer) {
#if defined(VPR_USE_TBB)
tbb::task_group g;
g.run([&] { update_slacks(analyzer); });
g.run([&] { update_criticalities(timing_graph, analyzer); });
g.wait();
#else
update_slacks(analyzer);
update_criticalities(timing_graph, analyzer);
#endif
}
void HoldSlackCrit::update_slacks(const tatum::HoldTimingAnalyzer& analyzer) {
for (AtomPinId pin : netlist_.pins()) {
update_pin_slack(pin, analyzer);
}
}
void HoldSlackCrit::update_pin_slack(const AtomPinId pin, const tatum::HoldTimingAnalyzer& analyzer) {
//Find the timing node associated with the pin
tatum::NodeId node = netlist_lookup_.atom_pin_tnode(pin);
VTR_ASSERT(node);
//Find the worst (least) slack at this node
auto tags = analyzer.hold_slacks(node);
auto min_tag_iter = find_minimum_tag(tags);
if (min_tag_iter != tags.end()) {
pin_slacks_[pin] = min_tag_iter->time().value();
} else {
//No tags (e.g. driven by constant generator)
pin_slacks_[pin] = std::numeric_limits<float>::infinity();
}
}
void HoldSlackCrit::update_criticalities(const tatum::TimingGraph& timing_graph, const tatum::HoldTimingAnalyzer& analyzer) {
//TODO: this calculates a simple shifted and scaled criticality, not clear if this is the
//right approach (e.g. should we use a more intellegent method like the one used by setup slack?)
float worst_slack = std::numeric_limits<float>::infinity();
float best_slack = -std::numeric_limits<float>::infinity();
for (tatum::NodeId node : timing_graph.nodes()) {
for (auto& tag : analyzer.hold_slacks(node)) {
float slack = tag.time().value();
worst_slack = std::min(worst_slack, slack);
best_slack = std::max(best_slack, slack);
}
}
//Calculate the transformation from slack to criticality,
//we scale and shift the result so the worst_slack takes on criticalty 1.0
//while the best slack takes on criticality 0.0
float scale = 1. / std::abs(best_slack - worst_slack);
float shift = -worst_slack;
//Update the criticalities of each pin
auto pins = netlist_.pins();
#if defined(VPR_USE_TBB)
tbb::parallel_for_each(pins.begin(), pins.end(), [&, this](auto pin) {
this->pin_criticalities_[pin] = this->calc_pin_criticality(pin, analyzer, scale, shift);
});
#else
for (auto pin : pins) {
pin_criticalities_[pin] = calc_pin_criticality(pin, analyzer, scale, shift);
}
#endif
}
float HoldSlackCrit::calc_pin_criticality(AtomPinId pin,
const tatum::HoldTimingAnalyzer& analyzer,
const float scale,
const float shift) {
tatum::NodeId node = netlist_lookup_.atom_pin_tnode(pin);
VTR_ASSERT(node);
float criticality = 0.;
for (auto tag : analyzer.hold_slacks(node)) {
float slack = tag.time().value();
float tag_criticality = 1. - scale * (slack + shift);
criticality = std::max(criticality, tag_criticality);
}
VTR_ASSERT(criticality >= 0.);
VTR_ASSERT(criticality <= 1.);
return criticality;
}