diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt index 0b0f3174..b453fa0f 100644 Binary files a/doc/OpenSTA.odt and b/doc/OpenSTA.odt differ diff --git a/include/sta/Search.hh b/include/sta/Search.hh index 510f418d..ba51608d 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -348,6 +348,10 @@ public: GatedClk *gatedClk() { return gated_clk_; } Genclks *genclks() { return genclks_; } void findClkVertexPins(PinSet &clk_pins); + void findFilteredArrivals(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool unconstrained); protected: void init(StaState *sta); diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index f2c73411..ca3545d1 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -16,6 +16,8 @@ #include "MakeTimingModel.hh" +#include + #include "Debug.hh" #include "Units.hh" #include "Transition.hh" @@ -30,8 +32,11 @@ #include "dcalc/GraphDelayCalc1.hh" #include "Sdc.hh" #include "StaState.hh" +#include "Graph.hh" #include "PathEnd.hh" +#include "Search.hh" #include "Sta.hh" +#include "VisitPathEnds.hh" namespace sta { @@ -61,8 +66,12 @@ MakeTimingModel::makeTimingModel(const char *cell_name, for (Clock *clk : *sdc_->clocks()) sta_->setPropagatedClock(clk); +#if 0 //findInputToOutputPaths(); findInputSetupHolds(); +#endif + sta_->searchPreamble(); + findInputSetupHolds(); findClkedOutputPaths(); cell_->finish(false, report_, debug_); @@ -142,7 +151,10 @@ MakeTimingModel::makePorts() delete port_iter; } +//////////////////////////////////////////////////////////////// + // input -> output combinational paths +// too slow to use void MakeTimingModel::findInputToOutputPaths() { @@ -183,75 +195,150 @@ MakeTimingModel::findInputToOutputPaths() } } +//////////////////////////////////////////////////////////////// + +typedef std::map ClockMargins; + +class MakeEndTimingArcs : public PathEndVisitor +{ +public: + MakeEndTimingArcs(Sta *sta); + MakeEndTimingArcs(const MakeEndTimingArcs&) = default; + virtual ~MakeEndTimingArcs() {} + virtual PathEndVisitor *copy() const; + virtual void visit(PathEnd *path_end); + void setInputPin(const Pin *input_pin); + void setInputRf(const RiseFall *input_rf); + const ClockMargins &margins() const { return margins_; } + +private: + Sta *sta_; + const Pin *input_pin_; + const RiseFall *input_rf_; + ClockMargins margins_; +}; + +MakeEndTimingArcs::MakeEndTimingArcs(Sta *sta) : + sta_(sta) +{ +} + +PathEndVisitor * +MakeEndTimingArcs::copy() const +{ + return new MakeEndTimingArcs(*this); +} + +void +MakeEndTimingArcs::setInputPin(const Pin *input_pin) +{ + input_pin_ = input_pin; + margins_.clear(); +} + +void +MakeEndTimingArcs::setInputRf(const RiseFall *input_rf) +{ + input_rf_ = input_rf; +} + +void +MakeEndTimingArcs::visit(PathEnd *path_end) +{ + ClockEdge *tgt_clk_edge = path_end->targetClkEdge(sta_); + Debug *debug = sta_->debug(); + const MinMax *min_max = path_end->minMax(sta_); + debugPrint(debug, "make_timing_model", 2, "%s %s %s %s -> clock %s", + path_end->typeName(), + min_max->asString(), + sta_->network()->pathName(input_pin_), + input_rf_->shortName(), + tgt_clk_edge->name()); + if (debug->check("make_timing_model", 3)) + sta_->reportPathEnd(path_end); + Arrival data_arrival = path_end->path()->arrival(sta_); + Delay clk_latency = path_end->targetClkDelay(sta_); + ArcDelay check_setup = path_end->margin(sta_); + float margin = data_arrival - clk_latency + check_setup; + RiseFallMinMax &margins = margins_[tgt_clk_edge]; + margins.setValue(input_rf_, min_max, margin); +} + // input -> register setup/hold +// Use default input arrival (set_input_delay with no clock) from inputs +// to find downstream register checks and output ports. void MakeTimingModel::findInputSetupHolds() { + Debug *debug = sta_->debug(); + VisitPathEnds visit_ends(sta_); + MakeEndTimingArcs end_visitor(sta_); InstancePinIterator *input_iter = network_->pinIterator(network_->topInstance()); while (input_iter->hasNext()) { Pin *input_pin = input_iter->next(); if (network_->direction(input_pin)->isInput() && !sta_->isClockSrc(input_pin)) { - LibertyPort *input_port = modelPort(input_pin); - for (Clock *clk : *sdc_->clocks()) { - for (const Pin *clk_pin : clk->pins()) { - LibertyPort *clk_port = modelPort(clk_pin); - for (RiseFall *clk_rf : RiseFall::range()) { - for (MinMax *min_max : MinMax::range()) { - MinMaxAll *min_max1 = min_max->asMinMaxAll(); - bool setup = min_max == MinMax::max(); - bool hold = !setup; + end_visitor.setInputPin(input_pin); + for (RiseFall *input_rf : RiseFall::range()) { + RiseFallBoth *input_rf1 = input_rf->asRiseFallBoth(); + sta_->setInputDelay(input_pin, input_rf1, + sdc_->defaultArrivalClock(), + sdc_->defaultArrivalClockEdge()->transition(), + nullptr, false, false, MinMaxAll::all(), false, 0.0); + + PinSet *from_pins = new PinSet; + from_pins->insert(input_pin); + ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr, + input_rf1); + search_->deleteFilteredArrivals(); + search_->findFilteredArrivals(from, nullptr, nullptr, false); + + end_visitor.setInputRf(input_rf); + for (Vertex *end : *search_->endpoints()) + visit_ends.visitPathEnds(end, corner_, MinMaxAll::all(), true, &end_visitor); + + sta_->removeInputDelay(input_pin, input_rf1, + sdc_->defaultArrivalClock(), + sdc_->defaultArrivalClockEdge()->transition(), + MinMaxAll::all()); + } + + const ClockMargins &clk_margins = end_visitor.margins(); + for (auto clk_edge_margins : clk_margins) { + ClockEdge *clk_edge = clk_edge_margins.first; + RiseFallMinMax &margins = clk_edge_margins.second; + for (MinMax *min_max : MinMax::range()) { + bool setup = (min_max == MinMax::max()); + TimingArcAttrs *attrs = nullptr; + for (RiseFall *input_rf : RiseFall::range()) { + float margin; + bool exists; + margins.value(input_rf, min_max, margin, exists); + if (exists) { + debugPrint(debug, "make_timing_model", 1, "%s %s %s -> clock %s %s", + sta_->network()->pathName(input_pin), + input_rf->shortName(), + min_max == MinMax::max() ? "setup" : "hold", + clk_edge->name(), + delayAsString(margin, sta_)); + ScaleFactorType scale_type = setup + ? ScaleFactorType::setup + : ScaleFactorType::hold; + TimingModel *check_model = makeScalarCheckModel(margin, scale_type, input_rf); + if (attrs == nullptr) + attrs = new TimingArcAttrs(); + attrs->setModel(input_rf, check_model); + } + } + if (attrs) { + LibertyPort *input_port = modelPort(input_pin); + for (const Pin *clk_pin : clk_edge->clock()->pins()) { + LibertyPort *clk_port = modelPort(clk_pin); + RiseFall *clk_rf = clk_edge->transition(); TimingRole *role = setup ? TimingRole::setup() : TimingRole::hold(); - TimingArcAttrs *attrs = nullptr; - for (RiseFall *input_rf : RiseFall::range()) { - RiseFallBoth *input_rf1 = input_rf->asRiseFallBoth(); - sta_->setInputDelay(input_pin, input_rf1, clk, clk_rf, - nullptr, false, false, min_max1, false, 0.0); - - PinSet *from_pins = new PinSet; - from_pins->insert(input_pin); - ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr, - input_rf1); - - ClockSet *to_clks = new ClockSet; - to_clks->insert(clk); - ExceptionTo *to = sta_->makeExceptionTo(nullptr, to_clks, nullptr, - clk_rf->asRiseFallBoth(), - RiseFallBoth::riseFall()); - PathEndSeq *ends = sta_->findPathEnds(from, nullptr, to, false, corner_, - min_max1, - 1, 1, false, - -INF, INF, false, nullptr, - setup, hold, setup, hold, setup, hold); - if (!ends->empty()) { - PathEnd *end = (*ends)[0]; - debugPrint(debug_, "make_timing_model", 1, "%s %s %s -> clock %s %s", - setup ? "setup" : "hold", - network_->pathName(input_pin), - input_rf->asString(), - clk->name(), - clk_rf->asString()); - if (debug_->check("make_timing_model", 2)) - sta_->reportPathEnd(end); - Arrival data_arrival = end->path()->arrival(sta_); - Delay clk_latency = end->targetClkDelay(sta_); - ArcDelay check_setup = end->margin(sta_); - float margin = data_arrival - clk_latency + check_setup; - - ScaleFactorType scale_type = setup - ? ScaleFactorType::setup - : ScaleFactorType::hold; - TimingModel *check_model = makeScalarCheckModel(margin, scale_type, input_rf); - if (attrs == nullptr) - attrs = new TimingArcAttrs(); - attrs->setModel(input_rf, check_model); - } - sta_->removeInputDelay(input_pin, input_rf1, clk, clk_rf, min_max1); - } - if (attrs) - lib_builder_->makeFromTransitionArcs(cell_, clk_port, - input_port, nullptr, - clk_rf, role, attrs); + lib_builder_->makeFromTransitionArcs(cell_, clk_port, + input_port, nullptr, + clk_rf, role, attrs); } } } @@ -260,6 +347,9 @@ MakeTimingModel::findInputSetupHolds() } } +//////////////////////////////////////////////////////////////// + +// Rewrite to use non-filtered arrivals at outputs from each clock. void MakeTimingModel::findClkedOutputPaths() { diff --git a/search/Search.cc b/search/Search.cc index 9b6c81d4..047de99b 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -442,6 +442,30 @@ Search::findPathEnds(ExceptionFrom *from, bool removal, bool clk_gating_setup, bool clk_gating_hold) +{ + findFilteredArrivals(from, thrus, to, unconstrained); + if (!sdc_->recoveryRemovalChecksEnabled()) + recovery = removal = false; + if (!sdc_->gatedClkChecksEnabled()) + clk_gating_setup = clk_gating_hold = false; + path_groups_ = makePathGroups(group_count, endpoint_count, unique_pins, + slack_min, slack_max, + group_names, setup, hold, + recovery, removal, + clk_gating_setup, clk_gating_hold); + ensureDownstreamClkPins(); + PathEndSeq *path_ends = path_groups_->makePathEnds(to, unconstrained_paths_, + corner, min_max, + sort_by_slack); + sdc_->reportClkToClkMaxCycleWarnings(); + return path_ends; +} + +void +Search::findFilteredArrivals(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool unconstrained) { unconstrained_paths_ = unconstrained; // Delete results from last findPathEnds. @@ -462,21 +486,6 @@ Search::findPathEnds(ExceptionFrom *from, // -from clocks // -to findAllArrivals(); - if (!sdc_->recoveryRemovalChecksEnabled()) - recovery = removal = false; - if (!sdc_->gatedClkChecksEnabled()) - clk_gating_setup = clk_gating_hold = false; - path_groups_ = makePathGroups(group_count, endpoint_count, unique_pins, - slack_min, slack_max, - group_names, setup, hold, - recovery, removal, - clk_gating_setup, clk_gating_hold); - ensureDownstreamClkPins(); - PathEndSeq *path_ends = path_groups_->makePathEnds(to, unconstrained_paths_, - corner, min_max, - sort_by_slack); - sdc_->reportClkToClkMaxCycleWarnings(); - return path_ends; } // From/thrus/to are used to make a filter exception. If the last @@ -3981,7 +3990,10 @@ Search::deletePathGroups() PathGroup * Search::pathGroup(const PathEnd *path_end) const { - return path_groups_->pathGroup(path_end); + if (path_groups_) + return path_groups_->pathGroup(path_end); + else + return nullptr; } bool diff --git a/tcl/Search.tcl b/tcl/Search.tcl index 6f4443f3..b90ec6dd 100644 --- a/tcl/Search.tcl +++ b/tcl/Search.tcl @@ -1032,14 +1032,21 @@ proc worst_clock_skew { args } { ################################################################ -define_cmd_args "write_timing_model" {[-corner corner] cell_name filename} +define_cmd_args "write_timing_model" {[-corner corner] \ + [-cell_name cell_name]\ + filename} proc write_timing_model { args } { - parse_key_args "write_timing_model" args keys {corner} flags {} - check_argc_eq2 "write_timing_model" $args + parse_key_args "write_timing_model" args \ + keys {-cell_name -corner} flags {} + check_argc_eq1 "write_timing_model" $args - set cell_name [lindex $args 0] - set filename [lindex $args 1] + set filename [lindex $args 0] + if { [info exists keys(-cell_name)] } { + set cell_name $keys(-cell_name) + } else { + set cell_name [get_name [[top_instance] cell]] + } set corner [parse_corner keys] write_timing_model_cmd $cell_name [file nativename $filename] $corner }