diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt index 8128a305..692ff99f 100644 Binary files a/doc/OpenSTA.odt and b/doc/OpenSTA.odt differ diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 9e9d1238..b1716365 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -964,6 +964,12 @@ public: const RiseFall *rf, const MinMax *min_max); + // Find the min clock period for rise/rise and fall/fall paths of a clock + // using the slack. This does NOT correctly predict min period when there + // are paths between different clocks. + float findClkMinPeriod(const Clock *clk, + bool include_port_paths); + // The following arrival/required/slack functions incrementally // update timing to the level of the vertex. They do NOT do multiple // passes required propagate arrivals around latch loops. diff --git a/search/Sta.cc b/search/Sta.cc index a8ca08fe..b1b78be2 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -67,10 +67,13 @@ #include "Genclks.hh" #include "ClkNetwork.hh" #include "Power.hh" +#include "VisitPathEnds.hh" +#include "PathExpanded.hh" namespace sta { using std::min; +using std::max; static const ClockEdge *clk_edge_wildcard = reinterpret_cast(1); @@ -3030,6 +3033,91 @@ Sta::vertexSlacks(Vertex *vertex, } } +//////////////////////////////////////////////////////////////// + +class MinPeriodEndVisitor : public PathEndVisitor +{ +public: + MinPeriodEndVisitor(const Clock *clk, + bool include_port_paths, + StaState *sta); + virtual PathEndVisitor *copy(); + virtual void visit(PathEnd *path_end); + float minPeriod() const { return min_period_; } + +private: + bool pathIsFromInputPort(PathEnd *path_end); + + const Clock *clk_; + bool include_port_paths_; + StaState *sta_; + float min_period_; +}; + +MinPeriodEndVisitor::MinPeriodEndVisitor(const Clock *clk, + bool include_port_paths, + StaState *sta) : + clk_(clk), + include_port_paths_(include_port_paths), + sta_(sta), + min_period_(0) +{ +} + +PathEndVisitor * +MinPeriodEndVisitor::copy() +{ + return new MinPeriodEndVisitor(clk_, include_port_paths_, sta_); +} + +void +MinPeriodEndVisitor::visit(PathEnd *path_end) +{ + Path *path = path_end->path(); + ClockEdge *src_edge = path_end->sourceClkEdge(sta_); + ClockEdge *tgt_edge = path_end->targetClkEdge(sta_); + if (path->minMax(sta_) == MinMax::max() + && src_edge->clock() == clk_ + && tgt_edge->clock() == clk_ + // Only consider rise/rise and fall/fall paths. + && src_edge->transition() == tgt_edge->transition() + && (include_port_paths_ + || !(path_end->isOutputDelay() + || pathIsFromInputPort(path_end)))) { + Slack slack = path_end->slack(sta_); + float period = clk_->period() - slack; + min_period_ = max(min_period_, period); + } +} + +bool +MinPeriodEndVisitor::pathIsFromInputPort(PathEnd *path_end) +{ + PathExpanded expanded(path_end->path(), sta_); + PathRef *start = expanded.startPath(); + Graph *graph = sta_->graph(); + const Pin *first_pin = start->pin(graph); + Network *network = sta_->network(); + return network->isTopLevelPort(first_pin); +} + +float +Sta::findClkMinPeriod(const Clock *clk, + bool include_port_paths) +{ + searchPreamble(); + search_->findArrivals(); + VisitPathEnds visit_ends(this); + MinPeriodEndVisitor min_period_visitor(clk, include_port_paths, this); + for (Vertex *vertex : *search_->endpoints()) { + findRequired(vertex); + visit_ends.visitPathEnds(vertex, &min_period_visitor); + } + return min_period_visitor.minPeriod(); +} + +//////////////////////////////////////////////////////////////// + void Sta::findRequired(Vertex *vertex) { diff --git a/tcl/Search.tcl b/tcl/Search.tcl index e995bfb1..1b154f4e 100644 --- a/tcl/Search.tcl +++ b/tcl/Search.tcl @@ -1075,5 +1075,34 @@ proc define_report_path_fields {} { set_report_path_field_properties "case" " " 11 0 } +################################################################ + +define_cmd_args "report_clock_min_period" \ + { [-clocks clocks] [-include_port_paths] } + +proc report_clock_min_period { args } { + parse_key_args "report_min_clock_period" args \ + keys {-clocks} flags {-include_port_paths} 0 + + if { [info exists keys(-clocks)] } { + set clks [get_clocks $keys(-clocks)] + } else { + set clks [all_clocks] + } + set include_port_paths [info exists flags(-include_port_paths)] + + foreach clk $clks { + set min_period [sta::find_clk_min_period $clk $include_port_paths] + if { $min_period == 0.0 } { + set min_period 0 + set fmax "INF" + } else { + # max frequency in MHz + set fmax [expr 1.0e-6 / $min_period] + } + puts "[get_name $clk] period_min = [sta::format_time $min_period 2] fmax = [format %.2f $fmax]" + } +} + # sta namespace end. } diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index 1364f882..f4c7b9b3 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -4702,6 +4702,15 @@ vertex_worst_slack_path(Vertex *vertex, return nullptr; } +Slack +find_clk_min_period(const Clock *clk, + bool ignore_port_paths) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + return sta->findClkMinPeriod(clk, ignore_port_paths); +} + TmpString * report_delay_calc_cmd(Edge *edge, TimingArc *arc,