add missing tatum file due to the folder name tags is in the git ignore list!!!
This commit is contained in:
parent
60cbcf9104
commit
2901a6eec5
|
@ -0,0 +1,156 @@
|
|||
#pragma once
|
||||
#include <iosfwd>
|
||||
#include <limits>
|
||||
|
||||
#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<float>::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<float>::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"
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
#pragma once
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
|
||||
#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 T>
|
||||
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<TimingTag> iterator;
|
||||
typedef Iterator<const TimingTag> const_iterator;
|
||||
|
||||
typedef tatum::util::Range<const_iterator> 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 T>
|
||||
class Iterator : public std::iterator<std::random_access_iterator_tag, T> {
|
||||
friend TimingTags;
|
||||
public:
|
||||
using value_type = typename std::iterator<std::random_access_iterator_tag, T>::value_type;
|
||||
using difference_type = typename std::iterator<std::random_access_iterator_tag, T>::difference_type;
|
||||
using pointer = typename std::iterator<std::random_access_iterator_tag, T>::pointer;
|
||||
using reference = typename std::iterator<std::random_access_iterator_tag, T>::reference;
|
||||
using iterator_category = typename std::iterator<std::random_access_iterator_tag, T>::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<bool,iterator> 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<bool,iterator> 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<TimingTag[]> 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"
|
|
@ -0,0 +1,327 @@
|
|||
#include <algorithm>
|
||||
#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<const TimingTags*>(this)->begin(type);
|
||||
return iterator(const_cast<TimingTag*>(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<const TimingTags*>(this)->end();
|
||||
return iterator(const_cast<TimingTag*>(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<const TimingTags*>(this)->end(type);
|
||||
return iterator(const_cast<TimingTag*>(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<bool,TimingTags::iterator> 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<bool,TimingTags::iterator> 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
|
Loading…
Reference in New Issue