diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/tags/TimingTag.hpp b/libs/EXTERNAL/libtatum/libtatum/tatum/tags/TimingTag.hpp new file mode 100644 index 000000000..5aa9eca23 --- /dev/null +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/tags/TimingTag.hpp @@ -0,0 +1,156 @@ +#pragma once +#include +#include + +#include "tatum/Time.hpp" +#include "tatum/TimingGraphFwd.hpp" + +namespace tatum { + +enum class TagType : unsigned char { + CLOCK_LAUNCH, + CLOCK_CAPTURE, + DATA_ARRIVAL, + DATA_REQUIRED, + SLACK, + UNKOWN +}; + +class TimingTag; + +std::ostream& operator<<(std::ostream& os, TagType type); + +bool is_const_gen_tag(const TimingTag& tag); + +/** + * The 'TimingTag' class represents an individual timing tag: the information associated + * with a node's arrival/required times. + * + * + * This primarily includes the actual arrival and required time, but also + * auxillary metadata such as the clock domain, and launching node. + * + * The clock domain in particular is used to tag arrival/required times from different + * clock domains at a single node. This enables us to perform only a single graph traversal + * and still analyze mulitple clocks. + * + * NOTE: Timing analyzers usually operate on the collection of tags at a particular node. + * This is modelled by the separate 'TimingTags' class. + */ +class TimingTag { + public: //Static + //Returns a tag suitable for use at a constant generator, during + //setup analysis. + // + ///In particular it's domains are left invalid (i.e. wildcards), + //and it's arrival time set to -inf (so it will be maxed out by + //any non-constant tag) + static TimingTag CONST_GEN_TAG_SETUP() { + return TimingTag(Time(-std::numeric_limits::infinity()), + DomainId::INVALID(), + DomainId::INVALID(), + NodeId::INVALID(), + TagType::DATA_ARRIVAL); + } + + //Returns a tag suitable for use at a constant generator, during + //hold analysis. + // + ///In particular it's domains are left invalid (i.e. wildcards), + //and it's arrival time set to +inf (so it will be minned out by + //any non-constant tag) + static TimingTag CONST_GEN_TAG_HOLD() { + return TimingTag(Time(+std::numeric_limits::infinity()), + DomainId::INVALID(), + DomainId::INVALID(), + NodeId::INVALID(), + TagType::DATA_ARRIVAL); + } + public: //Constructors + TimingTag(); + + ///\param arr_time_val The tagged arrival time + ///\param req_time_val The tagged required time + ///\param launch_domain The clock domain the tag was launched from + ///\param capture_domain The clock domain the tag was captured on + ///\param node The origin node's id (e.g. source/sink that originally launched/required this tag) + TimingTag(const Time& time_val, DomainId launch_domain, DomainId capture_domain, NodeId node, TagType type); + + ///\param arr_time_val The tagged arrival time + ///\param req_time_val The tagged required time + ///\param base_tag The tag from which to copy auxilary meta-data (e.g. domain, launch node) + TimingTag(const Time& time_val, NodeId origin, const TimingTag& base_tag); + + + public: //Accessors + ///\returns This tag's arrival time + const Time& time() const { return time_; } + + ///\returns This tag's launching clock domain + DomainId launch_clock_domain() const { return launch_clock_domain_; } + DomainId capture_clock_domain() const { return capture_clock_domain_; } + + ///\returns This tag's launching node's id + NodeId origin_node() const { return origin_node_; } + + TagType type() const { return type_; } + + public: //Mutators + ///\param new_time The new value set as the tag's time + void set_time(const Time& new_time) { time_ = new_time; } + + ///\param new_clock_domain The new value set as the tag's source clock domain + void set_launch_clock_domain(const DomainId new_clock_domain) { launch_clock_domain_ = new_clock_domain; } + + ///\param new_clock_domain The new value set as the tag's capture clock domain + void set_capture_clock_domain(const DomainId new_clock_domain) { capture_clock_domain_ = new_clock_domain; } + + ///\param new_launch_node The new value set as the tag's launching node + void set_origin_node(const NodeId new_origin_node) { origin_node_ = new_origin_node; } + + void set_type(const TagType new_type) { type_ = new_type; } + + /* + * Modification operations + * For the following the passed in time is maxed/minned with the + * respective arr/req time. If the value of this tag is updated + * the meta-data (domain, launch node etc) are copied from the + * base tag + */ + ///Updates the tag's arrival time if new_arr_time is larger than the current arrival time. + ///If the arrival time is updated, meta-data is also updated from base_tag + ///\param new_arr_time The arrival time to compare against + ///\param base_tag The tag from which meta-data is copied + void max(const Time& new_time, const NodeId origin, const TimingTag& base_tag); + + ///Updates the tag's arrival time if new_arr_time is smaller than the current arrival time. + ///If the arrival time is updated, meta-data is also updated from base_tag + ///\param new_arr_time The arrival time to compare against + ///\param base_tag The tag from which meta-data is copied + void min(const Time& new_time, const NodeId origin, const TimingTag& base_tag); + + private: + void update(const Time& new_time, const NodeId origin, const TimingTag& base_tag); + + /* + * Data + */ + Time time_; //Required time + NodeId origin_node_; //Node which launched this arr/req time + DomainId launch_clock_domain_; //Clock domain for arr/req times + DomainId capture_clock_domain_; //Clock domain for arr/req times + TagType type_; +}; + + +//For comparing the values of two timing tags +struct TimingTagValueComp { + bool operator()(const tatum::TimingTag& lhs, const tatum::TimingTag& rhs) { + return lhs.time().value() < rhs.time().value(); + } +}; + +} //namepsace + +//Implementation +#include "TimingTag.inl" diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/tags/TimingTag.inl b/libs/EXTERNAL/libtatum/libtatum/tatum/tags/TimingTag.inl new file mode 100644 index 000000000..e65d7ca7b --- /dev/null +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/tags/TimingTag.inl @@ -0,0 +1,99 @@ +#include "tatum/util/tatum_assert.hpp" + +namespace tatum { + +/* + * TimingTag implementation + */ + +inline TimingTag::TimingTag() + : time_(NAN) + , origin_node_(NodeId::INVALID()) + , launch_clock_domain_(DomainId::INVALID()) + , capture_clock_domain_(DomainId::INVALID()) + , type_(TagType::UNKOWN) + {} + +inline TimingTag::TimingTag(const Time& time_val, + const DomainId launch_domain, + const DomainId capture_domain, + const NodeId node, + const TagType new_type) + : time_(time_val) + , origin_node_(node) + , launch_clock_domain_(launch_domain) + , capture_clock_domain_(capture_domain) + , type_(new_type) + {} + +inline TimingTag::TimingTag(const Time& time_val, NodeId origin, const TimingTag& base_tag) + : time_(time_val) + , origin_node_(origin) + , launch_clock_domain_(base_tag.launch_clock_domain()) + , capture_clock_domain_(base_tag.capture_clock_domain()) + , type_(base_tag.type()) + {} + + +inline void TimingTag::update(const Time& new_time, const NodeId origin, const TimingTag& base_tag) { + TATUM_ASSERT(type() == base_tag.type()); //Type must be the same + + //Note that we check for a constant tag first, since we might + //update the time (which is used to verify whether the tag is a constant + if(is_const_gen_tag(*this)) { + + //If the tag is a constant, then we want to replace it with the + //first non-constant tag which comes along. This ensures that + //any constant tags get 'swallowed' by real tags and do not + //continue to propagate through the timing graph + set_launch_clock_domain(base_tag.launch_clock_domain()); + set_capture_clock_domain(base_tag.capture_clock_domain()); + } + + TATUM_ASSERT(( launch_clock_domain() == base_tag.launch_clock_domain()) + && capture_clock_domain() == base_tag.capture_clock_domain()); //Same domains + + //Update the tag + set_time(new_time); + set_origin_node(origin); + +} + +inline void TimingTag::max(const Time& new_time, const NodeId origin, const TimingTag& base_tag) { + //Need to min with existing value + if(!time().valid() || new_time > time()) { + //New value is smaller, or no previous valid value existed + //Update min + + update(new_time, origin, base_tag); + } +} + +inline void TimingTag::min(const Time& new_time, const NodeId origin, const TimingTag& base_tag) { + //Need to min with existing value + if(!time().valid() || new_time < time()) { + //New value is smaller, or no previous valid value existed + //Update min + update(new_time, origin, base_tag); + } +} + +inline std::ostream& operator<<(std::ostream& os, TagType type) { + if(type == TagType::DATA_ARRIVAL) os << "DATA_ARRIVAL"; + else if(type == TagType::DATA_REQUIRED) os << "DATA_REQUIRED"; + else if(type == TagType::CLOCK_LAUNCH) os << "CLOCK_LAUNCH"; + else if(type == TagType::CLOCK_CAPTURE) os << "CLOCK_CAPTURE"; + else if(type == TagType::SLACK) os << "SLACK"; + else TATUM_ASSERT_MSG(false, "Unrecognized TagType"); + return os; +} + +//Returns true, if tag is a tag from a constant generator +inline bool is_const_gen_tag(const TimingTag& tag) { + return !tag.launch_clock_domain() //Wildcard launch + && !tag.capture_clock_domain() //Wildcard capture + && std::isinf(tag.time().value()); //inf arrival, we allow +/- inf since different values may be used for setup/hold +} + + +} //namepsace diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/tags/TimingTags.cpp b/libs/EXTERNAL/libtatum/libtatum/tatum/tags/TimingTags.cpp new file mode 100644 index 000000000..63f9ab5ce --- /dev/null +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/tags/TimingTags.cpp @@ -0,0 +1,79 @@ +#include "tatum/tags/TimingTags.hpp" +#include "tatum/TimingGraphFwd.hpp" +#include "tatum/error.hpp" +namespace tatum { + +/* + * Tag utilities + */ +//Return the tag from the range [first,last) which has the lowest value +TimingTags::const_iterator find_minimum_tag(TimingTags::tag_range tags) { + + return std::min_element(tags.begin(), tags.end(), TimingTagValueComp()); +} + +//Return the tag from the range [first,last) which has the highest value +TimingTags::const_iterator find_maximum_tag(TimingTags::tag_range tags) { + + return std::max_element(tags.begin(), tags.end(), TimingTagValueComp()); +} + +TimingTags::const_iterator find_tag(TimingTags::tag_range tags, + DomainId launch_domain, + DomainId capture_domain) { + for(auto iter = tags.begin(); iter != tags.end(); ++iter) { + if(iter->launch_clock_domain() == launch_domain && iter->capture_clock_domain() == capture_domain) { + return iter; + } + } + + return tags.end(); +} + +//Returns true of the specified set of tags would constrain a node of type node_type +bool is_constrained(NodeType node_type, TimingTags::tag_range tags) { + bool has_clock_launch = false; + bool has_clock_capture = false; + bool has_data_required = false; + + for(const auto& tag : tags) { + if(tag.type() == TagType::CLOCK_LAUNCH) { + has_clock_launch = true; + } else if (tag.type() == TagType::CLOCK_CAPTURE) { + has_clock_capture = true; + } else if (tag.type() == TagType::DATA_REQUIRED) { + has_data_required = true; + } + } + + bool constrained = false; + + if(node_type == NodeType::SINK) { + //Constrained data nodes should have required times + // + //However, if a clock is generated on-chip (e.g. PLL) it may + //be driven off-chip so a primary-output sink may have only + //clock launch tags + constrained = has_data_required || has_clock_launch; + + } else if(node_type == NodeType::SOURCE) { + //Constrained data nodes should have required times + // + //However, clock sources may have only clock tags + constrained = has_data_required || (has_clock_launch && has_clock_capture); + + } else if(node_type == NodeType::IPIN || node_type == NodeType::OPIN) { + //Required time indicates a datapath node is constrained + constrained = has_data_required; + + } else if(node_type == NodeType::CPIN) { + //Clock pins should have both launch and capture to be constrained + constrained = has_clock_launch && has_clock_capture; + } else { + throw Error("Unrecognized node type"); + } + + return constrained; +} + +} diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/tags/TimingTags.hpp b/libs/EXTERNAL/libtatum/libtatum/tatum/tags/TimingTags.hpp new file mode 100644 index 000000000..0c1c78315 --- /dev/null +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/tags/TimingTags.hpp @@ -0,0 +1,223 @@ +#pragma once +#include +#include + +#include "tatum/tags/TimingTag.hpp" +#include "tatum/util/tatum_range.hpp" + +namespace tatum { + + +/** + * The 'TimingTags' class represents a collection of timing tags (see the 'TimingTag' class) + * that belong to a particular node in the timing graph. + * + * Any operations performed using this task generally consider *all* associated tags. + * For example, preforming a max_arr() call will apply the max accross all tags with matching + * clock domain. If no matching tag is found, the tag will be added to the set of tags. + * + * Implementation + * ==================== + * Since each node in the timing graph typically has only a few tags (usually 1 or 2), we + * perform linear searches to find match tags and tag ranges. + * + * Note that to allow efficient iteration of tag ranges (by type) we ensure that tags of the + * same type are adjacent in the storage vector (i.e. the vector is sorted by type) + */ +class TimingTags { + public: + template + class Iterator; + private: + //In practice the vast majority of nodes have only a handful of tags, + //so we reserve space for some to avoid costly memory allocations + constexpr static size_t DEFAULT_TAGS_TO_RESERVE = 3; + constexpr static size_t GROWTH_FACTOR = 2; + + public: + + typedef Iterator iterator; + typedef Iterator const_iterator; + + typedef tatum::util::Range tag_range; + + public: + + //Constructors + TimingTags(size_t num_reserve=DEFAULT_TAGS_TO_RESERVE); + TimingTags(const TimingTags&); + TimingTags(TimingTags&&); + TimingTags& operator=(TimingTags); + ~TimingTags() = default; + friend void swap(TimingTags& lhs, TimingTags& rhs); + + /* + * Getters + */ + ///\returns The number of timing tags in this set + size_t size() const; + bool empty() const { return size() == 0; } + + ///\returns A range of all tags + tag_range tags() const; + + ///\returns A range of all tags matching type + tag_range tags(const TagType type) const; + + + /* + * Modifiers + */ + ///Adds a TimingTag to the current set provided it has a valid clock domain + ///\param tag_pool The pool memory allocator used to allocate the tag + ///\param src_tag The source tag who is inserted. Note that the src_tag is copied when inserted (the original is unchanged) + void add_tag(const TimingTag& src_tag); + + /* + * Operations + */ + ///Updates the arrival time of this set of tags to be the maximum. + ///\param new_time The new arrival time to compare against + ///\param base_tag The associated metat-data for new_time + ///\remark Finds (or creates) the tag with the same clock domain as base_tag and update the arrival time if new_time is larger + void max(const Time& new_time, const NodeId origin, const TimingTag& base_tag, bool arr_must_be_valid=false); + + ///Updates the required time of this set of tags to be the minimum. + ///\param new_time The new arrival time to compare against + ///\param base_tag The associated metat-data for new_time + ///\remark Finds (or creates) the tag with the same clock domain as base_tag and update the required time if new_time is smaller + void min(const Time& new_time, const NodeId origin, const TimingTag& base_tag, bool arr_must_be_valid=false); + + ///Clears the tags in the current set + void clear(); + + public: + + //Iterator definition + template + class Iterator : public std::iterator { + friend TimingTags; + public: + using value_type = typename std::iterator::value_type; + using difference_type = typename std::iterator::difference_type; + using pointer = typename std::iterator::pointer; + using reference = typename std::iterator::reference; + using iterator_category = typename std::iterator::iterator_category; + public: + Iterator(): p_(nullptr) {} + Iterator(pointer p): p_(p) {} + Iterator(const Iterator& other): p_(other.p_) {} + Iterator& operator=(const Iterator& other) { p_ = other.p_; return *this; } + + friend bool operator==(Iterator a, Iterator b) { return a.p_ == b.p_; } + friend bool operator!=(Iterator a, Iterator b) { return a.p_ != b.p_; } + + reference operator*() { return *p_; } + const reference operator*() const { return *p_; } //Required for MSVC (gcc/clang are fine with only the non-cost version) + pointer operator->() { return p_; } + reference operator[](size_t n) { return *(p_ + n); } + + Iterator& operator++() { ++p_; return *this; } + Iterator operator++(int) { Iterator old = *this; ++p_; return old; } + Iterator& operator--() { --p_; return *this; } + Iterator operator--(int) { Iterator old = *this; --p_; return old; } + Iterator& operator+=(size_t n) { p_ += n; return *this; } + Iterator& operator-=(size_t n) { p_ -= n; return *this; } + friend Iterator operator+(Iterator lhs, size_t rhs) { return lhs += rhs; } + friend Iterator operator-(Iterator lhs, size_t rhs) { return lhs -= rhs; } + + friend difference_type operator-(const Iterator lhs, const Iterator rhs) { return lhs.p_ - rhs.p_; } + + friend bool operator<(Iterator lhs, Iterator rhs) { return lhs.p_ < rhs.p_; } + friend bool operator>(Iterator lhs, Iterator rhs) { return lhs.p_ > rhs.p_; } + friend bool operator<=(Iterator lhs, Iterator rhs) { return lhs.p_ <= rhs.p_; } + friend bool operator>=(Iterator lhs, Iterator rhs) { return lhs.p_ >= rhs.p_; } + friend void swap(Iterator lhs, Iterator rhs) { std::swap(lhs.p_, rhs.p_); } + private: + T* p_ = nullptr; + }; + + private: + + ///\returns An iterator to the first tag in the current set + iterator begin(); + const_iterator begin() const; + iterator begin(TagType type); + const_iterator begin(TagType type) const; + + ///\returns An iterator 'one-past-the-end' of the current set + iterator end(); + const_iterator end() const; + iterator end(TagType type); + const_iterator end(TagType type) const; + + size_t capacity() const; + + ///Finds a timing tag in the current set which matches tag + ///\returns A pair of bool and iterator. + // The bool is true if it is valid for iterator to be processed. + // The iterator is not equal to end(tag.type()) if a matching tag was found + std::pair find_matching_tag(const TimingTag& tag, bool arr_must_be_valid); + + ///Finds a TimingTag in the current set that matches the launch and capture clocks of tag + ///\returns An iterator to the tag if found, or end(type) if not found + iterator find_matching_tag(TagType type, DomainId launch_domain, DomainId capture_domain); + + ///Find a TimingTag matching the specified DATA_REQUIRED tag provided there is + //a valid associated DATA_ARRIVAL tag + ///\returns A a pair of bool and iterator. The bool indicates if a valid arrival was found, + /// the iterator is the required tag matching launch and capture which has a valid + // corresponding arrival time, or end(TagType::DATA_REQUIRED) + std::pair find_data_required_with_valid_data_arrival(DomainId launch_domain, DomainId capture_domain); + + + iterator insert(iterator iter, const TimingTag& tag); + void grow_insert(size_t index, const TimingTag& tag); + + void increment_size(TagType type); + + + private: + //We don't expect many tags in a node so unsigned short's/unsigned char's + //should be more than sufficient. This also allows the class + //to be packed down to 16 bytes (8 for counters, 8 for pointer) + // + //In its current configuration we can store at most: + // 65536 total tags (size_ and capacity_) + // 256 clock launch tags (num_clock_launch_tags_) + // 256 clock capture tags (num_clock_capture_tags_) + // 256 data arrival tags (num_data_arrival_tags_) + // 256 data required tags (num_data_required_tags_) + // (65536 - 4*256) slack tags (size_ - num_*) + unsigned short size_ = 0; + unsigned short capacity_ = 0; + unsigned char num_clock_launch_tags_ = 0; + unsigned char num_clock_capture_tags_ = 0; + unsigned char num_data_arrival_tags_ = 0; + unsigned char num_data_required_tags_ = 0; + std::unique_ptr tags_; + +}; + +/* + * Tag utilities + */ + +//Return the tag from the range [first,last) which has the lowest value +TimingTags::const_iterator find_minimum_tag(TimingTags::tag_range tags); + +//Return the tag from the range [first,last) which has the highest value +TimingTags::const_iterator find_maximum_tag(TimingTags::tag_range tags); + +//Return the tag for the specified clock domains +TimingTags::const_iterator find_tag(TimingTags::tag_range tags, + DomainId launch_domain, + DomainId capture_domain); + +//Returns true of the specified set of tags would constrain a node of type node_type +bool is_constrained(NodeType node_type, TimingTags::tag_range tags); + +} //namepsace + +//Implementation +#include "TimingTags.inl" diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/tags/TimingTags.inl b/libs/EXTERNAL/libtatum/libtatum/tatum/tags/TimingTags.inl new file mode 100644 index 000000000..05e614396 --- /dev/null +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/tags/TimingTags.inl @@ -0,0 +1,327 @@ +#include +#include "tatum/util/tatum_assert.hpp" + +namespace tatum { + +/* + * TimingTags implementation + */ + +//TODO: given that we know we typically add tags in CLOCK_LAUNCH, DATA_ARRIVAL, CLOCK_CAPTURE, DATA_REQUIRED +// order, we should probably order their storage that way + +inline TimingTags::TimingTags(size_t num_reserve) + : size_(0) + , capacity_(num_reserve) + , num_clock_launch_tags_(0) + , num_clock_capture_tags_(0) + , num_data_arrival_tags_(0) + , num_data_required_tags_(0) + , tags_(capacity_ ? new TimingTag[capacity_] : nullptr) + {} + +inline TimingTags::TimingTags(const TimingTags& other) + : size_(other.size()) + , capacity_(size_) + , num_clock_launch_tags_(other.num_clock_launch_tags_) + , num_clock_capture_tags_(other.num_clock_capture_tags_) + , num_data_arrival_tags_(other.num_data_arrival_tags_) + , num_data_required_tags_(other.num_data_required_tags_) + , tags_(capacity_ ? new TimingTag[capacity_] : nullptr) { + std::copy(other.tags_.get(), other.tags_.get() + other.size(), tags_.get()); +} + +inline TimingTags::TimingTags(TimingTags&& other) + : TimingTags(0) { + swap(*this, other); +} + +inline TimingTags& TimingTags::operator=(TimingTags other) { + swap(*this, other); + return *this; +} + +inline size_t TimingTags::size() const { + return size_; +} + +inline TimingTags::iterator TimingTags::begin() { + auto iter = iterator(tags_.get()); + + return iter; +} + +inline TimingTags::const_iterator TimingTags::begin() const { + return const_iterator(tags_.get()); +} + +inline TimingTags::iterator TimingTags::begin(TagType type) { + const_iterator const_iter = const_cast(this)->begin(type); + return iterator(const_cast(const_iter.p_)); +} + +inline TimingTags::const_iterator TimingTags::begin(TagType type) const { + const_iterator iter; + switch(type) { + case TagType::CLOCK_LAUNCH: + iter = begin(); + break; + case TagType::CLOCK_CAPTURE: + iter = begin() + num_clock_launch_tags_; + break; + case TagType::DATA_ARRIVAL: + iter = begin() + num_clock_launch_tags_ + num_clock_capture_tags_; + break; + case TagType::DATA_REQUIRED: + iter = begin() + num_clock_launch_tags_ + num_clock_capture_tags_ + num_data_arrival_tags_; + break; + case TagType::SLACK: + iter = begin() + num_clock_launch_tags_ + num_clock_capture_tags_ + num_data_arrival_tags_ + num_data_required_tags_; + break; + default: + TATUM_ASSERT_MSG(false, "Invalid tag type"); + } + return iter; +} + +inline TimingTags::iterator TimingTags::end() { + const_iterator const_iter = const_cast(this)->end(); + return iterator(const_cast(const_iter.p_)); +} + +inline TimingTags::const_iterator TimingTags::end() const { + auto iter = const_iterator(tags_.get() + size_); + TATUM_ASSERT_SAFE(iter.p_ >= tags_.get() && iter.p_ <= tags_.get() + size()); + return iter; +} + + +inline TimingTags::iterator TimingTags::end(TagType type) { + const_iterator const_iter = const_cast(this)->end(type); + return iterator(const_cast(const_iter.p_)); +} + +inline TimingTags::const_iterator TimingTags::end(TagType type) const { + const_iterator iter; + switch(type) { + case TagType::CLOCK_LAUNCH: + iter = begin(TagType::CLOCK_CAPTURE); + break; + case TagType::CLOCK_CAPTURE: + iter = begin(TagType::DATA_ARRIVAL); + break; + case TagType::DATA_ARRIVAL: + iter = begin(TagType::DATA_REQUIRED); + break; + case TagType::DATA_REQUIRED: + iter = begin(TagType::SLACK); + break; + case TagType::SLACK: + //Pass the true end + iter = end(); + break; + default: + TATUM_ASSERT_MSG(false, "Invalid tag type"); + } + TATUM_ASSERT_SAFE(iter.p_ >= tags_.get() && iter.p_ <= tags_.get() + size()); + return iter; +} + +inline TimingTags::tag_range TimingTags::tags() const { + return tatum::util::make_range(begin(), end()); +} + +inline TimingTags::tag_range TimingTags::tags(const TagType type) const { + return tatum::util::make_range(begin(type), end(type)); +} + +//Modifiers +inline void TimingTags::add_tag(const TimingTag& tag) { + //Find the position to insert this tag + // + //We keep tags of the same type together. + //We also prefer to insert new tags at the end if possible + //(since this is more efficient for the underlying vector storage) + + auto iter = end(tag.type()); + + //Insert the tag before the upper bound position + // This ensures tags_ is always in sorted order + insert(iter, tag); +} + +inline void TimingTags::max(const Time& new_time, const NodeId origin, const TimingTag& base_tag, bool arr_must_be_valid) { + auto bool_iter = find_matching_tag(base_tag, arr_must_be_valid); + + bool valid = bool_iter.first; + if(valid) { + auto iter = bool_iter.second; + if(iter == end(base_tag.type())) { + //An exact match was not found + + //First time we've seen this domain + TimingTag tag = TimingTag(new_time, origin, base_tag); + add_tag(tag); + } else { + iter->max(new_time, origin, base_tag); + } + } +} + +inline void TimingTags::min(const Time& new_time, const NodeId origin, const TimingTag& base_tag, bool arr_must_be_valid) { + auto bool_iter = find_matching_tag(base_tag, arr_must_be_valid); + + bool valid = bool_iter.first; + if(valid) { + auto iter = bool_iter.second; + if(iter == end(base_tag.type())) { + //An exact match was not found + + //First time we've seen this domain + TimingTag tag = TimingTag(new_time, origin, base_tag); + add_tag(tag); + } else { + iter->min(new_time, origin, base_tag); + } + } +} + +inline void TimingTags::clear() { + size_ = 0; + num_clock_launch_tags_ = 0; + num_clock_capture_tags_ = 0; + num_data_arrival_tags_ = 0; + num_data_required_tags_ = 0; +} + +inline std::pair TimingTags::find_matching_tag(const TimingTag& tag, bool arr_must_be_valid) { + if(arr_must_be_valid) { + TATUM_ASSERT(tag.type() == TagType::DATA_REQUIRED); + auto bool_iter = find_data_required_with_valid_data_arrival(tag.launch_clock_domain(), tag.capture_clock_domain()); + return bool_iter; + } else { + auto iter = find_matching_tag(tag.type(), tag.launch_clock_domain(), tag.capture_clock_domain()); + return {true, iter}; + } +} + +inline TimingTags::iterator TimingTags::find_matching_tag(TagType type, DomainId launch_domain, DomainId capture_domain) { + //Linear search for matching tag + auto b = begin(type); + auto e = end(type); + for(auto iter = b; iter != e; ++iter) { + bool match_launch = !launch_domain //Search wildcard + || !iter->launch_clock_domain() //Match wildcard + || launch_domain == iter->launch_clock_domain(); //Exact match + + bool match_capture = !capture_domain //Search wildcard + || !iter->capture_clock_domain() //Match wildcard + || capture_domain == iter->capture_clock_domain(); //Exact match + + if(match_launch && match_capture) { + return iter; + } + } + return e; +} + +inline std::pair TimingTags::find_data_required_with_valid_data_arrival(DomainId launch_domain, DomainId capture_domain) { + //Look for the matching arrival + auto arr_iter = find_matching_tag(TagType::DATA_ARRIVAL, launch_domain, DomainId::INVALID()); + if(arr_iter == end(TagType::DATA_ARRIVAL) || !arr_iter->time().valid()) { + //No valid arrival + return {false, end(TagType::DATA_REQUIRED)}; + } + + //Find the matching required + return {true, find_matching_tag(TagType::DATA_REQUIRED, launch_domain, capture_domain)}; +} + +inline size_t TimingTags::capacity() const { return capacity_; } + +inline TimingTags::iterator TimingTags::insert(iterator iter, const TimingTag& tag) { + size_t index = std::distance(begin(), iter); + TATUM_ASSERT(index <= size()); + + if(capacity() == 0 || capacity() == size()) { + //Grow and insert simultaneously + grow_insert(index, tag); + } else { + //Insert into existing capacity + TATUM_ASSERT(size() + 1 <= capacity()); + + //Shift everything one position right from end to index + //TODO: use std::copy_backward? + for(size_t i = size(); i != index; --i) { + tags_[i] = tags_[i - 1]; + } + + //Insert the new value in the hole at index created by shifting + tags_[index] = tag; + + //Update the sizes + increment_size(tag.type()); + } + + return begin() + index; +} + +inline void TimingTags::grow_insert(size_t index, const TimingTag& tag) { + size_t new_capacity = (capacity() == 0) ? 1 : GROWTH_FACTOR * capacity(); + + //We construct a new copy of ourselves at the new capacity and with the new + //tag inserted + TimingTags new_tags(new_capacity); + + std::copy_n(tags_.get(), index, new_tags.tags_.get()); //Copy before index + new_tags.tags_[index] = tag; //Insert the new value + std::copy_n(tags_.get() + index, size() - index, new_tags.tags_.get() + index + 1); //Copy after index + + //Copy the sizes + new_tags.size_ = size_; + new_tags.num_clock_launch_tags_ = num_clock_launch_tags_; + new_tags.num_clock_capture_tags_ = num_clock_capture_tags_; + new_tags.num_data_arrival_tags_ = num_data_arrival_tags_; + new_tags.num_data_required_tags_ = num_data_required_tags_; + + //Increment to account for the inserted tag + new_tags.increment_size(tag.type()); + + //Update ourselves + swap(*this, new_tags); +} + +inline void TimingTags::increment_size(TagType type) { + ++size_; + switch(type) { + case TagType::CLOCK_LAUNCH: + ++num_clock_launch_tags_; + break; + case TagType::CLOCK_CAPTURE: + ++num_clock_capture_tags_; + break; + case TagType::DATA_ARRIVAL: + ++num_data_arrival_tags_; + break; + case TagType::DATA_REQUIRED: + ++num_data_required_tags_; + break; + case TagType::SLACK: + //Pass + break; + default: + TATUM_ASSERT_MSG(false, "Invalid tag type"); + } +} + +inline void swap(TimingTags& lhs, TimingTags& rhs) { + std::swap(lhs.tags_, rhs.tags_); + std::swap(lhs.num_clock_launch_tags_, rhs.num_clock_launch_tags_); + std::swap(lhs.num_clock_capture_tags_, rhs.num_clock_capture_tags_); + std::swap(lhs.num_data_arrival_tags_, rhs.num_data_arrival_tags_); + std::swap(lhs.num_data_required_tags_, rhs.num_data_required_tags_); + std::swap(lhs.size_, rhs.size_); + std::swap(lhs.capacity_, rhs.capacity_); +} + +} //namepsace