blob: 72c90102962b6609da8065ddf18e8e17ea65ec8e [file]
#ifndef TATUM_COMMON_ANALYSIS_VISITOR_HPP
#define TATUM_COMMON_ANALYSIS_VISITOR_HPP
#include "tatum/error.hpp"
#include "tatum/TimingGraph.hpp"
#include "tatum/TimingConstraints.hpp"
#include "tatum/tags/TimingTags.hpp"
#include "tatum/delay_calc/DelayCalculator.hpp"
#include "tatum/graph_visitors/GraphVisitor.hpp"
namespace tatum { namespace detail {
/** \file
*
* Common analysis functionality for both setup and hold analysis.
*/
/** \class CommonAnalysisVisitor
*
* A class satisfying the GraphVisitor concept, which contains common
* node and edge processing code used by both setup and hold analysis.
*
* \see GraphVisitor
*
* \tparam AnalysisOps a class defining the setup/hold specific operations
* \see SetupAnalysisOps
* \see HoldAnalysisOps
*/
template<class AnalysisOps>
class CommonAnalysisVisitor : public GraphVisitor {
public:
CommonAnalysisVisitor(size_t num_tags, size_t num_slacks)
: ops_(num_tags, num_slacks) { }
void do_reset_node(const NodeId node_id) override { ops_.reset_node(node_id); }
void do_reset_edge(const EdgeId edge_id) override { ops_.reset_edge(edge_id); }
bool do_arrival_pre_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const NodeId node_id) override;
bool do_required_pre_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const NodeId node_id) override;
void do_arrival_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const DelayCalculator& dc, const NodeId node_id) override;
void do_required_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const DelayCalculator& dc, const NodeId node_id) override;
void do_slack_traverse_node(const TimingGraph& tg, const DelayCalculator& dc, const NodeId node) override;
protected:
AnalysisOps ops_;
private:
void do_arrival_traverse_edge(const TimingGraph& tg, const TimingConstraints& tc, const DelayCalculator& dc, const NodeId node_id, const EdgeId edge_id);
void do_required_traverse_edge(const TimingGraph& tg, const DelayCalculator& dc, const NodeId node_id, const EdgeId edge_id);
void do_slack_traverse_edge(const TimingGraph& tg, const DelayCalculator& dc, const EdgeId edge);
void mark_sink_required_times(const TimingGraph& tg, const TimingConstraints& tc, const DelayCalculator& dc, const NodeId node);
bool should_propagate_clocks(const TimingGraph& tg, const TimingConstraints& tc, const EdgeId edge_id) const;
bool should_propagate_clock_launch_tags(const TimingGraph& tg, const EdgeId edge_id) const;
bool should_propagate_clock_capture_tags(const TimingGraph& tg, const EdgeId edge_id) const;
bool should_propagate_data(const TimingGraph& tg, const EdgeId edge_id) const;
bool should_calculate_slack(const TimingTag& src_tag, const TimingTag& sink_tag) const;
bool is_clock_data_launch_edge(const TimingGraph& tg, const EdgeId edge_id) const;
bool is_clock_data_capture_edge(const TimingGraph& tg, const EdgeId edge_id) const;
};
/*
* Pre-traversal
*/
template<class AnalysisOps>
bool CommonAnalysisVisitor<AnalysisOps>::do_arrival_pre_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const NodeId node_id) {
//Logical Input
//We expect this function to only be called on nodes in the first level of the timing graph
//These nodes must have no un-disabled input edges (else they shouldn't be in the first level).
//In the normal case (primary input) there are no incoming edges. However if set_disable_timing
//was used it may be that the edges were explicitly disabled. We therefore verify that there are
//no un-disabled edges in the fanin of the current node.
TATUM_ASSERT_MSG(tg.node_num_active_in_edges(node_id) == 0, "Logical input has non-disabled input edges: timing graph not levelized.");
//
//We now generate the various clock/data launch tags associated with the arrival time traversal
//
NodeType node_type = tg.node_type(node_id);
bool node_constrained = false;
if(tc.node_is_constant_generator(node_id)) {
//We progpagate the tags from constant generators to ensure any sinks driven
//only by constant generators are recorded as constrained.
//
//We use a special tag to initialize constant generators which gets overritten
//by any non-constant tag at downstream nodes
TimingTag const_gen_tag = ops_.const_gen_tag();
ops_.add_tag(node_id, const_gen_tag);
node_constrained = true;
} else {
TATUM_ASSERT(node_type == NodeType::SOURCE);
if(tc.node_is_clock_source(node_id)) {
//Generate the appropriate clock tag
TATUM_ASSERT_MSG(ops_.get_tags(node_id, TagType::CLOCK_LAUNCH).size() == 0, "Uninitialized clock source should have no launch clock tags");
TATUM_ASSERT_MSG(ops_.get_tags(node_id, TagType::CLOCK_CAPTURE).size() == 0, "Uninitialized clock source should have no capture clock tags");
//Find the domain of this node (since it is a source)
DomainId domain_id = tc.node_clock_domain(node_id);
TATUM_ASSERT(domain_id);
//Initialize a clock launch tag from this to any capture domain
//
//Note: we assume that edge counting has set the effective period constraint assuming a
//launch edge at time zero + source latency. This means we don't need to do anything
//special for clocks with rising edges after time zero.
Time launch_source_latency = ops_.launch_source_latency(tc, domain_id);
TimingTag launch_tag = TimingTag(launch_source_latency,
domain_id,
DomainId::INVALID(), //Any capture
NodeId::INVALID(), //Origin
TagType::CLOCK_LAUNCH);
//Add the launch tag
ops_.add_tag(node_id, launch_tag);
//Initialize the clock capture tags from any valid launch domain to this domain
//
//Note that we enumerate all pairs of valid launch domains for the current domain
//(which is now treated as the capture domain), since each pair may have different constraints
for(DomainId launch_domain_id : tc.clock_domains()) {
if(tc.should_analyze(launch_domain_id, domain_id)) {
//Initialize the clock capture tag with the constraint, including the effect of any source latency
//
//Note: We assume that this period constraint has been resolved by edge counting for this
//domain pair. Note that it does not include the effect of clock uncertainty, which is handled
//when the caputre tag is converted into a data-arrival tag.
//
//Also note that this is the default clock constraint. If there is a different per capture node
//constraint this is also handled when setting the required time.
Time clock_constraint = ops_.clock_constraint(tc, launch_domain_id, domain_id);
Time capture_source_latency = ops_.capture_source_latency(tc, domain_id);
TimingTag capture_tag = TimingTag(Time(capture_source_latency) + Time(clock_constraint),
launch_domain_id,
domain_id,
NodeId::INVALID(), //Origin
TagType::CLOCK_CAPTURE);
ops_.add_tag(node_id, capture_tag);
node_constrained = true;
}
}
} else {
//A standard primary input, generate the appropriate data tags
TATUM_ASSERT_MSG(ops_.get_tags(node_id, TagType::DATA_ARRIVAL).size() == 0, "Primary input already has data tags");
auto input_constraints = ops_.input_constraints(tc, node_id);
if(!input_constraints.empty()) { //Some inputs may be unconstrained, so do not create tags for them
DomainId domain_id = tc.node_clock_domain(node_id);
TATUM_ASSERT(domain_id);
//The external clock may have latency
Time launch_source_latency = ops_.launch_source_latency(tc, domain_id);
//An input constraint means there is 'input_constraint' delay from when an external
//signal is launched by its clock (external to the chip) until it arrives at the
//primary input
Time input_constraint = ops_.input_constraint(tc, node_id, domain_id);
TATUM_ASSERT(input_constraint.valid());
//Initialize a data tag based on input delay constraint
TimingTag input_tag = TimingTag(launch_source_latency + input_constraint,
domain_id,
DomainId::INVALID(),
NodeId::INVALID(), //Origin
TagType::DATA_ARRIVAL);
ops_.add_tag(node_id, input_tag);
node_constrained = true;
}
}
}
return node_constrained;
}
template<class AnalysisOps>
bool CommonAnalysisVisitor<AnalysisOps>::do_required_pre_traverse_node(const TimingGraph& tg, const TimingConstraints& /*tc*/, const NodeId node_id) {
NodeType node_type = tg.node_type(node_id);
TATUM_ASSERT(node_type == NodeType::SINK);
return is_constrained(node_type, ops_.get_tags(node_id));
}
/*
* Arrival Time Operations
*/
template<class AnalysisOps>
void CommonAnalysisVisitor<AnalysisOps>::do_arrival_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const DelayCalculator& dc, NodeId node_id) {
//Pull from upstream sources to current node
for(EdgeId edge_id : tg.node_in_edges(node_id)) {
if(tg.edge_disabled(edge_id)) continue;
do_arrival_traverse_edge(tg, tc, dc, node_id, edge_id);
}
if(tg.node_type(node_id) == NodeType::SINK) {
mark_sink_required_times(tg, tc, dc, node_id);
}
}
template<class AnalysisOps>
void CommonAnalysisVisitor<AnalysisOps>::do_arrival_traverse_edge(const TimingGraph& tg, const TimingConstraints& tc, const DelayCalculator& dc, const NodeId node_id, const EdgeId edge_id) {
//Pulling values from upstream source node
NodeId src_node_id = tg.edge_src_node(edge_id);
if(should_propagate_clocks(tg, tc, edge_id)) {
/*
* Clock tags
*/
//Propagate the clock tags through the clock network
//The launch tags
if(should_propagate_clock_launch_tags(tg, edge_id)) {
TimingTags::tag_range src_launch_clk_tags = ops_.get_tags(src_node_id, TagType::CLOCK_LAUNCH);
if(!src_launch_clk_tags.empty()) {
const Time clk_launch_edge_delay = ops_.launch_clock_edge_delay(dc, tg, edge_id);
for(const TimingTag& src_launch_clk_tag : src_launch_clk_tags) {
//Standard propagation through the clock network
Time new_arr = src_launch_clk_tag.time() + clk_launch_edge_delay;
ops_.merge_arr_tags(node_id, new_arr, src_node_id, src_launch_clk_tag);
}
}
}
//The capture tags
if(should_propagate_clock_capture_tags(tg, edge_id)) {
TimingTags::tag_range src_capture_clk_tags = ops_.get_tags(src_node_id, TagType::CLOCK_CAPTURE);
if(!src_capture_clk_tags.empty()) {
const Time clk_capture_edge_delay = ops_.capture_clock_edge_delay(dc, tg, edge_id);
for(const TimingTag& src_capture_clk_tag : src_capture_clk_tags) {
//Standard propagation through the clock network
ops_.merge_arr_tags(node_id, src_capture_clk_tag.time() + clk_capture_edge_delay, src_node_id, src_capture_clk_tag);
}
}
}
}
/*
* Data Arrival tags
*/
if(is_clock_data_launch_edge(tg, edge_id)) {
//Convert the launch clock into a data arrival
//We convert the clock arrival time at the upstream node into a data
//arrival time at this node (since the clock's arrival launches the data).
TATUM_ASSERT_SAFE(tg.node_type(node_id) == NodeType::SOURCE);
TimingTags::tag_range src_launch_clk_tags = ops_.get_tags(src_node_id, TagType::CLOCK_LAUNCH);
if(!src_launch_clk_tags.empty()) {
const Time launch_edge_delay = ops_.launch_clock_edge_delay(dc, tg, edge_id);
for(const TimingTag& src_launch_clk_tag : src_launch_clk_tags) {
//Convert clock launch into data arrival
TimingTag data_arr_tag = src_launch_clk_tag;
data_arr_tag.set_type(TagType::DATA_ARRIVAL);
Time arr_time = src_launch_clk_tag.time() + launch_edge_delay;
//Mark propagated launch time as a DATA tag
ops_.merge_arr_tags(node_id,
arr_time,
NodeId::INVALID(), //Origin
data_arr_tag);
}
}
}
if(should_propagate_data(tg, edge_id)) {
//Standard data path propagation
TimingTags::tag_range src_data_tags = ops_.get_tags(src_node_id, TagType::DATA_ARRIVAL);
if(!src_data_tags.empty()) {
const Time edge_delay = ops_.data_edge_delay(dc, tg, edge_id);
TATUM_ASSERT_SAFE(edge_delay.valid());
for(const TimingTag& src_data_tag : src_data_tags) {
Time new_arr = src_data_tag.time() + edge_delay;
ops_.merge_arr_tags(node_id, new_arr, src_node_id, src_data_tag);
}
}
}
//NOTE: we do not handle clock caputure edges (which create required times) here, but in
// mark_sink_required_times(), after all edges have been processed (i.e. all arrival times
// set)
//
//To calulate the required times at a node we must know all the arrival times at the node.
//Since we don't know the order the incomming edges are processed, we don't know whether all
//arrival times have been set until *after* all edges have been processed.
//
//As a result we set the required times only after all the edges have been processed
}
/*
* Required Time Operations
*/
template<class AnalysisOps>
void CommonAnalysisVisitor<AnalysisOps>::do_required_traverse_node(const TimingGraph& tg, const TimingConstraints& /*tc*/, const DelayCalculator& dc, const NodeId node_id) {
//Don't propagate required times through the clock network
if(tg.node_type(node_id) == NodeType::CPIN) return;
//Pull from downstream sinks to current node
for(EdgeId edge_id : tg.node_out_edges(node_id)) {
if(tg.edge_disabled(edge_id)) continue;
do_required_traverse_edge(tg, dc, node_id, edge_id);
}
}
template<class AnalysisOps>
void CommonAnalysisVisitor<AnalysisOps>::do_required_traverse_edge(const TimingGraph& tg, const DelayCalculator& dc, const NodeId node_id, const EdgeId edge_id) {
//Pulling values from downstream sink node
NodeId sink_node_id = tg.edge_sink_node(edge_id);
TimingTags::tag_range sink_data_tags = ops_.get_tags(sink_node_id, TagType::DATA_REQUIRED);
if(!sink_data_tags.empty()) {
const Time& edge_delay = ops_.data_edge_delay(dc, tg, edge_id);
TATUM_ASSERT_SAFE(edge_delay.valid());
for(const TimingTag& sink_tag : sink_data_tags) {
//We only propogate the required time if we have a valid matching arrival time
ops_.merge_req_tags(node_id, sink_tag.time() - edge_delay, sink_node_id, sink_tag, true);
}
}
}
template<class AnalysisOps>
void CommonAnalysisVisitor<AnalysisOps>::do_slack_traverse_node(const TimingGraph& tg, const DelayCalculator& dc, const NodeId node) {
//Calculate the slack for each edge
for(const EdgeId edge : tg.node_in_edges(node)) {
do_slack_traverse_edge(tg, dc, edge);
}
//Calculate the slacks at each node
for(const TimingTag& arr_tag : ops_.get_tags(node, TagType::DATA_ARRIVAL)) {
for(const TimingTag& req_tag : ops_.get_tags(node, TagType::DATA_REQUIRED)) {
if(!should_calculate_slack(arr_tag, req_tag)) continue;
Time slack_value = ops_.calculate_slack(req_tag.time(), arr_tag.time());
ops_.merge_slack_tags(node, slack_value, req_tag);
}
}
}
template<class AnalysisOps>
void CommonAnalysisVisitor<AnalysisOps>::do_slack_traverse_edge(const TimingGraph& tg, const DelayCalculator& dc, const EdgeId edge) {
NodeId src_node = tg.edge_src_node(edge);
NodeId sink_node = tg.edge_sink_node(edge);
auto src_arr_tags = ops_.get_tags(src_node, TagType::DATA_ARRIVAL);
auto sink_req_tags = ops_.get_tags(sink_node, TagType::DATA_REQUIRED);
Time edge_delay;
if(is_clock_data_launch_edge(tg, edge)) {
edge_delay = ops_.launch_clock_edge_delay(dc, tg, edge);
} else if(is_clock_data_capture_edge(tg, edge)) {
edge_delay = ops_.capture_clock_edge_delay(dc, tg, edge);
} else {
edge_delay = ops_.data_edge_delay(dc, tg, edge);
}
for(const tatum::TimingTag& src_arr_tag : src_arr_tags) {
for(const tatum::TimingTag& sink_req_tag : sink_req_tags) {
if(!should_calculate_slack(src_arr_tag, sink_req_tag)) continue;
Time slack_value = sink_req_tag.time() - src_arr_tag.time() - edge_delay;
ops_.merge_slack_tags(edge, slack_value, sink_req_tag);
}
}
}
template<class AnalysisOps>
void CommonAnalysisVisitor<AnalysisOps>::mark_sink_required_times(const TimingGraph& tg, const TimingConstraints& tc, const DelayCalculator& dc, const NodeId node_id) {
//Mark the required times of the current sink node
TATUM_ASSERT(tg.node_type(node_id) == NodeType::SINK);
//Note: since we add tags at the current node (and the tags are all stored together),
//we must *copy* the data arrival tags before adding any new tags (since adding new
//tags may invalidate the old tag references)
auto data_arr_range = ops_.get_tags(node_id, TagType::DATA_ARRIVAL);
std::vector<TimingTag> node_data_arr_tags(data_arr_range.begin(), data_arr_range.end());
EdgeId clock_capture_edge = tg.node_clock_capture_edge(node_id);
if(clock_capture_edge) {
//Required time at sink FF
NodeId src_node_id = tg.edge_src_node(clock_capture_edge);
TimingTags::tag_range src_capture_clk_tags = ops_.get_tags(src_node_id, TagType::CLOCK_CAPTURE);
const Time capture_edge_delay = ops_.capture_clock_edge_delay(dc, tg, clock_capture_edge);
for(const TimingTag& src_capture_clk_tag : src_capture_clk_tags) {
DomainId clock_launch_domain = src_capture_clk_tag.launch_clock_domain();
DomainId clock_capture_domain = src_capture_clk_tag.capture_clock_domain();
for(const TimingTag& node_data_arr_tag : node_data_arr_tags) {
DomainId data_launch_domain = node_data_arr_tag.launch_clock_domain();
if(is_const_gen_tag(node_data_arr_tag)) {
//A constant generator tag. Required time is not terribly well defined,
//so just use the capture clock tag values since we nevery look at these
//tags except to check that any downstream nodes have been constrained
data_launch_domain = src_capture_clk_tag.capture_clock_domain();
}
//We produce a fully specified capture clock tags (both launch and capture) so we only want
//to consider the capture clock tag which matches the data launch domain
bool same_launch_domain = (data_launch_domain == clock_launch_domain);
//We only want to analyze paths between domains where a valid constraint has been specified
bool valid_launch_capture_pair = tc.should_analyze(data_launch_domain, clock_capture_domain, node_id);
if(same_launch_domain && valid_launch_capture_pair) {
//We only set a required time if the source domain actually reaches this sink
//domain. This is indicated by the presence of an arrival tag (which should have
//a valid arrival time).
TATUM_ASSERT(node_data_arr_tag.time().valid());
//If there is a per-sink override for the clock constraint we need to adjust the clock
//arrival time from the default
Time clock_constraint_offset = ops_.clock_constraint(tc, data_launch_domain, clock_capture_domain, node_id)
- ops_.clock_constraint(tc, data_launch_domain, clock_capture_domain);
//We apply the clock uncertainty to the generated required time tag
Time clock_uncertainty = ops_.clock_uncertainty(tc, data_launch_domain, clock_capture_domain);
Time req_time = src_capture_clk_tag.time() //Latency + propagated clock network delay to CPIN
+ clock_constraint_offset //Period constraint adjustment
+ capture_edge_delay //CPIN to sink delay (Thld, or Tsu)
+ Time(clock_uncertainty); //Clock period uncertainty
TimingTag node_data_req_tag(req_time,
data_launch_domain,
clock_capture_domain,
NodeId::INVALID(), //Origin
TagType::DATA_REQUIRED);
ops_.add_tag(node_id, node_data_req_tag);
}
}
}
} else {
//Must be a primary-output sink, need to set required tags based on output constraints
DomainId io_capture_domain = tc.node_clock_domain(node_id);
//Any constrained primary outputs should have a specified clock domain
// Note that some outputs may not be constrained and hence should not get required times
if(io_capture_domain) {
//An output constraint means there is output_constraint delay outside the chip,
//as a result signals need to reach the primary-output at least output_constraint
//before the capture clock.
//
//Hence we use a negative output constraint value to subtract the output constraint
//from the target clock constraint
Time output_constraint = -ops_.output_constraint(tc, node_id, io_capture_domain);
if (output_constraint.valid()) {
//Since there is no propagated clock tag to primary outputs, we need to account for
//the capture source clock latency
Time capture_clock_source_latency = ops_.capture_source_latency(tc, io_capture_domain);
for(const TimingTag& node_data_arr_tag : node_data_arr_tags) {
DomainId data_launch_domain = node_data_arr_tag.launch_clock_domain();
if(is_const_gen_tag(node_data_arr_tag)) {
//A constant generator tag. Required time is not terribly well defined,
//so just use the inter-domain values since we nevery look at these
//tags except to check that any downstream nodes have been constrained
data_launch_domain = io_capture_domain;
}
//Should we be analyzing paths between these two domains?
if(tc.should_analyze(data_launch_domain, io_capture_domain, node_id)) {
//We only set a required time if the source domain actually reaches this sink
//domain. This is indicated by the presence of an arrival tag (which should have
//a valid arrival time).
TATUM_ASSERT(node_data_arr_tag.time().valid());
Time constraint = ops_.clock_constraint(tc, data_launch_domain, io_capture_domain, node_id);
Time clock_uncertainty = ops_.clock_uncertainty(tc, data_launch_domain, io_capture_domain);
//Calulate the required time
Time req_time = Time(constraint) //Period constraint
+ Time(capture_clock_source_latency) //Latency from true clock source to def'n point
+ Time(output_constraint) //Output delay
+ Time(clock_uncertainty); //Clock period uncertainty
TimingTag node_data_req_tag(req_time,
data_launch_domain,
io_capture_domain,
NodeId::INVALID(), //Origin
TagType::DATA_REQUIRED);
ops_.add_tag(node_id, node_data_req_tag);
}
}
}
}
}
}
template<class AnalysisOps>
bool CommonAnalysisVisitor<AnalysisOps>::should_propagate_clocks(const TimingGraph& tg, const TimingConstraints& tc, const EdgeId edge_id) const {
//We want to propagate clock tags through the arbitrary nodes making up the clock network until
//we hit another source node (i.e. a FF's output source).
//
//To allow tags to propagte from the original source (i.e. the input clock pin) we also allow
//propagation from defined clock sources
NodeId src_node_id = tg.edge_src_node(edge_id);
NodeId sink_node_id = tg.edge_sink_node(edge_id);
if (tg.node_type(sink_node_id) != NodeType::SOURCE) {
//Not a source, allow propagation
if (tc.node_is_clock_source(src_node_id)) {
//The source is a clock source
TATUM_ASSERT_MSG(tg.node_type(src_node_id) == NodeType::SOURCE, "Only SOURCEs can be clock sources");
TATUM_ASSERT_MSG(tg.node_in_edges(src_node_id).empty(), "Clock sources should have no incoming edges");
}
return true;
}
return false;
}
template<class AnalysisOps>
bool CommonAnalysisVisitor<AnalysisOps>::should_propagate_clock_launch_tags(const TimingGraph& tg, const EdgeId edge_id) const {
return !is_clock_data_capture_edge(tg, edge_id);
}
template<class AnalysisOps>
bool CommonAnalysisVisitor<AnalysisOps>::should_propagate_clock_capture_tags(const TimingGraph& tg, const EdgeId edge_id) const {
NodeId sink_node = tg.edge_sink_node(edge_id);
return tg.node_type(sink_node) != NodeType::SINK;
}
template<class AnalysisOps>
bool CommonAnalysisVisitor<AnalysisOps>::is_clock_data_launch_edge(const TimingGraph& tg, const EdgeId edge_id) const {
NodeId edge_src_node = tg.edge_src_node(edge_id);
NodeId edge_sink_node = tg.edge_sink_node(edge_id);
return (tg.node_type(edge_src_node) == NodeType::CPIN) && (tg.node_type(edge_sink_node) == NodeType::SOURCE);
}
template<class AnalysisOps>
bool CommonAnalysisVisitor<AnalysisOps>::is_clock_data_capture_edge(const TimingGraph& tg, const EdgeId edge_id) const {
NodeId edge_src_node = tg.edge_src_node(edge_id);
NodeId edge_sink_node = tg.edge_sink_node(edge_id);
return (tg.node_type(edge_src_node) == NodeType::CPIN) && (tg.node_type(edge_sink_node) == NodeType::SINK);
}
template<class AnalysisOps>
bool CommonAnalysisVisitor<AnalysisOps>::should_propagate_data(const TimingGraph& tg, const EdgeId edge_id) const {
//We want to propagate data tags unless then re-enter the clock network
NodeId src_node_id = tg.edge_src_node(edge_id);
NodeType src_node_type = tg.node_type(src_node_id);
if (src_node_type != NodeType::CPIN) {
//Do not allow data tags to propagate through clock pins
return true;
}
return false;
}
template<class AnalysisOps>
bool CommonAnalysisVisitor<AnalysisOps>::should_calculate_slack(const TimingTag& src_tag, const TimingTag& sink_tag) const {
TATUM_ASSERT_SAFE(src_tag.type() == TagType::DATA_ARRIVAL && sink_tag.type() == TagType::DATA_REQUIRED);
//NOTE: we do not need to check the constraints to determine whether this domain pair should be analyzed,
// this check has already been done when we created the DATA_REQUIRED tags (i.e. sink tags in this context),
// ensuring we only calculate slack for valid domain pairs (otherwise the DATA_REQUIREd tag would note exist)
return src_tag.launch_clock_domain() == sink_tag.launch_clock_domain();
}
}} //namepsace
#endif