diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index 99ed4942..6afcaf20 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -301,7 +301,8 @@ ArnoldiDelayCalc::findParasitic(const Pin *drvr_pin, Net *net = network_->net(drvr_pin); parasitics_->deleteParasiticNetwork(net, parasitic_ap); } - reduced_parasitic_drvrs_.push_back(drvr_pin); + // Arnoldi parasitics their own class that are not saved in the parasitic db. + unsaved_parasitics_.push_back(parasitic); return parasitic; } } diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 9a95417b..ff3a9f16 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -1550,7 +1550,6 @@ DmpCeffDelayCalc::inputPortDelay(const Pin *port_pin, const DcalcAnalysisPt *dcalc_ap) { dmp_alg_ = nullptr; - input_port_ = true; RCDelayCalc::inputPortDelay(port_pin, in_slew, rf, parasitic, dcalc_ap); } diff --git a/dcalc/DmpCeff.hh b/dcalc/DmpCeff.hh index 2844130b..5f8360d2 100644 --- a/dcalc/DmpCeff.hh +++ b/dcalc/DmpCeff.hh @@ -88,7 +88,6 @@ protected: double rpi, double c1); - bool input_port_; static bool unsuppored_model_warned_; private: diff --git a/dcalc/RCDelayCalc.cc b/dcalc/RCDelayCalc.cc index cb9c115c..3aec6224 100644 --- a/dcalc/RCDelayCalc.cc +++ b/dcalc/RCDelayCalc.cc @@ -48,6 +48,7 @@ RCDelayCalc::inputPortDelay(const Pin *, drvr_cell_ = nullptr; drvr_library_ = network_->defaultLibertyLibrary(); multi_drvr_slew_factor_ = 1.0F; + input_port_ = true; } // For DSPF on an input port the elmore delay is used as the time diff --git a/dcalc/RCDelayCalc.hh b/dcalc/RCDelayCalc.hh index fcffefbc..24530959 100644 --- a/dcalc/RCDelayCalc.hh +++ b/dcalc/RCDelayCalc.hh @@ -41,6 +41,7 @@ protected: const LibertyCell *drvr_cell_; const Parasitic *drvr_parasitic_; + bool input_port_; }; } // namespace diff --git a/dcalc/SimpleRCDelayCalc.cc b/dcalc/SimpleRCDelayCalc.cc index 634085a4..394e10e5 100644 --- a/dcalc/SimpleRCDelayCalc.cc +++ b/dcalc/SimpleRCDelayCalc.cc @@ -66,6 +66,7 @@ SimpleRCDelayCalc::gateDelay(const LibertyCell *drvr_cell, ArcDelay &gate_delay, Slew &drvr_slew) { + input_port_ = false; drvr_parasitic_ = drvr_parasitic; drvr_rf_ = arc->toEdge()->asRiseFall(); drvr_cell_ = drvr_cell; diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt index f62a343c..20a0359f 100644 Binary files a/doc/OpenSTA.odt and b/doc/OpenSTA.odt differ diff --git a/doc/OpenSTA.pdf b/doc/OpenSTA.pdf index a35bbbfa..a99d3a7f 100644 Binary files a/doc/OpenSTA.pdf and b/doc/OpenSTA.pdf differ diff --git a/graph/DelayNormal2.cc b/graph/DelayNormal2.cc index d9a3eebd..a58787a3 100644 --- a/graph/DelayNormal2.cc +++ b/graph/DelayNormal2.cc @@ -403,7 +403,7 @@ delayAsString(const Delay &delay, if (sta->pocvEnabled()) { float sigma_early = delay.sigma(EarlyLate::early()); float sigma_late = delay.sigma(EarlyLate::late()); - return stringPrintTmp("%s[%s : %s]", + return stringPrintTmp("%s[%s:%s]", unit->asString(delay.mean(), digits), unit->asString(sigma_early, digits), unit->asString(sigma_late, digits)); diff --git a/include/sta/LibertyClass.hh b/include/sta/LibertyClass.hh index 93d8df60..3b0f4d5b 100644 --- a/include/sta/LibertyClass.hh +++ b/include/sta/LibertyClass.hh @@ -136,12 +136,6 @@ enum class TableAxisVariable { enum class PathType { clk, data, clk_and_data }; const int path_type_count = 2; -// Rise/fall to rise/fall. -const int timing_arc_index_bit_count = 2; -const int timing_arc_index_max = (1< timing_arc_set_index_max) - criticalError(235, "timing arc set max index exceeded"); timing_arc_sets_.push_back(arc_set); LibertyPort *from = arc_set->from(); diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index ee578c67..5e811226 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -2532,7 +2532,7 @@ LibertyReader::endOutputCurrentRiseFall(LibertyGroup *group) waveform->cap()); } Table1 *ref_time_tbl = new Table1(ref_times, slew_axis); - OutputWaveforms *output_current = new OutputWaveforms(slew_axis, cap_axis, + OutputWaveforms *output_current = new OutputWaveforms(slew_axis, cap_axis, rf_, current_waveforms, ref_time_tbl); timing_->setOutputWaveforms(rf_, output_current); diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 7e2be728..8d6dd3fe 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -871,35 +871,8 @@ Table1::findValue(float axis_value1, float Table1::findValue(float axis_value1) const { - float value; - bool extrapolated; - findValue(axis_value1, value, extrapolated); - return value; -} - -float -Table1::findValueClip(float axis_value1, - float clip_value) const -{ - float value; - bool extrapolated; - findValue(axis_value1, value, extrapolated); - if (extrapolated) - return clip_value; - else - return value; -} - -void -Table1::findValue(float axis_value1, - // Return values. - float &value, - bool &extrapolated) const -{ - if (axis1_->size() == 1) { - value = this->value(axis_value1); - extrapolated = false; - } + if (axis1_->size() == 1) + return this->value(axis_value1); else { size_t axis_index1 = axis1_->findAxisIndex(axis_value1); float x1 = axis_value1; @@ -908,8 +881,30 @@ Table1::findValue(float axis_value1, float y1 = this->value(axis_index1); float y2 = this->value(axis_index1 + 1); float dx1 = (x1 - x1l) / (x1u - x1l); - value = (1 - dx1) * y1 + dx1 * y2; - extrapolated = (x1 < x1l || x1 >= x1u); + return (1 - dx1) * y1 + dx1 * y2; + } +} + +float +Table1::findValueClip(float axis_value1) const +{ + if (axis1_->size() == 1) + return this->value(axis_value1); + else { + size_t axis_index1 = axis1_->findAxisIndex(axis_value1); + float x1 = axis_value1; + float x1l = axis1_->axisValue(axis_index1); + float x1u = axis1_->axisValue(axis_index1 + 1); + if (x1 < x1l) + return this->value(0); + else if (x1 > x1u) + return this->value(axis1_->size() - 1); + else { + float y1 = this->value(axis_index1); + float y2 = this->value(axis_index1 + 1); + float dx1 = (x1 - x1l) / (x1u - x1l); + return (1 - dx1) * y1 + dx1 * y2; + } } } @@ -1584,10 +1579,12 @@ tableVariableUnit(TableAxisVariable variable, OutputWaveforms::OutputWaveforms(TableAxisPtr slew_axis, TableAxisPtr cap_axis, + const RiseFall *rf, Table1Seq ¤t_waveforms, Table1 *ref_times) : slew_axis_(slew_axis), cap_axis_(cap_axis), + rf_(rf), current_waveforms_(current_waveforms), voltage_waveforms_(current_waveforms.size()), ref_times_(ref_times) @@ -1655,7 +1652,6 @@ OutputWaveforms::voltageWaveform(float slew, time_values); // Interpolate waveform samples at time steps. - float value = values00->value(values00->values()->size() - 1); size_t index1 = slew_index; size_t index2 = cap_index; float x1 = slew; @@ -1667,30 +1663,26 @@ OutputWaveforms::voltageWaveform(float slew, float x2u = cap_axis_->axisValue(index2 + 1); float dx2 = (x2 - x2l) / (x2u - x2l); FloatSeq *values = new FloatSeq; - bool waveform_started = false; float prev_value = 0.0; - constexpr float value_tol = .01; - float value_end = value * (1.0 - value_tol); + constexpr float value_tol = .0001; for (size_t i = 0; i <= time_step_count; i++) { float time = time_min + time_step * i; - float y00 = values00->findValue(time); - float y10 = values10->findValue(time); - float y11 = values11->findValue(time); - float y01 = values01->findValue(time); - float voltage + if (time > time_max) + break; + float y00 = values00->findValueClip(time); + float y10 = values10->findValueClip(time); + float y11 = values11->findValueClip(time); + float y01 = values01->findValueClip(time); + float value = (1 - dx1) * (1 - dx2) * y00 + dx1 * (1 - dx2) * y10 + dx1 * dx2 * y11 + (1 - dx1) * dx2 * y01; - if (voltage > value_tol) - waveform_started = true; - if (waveform_started) { + if (i == 0 || abs(value - prev_value) > value_tol) { time_values->push_back(time); - values->push_back(voltage); + values->push_back(value); } - if (prev_value > value_end) - break; - prev_value = voltage; + prev_value = value; } return Table1(values, time_axis); } @@ -1713,7 +1705,8 @@ OutputWaveforms::voltageWaveform(size_t wave_index, float prev_current = currents->value(0); float voltage = 0.0; voltages1->push_back(voltage); - bool invert = currents->value(time_axis->size() - 1) < 0.0; + bool always_rise = true; + bool invert = (always_rise && rf_ == RiseFall::fall()); for (size_t i = 1; i < time_axis->size(); i++) { float time = time_axis->axisValue(i); float current = currents->value(i); @@ -1723,6 +1716,10 @@ OutputWaveforms::voltageWaveform(size_t wave_index, prev_time = time; prev_current = current; } + if (!always_rise && rf_ == RiseFall::fall()) { + for (size_t i = 0; i < voltages1->size(); i++) + (*voltages1)[i] -= voltage; + } } return voltages; } @@ -1773,21 +1770,26 @@ OutputWaveforms::currentWaveform(float slew, float x2u = cap_axis_->axisValue(index2 + 1); float dx2 = (x2 - x2l) / (x2u - x2l); FloatSeq *values = new FloatSeq; + float prev_value = 0.0; + constexpr float value_tol = 1e-6; for (size_t i = 0; i <= time_step_count; i++) { float time = time_min + time_step * i; - float y00 = values00->findValueClip(time, 0.0); - float y10 = values10->findValueClip(time, 0.0); - float y11 = values11->findValueClip(time, 0.0); - float y01 = values01->findValueClip(time, 0.0); + if (time > time_max) + break; + float y00 = values00->findValueClip(time); + float y10 = values10->findValueClip(time); + float y11 = values11->findValueClip(time); + float y01 = values01->findValueClip(time); float value = (1 - dx1) * (1 - dx2) * y00 + dx1 * (1 - dx2) * y10 + dx1 * dx2 * y11 + (1 - dx1) * dx2 * y01; - time_values->push_back(time); - values->push_back(value); - if (time > time_max) - break; + if (i == 0 || abs(value - prev_value) > value_tol) { + time_values->push_back(time); + values->push_back(value); + } + prev_value = value; } return Table1(values, time_axis); } diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc index 318290d7..4176985e 100644 --- a/liberty/TimingArc.cc +++ b/liberty/TimingArc.cc @@ -239,7 +239,8 @@ TimingArcIndex TimingArcSet::addTimingArc(TimingArc *arc) { TimingArcIndex arc_index = arcs_.size(); - if (arc_index > timing_arc_index_max) + // Rise/fall to rise/fall. + if (arc_index > RiseFall::index_count * RiseFall::index_count) criticalError(243, "timing arc max index exceeded\n"); arcs_.push_back(arc); diff --git a/network/SdcNetwork.cc b/network/SdcNetwork.cc index 12a652fd..586ba3f8 100644 --- a/network/SdcNetwork.cc +++ b/network/SdcNetwork.cc @@ -22,6 +22,8 @@ namespace sta { +using std::to_string; + static string escapeDividers(const char *token, const Network *network); @@ -670,12 +672,11 @@ SdcNetwork::findPortsMatching(const Cell *cell, matches = network_->findPortsMatching(cell, &escaped_pattern1); if (matches.empty()) { // Try escaping base foo\[0\][1] - string escaped2; - string escaped_bus_name = escapeBrackets(bus_name.c_str(), this); - stringPrint(escaped2, "%s[%d]", - escaped_bus_name.c_str(), - index); - PatternMatch escaped_pattern2(escaped2.c_str(), pattern); + string escaped_name = escapeBrackets(bus_name.c_str(), this); + escaped_name += '['; + escaped_name += to_string(index); + escaped_name += ']'; + PatternMatch escaped_pattern2(escaped_name.c_str(), pattern); matches = network_->findPortsMatching(cell, &escaped_pattern2); } } @@ -929,8 +930,11 @@ SdcNetwork::visitPinTail(const Instance *instance, Port *port = port_iter->next(); const char *port_name = network_->name(port); if (network_->hasMembers(port)) { - bool bus_matches = tail->match(port_name) - || tail->match(escapeDividers(port_name, network_)); + bool bus_matches = tail->match(port_name); + if (!bus_matches) { + string escaped_name = escapeDividers(port_name, network_); + bus_matches = tail->match(escaped_name); + } PortMemberIterator *member_iter = network_->memberIterator(port); while (member_iter->hasNext()) { Port *member_port = member_iter->next(); @@ -942,8 +946,12 @@ SdcNetwork::visitPinTail(const Instance *instance, } else { const char *member_name = network_->name(member_port); - if (tail->match(member_name) - || tail->match(escapeDividers(member_name, network_))) { + bool member_matches = tail->match(member_name); + if (!member_matches) { + string escaped_name = escapeDividers(member_name, network_); + member_matches = tail->match(escaped_name); + } + if (member_matches) { matches.push_back(pin); found_match = true; } @@ -952,13 +960,19 @@ SdcNetwork::visitPinTail(const Instance *instance, } delete member_iter; } - else if (tail->match(port_name) - || tail->match(escapeDividers(port_name, network_))) { - Pin *pin = network_->findPin(instance, port); - if (pin) { - matches.push_back(pin); - found_match = true; - } + else { + bool port_matches = tail->match(port_name); + if (!port_matches) { + string escaped_name = escapeDividers(port_name, network_); + port_matches = tail->match(escaped_name); + } + if (port_matches) { + Pin *pin = network_->findPin(instance, port); + if (pin) { + matches.push_back(pin); + found_match = true; + } + } } } delete port_iter; @@ -1125,9 +1139,9 @@ SdcNetwork::visitMatches(const Instance *parent, network_->findChildrenMatching(parent, &matcher, matches); if (has_brkts && matches.empty()) { // Look for matches after escaping brackets. - const PatternMatch escaped_brkts(escapeBrackets(inst_path, this), - pattern); - network_->findChildrenMatching(parent, &escaped_brkts, matches); + string escaped_brkts = escapeBrackets(inst_path, this); + const PatternMatch escaped_pattern(escaped_brkts, pattern); + network_->findChildrenMatching(parent, &escaped_pattern, matches); } if (!matches.empty()) { // Found instance matches for the sub-path up to this divider. diff --git a/sdc/ExceptionPath.cc b/sdc/ExceptionPath.cc index 76543c18..78678649 100644 --- a/sdc/ExceptionPath.cc +++ b/sdc/ExceptionPath.cc @@ -1805,8 +1805,8 @@ ExceptionThru::matches(const Pin *from_pin, const Network *network) { EdgePins edge_pins(from_pin, to_pin); - return ((pins_ && pins_->hasKey(to_pin)) - || (edges_ && edges_->hasKey(edge_pins)) + return ((pins_ && to_pin && pins_->hasKey(to_pin)) + || (edges_ && from_pin && to_pin && edges_->hasKey(edge_pins)) || (nets_ && to_pin && nets_->hasKey(network->net(to_pin))) || (insts_ && to_pin && insts_->hasKey(network->instance(to_pin)))) && rf_->matches(to_rf); diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index e36c87c3..ecf8d0ea 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -15,6 +15,7 @@ // along with this program. If not, see . #include "MakeTimingModel.hh" +#include "MakeTimingModelPvt.hh" #include #include @@ -45,16 +46,33 @@ namespace sta { using std::max; using std::make_shared; -MakeTimingModel::MakeTimingModel(const Corner *corner, +LibertyLibrary * +makeTimingModel(const char *lib_name, + const char *cell_name, + const char *filename, + const Corner *corner, + Sta *sta) +{ + MakeTimingModel maker(lib_name, cell_name, filename, corner, sta); + return maker.makeTimingModel(); +} + +MakeTimingModel::MakeTimingModel(const char *lib_name, + const char *cell_name, + const char *filename, + const Corner *corner, Sta *sta) : StaState(sta), - sta_(sta), - cell_(nullptr), + lib_name_(lib_name), + cell_name_(cell_name), + filename_(filename), corner_(corner), + cell_(nullptr), min_max_(MinMax::max()), lib_builder_(new LibertyBuilder), tbl_template_index_(1), - sdc_backup_(nullptr) + sdc_backup_(nullptr), + sta_(sta) { } @@ -64,15 +82,13 @@ MakeTimingModel::~MakeTimingModel() } LibertyLibrary * -MakeTimingModel::makeTimingModel(const char *lib_name, - const char *cell_name, - const char *filename) +MakeTimingModel::makeTimingModel() { saveSdc(); tbl_template_index_ = 1; - makeLibrary(lib_name, filename); - makeCell(cell_name, filename); + makeLibrary(); + makeCell(); makePorts(); sta_->searchPreamble(); @@ -87,7 +103,7 @@ MakeTimingModel::makeTimingModel(const char *lib_name, return library_; } -// Move set_input_delay/set_output_delay/set_load's to the side. +// Move sdc commands used by makeTimingModel to the side. void MakeTimingModel::saveSdc() { @@ -109,10 +125,9 @@ MakeTimingModel::restoreSdc() } void -MakeTimingModel::makeLibrary(const char *lib_name, - const char *filename) +MakeTimingModel::makeLibrary() { - library_ = network_->makeLibertyLibrary(lib_name, filename); + library_ = network_->makeLibertyLibrary(lib_name_, filename_); LibertyLibrary *default_lib = network_->defaultLibertyLibrary(); *library_->units()->timeUnit() = *default_lib->units()->timeUnit(); *library_->units()->capacitanceUnit() = *default_lib->units()->capacitanceUnit(); @@ -136,10 +151,9 @@ MakeTimingModel::makeLibrary(const char *lib_name, } void -MakeTimingModel::makeCell(const char *cell_name, - const char *filename) +MakeTimingModel::makeCell() { - cell_ = lib_builder_->makeCell(library_, cell_name, filename); + cell_ = lib_builder_->makeCell(library_, cell_name_, filename_); } void @@ -202,21 +216,18 @@ public: 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 ClockEdgeDelays &margins() const { return margins_; } private: - Sta *sta_; - const Pin *input_pin_; const RiseFall *input_rf_; ClockEdgeDelays margins_; + Sta *sta_; }; MakeEndTimingArcs::MakeEndTimingArcs(Sta *sta) : - sta_(sta), - input_pin_(nullptr), - input_rf_(nullptr) + input_rf_(nullptr), + sta_(sta) { } @@ -226,13 +237,6 @@ 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) { @@ -257,8 +261,7 @@ MakeEndTimingArcs::visit(PathEnd *path_end) ? data_delay - clk_latency + check_margin : clk_latency - data_delay + check_margin; float delay1 = delayAsFloat(margin, MinMax::max(), sta_); - debugPrint(debug, "make_timing_model", 2, "%s %s -> %s clock %s %s %s %s", - network->pathName(input_pin_), + debugPrint(debug, "make_timing_model", 2, "%s -> %s clock %s %s %s %s", input_rf_->shortName(), network->pathName(src_path->pin(sta_)), tgt_clk_edge->name(), @@ -285,44 +288,56 @@ MakeEndTimingArcs::visit(PathEnd *path_end) void MakeTimingModel::findTimingFromInputs() { - 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)) { - end_visitor.setInputPin(input_pin); - OutputPinDelays output_delays; - 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(), true, 0.0); + search_->deleteFilteredArrivals(); - PinSet *from_pins = new PinSet(network_); - 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); - findOutputDelays(input_rf, output_delays); - - sta_->removeInputDelay(input_pin, input_rf1, - sdc_->defaultArrivalClock(), - sdc_->defaultArrivalClockEdge()->transition(), - MinMaxAll::all()); - } - makeSetupHoldTimingArcs(input_pin, end_visitor.margins()); - makeInputOutputTimingArcs(input_pin, output_delays); - } + Instance *top_inst = network_->topInstance(); + Cell *top_cell = network_->cell(top_inst); + CellPortBitIterator *port_iter = network_->portBitIterator(top_cell); + while (port_iter->hasNext()) { + Port *input_port = port_iter->next(); + if (network_->direction(input_port)->isInput()) + findTimingFromInput(input_port); + } + delete port_iter; +} + +void +MakeTimingModel::findTimingFromInput(Port *input_port) +{ + Instance *top_inst = network_->topInstance(); + Pin *input_pin = network_->findPin(top_inst, input_port); + if (!sta_->isClockSrc(input_pin)) { + MakeEndTimingArcs end_visitor(sta_); + OutputPinDelays output_delays; + 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(), true, 0.0); + + PinSet *from_pins = new PinSet(network_); + from_pins->insert(input_pin); + ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr, + input_rf1); + search_->findFilteredArrivals(from, nullptr, nullptr, false, false); + + end_visitor.setInputRf(input_rf); + VertexSeq endpoints = search_->filteredEndpoints(); + VisitPathEnds visit_ends(sta_); + for (Vertex *end : endpoints) + visit_ends.visitPathEnds(end, corner_, MinMaxAll::all(), true, &end_visitor); + findOutputDelays(input_rf, output_delays); + search_->deleteFilteredArrivals(); + + sta_->removeInputDelay(input_pin, input_rf1, + sdc_->defaultArrivalClock(), + sdc_->defaultArrivalClockEdge()->transition(), + MinMaxAll::all()); + } + makeSetupHoldTimingArcs(input_pin, end_visitor.margins()); + makeInputOutputTimingArcs(input_pin, output_delays); } - delete input_iter; } void @@ -570,6 +585,7 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, drvr_self_delay, drvr_self_slew); const TableModel *drvr_table = drvr_gate_model->delayModel(); + const TableTemplate *drvr_template = drvr_table->tblTemplate(); const TableAxisPtr drvr_load_axis = loadCapacitanceAxis(drvr_table); if (drvr_load_axis) { const FloatSeq *drvr_axis_values = drvr_load_axis->values(); @@ -593,20 +609,15 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, TableAxisPtr load_axis = std::make_shared(TableAxisVariable::total_output_net_capacitance, axis_values); - + TablePtr delay_table = make_shared(load_values, load_axis); TablePtr slew_table = make_shared(slew_values, load_axis); - string template_name = "template_"; - template_name += std::to_string(tbl_template_index_++); - - TableTemplate *tbl_template = new TableTemplate(template_name.c_str()); - tbl_template->setAxis1(load_axis); - library_->addTableTemplate(tbl_template, TableTemplateType::delay); - - TableModel *delay_model = new TableModel(delay_table, tbl_template, + TableTemplate *model_template = ensureTableTemplate(drvr_template, + load_axis); + TableModel *delay_model = new TableModel(delay_table, model_template, ScaleFactorType::cell, rf); - TableModel *slew_model = new TableModel(slew_table, tbl_template, + TableModel *slew_model = new TableModel(slew_table, model_template, ScaleFactorType::cell, rf); GateTableModel *gate_model = new GateTableModel(delay_model, nullptr, slew_model, nullptr, @@ -624,6 +635,23 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, return makeGateModelScalar(delay, slew, rf); } +TableTemplate * +MakeTimingModel::ensureTableTemplate(const TableTemplate *drvr_template, + TableAxisPtr load_axis) +{ + TableTemplate *model_template = template_map_.findKey(drvr_template); + if (model_template == nullptr) { + string template_name = "template_"; + template_name += std::to_string(tbl_template_index_++); + + model_template = new TableTemplate(template_name.c_str()); + model_template->setAxis1(load_axis); + library_->addTableTemplate(model_template, TableTemplateType::delay); + template_map_[drvr_template] = model_template; + } + return model_template; +} + TableAxisPtr MakeTimingModel::loadCapacitanceAxis(const TableModel *table) { diff --git a/search/MakeTimingModel.hh b/search/MakeTimingModel.hh index 03735585..1b6e6361 100644 --- a/search/MakeTimingModel.hh +++ b/search/MakeTimingModel.hh @@ -16,81 +16,17 @@ #pragma once -#include - -#include "LibertyClass.hh" -#include "SdcClass.hh" -#include "SearchClass.hh" -#include "StaState.hh" -#include "RiseFallMinMax.hh" - namespace sta { +class LibertyLibrary; +class Corner; class Sta; -class LibertyBuilder; -class OutputDelays -{ -public: - OutputDelays(); - TimingSense timingSense() const; - - RiseFallMinMax delays; - // input edge -> output edge path exists for unateness - bool rf_path_exists[RiseFall::index_count][RiseFall::index_count]; -}; - -typedef std::map ClockEdgeDelays; -typedef std::map OutputPinDelays; - -class MakeTimingModel : public StaState -{ -public: - MakeTimingModel(const Corner *corner, - Sta *sta); - ~MakeTimingModel(); - LibertyLibrary *makeTimingModel(const char *lib_name, - const char *cell_name, - const char *filename); - -private: - void makeLibrary(const char *lib_name, - const char *filename); - void makeCell(const char *cell_name, - const char *filename); - void makePorts(); - void checkClock(Clock *clk); - void findTimingFromInputs(); - void findClkedOutputPaths(); - void findOutputDelays(const RiseFall *input_rf, - OutputPinDelays &output_pin_delays); - void makeSetupHoldTimingArcs(const Pin *input_pin, - const ClockEdgeDelays &clk_margins); - void makeInputOutputTimingArcs(const Pin *input_pin, - OutputPinDelays &output_pin_delays); - TimingModel *makeScalarCheckModel(float value, - ScaleFactorType scale_factor_type, - RiseFall *rf); - TimingModel *makeGateModelScalar(Delay delay, - Slew slew, - RiseFall *rf); - TimingModel *makeGateModelTable(const Pin *output_pin, - Delay delay, - RiseFall *rf); - TableAxisPtr loadCapacitanceAxis(const TableModel *table); - LibertyPort *modelPort(const Pin *pin); - - void saveSdc(); - void restoreSdc(); - - Sta *sta_; - LibertyLibrary *library_; - LibertyCell *cell_; - const Corner *corner_; - MinMax *min_max_; - LibertyBuilder *lib_builder_; - int tbl_template_index_; - Sdc *sdc_backup_; -}; +LibertyLibrary * +makeTimingModel(const char *lib_name, + const char *cell_name, + const char *filename, + const Corner *corner, + Sta *sta); } // namespace diff --git a/search/MakeTimingModelPvt.hh b/search/MakeTimingModelPvt.hh new file mode 100644 index 00000000..2895acee --- /dev/null +++ b/search/MakeTimingModelPvt.hh @@ -0,0 +1,103 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2023, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include + +#include "LibertyClass.hh" +#include "SdcClass.hh" +#include "SearchClass.hh" +#include "StaState.hh" +#include "RiseFallMinMax.hh" + +namespace sta { + +class Sta; +class LibertyBuilder; + +class OutputDelays +{ +public: + OutputDelays(); + TimingSense timingSense() const; + + RiseFallMinMax delays; + // input edge -> output edge path exists for unateness + bool rf_path_exists[RiseFall::index_count][RiseFall::index_count]; +}; + +typedef std::map ClockEdgeDelays; +typedef std::map OutputPinDelays; + +class MakeTimingModel : public StaState +{ +public: + MakeTimingModel(const char *lib_name, + const char *cell_name, + const char *filename, + const Corner *corner, + Sta *sta); + ~MakeTimingModel(); + LibertyLibrary *makeTimingModel(); + +private: + void makeLibrary(); + void makeCell(); + void makePorts(); + void checkClock(Clock *clk); + void findTimingFromInputs(); + void findTimingFromInput(Port *input_port); + void findClkedOutputPaths(); + void findOutputDelays(const RiseFall *input_rf, + OutputPinDelays &output_pin_delays); + void makeSetupHoldTimingArcs(const Pin *input_pin, + const ClockEdgeDelays &clk_margins); + void makeInputOutputTimingArcs(const Pin *input_pin, + OutputPinDelays &output_pin_delays); + TimingModel *makeScalarCheckModel(float value, + ScaleFactorType scale_factor_type, + RiseFall *rf); + TimingModel *makeGateModelScalar(Delay delay, + Slew slew, + RiseFall *rf); + TimingModel *makeGateModelTable(const Pin *output_pin, + Delay delay, + RiseFall *rf); + TableTemplate *ensureTableTemplate(const TableTemplate *drvr_template, + TableAxisPtr load_axis); + TableAxisPtr loadCapacitanceAxis(const TableModel *table); + LibertyPort *modelPort(const Pin *pin); + + void saveSdc(); + void restoreSdc(); + + const char *lib_name_; + const char *cell_name_; + const char *filename_; + const Corner *corner_; + LibertyLibrary *library_; + LibertyCell *cell_; + MinMax *min_max_; + LibertyBuilder *lib_builder_; + // Output driver table model template to model template. + Map template_map_; + int tbl_template_index_; + Sdc *sdc_backup_; + Sta *sta_; +}; + +} // namespace diff --git a/search/Property.cc b/search/Property.cc index 459ca7a0..f67764a8 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -882,6 +882,10 @@ getProperty(const Pin *pin, ClockSet clks = sta->clocks(pin); return PropertyValue(&clks); } + else if (stringEqual(property, "clock_domains")) { + ClockSet clks = sta->clockDomains(pin); + return PropertyValue(&clks); + } else if (stringEqual(property, "activity")) { PwrActivity activity = sta->findClkedActivity(pin); return PropertyValue(&activity); diff --git a/search/Search.cc b/search/Search.cc index 00e47943..d1e25b1a 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -250,6 +250,7 @@ Search::init(StaState *sta) filter_ = nullptr; filter_from_ = nullptr; filter_to_ = nullptr; + filtered_arrivals_ = new VertexSet(graph_); found_downstream_clk_pins_ = false; } @@ -287,6 +288,7 @@ Search::~Search() delete worst_slacks_; delete check_crpr_; delete genclks_; + delete filtered_arrivals_; deleteFilter(); deletePathGroups(); } @@ -403,6 +405,7 @@ Search::deletePaths() Vertex *vertex = vertex_iter.next(); vertex->deletePaths(); } + filtered_arrivals_->clear(); graph_->clearArrivals(); graph_->clearPrevPaths(); arrivals_exist_ = false; @@ -444,7 +447,7 @@ Search::findPathEnds(ExceptionFrom *from, bool clk_gating_setup, bool clk_gating_hold) { - findFilteredArrivals(from, thrus, to, unconstrained); + findFilteredArrivals(from, thrus, to, unconstrained, true); if (!sdc_->recoveryRemovalChecksEnabled()) recovery = removal = false; if (!sdc_->gatedClkChecksEnabled()) @@ -466,7 +469,8 @@ void Search::findFilteredArrivals(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, - bool unconstrained) + bool unconstrained, + bool thru_latches) { unconstrained_paths_ = unconstrained; // Delete results from last findPathEnds. @@ -480,13 +484,13 @@ Search::findFilteredArrivals(ExceptionFrom *from, || from->instances())) || thrus) { filter_ = sdc_->makeFilterPath(from, thrus, nullptr); - findFilteredArrivals(); + findFilteredArrivals(thru_latches); } else // These cases do not require filtered arrivals. // -from clocks // -to - findAllArrivals(); + findAllArrivals(thru_latches); } // From/thrus/to are used to make a filter exception. If the last @@ -503,24 +507,34 @@ Search::deleteFilteredArrivals() && (from->pins() || from->instances())) || thrus) { - VertexIterator vertex_iter(graph_); - while (vertex_iter.hasNext()) { - Vertex *vertex = vertex_iter.next(); - TagGroup *tag_group = tagGroup(vertex); - if (tag_group - && tag_group->hasFilterTag()) { - // Vertex's tag_group will be deleted. - deletePaths(vertex); - arrivalInvalid(vertex); - requiredInvalid(vertex); - } + for (Vertex *vertex : *filtered_arrivals_) { + deletePaths(vertex); + arrivalInvalid(vertex); + requiredInvalid(vertex); } + bool check_filter_arrivals = false; + if (check_filter_arrivals) { + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + TagGroup *tag_group = tagGroup(vertex); + if (tag_group + && tag_group->hasFilterTag()) + filtered_arrivals_->erase(vertex); + } + if (!filtered_arrivals_->empty()) { + report_->reportLine("Filtered verticies mismatch"); + for (Vertex *vertex : *filtered_arrivals_) + report_->reportLine(" %s", vertex->name(network_)); + } + } + filtered_arrivals_->clear(); deleteFilterTagGroups(); deleteFilterClkInfos(); deleteFilterTags(); } + deleteFilter(); } - deleteFilter(); } void @@ -567,17 +581,19 @@ Search::deleteFilterClkInfos() } void -Search::findFilteredArrivals() +Search::findFilteredArrivals(bool thru_latches) { - findArrivals1(); + filtered_arrivals_->clear(); + findArrivalsSeed(); seedFilterStarts(); Level max_level = levelize_->maxLevel(); // Search always_to_endpoint to search from exisiting arrivals at // fanin startpoints to reach -thru/-to endpoints. arrival_visitor_->init(true); // Iterate until data arrivals at all latches stop changing. - for (int pass = 1; pass <= 2 || havePendingLatchOutputs() ; pass++) { - enqueuePendingLatchOutputs(); + for (int pass = 1; pass == 1 || (thru_latches && havePendingLatchOutputs()) ; pass++) { + if (thru_latches) + enqueuePendingLatchOutputs(); debugPrint(debug_, "search", 1, "find arrivals pass %d", pass); int arrival_count = arrival_iter_->visitParallel(max_level, arrival_visitor_); @@ -586,6 +602,17 @@ Search::findFilteredArrivals() arrivals_exist_ = true; } +VertexSeq +Search::filteredEndpoints() +{ + VertexSeq ends; + for (Vertex *vertex : *filtered_arrivals_) { + if (isEndpoint(vertex)) + ends.push_back(vertex); + } + return ends; +} + class SeedFaninsThruHierPin : public HierPinThruVisitor { public: @@ -652,6 +679,7 @@ Search::deleteVertexBefore(Vertex *vertex) deletePaths(vertex); arrival_iter_->deleteVertexBefore(vertex); invalid_arrivals_->erase(vertex); + filtered_arrivals_->erase(vertex); } if (requireds_exist_) { required_iter_->deleteVertexBefore(vertex); @@ -926,18 +954,18 @@ Search::visitEndpoints(VertexVisitor *visitor) void Search::findAllArrivals() { - arrival_visitor_->init(false); - findAllArrivals(arrival_visitor_); + findAllArrivals(true); } void -Search::findAllArrivals(VertexVisitor *arrival_visitor) +Search::findAllArrivals(bool thru_latches) { + arrival_visitor_->init(false); // Iterate until data arrivals at all latches stop changing. - for (int pass = 1; pass == 1 || havePendingLatchOutputs(); pass++) { + for (int pass = 1; pass == 1 || (thru_latches && havePendingLatchOutputs()); pass++) { enqueuePendingLatchOutputs(); debugPrint(debug_, "search", 1, "find arrivals pass %d", pass); - findArrivals(levelize_->maxLevel(), arrival_visitor); + findArrivals1(levelize_->maxLevel()); } } @@ -971,17 +999,16 @@ void Search::findArrivals(Level level) { arrival_visitor_->init(false); - findArrivals(level, arrival_visitor_); + findArrivals1(level); } void -Search::findArrivals(Level level, - VertexVisitor *arrival_visitor) +Search::findArrivals1(Level level) { debugPrint(debug_, "search", 1, "find arrivals to level %d", level); - findArrivals1(); + findArrivalsSeed(); Stats stats(debug_, report_); - int arrival_count = arrival_iter_->visitParallel(level, arrival_visitor); + int arrival_count = arrival_iter_->visitParallel(level, arrival_visitor_); stats.report("Find arrivals"); if (arrival_iter_->empty() && invalid_arrivals_->empty()) { @@ -993,7 +1020,7 @@ Search::findArrivals(Level level, } void -Search::findArrivals1() +Search::findArrivalsSeed() { if (!arrivals_seeded_) { genclks_->ensureInsertionDelays(); @@ -2658,6 +2685,8 @@ Search::setVertexArrivals(Vertex *vertex, } tag_bldr->copyArrivals(tag_group, prev_arrivals, prev_paths); vertex->setTagGroupIndex(tag_group->index()); + if (tag_group->hasFilterTag()) + filtered_arrivals_->insert(vertex); if (has_requireds) { requiredInvalid(vertex); @@ -2682,6 +2711,8 @@ Search::setVertexArrivals(Vertex *vertex, tag_bldr->copyArrivals(tag_group, arrivals, prev_paths); vertex->setTagGroupIndex(tag_group->index()); + if (tag_group->hasFilterTag()) + filtered_arrivals_->insert(vertex); } } } @@ -2987,6 +3018,42 @@ Search::timingDerate(Vertex *from_vertex, } } +ClockSet +Search::clockDomains(const Vertex *vertex) const +{ + ClockSet clks; + clockDomains(vertex, clks); + return clks; +} + +void +Search::clockDomains(const Vertex *vertex, + // Return value. + ClockSet &clks) const +{ + VertexPathIterator path_iter(const_cast(vertex), this); + while (path_iter.hasNext()) { + Path *path = path_iter.next(); + const Clock *clk = path->clock(this); + if (clk) + clks.insert(const_cast(clk)); + } +} + +ClockSet +Search::clockDomains(const Pin *pin) const +{ + ClockSet clks; + Vertex *vertex; + Vertex *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + if (vertex) + clockDomains(vertex, clks); + if (bidirect_drvr_vertex) + clockDomains(bidirect_drvr_vertex, clks); + return clks; +} + ClockSet Search::clocks(const Vertex *vertex) const { diff --git a/search/Sta.cc b/search/Sta.cc index 10eb408b..29c1319d 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -1858,11 +1858,11 @@ Sta::removeCaseAnalysis(Pin *pin) } void -Sta::setInputDelay(Pin *pin, +Sta::setInputDelay(const Pin *pin, const RiseFallBoth *rf, - Clock *clk, + const Clock *clk, const RiseFall *clk_rf, - Pin *ref_pin, + const Pin *ref_pin, bool source_latency_included, bool network_latency_included, const MinMaxAll *min_max, @@ -1877,22 +1877,22 @@ Sta::setInputDelay(Pin *pin, } void -Sta::removeInputDelay(Pin *pin, - RiseFallBoth *rf, - Clock *clk, - RiseFall *clk_rf, - MinMaxAll *min_max) +Sta::removeInputDelay(const Pin *pin, + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMaxAll *min_max) { sdc_->removeInputDelay(pin, rf, clk, clk_rf, min_max); search_->arrivalInvalid(pin); } void -Sta::setOutputDelay(Pin *pin, +Sta::setOutputDelay(const Pin *pin, const RiseFallBoth *rf, - Clock *clk, + const Clock *clk, const RiseFall *clk_rf, - Pin *ref_pin, + const Pin *ref_pin, bool source_latency_included, bool network_latency_included, const MinMaxAll *min_max, @@ -1907,11 +1907,11 @@ Sta::setOutputDelay(Pin *pin, } void -Sta::removeOutputDelay(Pin *pin, - RiseFallBoth *rf, - Clock *clk, - RiseFall *clk_rf, - MinMaxAll *min_max) +Sta::removeOutputDelay(const Pin *pin, + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMaxAll *min_max) { sdc_->removeOutputDelay(pin, rf, clk, clk_rf, min_max); sdcChangedGraph(); @@ -4681,6 +4681,14 @@ Sta::clocks(const Pin *pin) return search_->clocks(pin); } +ClockSet +Sta::clockDomains(const Pin *pin) +{ + searchPreamble(); + search_->findAllArrivals(); + return search_->clockDomains(pin); +} + //////////////////////////////////////////////////////////////// InstanceSet @@ -5619,9 +5627,8 @@ Sta::writeTimingModel(const char *lib_name, const char *filename, const Corner *corner) { - MakeTimingModel maker(corner, this); - LibertyLibrary *library = maker.makeTimingModel(lib_name, cell_name, - filename); + LibertyLibrary *library = makeTimingModel(lib_name, cell_name, filename, + corner, this); writeLiberty(library, filename, this); } diff --git a/search/Tag.cc b/search/Tag.cc index b29e97f4..e895c730 100644 --- a/search/Tag.cc +++ b/search/Tag.cc @@ -97,7 +97,7 @@ Tag::asString(bool report_index, string result; if (report_index) - result += std::to_string(index_); + result += std::to_string(index_) + " "; if (report_rf_min_max) { const RiseFall *rf = transition(); diff --git a/search/WritePathSpice.cc b/search/WritePathSpice.cc index 97caf9ac..99ec69df 100644 --- a/search/WritePathSpice.cc +++ b/search/WritePathSpice.cc @@ -170,6 +170,8 @@ private: float slewAxisMinValue(TimingArc *arc); float pgPortVoltage(LibertyPgPort *pg_port); void writePrintStmt(); + float railToRailSlew(float slew, + const RiseFall *rf); // Stage "accessors". // @@ -408,11 +410,21 @@ WritePathSpice::maxTime() else { float end_slew = findSlew(path_); float arrival = delayAsFloat(path_->arrival(this)); - float max_time = input_slew / 2.0 + arrival + end_slew; + float max_time = railToRailSlew(input_slew, rf) + arrival + + railToRailSlew(end_slew, rf); return max_time; } } +float +WritePathSpice::railToRailSlew(float slew, + const RiseFall *rf) +{ + float lower = default_library_->slewLowerThreshold(rf); + float upper = default_library_->slewUpperThreshold(rf); + return slew / (upper - lower); +} + void WritePathSpice::writeStageInstances() { @@ -492,8 +504,11 @@ WritePathSpice::writeInputWaveform() const RiseFall *rf = input_path->transition(this); TimingArc *next_arc = stageGateArc(input_stage + 1); float slew0 = findSlew(input_path, rf, next_arc); - // Arbitrary offset. - float time0 = slew0; + + float threshold = default_library_->inputThreshold(rf); + float dt = railToRailSlew(slew0, rf); + float time0 = dt * threshold; + int volt_index = 1; const Pin *drvr_pin = stageDrvrPin(input_stage); const Pin *load_pin = stageLoadPin(input_stage); @@ -689,12 +704,11 @@ WritePathSpice::writeWaveformEdge(const RiseFall *rf, volt1 = gnd_voltage_; } float threshold = default_library_->inputThreshold(rf); - float lower = default_library_->slewLowerThreshold(rf); - float upper = default_library_->slewUpperThreshold(rf); - float dt = slew / (upper - lower); + float dt = railToRailSlew(slew, rf); float time0 = time - dt * threshold; float time1 = time0 + dt; - streamPrint(spice_stream_, "+%.3e %.3e\n", time0, volt0); + if (time0 > 0.0) + streamPrint(spice_stream_, "+%.3e %.3e\n", time0, volt0); streamPrint(spice_stream_, "+%.3e %.3e\n", time1, volt1); } diff --git a/tcl/Search.tcl b/tcl/Search.tcl index 1e464f1f..7b2a40ea 100644 --- a/tcl/Search.tcl +++ b/tcl/Search.tcl @@ -249,7 +249,7 @@ proc report_delays_wrt_clk { vertex what clk clk_rf } { set rise_fmt [format_delays $rise] set fall_fmt [format_delays $fall] if {$clk != "NULL"} { - set clk_str " ([get_name $clk] [rise_fall_short_name $clk_rf])" + set clk_str " ([get_name $clk] [rf_short_name $clk_rf])" } else { set clk_str "" } @@ -281,7 +281,7 @@ proc report_wrt_clk { vertex what clk clk_rf } { set rise_fmt [format_times $rise $sta_report_default_digits] set fall_fmt [format_times $fall $sta_report_default_digits] if {$clk != "NULL"} { - set clk_str " ([get_name $clk] [rise_fall_short_name $clk_rf])" + set clk_str " ([get_name $clk] [rf_short_name $clk_rf])" } else { set clk_str "" } @@ -289,16 +289,6 @@ proc report_wrt_clk { vertex what clk clk_rf } { } } -proc rise_fall_short_name { rf } { - if { $rf == "rise" } { - return [rise_short_name] - } elseif { $rf == "fall" } { - return [fall_short_name] - } else { - error "unknown transition name $rf" - } -} - proc times_are_inf { times } { foreach time $times { if { $time < 1e+10 && $time > -1e+10 } { @@ -1162,5 +1152,43 @@ proc max_fanout_check_slack_limit {} { return [expr [sta::max_fanout_check_slack] / [sta::max_fanout_check_limit]] } +################################################################ + +proc rf_short_name { rf } { + if { [rf_is_rise $rf] } { + return [rise_short_name] + } elseif { [rf_is_fall $rf] } { + return [fall_short_name] + } else { + error "unknown transition name $rf" + } +} + +proc opposite_rf { rf } { + if { [rf_is_rise $rf] } { + return "fall" + } elseif { [rf_is_fall $rf] } { + return "rise" + } else { + error "opposite_rf unknown transition $rf" + } +} + +proc rf_is_rise { rf } { + if { $rf == "rise" || $rf == "^" || $rf == "r"} { + return 1 + } else { + return 0 + } +} + +proc rf_is_fall { rf } { + if { $rf == "fall" || $rf == "v" || $rf == "f"} { + return 1 + } else { + return 0 + } +} + # sta namespace end. }