blob: 510fadd9e514a40073871e536c8c51aac04827ed [file]
#include <iostream>
#include <limits>
#include "tatum/util/tatum_assert.hpp"
#include "tatum/TimingConstraints.hpp"
using std::cout;
using std::endl;
namespace tatum {
TimingConstraints::domain_range TimingConstraints::clock_domains() const {
return tatum::util::make_range(domain_ids_.begin(), domain_ids_.end());
}
std::string TimingConstraints::clock_domain_name(const DomainId id) const {
if(!id) {
return std::string("*");
}
return domain_names_[id];
}
NodeId TimingConstraints::clock_domain_source_node(const DomainId id) const {
return domain_sources_[id];
}
bool TimingConstraints::is_virtual_clock(const DomainId id) const {
//No source node indicates a virtual clock
return !bool(clock_domain_source_node(id));
}
DomainId TimingConstraints::node_clock_domain(const NodeId id) const {
//This is currenlty a linear search through all clock sources and
//I/O constraints, could be made more efficient but it is only called
//rarely (i.e. during pre-traversals)
//Is it a clock source?
DomainId source_domain = find_node_source_clock_domain(id);
if(source_domain) return source_domain;
//Does it have an input constarint?
for(DelayType delay_type : {DelayType::MAX, DelayType::MIN}) {
for(auto kv : input_constraints(delay_type)) {
auto node_id = kv.first;
auto domain_id = kv.second.domain;
//TODO: Assumes a single clock per node
if(node_id == id) return domain_id;
}
}
//Does it have an output constraint?
for(DelayType delay_type : {DelayType::MAX, DelayType::MIN}) {
for(auto kv : output_constraints(delay_type)) {
auto node_id = kv.first;
auto domain_id = kv.second.domain;
//TODO: Assumes a single clock per node
if(node_id == id) return domain_id;
}
}
//None found
return DomainId::INVALID();
}
bool TimingConstraints::node_is_clock_source(const NodeId id) const {
//Returns a DomainId which converts to true if valid
return bool(find_node_source_clock_domain(id));
}
bool TimingConstraints::node_is_constant_generator(const NodeId id) const {
return constant_generators_.count(id);
}
DomainId TimingConstraints::find_node_source_clock_domain(const NodeId node_id) const {
//We don't expect many clocks, so the linear search should be fine
for(auto domain_id : clock_domains()) {
if(clock_domain_source_node(domain_id) == node_id) {
return domain_id;
}
}
return DomainId::INVALID();
}
DomainId TimingConstraints::find_clock_domain(const std::string& name) const {
//Linear search for name
// We don't expect a large number of domains
for(DomainId id : clock_domains()) {
if(clock_domain_name(id) == name) {
return id;
}
}
//Not found
return DomainId::INVALID();
}
bool TimingConstraints::should_analyze(const DomainId src_domain, const DomainId sink_domain, const NodeId capture_node) const {
TATUM_ASSERT(src_domain);
TATUM_ASSERT(sink_domain);
//If there is a domain pair + capture node or domain pair constraint then it should be analyzed
return setup_constraints_.count(NodeDomainPair(src_domain, sink_domain, capture_node))
|| setup_constraints_.count(NodeDomainPair(src_domain, sink_domain, NodeId::INVALID()))
|| hold_constraints_.count(NodeDomainPair(src_domain, sink_domain, capture_node))
|| hold_constraints_.count(NodeDomainPair(src_domain, sink_domain, NodeId::INVALID()));
}
Time TimingConstraints::hold_constraint(const DomainId src_domain, const DomainId sink_domain, const NodeId capture_node) const {
//Try to find the capture node-specific constraint
auto iter = hold_constraints_.find(NodeDomainPair(src_domain, sink_domain, capture_node));
if(iter != hold_constraints_.end()) {
return iter->second;
}
//If no capture node specific constraint was found, fallback to the domain pair constriant
iter = hold_constraints_.find(NodeDomainPair(src_domain, sink_domain, NodeId::INVALID()));
if(iter != hold_constraints_.end()) {
return iter->second;
}
//No constraint found
return std::numeric_limits<Time>::quiet_NaN();
}
Time TimingConstraints::setup_constraint(const DomainId src_domain, const DomainId sink_domain, const NodeId capture_node) const {
//Try to find the capture node-specific constraint
auto iter = setup_constraints_.find(NodeDomainPair(src_domain, sink_domain, capture_node));
if(iter != setup_constraints_.end()) {
return iter->second;
}
//If no capture node specific constraint was found, fallback to the domain pair constriant
iter = setup_constraints_.find(NodeDomainPair(src_domain, sink_domain, NodeId::INVALID()));
if(iter != setup_constraints_.end()) {
return iter->second;
}
//No constraint found
return std::numeric_limits<Time>::quiet_NaN();
}
Time TimingConstraints::setup_clock_uncertainty(const DomainId src_domain, const DomainId sink_domain) const {
auto iter = setup_clock_uncertainties_.find(DomainPair(src_domain, sink_domain));
if(iter == setup_clock_uncertainties_.end()) {
return Time(0.); //Defaults to zero if unspecified
}
return iter->second;
}
Time TimingConstraints::hold_clock_uncertainty(const DomainId src_domain, const DomainId sink_domain) const {
auto iter = hold_clock_uncertainties_.find(DomainPair(src_domain, sink_domain));
if(iter == hold_clock_uncertainties_.end()) {
return Time(0.); //Defaults to zero if unspecified
}
return iter->second;
}
Time TimingConstraints::input_constraint(const NodeId node_id, const DomainId domain_id, const DelayType delay_type) const {
if (delay_type == DelayType::MAX) {
auto iter = find_io_constraint(node_id, domain_id, max_input_constraints_);
if(iter != max_input_constraints_.end()) {
return iter->second.constraint;
}
} else {
TATUM_ASSERT(delay_type == DelayType::MIN);
auto iter = find_io_constraint(node_id, domain_id, min_input_constraints_);
if(iter != min_input_constraints_.end()) {
return iter->second.constraint;
}
}
return std::numeric_limits<Time>::quiet_NaN();
}
Time TimingConstraints::output_constraint(const NodeId node_id, const DomainId domain_id, const DelayType delay_type) const {
if (delay_type == DelayType::MAX) {
auto iter = find_io_constraint(node_id, domain_id, max_output_constraints_);
if(iter != max_output_constraints_.end()) {
return iter->second.constraint;
}
} else {
TATUM_ASSERT(delay_type == DelayType::MIN);
auto iter = find_io_constraint(node_id, domain_id, min_output_constraints_);
if(iter != min_output_constraints_.end()) {
return iter->second.constraint;
}
}
return std::numeric_limits<Time>::quiet_NaN();
}
Time TimingConstraints::source_latency(const DomainId domain, const ArrivalType arrival_type) const {
if (arrival_type == ArrivalType::EARLY) {
auto iter = source_latencies_early_.find(domain);
if(iter == source_latencies_early_.end()) {
return Time(0.); //Defaults to zero if unspecified
}
return iter->second;
} else {
TATUM_ASSERT(arrival_type == ArrivalType::LATE);
auto iter = source_latencies_late_.find(domain);
if(iter == source_latencies_late_.end()) {
return Time(0.); //Defaults to zero if unspecified
}
return iter->second;
}
}
TimingConstraints::constant_generator_range TimingConstraints::constant_generators() const {
return tatum::util::make_range(constant_generators_.begin(), constant_generators_.end());
}
TimingConstraints::clock_constraint_range TimingConstraints::setup_constraints() const {
return tatum::util::make_range(setup_constraints_.begin(), setup_constraints_.end());
}
TimingConstraints::clock_constraint_range TimingConstraints::hold_constraints() const {
return tatum::util::make_range(hold_constraints_.begin(), hold_constraints_.end());
}
TimingConstraints::clock_uncertainty_range TimingConstraints::setup_clock_uncertainties() const {
return tatum::util::make_range(setup_clock_uncertainties_.begin(), setup_clock_uncertainties_.end());
}
TimingConstraints::clock_uncertainty_range TimingConstraints::hold_clock_uncertainties() const {
return tatum::util::make_range(hold_clock_uncertainties_.begin(), hold_clock_uncertainties_.end());
}
TimingConstraints::io_constraint_range TimingConstraints::input_constraints(const DelayType delay_type) const {
if (delay_type == DelayType::MAX) {
return tatum::util::make_range(max_input_constraints_.begin(), max_input_constraints_.end());
} else {
TATUM_ASSERT(delay_type == DelayType::MIN);
return tatum::util::make_range(min_input_constraints_.begin(), min_input_constraints_.end());
}
}
TimingConstraints::io_constraint_range TimingConstraints::output_constraints(const DelayType delay_type) const {
if (delay_type == DelayType::MAX) {
return tatum::util::make_range(max_output_constraints_.begin(), max_output_constraints_.end());
} else {
TATUM_ASSERT(delay_type == DelayType::MIN);
return tatum::util::make_range(min_output_constraints_.begin(), min_output_constraints_.end());
}
}
TimingConstraints::io_constraint_range TimingConstraints::input_constraints(const NodeId id, const DelayType delay_type) const {
if (delay_type == DelayType::MAX) {
auto range = max_input_constraints_.equal_range(id);
return tatum::util::make_range(range.first, range.second);
} else {
TATUM_ASSERT(delay_type == DelayType::MIN);
auto range = min_input_constraints_.equal_range(id);
return tatum::util::make_range(range.first, range.second);
}
}
TimingConstraints::io_constraint_range TimingConstraints::output_constraints(const NodeId id, const DelayType delay_type) const {
if (delay_type == DelayType::MAX) {
auto range = max_output_constraints_.equal_range(id);
return tatum::util::make_range(range.first, range.second);
} else {
TATUM_ASSERT(delay_type == DelayType::MIN);
auto range = min_output_constraints_.equal_range(id);
return tatum::util::make_range(range.first, range.second);
}
}
TimingConstraints::source_latency_range TimingConstraints::source_latencies(ArrivalType arrival_type) const {
if (arrival_type == ArrivalType::EARLY) {
return tatum::util::make_range(source_latencies_early_.begin(), source_latencies_early_.end());
} else {
TATUM_ASSERT(arrival_type == ArrivalType::LATE);
return tatum::util::make_range(source_latencies_late_.begin(), source_latencies_late_.end());
}
}
DomainId TimingConstraints::create_clock_domain(const std::string name) {
DomainId id = find_clock_domain(name);
if(!id) {
//Create it
id = DomainId(domain_ids_.size());
domain_ids_.push_back(id);
domain_names_.push_back(name);
domain_sources_.emplace_back(NodeId::INVALID());
TATUM_ASSERT(clock_domain_name(id) == name);
TATUM_ASSERT(find_clock_domain(name) == id);
}
return id;
}
void TimingConstraints::set_setup_constraint(const DomainId src_domain, const DomainId sink_domain, const Time constraint) {
set_setup_constraint(src_domain, sink_domain, NodeId::INVALID(), constraint);
}
void TimingConstraints::set_setup_constraint(const DomainId src_domain, const DomainId sink_domain, const NodeId capture_node, const Time constraint) {
auto key = NodeDomainPair(src_domain, sink_domain, capture_node);
setup_constraints_[key] = constraint;
}
void TimingConstraints::set_hold_constraint(const DomainId src_domain, const DomainId sink_domain, const Time constraint) {
set_hold_constraint(src_domain, sink_domain, NodeId::INVALID(), constraint);
}
void TimingConstraints::set_hold_constraint(const DomainId src_domain, const DomainId sink_domain, const NodeId capture_node, const Time constraint) {
auto key = NodeDomainPair(src_domain, sink_domain, capture_node);
hold_constraints_[key] = constraint;
}
void TimingConstraints::set_setup_clock_uncertainty(const DomainId src_domain, const DomainId sink_domain, const Time uncertainty) {
auto key = DomainPair(src_domain, sink_domain);
setup_clock_uncertainties_[key] = uncertainty;
}
void TimingConstraints::set_hold_clock_uncertainty(const DomainId src_domain, const DomainId sink_domain, const Time uncertainty) {
auto key = DomainPair(src_domain, sink_domain);
hold_clock_uncertainties_[key] = uncertainty;
}
void TimingConstraints::set_input_constraint(const NodeId node_id, const DomainId domain_id, const DelayType delay_type, const Time constraint) {
if (delay_type == DelayType::MAX) {
auto iter = find_io_constraint(node_id, domain_id, max_input_constraints_);
if(iter != max_input_constraints_.end()) {
//Found, update
iter->second.constraint = constraint;
} else {
//Not found create it
max_input_constraints_.insert(std::make_pair(node_id, IoConstraint(domain_id, constraint)));
}
} else {
TATUM_ASSERT(delay_type == DelayType::MIN);
auto iter = find_io_constraint(node_id, domain_id, min_input_constraints_);
if(iter != min_input_constraints_.end()) {
//Found, update
iter->second.constraint = constraint;
} else {
//Not found create it
min_input_constraints_.insert(std::make_pair(node_id, IoConstraint(domain_id, constraint)));
}
}
}
void TimingConstraints::set_output_constraint(const NodeId node_id, const DomainId domain_id, const DelayType delay_type, const Time constraint) {
if (delay_type == DelayType::MAX) {
auto iter = find_io_constraint(node_id, domain_id, max_output_constraints_);
if(iter != max_output_constraints_.end()) {
//Found, update
iter->second.constraint = constraint;
} else {
//Not found create it
max_output_constraints_.insert(std::make_pair(node_id, IoConstraint(domain_id, constraint)));
}
} else {
TATUM_ASSERT(delay_type == DelayType::MIN);
auto iter = find_io_constraint(node_id, domain_id, min_output_constraints_);
if(iter != min_output_constraints_.end()) {
//Found, update
iter->second.constraint = constraint;
} else {
//Not found create it
min_output_constraints_.insert(std::make_pair(node_id, IoConstraint(domain_id, constraint)));
}
}
}
void TimingConstraints::set_source_latency(const DomainId domain, const ArrivalType arrival_type, const Time latency) {
if (arrival_type == ArrivalType::EARLY) {
source_latencies_early_[domain] = latency;
} else {
TATUM_ASSERT(arrival_type == ArrivalType::LATE);
source_latencies_late_[domain] = latency;
}
}
void TimingConstraints::set_clock_domain_source(const NodeId node_id, const DomainId domain_id) {
domain_sources_[domain_id] = node_id;
}
void TimingConstraints::set_constant_generator(const NodeId node_id, bool is_constant_generator) {
if(is_constant_generator) {
constant_generators_.insert(node_id);
} else {
constant_generators_.erase(node_id);
}
}
void TimingConstraints::remap_nodes(const tatum::util::linear_map<NodeId,NodeId>& node_map) {
//Domain Sources
tatum::util::linear_map<DomainId,NodeId> remapped_domain_sources(domain_sources_.size());
for(size_t domain_idx = 0; domain_idx < domain_sources_.size(); ++domain_idx) {
DomainId domain_id(domain_idx);
NodeId old_node_id = domain_sources_[domain_id];
if(old_node_id) {
remapped_domain_sources[domain_id] = node_map[old_node_id];
}
}
domain_sources_ = std::move(remapped_domain_sources);
//Constant generators
std::unordered_set<NodeId> remapped_constant_generators;
for(NodeId node_id : constant_generators_) {
remapped_constant_generators.insert(node_map[node_id]);
}
constant_generators_ = std::move(remapped_constant_generators);
//Max Input Constraints
std::multimap<NodeId,IoConstraint> remapped_max_input_constraints;
for(auto kv : max_input_constraints_) {
NodeId new_node_id = node_map[kv.first];
remapped_max_input_constraints.insert(std::make_pair(new_node_id, kv.second));
}
max_input_constraints_ = std::move(remapped_max_input_constraints);
//Min Input Constraints
std::multimap<NodeId,IoConstraint> remapped_min_input_constraints;
for(auto kv : min_input_constraints_) {
NodeId new_node_id = node_map[kv.first];
remapped_min_input_constraints.insert(std::make_pair(new_node_id, kv.second));
}
min_input_constraints_ = std::move(remapped_min_input_constraints);
//Max Output Constraints
std::multimap<NodeId,IoConstraint> remapped_max_output_constraints;
for(auto kv : max_output_constraints_) {
NodeId new_node_id = node_map[kv.first];
remapped_max_output_constraints.insert(std::make_pair(new_node_id, kv.second));
}
max_output_constraints_ = std::move(remapped_max_output_constraints);
//Min Output Constraints
std::multimap<NodeId,IoConstraint> remapped_min_output_constraints;
for(auto kv : min_output_constraints_) {
NodeId new_node_id = node_map[kv.first];
remapped_min_output_constraints.insert(std::make_pair(new_node_id, kv.second));
}
min_output_constraints_ = std::move(remapped_min_output_constraints);
}
void TimingConstraints::print_constraints() const {
cout << "Setup Clock Constraints" << endl;
for(auto kv : setup_constraints()) {
auto key = kv.first;
Time constraint = kv.second;
cout << "SRC: " << key.domain_pair.src_domain_id;
cout << " SINK: " << key.domain_pair.sink_domain_id;
cout << " CAPTURE_NODE: " << key.capture_node;
cout << " Constraint: " << constraint;
cout << endl;
}
cout << "Hold Clock Constraints" << endl;
for(auto kv : hold_constraints()) {
auto key = kv.first;
Time constraint = kv.second;
cout << "SRC: " << key.domain_pair.src_domain_id;
cout << " SINK: " << key.domain_pair.sink_domain_id;
cout << " CAPTURE_NODE: " << key.capture_node;
cout << " Constraint: " << constraint;
cout << endl;
}
cout << "Max Input Constraints" << endl;
for(auto kv : input_constraints(DelayType::MAX)) {
auto node_id = kv.first;
auto io_constraint = kv.second;
cout << "Node: " << node_id;
cout << " Domain: " << io_constraint.domain;
cout << " Constraint: " << io_constraint.constraint;
cout << endl;
}
cout << "Min Input Constraints" << endl;
for(auto kv : input_constraints(DelayType::MIN)) {
auto node_id = kv.first;
auto io_constraint = kv.second;
cout << "Node: " << node_id;
cout << " Domain: " << io_constraint.domain;
cout << " Constraint: " << io_constraint.constraint;
cout << endl;
}
cout << "Max Output Constraints" << endl;
for(auto kv : output_constraints(DelayType::MAX)) {
auto node_id = kv.first;
auto io_constraint = kv.second;
cout << "Node: " << node_id;
cout << " Domain: " << io_constraint.domain;
cout << " Constraint: " << io_constraint.constraint;
cout << endl;
}
cout << "Min Output Constraints" << endl;
for(auto kv : output_constraints(DelayType::MIN)) {
auto node_id = kv.first;
auto io_constraint = kv.second;
cout << "Node: " << node_id;
cout << " Domain: " << io_constraint.domain;
cout << " Constraint: " << io_constraint.constraint;
cout << endl;
}
cout << "Setup Clock Uncertainty" << endl;
for(auto kv : setup_clock_uncertainties()) {
auto key = kv.first;
Time uncertainty = kv.second;
cout << "SRC: " << key.src_domain_id;
cout << " SINK: " << key.sink_domain_id;
cout << " Uncertainty: " << uncertainty;
cout << endl;
}
cout << "Hold Clock Uncertainty" << endl;
for(auto kv : hold_clock_uncertainties()) {
auto key = kv.first;
Time uncertainty = kv.second;
cout << "SRC: " << key.src_domain_id;
cout << " SINK: " << key.sink_domain_id;
cout << " Uncertainty: " << uncertainty;
cout << endl;
}
cout << "Early Source Latency" << endl;
for(auto kv : source_latencies(ArrivalType::EARLY)) {
auto domain = kv.first;
Time latency = kv.second;
cout << "Domain: " << domain;
cout << " Latency: " << latency;
cout << endl;
}
cout << "Late Source Latency" << endl;
for(auto kv : source_latencies(ArrivalType::LATE)) {
auto domain = kv.first;
Time latency = kv.second;
cout << "Domain: " << domain;
cout << " Latency: " << latency;
cout << endl;
}
}
TimingConstraints::io_constraint_iterator TimingConstraints::find_io_constraint(const NodeId node_id, const DomainId domain_id, const std::multimap<NodeId,IoConstraint>& io_constraints) const {
auto range = io_constraints.equal_range(node_id);
for(auto iter = range.first; iter != range.second; ++iter) {
if(iter->second.domain == domain_id) return iter;
}
//Not found
return io_constraints.end();
}
TimingConstraints::mutable_io_constraint_iterator TimingConstraints::find_io_constraint(const NodeId node_id, const DomainId domain_id, std::multimap<NodeId,IoConstraint>& io_constraints) {
auto range = io_constraints.equal_range(node_id);
for(auto iter = range.first; iter != range.second; ++iter) {
if(iter->second.domain == domain_id) return iter;
}
//Not found
return io_constraints.end();
}
} //namepsace