diff --git a/README.md b/README.md index 5e15721b..3d4ca48d 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,21 @@ See doc/OpenSTA.pdf for command documentiaton. See doc/StaApi.txt for timing engine API documentiaton. See doc/ChangeLog.txt for changes to commands. +OpenSTA is dual licensed. It is released under GPL v3 as OpenSTA and +is also licensed for commerical applications by Parallax Software without +the GPL's requirements. + +OpenSTA is open source, meaning the sources are published and can be compiled locally. +Derivatives works are supported as long as they adhere to the GPL license requirements. +However, OpenSTA is not supported by a public community of developers as many other +open source projects are. The copyright and develpment are exclusive to Parallax +Software. OpenSTA does not accept external code contributions. + +The official git repository is located at +https://github.com/jjcherry56/OpenSTA.git. Any forks from this code +base have not passed extensive regression testing which is not +publicly available. + ## Build OpenSTA is built with CMake. @@ -197,19 +212,8 @@ case directory. * William Scott authored the arnoldi delay calculator at Blaze, Inc which was subsequently licensed to Nefelus, Inc that has graciously contributed it to OpenSTA. -## Contributions - -External code contributions are not supported. - -https://en.wikipedia.org/wiki/Contributor_License_Agreement -https://opensource.google/docs/cla/ - ## License -OpenSTA is dual licensed. It is released under GPL v3 as OpenSTA and -is also licensed for commerical applications by Parallax Software without -the GPL's requirements. - OpenSTA, Static Timing Analyzer Copyright (c) 2023, Parallax Software, Inc. @@ -225,4 +229,3 @@ 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 . - diff --git a/dcalc/DelayCalc.i b/dcalc/DelayCalc.i index 64424f19..561f4c1d 100644 --- a/dcalc/DelayCalc.i +++ b/dcalc/DelayCalc.i @@ -48,4 +48,15 @@ set_delay_calc_incremental_tolerance(float tol) sta::Sta::sta()->setIncrementalDelayTolerance(tol); } +TmpString * +report_delay_calc_cmd(Edge *edge, + TimingArc *arc, + const Corner *corner, + const MinMax *min_max, + int digits) +{ + cmdLinkedNetwork(); + return Sta::sta()->reportDelayCalc(edge, arc, corner, min_max, digits); +} + %} // inline diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 04753cc7..db628187 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -49,6 +49,7 @@ class StaState; class Corner; class Corners; class DcalcAnalysisPt; +class DriverWaveform; typedef Map TableTemplateMap; typedef Vector TableTemplateSeq; @@ -77,6 +78,7 @@ typedef Map OcvDerateMap; typedef Vector InternalPowerAttrsSeq; typedef Map SupplyVoltageMap; typedef Map LibertyPgPortMap; +typedef Map DriverWaveformMap; enum class ClockGateType { none, latch_posedge, latch_negedge, other }; @@ -315,6 +317,10 @@ public: Corners *corners, Report *report); + DriverWaveform *findDriverWaveform(const char *name); + DriverWaveform *driverWaveformDefault() { return driver_waveform_default_; } + void addDriverWaveform(DriverWaveform *driver_waveform); + protected: float degradeWireSlew(const LibertyCell *cell, const Pvt *pvt, @@ -363,6 +369,9 @@ protected: OcvDerateMap ocv_derate_map_; SupplyVoltageMap supply_voltage_map_; LibertyCellSeq *buffers_; + DriverWaveformMap driver_waveform_map_; + // Unnamed driver waveform. + DriverWaveform *driver_waveform_default_; static constexpr float input_threshold_default_ = .5; static constexpr float output_threshold_default_ = .5; @@ -780,6 +789,9 @@ public: void setRelatedPowerPin(const char *related_power_pin); ReceiverModelPtr receiverModel() const { return receiver_model_; } void setReceiverModel(ReceiverModelPtr receiver_model); + DriverWaveform *driverWaveform(const RiseFall *rf) const; + void setDriverWaveform(DriverWaveform *driver_waveform, + const RiseFall *rf); static bool equiv(const LibertyPort *port1, const LibertyPort *port2); @@ -820,6 +832,7 @@ protected: const char *related_power_pin_; Vector corner_ports_; ReceiverModelPtr receiver_model_; + DriverWaveform *driver_waveform_[RiseFall::index_count]; unsigned int min_pulse_width_exists_:RiseFall::index_count; bool min_period_exists_:1; diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 9ae4a6f2..36bd2ef4 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -999,13 +999,17 @@ public: Arrival vertexArrival(Vertex *vertex, const RiseFall *rf, const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap); + const PathAnalysisPt *path_ap, + const MinMax *min_max); // Min/max across all clock tags. Arrival vertexArrival(Vertex *vertex, const RiseFall *rf, const PathAnalysisPt *path_ap); Arrival vertexArrival(Vertex *vertex, const MinMax *min_max); + Arrival pinArrival(const Pin *pin, + const RiseFall *rf, + const MinMax *min_max); Required vertexRequired(Vertex *vertex, const MinMax *min_max); Required vertexRequired(Vertex *vertex, diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index 39375eb7..39d1c320 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -33,12 +33,12 @@ class Unit; class Units; class Report; class Table; -class OutputCurrent; -class OutputCurrentWaveform; +class OutputWaveforms; +class Table1; typedef Vector FloatSeq; typedef Vector FloatTable; -typedef Vector OutputCurrentWaveformSeq; +typedef Vector Table1Seq; TableAxisVariable stringTableAxisVariable(const char *variable); @@ -56,7 +56,7 @@ public: TableModel *slew_model, TableModel *slew_sigma_models[EarlyLate::index_count], ReceiverModelPtr receiver_model, - OutputCurrent *output_current); + OutputWaveforms *output_waveforms); virtual ~GateTableModel(); virtual void gateDelay(const LibertyCell *cell, const Pvt *pvt, @@ -80,6 +80,8 @@ public: const TableModel *delayModel() const { return delay_model_; } const TableModel *slewModel() const { return slew_model_; } + ReceiverModelPtr receiverModel() const { return receiver_model_; } + OutputWaveforms *outputWaveforms() const { return output_waveforms_; } // Check the axes before making the model. // Return true if the model axes are supported. static bool checkAxes(const TablePtr table); @@ -127,7 +129,7 @@ protected: TableModel *slew_model_; TableModel *slew_sigma_models_[EarlyLate::index_count]; ReceiverModelPtr receiver_model_; - OutputCurrent *output_current_; + OutputWaveforms *output_waveforms_; }; class CheckTableModel : public CheckTimingModel @@ -273,16 +275,16 @@ public: size_t axis_idx2, size_t axis_idx3) const = 0; // Table interpolated lookup. - virtual float findValue(float value1, - float value2, - float value3) const = 0; + virtual float findValue(float axis_value1, + float axis_value2, + float axis_value3) const = 0; // Table interpolated lookup with scale factor. float findValue(const LibertyLibrary *library, const LibertyCell *cell, const Pvt *pvt, - float value1, - float value2, - float value3) const; + float axis_value1, + float axis_value2, + float axis_value3) const; virtual void reportValue(const char *result_name, const LibertyLibrary *library, const LibertyCell *cell, @@ -304,12 +306,12 @@ class Table0 : public Table public: Table0(float value); virtual int order() const { return 0; } - virtual float value(size_t index1, - size_t index2, - size_t index3) const; - virtual float findValue(float value1, - float value2, - float value3) const; + virtual float value(size_t axis_index1, + size_t axis_index2, + size_t axis_index3) const; + virtual float findValue(float axis_value1, + float axis_value2, + float axis_value3) const; virtual void reportValue(const char *result_name, const LibertyLibrary *library, const LibertyCell *cell, @@ -333,15 +335,25 @@ private: class Table1 : public Table { public: + Table1(); Table1(FloatSeq *values, TableAxisPtr axis1); virtual ~Table1(); + Table1(Table1 &&table); + Table1 &operator= (Table1 &&table); virtual int order() const { return 1; } virtual TableAxisPtr axis1() const { return axis1_; } - virtual float value(size_t index1, - size_t index2, - size_t index3) const; + virtual float value(size_t axis_index1, + size_t axis_index2, + size_t axis_index3) const; float value(size_t index1) const; + float findValue(float axis_value1) const; + void findValue(float axis_value1, + // Return values. + float &value, + bool &extrapolated) const; + float findValueClip(float axis_value1, + float clip_value) const; virtual float findValue(float value1, float value2, float value3) const; @@ -358,6 +370,7 @@ public: string *result) const; virtual void report(const Units *units, Report *report) const; + FloatSeq *values() const { return values_; } using Table::findValue; private: @@ -376,14 +389,14 @@ public: virtual int order() const { return 2; } TableAxisPtr axis1() const { return axis1_; } TableAxisPtr axis2() const { return axis2_; } - virtual float value(size_t index1, - size_t index2, - size_t index3) const; - float value(size_t index1, - size_t index2) const; - virtual float findValue(float value1, - float value2, - float value3) const; + virtual float value(size_t axis_index1, + size_t axis_index2, + size_t axis_index3) const; + float value(size_t axis_index1, + size_t axis_index2) const; + virtual float findValue(float axis_value1, + float axis_value2, + float axis_value3) const; FloatTable *values3() { return values_; } virtual void reportValue(const char *result_name, const LibertyLibrary *library, @@ -419,12 +432,12 @@ public: virtual ~Table3() {} virtual int order() const { return 3; } TableAxisPtr axis3() const { return axis3_; } - virtual float value(size_t index1, - size_t index2, - size_t index3) const; - virtual float findValue(float value1, - float value2, - float value3) const; + virtual float value(size_t axis_index1, + size_t axis_index2, + size_t axis_index3) const; + virtual float findValue(float axis_value1, + float axis_value2, + float axis_value3) const; virtual void reportValue(const char *result_name, const LibertyLibrary *library, const LibertyCell *cell, @@ -462,6 +475,8 @@ public: size_t &index, bool &exists) const; FloatSeq *values() const { return values_; } + float min() const { return (*values_)[0]; } + float max() const { return (*values_)[values_->size() - 1]; } private: TableAxisVariable variable_; @@ -484,63 +499,47 @@ private: TableModel *capacitance_models_[2][RiseFall::index_count]; }; -class OutputCurrentWaveform +// Two dimensional (slew/cap) table of one dimensional time/current tables. +class OutputWaveforms { public: - OutputCurrentWaveform(float axis_value1, - float axis_value2, - TableAxisPtr axis, - Table1 *currents, - float reference_time); - ~OutputCurrentWaveform(); - float axisValue1() const { return axis_value1_; } - float axisValue2() const { return axis_value2_; } - TableAxisPtr axis() const { return axis_; } - Table1 *currents() const { return currents_; } - float referenceTime() const { return reference_time_; } - void reportWaveform(const Units *units, - int digits, - string *result); + OutputWaveforms(TableAxisPtr slew_axis, + TableAxisPtr cap_axis, + Table1Seq ¤t_waveforms, + Table1 *ref_times); + ~OutputWaveforms(); + Table1 voltageWaveform(float in_slew, + float load_cap); + Table1 currentWaveform(float slew, + float cap); + float referenceTime(float slew); static bool checkAxes(TableTemplate *tbl_template); - -private: - float axis_value1_; - float axis_value2_; - TableAxisPtr axis_; - Table1 *currents_; - float reference_time_; -}; - -// Two dimensional table of one dimensional time/current tables. -class OutputCurrent -{ -public: - OutputCurrent(TableAxisPtr axis1, - TableAxisPtr axis2, - Vector &waveforms); - ~OutputCurrent(); - void reportWaveform(const LibertyCell *cell, - const Pvt *pvt, - float in_slew, - float load_cap, - int digits, - string *result) const; private: - void findAxisValues(float in_slew, - float load_cap, - // Return values. - float &axis_value1, - float &axis_value2) const; - float axisValue(TableAxisPtr axis, - float in_slew, - float load_cap) const; + Table1 *voltageWaveform(size_t wave_index, + float cap); // Row. - TableAxisPtr axis1_; + TableAxisPtr slew_axis_; // Column. - TableAxisPtr axis2_; - OutputCurrentWaveformSeq waveforms_; + TableAxisPtr cap_axis_; + Table1Seq current_waveforms_; + Table1Seq voltage_waveforms_; + Table1 *ref_times_; +}; + +class DriverWaveform +{ +public: + DriverWaveform(const char *name, + TablePtr waveforms); + ~DriverWaveform(); + const char *name() const { return name_; } + Table1 waveform(float slew); + +private: + const char *name_; + TablePtr waveforms_; }; } // namespace diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 4762e2be..69e3780d 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -82,7 +82,8 @@ LibertyLibrary::LibertyLibrary(const char *name, default_operating_conditions_(nullptr), ocv_arc_depth_(0.0), default_ocv_derate_(nullptr), - buffers_(nullptr) + buffers_(nullptr), + driver_waveform_default_(nullptr) { // Scalar templates are builtin. for (int i = 0; i != table_template_type_count; i++) { @@ -860,6 +861,23 @@ LibertyLibrary::supplyExists(const char *supply_name) const return supply_voltage_map_.hasKey(supply_name); } +DriverWaveform * +LibertyLibrary::findDriverWaveform(const char *name) +{ + return driver_waveform_map_[name]; +} + +void +LibertyLibrary::addDriverWaveform(DriverWaveform *driver_waveform) +{ + if (driver_waveform->name()) + driver_waveform_map_[driver_waveform->name()] = driver_waveform; + else { + delete driver_waveform_default_; + driver_waveform_default_ = driver_waveform; + } +} + //////////////////////////////////////////////////////////////// LibertyCellIterator::LibertyCellIterator(const LibertyLibrary *library) : @@ -1920,6 +1938,7 @@ LibertyPort::LibertyPort(LibertyCell *cell, related_ground_pin_(nullptr), related_power_pin_(nullptr), receiver_model_(nullptr), + driver_waveform_{nullptr, nullptr}, min_pulse_width_exists_(false), min_period_exists_(false), is_clk_(false), @@ -2519,6 +2538,19 @@ portLibertyToSta(const char *port_name) return sta_name; } +DriverWaveform * +LibertyPort::driverWaveform(const RiseFall *rf) const +{ + return driver_waveform_[rf->index()]; +} + +void +LibertyPort::setDriverWaveform(DriverWaveform *driver_waveform, + const RiseFall *rf) +{ + driver_waveform_[rf->index()] = driver_waveform; +} + //////////////////////////////////////////////////////////////// LibertyPortSeq diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 9a656adf..e47fc6e0 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -501,10 +501,14 @@ LibertyReader::defineVisitors() defineGroupVisitor("output_current_fall", &LibertyReader::beginOutputCurrentFall, &LibertyReader::endOutputCurrentRiseFall); - defineGroupVisitor("vector", - &LibertyReader::beginVector, - &LibertyReader::endVector); + defineGroupVisitor("vector", &LibertyReader::beginVector, &LibertyReader::endVector); defineAttrVisitor("reference_time", &LibertyReader::visitReferenceTime); + defineGroupVisitor("normalized_driver_waveform", + &LibertyReader::beginNormalizedDriverWaveform, + &LibertyReader::endNormalizedDriverWaveform); + defineAttrVisitor("driver_waveform_name", &LibertyReader::visitDriverWaveformName); + defineAttrVisitor("driver_waveform_rise", &LibertyReader::visitDriverWaveformRise); + defineAttrVisitor("driver_waveform_fall", &LibertyReader::visitDriverWaveformFall); } void @@ -2283,7 +2287,7 @@ TimingGroup::makeTableModels(LibertyReader *reader) transition, slew_sigma_[rf_index], receiver_model_, - output_current_[rf_index])); + output_waveforms_[rf_index])); TimingType timing_type = attrs_->timingType(); if (timing_type == TimingType::clear || timing_type == TimingType::combinational @@ -2480,8 +2484,10 @@ void LibertyReader::beginOutputCurrent(RiseFall *rf, LibertyGroup *group) { - if (timing_) + if (timing_) { rf_ = rf; + output_currents_.clear(); + } else libWarn(907, group, "output_current_%s group not in timing group.", rf->name()); @@ -2490,54 +2496,59 @@ LibertyReader::beginOutputCurrent(RiseFall *rf, void LibertyReader::endOutputCurrentRiseFall(LibertyGroup *group) { - Set axis_set1, axis_set2; - FloatSeq *axis_values1 = new FloatSeq; - FloatSeq *axis_values2 = new FloatSeq; - for (OutputCurrentWaveform *waveform : output_current_waveforms_) { - float axis_value1 = waveform->axisValue1(); - if (!axis_set1.hasKey(axis_value1)) { - axis_set1.insert(axis_value1); - axis_values1->push_back(axis_value1); + Set slew_set, cap_set; + FloatSeq *slew_values = new FloatSeq; + FloatSeq *cap_values = new FloatSeq; + for (OutputWaveform *waveform : output_currents_) { + float slew = waveform->slew(); + if (!slew_set.hasKey(slew)) { + slew_set.insert(slew); + slew_values->push_back(slew); } - float axis_value2 = waveform->axisValue2(); - if (!axis_set2.hasKey(axis_value2)) { - axis_set2.insert(axis_value2); - axis_values2->push_back(axis_value2); + float cap = waveform->cap(); + if (!cap_set.hasKey(cap)) { + cap_set.insert(cap); + cap_values->push_back(cap); } } - sort(axis_values1, std::less()); - sort(axis_values2, std::less()); - TableAxisPtr axis1 = std::make_shared(axis_[0]->variable(), - axis_values1); - TableAxisPtr axis2 = std::make_shared(axis_[1]->variable(), - axis_values2); - Vector waveforms(axis1->size() * axis2->size()); - for (OutputCurrentWaveform *waveform : output_current_waveforms_) { - size_t index1, index2; - bool exists1, exists2; - axis1->findAxisIndex(waveform->axisValue1(), index1, exists1); - axis2->findAxisIndex(waveform->axisValue2(), index2, exists2); - if (exists1 && exists2) { - size_t index = index1 * axis2->size() + index2; - waveforms[index] = waveform; + sort(slew_values, std::less()); + sort(cap_values, std::less()); + TableAxisPtr slew_axis = make_shared(TableAxisVariable::input_net_transition, + slew_values); + TableAxisPtr cap_axis = make_shared(TableAxisVariable::total_output_net_capacitance, + cap_values); + FloatSeq *ref_times = new FloatSeq(slew_values->size()); + Table1Seq current_waveforms(slew_axis->size() * cap_axis->size()); + for (OutputWaveform *waveform : output_currents_) { + size_t slew_index, cap_index; + bool slew_exists, cap_exists; + slew_axis->findAxisIndex(waveform->slew(), slew_index, slew_exists); + cap_axis->findAxisIndex(waveform->cap(), cap_index, cap_exists); + if (slew_exists && cap_exists) { + size_t index = slew_index * cap_axis->size() + cap_index; + current_waveforms[index] = waveform->currents(); + (*ref_times)[slew_index] = waveform->referenceTime(); } else libWarn(913, group, "output current waveform %.2e %.2e not found.", - waveform->axisValue1(), - waveform->axisValue2()); + waveform->slew(), + waveform->cap()); } - OutputCurrent *output_current = new OutputCurrent(axis1, axis2, waveforms); - timing_->setOutputCurrent(rf_, output_current); + Table1 *ref_time_tbl = new Table1(ref_times, slew_axis); + OutputWaveforms *output_current = new OutputWaveforms(slew_axis, cap_axis, + current_waveforms, + ref_time_tbl); + timing_->setOutputWaveforms(rf_, output_current); } void LibertyReader::beginVector(LibertyGroup *group) { if (timing_) { - beginTable(group, TableTemplateType::output_current, 1.0); + beginTable(group, TableTemplateType::output_current, current_scale_); scale_factor_type_ = ScaleFactorType::unknown; reference_time_exists_ = false; - if (!OutputCurrentWaveform::checkAxes(tbl_template_)) + if (tbl_template_ && !OutputWaveforms::checkAxes(tbl_template_)) libWarn(118, group, "unsupported model axis."); } } @@ -2546,32 +2557,41 @@ void LibertyReader::visitReferenceTime(LibertyAttr *attr) { getAttrFloat(attr, reference_time_, reference_time_exists_); + if (reference_time_exists_) + reference_time_ *= time_scale_; } void LibertyReader::endVector(LibertyGroup *group) { - if (timing_) { - FloatSeq *axis_values1 = axis_values_[0]; - FloatSeq *axis_values2 = axis_values_[1]; - if (axis_values1->size() == 1 && axis_values2->size() == 1) { + if (timing_ && tbl_template_) { + FloatSeq *slew_values = axis_values_[0]; + FloatSeq *cap_values = axis_values_[1]; + // Canonicalize axis order. + if (tbl_template_->axis1()->variable() == TableAxisVariable::input_net_transition) { + slew_values = axis_values_[0]; + cap_values = axis_values_[1]; + } + else { + slew_values = axis_values_[1]; + cap_values = axis_values_[0]; + } + + if (slew_values->size() == 1 && cap_values->size() == 1) { // Convert 1x1xN Table3 to Table1. - float axis_value1 = (*axis_values1)[0]; - float axis_value2 = (*axis_values2)[0]; + float slew = (*slew_values)[0]; + float cap = (*cap_values)[0]; Table3 *table3 = dynamic_cast(table_.get()); FloatTable *values3 = table3->values3(); // Steal the values. FloatSeq *values = (*values3)[0]; (*values3)[0] = nullptr; Table1 *table1 = new Table1(values, axis_[2]); - OutputCurrentWaveform *waveform = new OutputCurrentWaveform(axis_value1, - axis_value2, - axis_[2], table1, - reference_time_); - output_current_waveforms_.push_back(waveform); + OutputWaveform *waveform = new OutputWaveform(slew, cap, table1, reference_time_); + output_currents_.push_back(waveform); } else - libWarn(912, group->line(), "vector index_1 and index_2 must have one value."); + libWarn(912,group->line(), "vector index_1 and index_2 must have exactly one value."); if (!reference_time_exists_) libWarn(908, group->line(), "vector reference_time not found."); reference_time_exists_ = false; @@ -2580,6 +2600,68 @@ LibertyReader::endVector(LibertyGroup *group) /////////////////////////////////////////////////////////////// +void +LibertyReader::beginNormalizedDriverWaveform(LibertyGroup *group) +{ + beginTable(group, TableTemplateType::delay, time_scale_); + driver_waveform_name_ = nullptr; +} + +void +LibertyReader::visitDriverWaveformName(LibertyAttr *attr) +{ + driver_waveform_name_ = stringCopy(getAttrString(attr)); +} + +void +LibertyReader::endNormalizedDriverWaveform(LibertyGroup *group) +{ + if (table_) { + if (table_->axis1()->variable() == TableAxisVariable::input_net_transition) { + if (table_->axis2()->variable() == TableAxisVariable::normalized_voltage) { + // Null driver_waveform_name_ means it is the default unnamed waveform. + DriverWaveform *driver_waveform = new DriverWaveform(driver_waveform_name_, + table_); + library_->addDriverWaveform(driver_waveform); + + } + else + libWarn(914, group, "normalized_driver_waveform variable_2 must be normalized_voltage"); + } + else + libWarn(915, group, "normalized_driver_waveform variable_1 must be input_net_transition"); + } + endTableModel(); +} + +void +LibertyReader::visitDriverWaveformRise(LibertyAttr *attr) +{ + visitDriverWaveformRiseFall(attr, RiseFall::rise()); +} + +void +LibertyReader::visitDriverWaveformFall(LibertyAttr *attr) +{ + visitDriverWaveformRiseFall(attr, RiseFall::fall()); +} + +void +LibertyReader::visitDriverWaveformRiseFall(LibertyAttr *attr, + const RiseFall *rf) +{ + if (ports_) { + const char *driver_waveform_name = getAttrString(attr); + DriverWaveform *driver_waveform = library_->findDriverWaveform(driver_waveform_name); + if (driver_waveform) { + for (LibertyPort *port : *ports_) + port->setDriverWaveform(driver_waveform, rf); + } + } +} + +/////////////////////////////////////////////////////////////// + void LibertyReader::makeInternalPowers(LibertyPort *port, InternalPowerGroup *power_group) @@ -5365,7 +5447,7 @@ TimingGroup::TimingGroup(int line) : intrinsic_exists_[rf_index] = false; resistance_[rf_index] = 0.0F; resistance_exists_[rf_index] = false; - output_current_[rf_index] = nullptr; + output_waveforms_[rf_index] = nullptr; for (auto el_index : EarlyLate::rangeIndex()) { delay_sigma_[rf_index][el_index] = nullptr; @@ -5496,17 +5578,17 @@ TimingGroup::setReceiverModel(ReceiverModelPtr receiver_model) receiver_model_ = receiver_model; } -OutputCurrent * -TimingGroup::outputCurrent(RiseFall *rf) +OutputWaveforms * +TimingGroup::outputWaveforms(RiseFall *rf) { - return output_current_[rf->index()]; + return output_waveforms_[rf->index()]; } void -TimingGroup::setOutputCurrent(RiseFall *rf, - OutputCurrent *output_current) +TimingGroup::setOutputWaveforms(RiseFall *rf, + OutputWaveforms *output_waveforms) { - output_current_[rf->index()] = output_current; + output_waveforms_[rf->index()] = output_waveforms; } //////////////////////////////////////////////////////////////// @@ -5677,4 +5759,22 @@ PortNameBitIterator::findRangeBusNameNext() range_name_next_ = nullptr; } +//////////////////////////////////////////////////////////////// + +OutputWaveform::OutputWaveform(float slew, + float cap, + Table1 *currents, + float reference_time) : + slew_(slew), + cap_(cap), + currents_(currents), + reference_time_(reference_time) +{ +} + +OutputWaveform::~OutputWaveform() +{ + delete currents_; +} + } // namespace diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index 23749183..15f3af0a 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -45,6 +45,7 @@ class LeakagePowerGroup; class PortNameBitIterator; class TimingArcBuilder; class LibertyAttr; +class OutputWaveform; typedef void (LibertyReader::*LibraryAttrVisitor)(LibertyAttr *attr); typedef void (LibertyReader::*LibraryGroupVisitor)(LibertyGroup *group); @@ -57,7 +58,7 @@ typedef Vector TimingGroupSeq; typedef Vector InternalPowerGroupSeq; typedef Vector LeakagePowerGroupSeq; typedef void (LibertyPort::*LibertyPortBoolSetter)(bool value); -typedef Vector OutputCurrentWaveformSeq; +typedef Vector OutputWaveformSeq; class LibertyReader : public LibertyGroupVisitor { @@ -443,6 +444,15 @@ public: void endVector(LibertyGroup *group); void visitReferenceTime(LibertyAttr *attr); + void beginNormalizedDriverWaveform(LibertyGroup *group); + void endNormalizedDriverWaveform(LibertyGroup *group); + void visitDriverWaveformName(LibertyAttr *attr); + + void visitDriverWaveformRise(LibertyAttr *attr); + void visitDriverWaveformFall(LibertyAttr *attr); + void visitDriverWaveformRiseFall(LibertyAttr *attr, + const RiseFall *rf); + // Visitors for derived classes to overload. virtual void beginGroup1(LibertyGroup *) {} virtual void beginGroup2(LibertyGroup *) {} @@ -617,10 +627,11 @@ protected: bool have_resistance_unit_; const char *default_operating_condition_; ReceiverModelPtr receiver_model_; - OutputCurrentWaveformSeq output_current_waveforms_; - OutputCurrent *output_current_; + OutputWaveformSeq output_currents_; + OutputWaveforms *output_waveforms_; float reference_time_; bool reference_time_exists_; + const char *driver_waveform_name_; static constexpr char escape_ = '\\'; @@ -783,9 +794,9 @@ public: EarlyLate *early_late, TableModel *model); void setReceiverModel(ReceiverModelPtr receiver_model); - OutputCurrent *outputCurrent(RiseFall *rf); - void setOutputCurrent(RiseFall *rf, - OutputCurrent *output_current); + OutputWaveforms *outputWaveforms(RiseFall *rf); + void setOutputWaveforms(RiseFall *rf, + OutputWaveforms *output_current); protected: void makeLinearModels(LibertyLibrary *library); @@ -803,7 +814,7 @@ protected: TableModel *transition_[RiseFall::index_count]; TableModel *delay_sigma_[RiseFall::index_count][EarlyLate::index_count]; TableModel *slew_sigma_[RiseFall::index_count][EarlyLate::index_count]; - OutputCurrent *output_current_[RiseFall::index_count]; + OutputWaveforms *output_waveforms_[RiseFall::index_count]; ReceiverModelPtr receiver_model_; }; @@ -858,4 +869,24 @@ protected: unsigned size_; }; +class OutputWaveform +{ +public: + OutputWaveform(float axis_value1, + float axis_value2, + Table1 *currents, + float reference_time); + ~OutputWaveform(); + float slew() const { return slew_; } + float cap() const { return cap_; } + Table1 *currents() const { return currents_; } + float referenceTime() const { return reference_time_; } + +private: + float slew_; + float cap_; + Table1 *currents_; + float reference_time_; +}; + } // namespace diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 1429e5ed..8bb00889 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -26,6 +26,10 @@ namespace sta { using std::string; +using std::min; +using std::max; +using std::abs; +using std::make_shared; static void deleteSigmaModels(TableModel *models[EarlyLate::index_count]); @@ -43,15 +47,19 @@ GateTableModel::GateTableModel(TableModel *delay_model, TableModel *slew_model, TableModel *slew_sigma_models[EarlyLate::index_count], ReceiverModelPtr receiver_model, - OutputCurrent *output_current) : + OutputWaveforms *output_waveforms) : delay_model_(delay_model), slew_model_(slew_model), receiver_model_(receiver_model), - output_current_(output_current) + output_waveforms_(output_waveforms) { for (auto el_index : EarlyLate::rangeIndex()) { - slew_sigma_models_[el_index] = slew_sigma_models ? slew_sigma_models[el_index] : nullptr; - delay_sigma_models_[el_index] = delay_sigma_models ? delay_sigma_models[el_index] : nullptr; + slew_sigma_models_[el_index] = slew_sigma_models + ? slew_sigma_models[el_index] + : nullptr; + delay_sigma_models_[el_index] = delay_sigma_models + ? delay_sigma_models[el_index] + : nullptr; } } @@ -59,7 +67,7 @@ GateTableModel::~GateTableModel() { delete delay_model_; delete slew_model_; - delete output_current_; + delete output_waveforms_; deleteSigmaModels(slew_sigma_models_); deleteSigmaModels(delay_sigma_models_); } @@ -165,8 +173,6 @@ GateTableModel::reportGateDelay(const LibertyCell *cell, load_cap, related_out_cap); if (drvr_slew < 0.0) *result += "Negative slew clipped to 0.0\n"; - if (output_current_) - output_current_->reportWaveform(cell, pvt, in_slew, load_cap, digits, result); } void @@ -651,22 +657,22 @@ TableModel::axis3() const } float -TableModel::value(size_t index1, - size_t index2, - size_t index3) const +TableModel::value(size_t axis_index1, + size_t axis_index2, + size_t axis_index3) const { - return table_->value(index1, index2, index3); + return table_->value(axis_index1, axis_index2, axis_index3); } float TableModel::findValue(const LibertyLibrary *library, const LibertyCell *cell, const Pvt *pvt, - float value1, - float value2, - float value3) const + float axis_value1, + float axis_value2, + float axis_value3) const { - return table_->findValue(value1, value2, value3) + return table_->findValue(axis_value1, axis_value2, axis_value3) * scaleFactor(library, cell, pvt); } @@ -801,6 +807,13 @@ Table0::report(const Units *units, //////////////////////////////////////////////////////////////// +Table1::Table1() : + Table(), + values_(nullptr), + axis1_(nullptr) +{ +} + Table1::Table1(FloatSeq *values, TableAxisPtr axis1) : Table(), @@ -809,41 +822,94 @@ Table1::Table1(FloatSeq *values, { } +Table1::Table1(Table1 &&table) : + Table(), + values_(table.values_), + axis1_(table.axis1_) +{ + table.values_ = nullptr; + table.axis1_ = nullptr; +} + Table1::~Table1() { delete values_; } +Table1 & +Table1::operator=(Table1 &&table) +{ + values_ = table.values_; + axis1_ = table.axis1_; + table.values_ = nullptr; + table.axis1_ = nullptr; + return *this; +} + float -Table1::value(size_t index1, +Table1::value(size_t axis_index1, size_t, size_t) const { - return value(index1); + return value(axis_index1); } float -Table1::value(size_t index1) const +Table1::value(size_t axis_index1) const { - return (*values_)[index1]; + return (*values_)[axis_index1]; } float -Table1::findValue(float value1, +Table1::findValue(float axis_value1, float, float) const { - if (axis1_->size() == 1) - return value(value1); + return findValue(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; + } else { - size_t index1 = axis1_->findAxisIndex(value1); - float x1 = value1; - float x1l = axis1_->axisValue(index1); - float x1u = axis1_->axisValue(index1 + 1); - float y1 = value(index1); - float y2 = value(index1 + 1); + 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); + 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; + value = (1 - dx1) * y1 + dx1 * y2; + extrapolated = (x1 < x1l || x1 >= x1u); } } @@ -938,25 +1004,25 @@ Table2::~Table2() } float -Table2::value(size_t index1, - size_t index2, +Table2::value(size_t axis_index1, + size_t axis_index2, size_t) const { - return value(index1, index2); + return value(axis_index1, axis_index2); } float -Table2::value(size_t index1, - size_t index2) const +Table2::value(size_t axis_index1, + size_t axis_index2) const { - FloatSeq *row = (*values_)[index1]; - return (*row)[index2]; + FloatSeq *row = (*values_)[axis_index1]; + return (*row)[axis_index2]; } // Bilinear Interpolation. float -Table2::findValue(float value1, - float value2, +Table2::findValue(float axis_value1, + float axis_value2, float) const { size_t size1 = axis1_->size(); @@ -965,13 +1031,13 @@ Table2::findValue(float value1, if (size2 == 1) return value(0, 0); else { - size_t index2 = axis2_->findAxisIndex(value2); - float x2 = value2; - float y00 = value(0, index2); - float x2l = axis2_->axisValue(index2); - float x2u = axis2_->axisValue(index2 + 1); + size_t axis_index2 = axis2_->findAxisIndex(axis_value2); + float x2 = axis_value2; + float y00 = value(0, axis_index2); + float x2l = axis2_->axisValue(axis_index2); + float x2u = axis2_->axisValue(axis_index2 + 1); float dx2 = (x2 - x2l) / (x2u - x2l); - float y01 = value(0, index2 + 1); + float y01 = value(0, axis_index2 + 1); float tbl_value = (1 - dx2) * y00 + dx2 * y01; @@ -979,33 +1045,33 @@ Table2::findValue(float value1, } } else if (size2 == 1) { - size_t index1 = axis1_->findAxisIndex(value1); - float x1 = value1; - float y00 = value(index1, 0); - float x1l = axis1_->axisValue(index1); - float x1u = axis1_->axisValue(index1 + 1); + size_t axis_index1 = axis1_->findAxisIndex(axis_value1); + float x1 = axis_value1; + float y00 = value(axis_index1, 0); + float x1l = axis1_->axisValue(axis_index1); + float x1u = axis1_->axisValue(axis_index1 + 1); float dx1 = (x1 - x1l) / (x1u - x1l); - float y10 = value(index1 + 1, 0); + float y10 = value(axis_index1 + 1, 0); float tbl_value = (1 - dx1) * y00 + dx1 * y10; return tbl_value; } else { - size_t index1 = axis1_->findAxisIndex(value1); - size_t index2 = axis2_->findAxisIndex(value2); - float x1 = value1; - float x2 = value2; - float y00 = value(index1, index2); - float x1l = axis1_->axisValue(index1); - float x1u = axis1_->axisValue(index1 + 1); + size_t axis_index1 = axis1_->findAxisIndex(axis_value1); + size_t axis_index2 = axis2_->findAxisIndex(axis_value2); + float x1 = axis_value1; + float x2 = axis_value2; + float y00 = value(axis_index1, axis_index2); + float x1l = axis1_->axisValue(axis_index1); + float x1u = axis1_->axisValue(axis_index1 + 1); float dx1 = (x1 - x1l) / (x1u - x1l); - float y10 = value(index1 + 1, index2); - float y11 = value(index1 + 1, index2 + 1); - float x2l = axis2_->axisValue(index2); - float x2u = axis2_->axisValue(index2 + 1); + float y10 = value(axis_index1 + 1, axis_index2); + float y11 = value(axis_index1 + 1, axis_index2 + 1); + float x2l = axis2_->axisValue(axis_index2); + float x2u = axis2_->axisValue(axis_index2 + 1); float dx2 = (x2 - x2l) / (x2u - x2l); - float y01 = value(index1, index2 + 1); + float y01 = value(axis_index1, axis_index2 + 1); float tbl_value = (1 - dx1) * (1 - dx2) * y00 + dx1 * (1 - dx2) * y10 @@ -1123,30 +1189,30 @@ Table3::Table3(FloatTable *values, } float -Table3::value(size_t index1, - size_t index2, - size_t index3) const +Table3::value(size_t axis_index1, + size_t axis_index2, + size_t axis_index3) const { - size_t row = index1 * axis2_->size() + index2; - return values_->operator[](row)->operator[](index3); + size_t row = axis_index1 * axis2_->size() + axis_index2; + return values_->operator[](row)->operator[](axis_index3); } // Bilinear Interpolation. float -Table3::findValue(float value1, - float value2, - float value3) const +Table3::findValue(float axis_value1, + float axis_value2, + float axis_value3) const { - size_t index1 = axis1_->findAxisIndex(value1); - size_t index2 = axis2_->findAxisIndex(value2); - size_t index3 = axis3_->findAxisIndex(value3); - float x1 = value1; - float x2 = value2; - float x3 = value3; + size_t axis_index1 = axis1_->findAxisIndex(axis_value1); + size_t axis_index2 = axis2_->findAxisIndex(axis_value2); + size_t axis_index3 = axis3_->findAxisIndex(axis_value3); + float x1 = axis_value1; + float x2 = axis_value2; + float x3 = axis_value3; float dx1 = 0.0; float dx2 = 0.0; float dx3 = 0.0; - float y000 = value(index1, index2, index3); + float y000 = value(axis_index1, axis_index2, axis_index3); float y001 = 0.0; float y010 = 0.0; float y011 = 0.0; @@ -1156,31 +1222,31 @@ Table3::findValue(float value1, float y111 = 0.0; if (axis1_->size() != 1) { - float x1l = axis1_->axisValue(index1); - float x1u = axis1_->axisValue(index1 + 1); + float x1l = axis1_->axisValue(axis_index1); + float x1u = axis1_->axisValue(axis_index1 + 1); dx1 = (x1 - x1l) / (x1u - x1l); - y100 = value(index1 + 1, index2, index3); + y100 = value(axis_index1 + 1, axis_index2, axis_index3); if (axis3_->size() != 1) - y101 = value(index1 + 1, index2, index3 + 1); + y101 = value(axis_index1 + 1, axis_index2, axis_index3 + 1); if (axis2_->size() != 1) { - y110 = value(index1 + 1, index2 + 1, index3); + y110 = value(axis_index1 + 1, axis_index2 + 1, axis_index3); if (axis3_->size() != 1) - y111 = value(index1 + 1, index2 + 1, index3 + 1); + y111 = value(axis_index1 + 1, axis_index2 + 1, axis_index3 + 1); } } if (axis2_->size() != 1) { - float x2l = axis2_->axisValue(index2); - float x2u = axis2_->axisValue(index2 + 1); + float x2l = axis2_->axisValue(axis_index2); + float x2u = axis2_->axisValue(axis_index2 + 1); dx2 = (x2 - x2l) / (x2u - x2l); - y010 = value(index1, index2 + 1, index3); + y010 = value(axis_index1, axis_index2 + 1, axis_index3); if (axis3_->size() != 1) - y011 = value(index1, index2 + 1, index3 + 1); + y011 = value(axis_index1, axis_index2 + 1, axis_index3 + 1); } if (axis3_->size() != 1) { - float x3l = axis3_->axisValue(index3); - float x3u = axis3_->axisValue(index3 + 1); + float x3l = axis3_->axisValue(axis_index3); + float x3u = axis3_->axisValue(axis_index3 + 1); dx3 = (x3 - x3l) / (x3u - x3l); - y001 = value(index1, index2, index3 + 1); + y001 = value(axis_index1, axis_index2, axis_index3 + 1); } float tbl_value @@ -1244,15 +1310,15 @@ Table3::reportValue(const char *result_name, *result += unit3->asString(value3, digits); *result += '\n'; - size_t index1 = axis1_->findAxisIndex(value1); - size_t index2 = axis2_->findAxisIndex(value2); - size_t index3 = axis3_->findAxisIndex(value3); + size_t axis_index1 = axis1_->findAxisIndex(value1); + size_t axis_index2 = axis2_->findAxisIndex(value2); + size_t axis_index3 = axis3_->findAxisIndex(value3); *result += " | | "; - *result += unit3->asString(axis3_->axisValue(index3), digits); + *result += unit3->asString(axis3_->axisValue(axis_index3), digits); if (axis3_->size() != 1) { *result += " "; - *result += unit3->asString(axis3_->axisValue(index3 + 1), digits); + *result += unit3->asString(axis3_->axisValue(axis_index3 + 1), digits); } *result += '\n'; @@ -1260,13 +1326,13 @@ Table3::reportValue(const char *result_name, if (axis1_->size() != 1) { *result += " "; - *result += unit1->asString(axis1_->axisValue(index1+1), digits); + *result += unit1->asString(axis1_->axisValue(axis_index1+1), digits); *result += " v / "; - *result += table_unit->asString(value(index1+1,index2,index3), + *result += table_unit->asString(value(axis_index1+1,axis_index2,axis_index3), digits); if (axis3_->size() != 1) { *result += " "; - *result += table_unit->asString(value(index1+1,index2,index3+1), + *result += table_unit->asString(value(axis_index1+1,axis_index2,axis_index3+1), digits); } } @@ -1276,14 +1342,14 @@ Table3::reportValue(const char *result_name, } *result += '\n'; - *result += unit1->asString(axis1_->axisValue(index1), digits); + *result += unit1->asString(axis1_->axisValue(axis_index1), digits); *result += " "; - *result += unit2->asString(axis2_->axisValue(index2), digits); + *result += unit2->asString(axis2_->axisValue(axis_index2), digits); *result += " | "; - *result += table_unit->asString(value(index1, index2, index3), digits); + *result += table_unit->asString(value(axis_index1, axis_index2, axis_index3), digits); if (axis3_->size() != 1) { *result += " "; - *result += table_unit->asString(value(index1, index2, index3+1), + *result += table_unit->asString(value(axis_index1, axis_index2, axis_index3+1), digits); } *result += '\n'; @@ -1291,25 +1357,25 @@ Table3::reportValue(const char *result_name, *result += " |/ "; if (axis1_->size() != 1 && axis2_->size() != 1) { - *result += table_unit->asString(value(index1+1,index2+1,index3), + *result += table_unit->asString(value(axis_index1+1,axis_index2+1,axis_index3), digits); if (axis3_->size() != 1) { *result += " "; - *result +=table_unit->asString(value(index1+1,index2+1,index3+1), + *result +=table_unit->asString(value(axis_index1+1,axis_index2+1,axis_index3+1), digits); } } *result += '\n'; *result += " "; - *result += unit2->asString(axis2_->axisValue(index2 + 1), digits); + *result += unit2->asString(axis2_->axisValue(axis_index2 + 1), digits); *result += " | "; if (axis2_->size() != 1) { - *result += table_unit->asString(value(index1, index2+1, index3), + *result += table_unit->asString(value(axis_index1, axis_index2+1, axis_index3), digits); if (axis3_->size() != 1) { *result += " "; - *result +=table_unit->asString(value(index1, index2+1,index3+1), + *result +=table_unit->asString(value(axis_index1, axis_index2+1,axis_index3+1), digits); } } @@ -1338,24 +1404,24 @@ Table3::report(const Units *units, const Unit *unit1 = axis1_->unit(units); const Unit *unit2 = axis2_->unit(units); const Unit *unit3 = axis3_->unit(units); - for (size_t index1 = 0; index1 < axis1_->size(); index1++) { + for (size_t axis_index1 = 0; axis_index1 < axis1_->size(); axis_index1++) { report->reportLine("%s %s", tableVariableString(axis1_->variable()), - unit1->asString(axis1_->axisValue(index1), digits)); + unit1->asString(axis1_->axisValue(axis_index1), digits)); report->reportLine("%s", tableVariableString(axis3_->variable())); report->reportLine(" ------------------------------"); string line = " "; - for (size_t index3 = 0; index3 < axis3_->size(); index3++) { - line += unit3->asString(axis3_->axisValue(index3), digits); + for (size_t axis_index3 = 0; axis_index3 < axis3_->size(); axis_index3++) { + line += unit3->asString(axis3_->axisValue(axis_index3), digits); line += " "; } report->reportLineString(line); - for (size_t index2 = 0; index2 < axis2_->size(); index2++) { - line = unit2->asString(axis2_->axisValue(index2),digits); + for (size_t axis_index2 = 0; axis_index2 < axis2_->size(); axis_index2++) { + line = unit2->asString(axis2_->axisValue(axis_index2),digits); line += " |"; - for (size_t index3 = 0; index3 < axis3_->size(); index3++) { - line += table_unit->asString(value(index1, index2, index3), digits); + for (size_t axis_index3 = 0; axis_index3 < axis3_->size(); axis_index3++) { + line += table_unit->asString(value(axis_index1, axis_index2, axis_index3), digits); line += " "; } report->reportLineString(line); @@ -1514,26 +1580,30 @@ tableVariableUnit(TableAxisVariable variable, //////////////////////////////////////////////////////////////// -OutputCurrentWaveform::OutputCurrentWaveform(float axis_value1, - float axis_value2, - TableAxisPtr axis, - Table1 *currents, - float reference_time) : - axis_value1_(axis_value1), - axis_value2_(axis_value2), - axis_(axis), - currents_(currents), - reference_time_(reference_time) + +//////////////////////////////////////////////////////////////// + +OutputWaveforms::OutputWaveforms(TableAxisPtr slew_axis, + TableAxisPtr cap_axis, + Table1Seq ¤t_waveforms, + Table1 *ref_times) : + slew_axis_(slew_axis), + cap_axis_(cap_axis), + current_waveforms_(current_waveforms), + voltage_waveforms_(current_waveforms.size()), + ref_times_(ref_times) { } -OutputCurrentWaveform::~OutputCurrentWaveform() +OutputWaveforms::~OutputWaveforms() { - delete currents_; + current_waveforms_.deleteContents(); + voltage_waveforms_.deleteContents(); + delete ref_times_; } bool -OutputCurrentWaveform::checkAxes(TableTemplate *tbl_template) +OutputWaveforms::checkAxes(TableTemplate *tbl_template) { TableAxisPtr axis1 = tbl_template->axis1(); TableAxisPtr axis2 = tbl_template->axis2(); @@ -1549,83 +1619,215 @@ OutputCurrentWaveform::checkAxes(TableTemplate *tbl_template) && axis3->variable() == TableAxisVariable::time); } -void -OutputCurrentWaveform::reportWaveform(const Units *units, - int digits, - string *result) +Table1 +OutputWaveforms::voltageWaveform(float slew, + float cap) { - const Unit *time_unit = units->timeUnit(); - const Unit *current_unit = units->currentUnit(); - for (size_t i = 0; i < axis_->values()->size(); i++) { - *result += time_unit->asString(axis_->axisValue(i), digits); - *result += " "; + size_t slew_index = slew_axis_->findAxisIndex(slew); + size_t cap_index = cap_axis_->findAxisIndex(cap); + size_t wave_index00 = slew_index * cap_axis_->size() + cap_index; + size_t wave_index01 = slew_index * cap_axis_->size() + (cap_index + 1); + size_t wave_index10 = (slew_index + 1) * cap_axis_->size() + cap_index; + size_t wave_index11 = (slew_index + 1) * cap_axis_->size() + (cap_index + 1); + float cap0 = cap_axis_->axisValue(cap_index); + float cap1 = cap_axis_->axisValue(cap_index + 1); + const Table1 *values00 = voltageWaveform(wave_index00, cap0); + const Table1 *values01 = voltageWaveform(wave_index01, cap1); + const Table1 *values10 = voltageWaveform(wave_index10, cap0); + const Table1 *values11 = voltageWaveform(wave_index11, cap1); + TableAxisPtr time_axis00 = values00->axis1(); + TableAxisPtr time_axis01 = values01->axis1(); + TableAxisPtr time_axis10 = values10->axis1(); + TableAxisPtr time_axis11 = values11->axis1(); + + // Find time axis min/max. + size_t time_step_count = 20; + float time_min = time_axis00->min(); + time_min = min(time_min, time_axis01->min()); + time_min = min(time_min, time_axis10->min()); + time_min = min(time_min, time_axis11->min()); + float time_max = time_axis00->max(); + time_max = max(time_max, time_axis01->max()); + time_max = max(time_max, time_axis10->max()); + time_max = max(time_max, time_axis11->max()); + float time_step = (time_max - time_min) / time_step_count; + FloatSeq *time_values = new FloatSeq; + TableAxisPtr time_axis = make_shared(time_axis00->variable(), + 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; + float x2 = cap; + float x1l = slew_axis_->axisValue(index1); + float x1u = slew_axis_->axisValue(index1 + 1); + float dx1 = (x1 - x1l) / (x1u - x1l); + float x2l = cap_axis_->axisValue(index2); + 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); + 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 + = (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) { + time_values->push_back(time); + values->push_back(voltage); + } + if (prev_value > value_end) + break; + prev_value = voltage; } - *result += '\n'; - for (size_t i = 0; i < currents_->axis1()->size(); i++) { - *result += current_unit->asString(currents_->value(i), digits); - *result += " "; + return Table1(values, time_axis); +} + +Table1 * +OutputWaveforms::voltageWaveform(size_t wave_index, + float cap) +{ + Table1 *voltages = voltage_waveforms_[wave_index]; + if (voltages == nullptr) { + FloatSeq *voltages1 = new FloatSeq; + Table1 *currents = current_waveforms_[wave_index]; + voltages = new Table1(voltages1, currents->axis1()); + voltage_waveforms_[wave_index] = voltages; + + // i = C dv/dt + // Integrate current waveform to find voltage waveform. + TableAxisPtr time_axis = currents->axis1(); + float prev_time = time_axis->axisValue(0); + float prev_current = currents->value(0); + float voltage = 0.0; + voltages1->push_back(voltage); + bool invert = currents->value(time_axis->size() - 1) < 0.0; + for (size_t i = 1; i < time_axis->size(); i++) { + float time = time_axis->axisValue(i); + float current = currents->value(i); + float dv = (current + prev_current) / 2.0 * (time - prev_time) / cap; + voltage += invert ? -dv : dv; + voltages1->push_back(voltage); + prev_time = time; + prev_current = current; + } } - *result += '\n'; + return voltages; +} + +Table1 +OutputWaveforms::currentWaveform(float slew, + float cap) +{ + size_t slew_index = slew_axis_->findAxisIndex(slew); + size_t cap_index = cap_axis_->findAxisIndex(cap); + size_t wave_index00 = slew_index * cap_axis_->size() + cap_index; + size_t wave_index01 = slew_index * cap_axis_->size() + (cap_index + 1); + size_t wave_index10 = (slew_index + 1) * cap_axis_->size() + cap_index; + size_t wave_index11 = (slew_index + 1) * cap_axis_->size() + (cap_index + 1); + const Table1 *values00 = current_waveforms_[wave_index00]; + const Table1 *values01 = current_waveforms_[wave_index01]; + const Table1 *values10 = current_waveforms_[wave_index10]; + const Table1 *values11 = current_waveforms_[wave_index11]; + TableAxisPtr time_axis00 = values00->axis1(); + TableAxisPtr time_axis01 = values01->axis1(); + TableAxisPtr time_axis10 = values10->axis1(); + TableAxisPtr time_axis11 = values11->axis1(); + + // Find time axis min/max. + size_t time_step_count = 20; + float time_min = time_axis00->min(); + time_min = min(time_min, time_axis01->min()); + time_min = min(time_min, time_axis10->min()); + time_min = min(time_min, time_axis11->min()); + float time_max = time_axis00->max(); + time_max = max(time_max, time_axis01->max()); + time_max = max(time_max, time_axis10->max()); + time_max = max(time_max, time_axis11->max()); + float time_step = (time_max - time_min) / time_step_count; + FloatSeq *time_values = new FloatSeq; + TableAxisPtr time_axis = make_shared(time_axis00->variable(), + time_values); + + // Interpolate waveform samples at time steps. + size_t index1 = slew_index; + size_t index2 = cap_index; + float x1 = slew; + float x2 = cap; + float x1l = slew_axis_->axisValue(index1); + float x1u = slew_axis_->axisValue(index1 + 1); + float dx1 = (x1 - x1l) / (x1u - x1l); + float x2l = cap_axis_->axisValue(index2); + float x2u = cap_axis_->axisValue(index2 + 1); + float dx2 = (x2 - x2l) / (x2u - x2l); + FloatSeq *values = new FloatSeq; + 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); + 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; + } + return Table1(values, time_axis); +} + +float +OutputWaveforms::referenceTime(float slew) +{ + return ref_times_->findValue(slew); } //////////////////////////////////////////////////////////////// -OutputCurrent::OutputCurrent(TableAxisPtr axis1, - TableAxisPtr axis2, - OutputCurrentWaveformSeq &waveforms) : - axis1_(axis1), - axis2_(axis2), +DriverWaveform::DriverWaveform(const char *name, + TablePtr waveforms) : + name_(name), waveforms_(waveforms) { } -OutputCurrent::~OutputCurrent() +DriverWaveform::~DriverWaveform() { - waveforms_.deleteContents(); + stringDelete(name_); } -void -OutputCurrent::reportWaveform(const LibertyCell *cell, - const Pvt *, - float in_slew, - float load_cap, - int digits, - string *result) const +Table1 +DriverWaveform::waveform(float slew) { - float axis_value1, axis_value2; - findAxisValues(in_slew, load_cap, - axis_value1, axis_value2); - size_t index1 = axis1_->findAxisIndex(axis_value1); - size_t index2 = axis2_->findAxisIndex(axis_value2); - size_t index = index1 * axis2_->size() + index2; - waveforms_[index]->reportWaveform(cell->libertyLibrary()->units(), digits, result); -} - -void -OutputCurrent::findAxisValues(float in_slew, - float load_cap, - // Return values. - float &axis_value1, - float &axis_value2) const -{ - axis_value1 = axisValue(axis1_, in_slew, load_cap); - axis_value2 = axisValue(axis2_, in_slew, load_cap); -} - -float -OutputCurrent::axisValue(TableAxisPtr axis, - float in_slew, - float load_cap) const -{ - TableAxisVariable var = axis->variable(); - if (var == TableAxisVariable::input_net_transition) - return in_slew; - else if (var == TableAxisVariable::total_output_net_capacitance) - return load_cap; - else { - criticalError(240, "unsupported table axes"); - return 0.0; + TableAxisPtr volt_axis = waveforms_->axis2(); + FloatSeq *time_values = new FloatSeq; + FloatSeq *volt_values = new FloatSeq; + for (float volt : *volt_axis->values()) { + float time = waveforms_->findValue(slew, volt, 0.0); + time_values->push_back(time); + volt_values->push_back(volt); } + TableAxisPtr time_axis = make_shared(TableAxisVariable::time, + time_values); + Table1 waveform(volt_values, time_axis); + return waveform; } } // namespace diff --git a/messages.txt b/messages.txt index 099ef8ab..328128f1 100644 --- a/messages.txt +++ b/messages.txt @@ -1,173 +1,173 @@ 0001 DmpCeff.cc:1597 cell %s delay model not supported on SPF parasitics by DMP delay calculator -0002 Liberty.cc:749 cell %s/%s port %s not found in cell %s/%s. -0003 Liberty.cc:775 cell %s/%s %s -> %s timing group %s not found in cell %s/%s. -0004 Liberty.cc:1710 cell %s/%s %s -> %s latch enable %s_edge is inconsistent with %s -> %s setup_%s check. -0005 Liberty.cc:1724 cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense. -0006 Liberty.cc:1732 cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense. +0002 Liberty.cc:750 cell %s/%s port %s not found in cell %s/%s. +0003 Liberty.cc:776 cell %s/%s %s -> %s timing group %s not found in cell %s/%s. +0004 Liberty.cc:1728 cell %s/%s %s -> %s latch enable %s_edge is inconsistent with %s -> %s setup_%s check. +0005 Liberty.cc:1742 cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense. +0006 Liberty.cc:1750 cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense. 0007 LibertyExpr.cc:82 %s references unknown port %s. -0008 ConcreteNetwork.cc:1924 cell type %s can not be linked. +0008 ConcreteNetwork.cc:1917 cell type %s can not be linked. 0009 CycleAccting.cc:87 No common period was found between clocks %s and %s. 0010 Genclks.cc:274 no master clock found for generated clock %s. 0013 Genclks.cc:938 generated clock %s source pin %s missing paths from master clock %s. 0015 Sim.cc:865 propagated logic value %c differs from constraint value of %c on pin %s. -0016 LibertyReader.cc:1045 default_max_fanout is 0.0. +0016 LibertyReader.cc:1049 default_max_fanout is 0.0. 0017 Sta.cc:2093 '%s' is not a valid endpoint. 0018 Sta.cc:2017 '%s' is not a valid start point. 0021 SpefParse.yy:805 %d is not positive. 0022 SpefParse.yy:814 %.4f is not positive. 0023 SpefParse.yy:820 %.4f is not positive. -0024 WritePathSpice.cc:425 pg_pin %s/%s voltage %s not found, -0025 WritePathSpice.cc:432 Liberty pg_port %s/%s missing voltage_name attribute, -0026 WritePathSpice.cc:960 %s pg_port %s not found, -0027 WritePathSpice.cc:1015 no register/latch found for path from %s to %s, -0028 WritePathSpice.cc:1382 The following subkcts are missing from %s -0029 WritePathSpice.cc:1440 subckt %s port %s has no corresponding liberty port, pg_port and is not power or ground. -0030 LibertyReader.cc:624 library missing name. -0031 LibertyReader.cc:656 default_wire_load %s not found. -0032 LibertyReader.cc:667 default_wire_selection %s not found. -0033 LibertyReader.cc:689 input_threshold_pct_%s not found. -0034 LibertyReader.cc:693 output_threshold_pct_%s not found. -0035 LibertyReader.cc:697 slew_lower_threshold_pct_%s not found. -0036 LibertyReader.cc:701 slew_upper_threshold_pct_%s not found. -0037 LibertyReader.cc:706 Library %s is missing one or more thresholds. -0038 LibertyReader.cc:796 unknown unit multiplier %s. -0039 LibertyReader.cc:815 unknown unit scale %c. -0040 LibertyReader.cc:818 unknown unit suffix %s. -0041 LibertyReader.cc:844 capacitive_load_units are not ff or pf. -0042 LibertyReader.cc:847 capacitive_load_units are not a string. -0043 LibertyReader.cc:850 capacitive_load_units missing suffix. -0044 LibertyReader.cc:853 capacitive_load_units scale is not a float. -0045 LibertyReader.cc:856 capacitive_load_units missing scale and suffix. -0046 LibertyReader.cc:859 capacitive_load_unit missing values suffix. -0047 LibertyReader.cc:877 delay_model %s not supported. -0048 LibertyReader.cc:881 delay_model %s not supported. -0049 LibertyReader.cc:885 delay_model %s not supported. -0050 LibertyReader.cc:890 delay_model %s not supported. +0024 WritePathSpice.cc:458 pg_pin %s/%s voltage %s not found, +0025 WritePathSpice.cc:465 Liberty pg_port %s/%s missing voltage_name attribute, +0026 WritePathSpice.cc:1037 %s pg_port %s not found, +0027 WritePathSpice.cc:1092 no register/latch found for path from %s to %s, +0028 WritePathSpice.cc:1464 The subkct file %s is missing definitions for %s +0029 WritePathSpice.cc:1562 subckt %s port %s has no corresponding liberty port, pg_port and is not power or ground. +0030 LibertyReader.cc:628 library missing name. +0031 LibertyReader.cc:660 default_wire_load %s not found. +0032 LibertyReader.cc:671 default_wire_selection %s not found. +0033 LibertyReader.cc:693 input_threshold_pct_%s not found. +0034 LibertyReader.cc:697 output_threshold_pct_%s not found. +0035 LibertyReader.cc:701 slew_lower_threshold_pct_%s not found. +0036 LibertyReader.cc:705 slew_upper_threshold_pct_%s not found. +0037 LibertyReader.cc:710 Library %s is missing one or more thresholds. +0038 LibertyReader.cc:800 unknown unit multiplier %s. +0039 LibertyReader.cc:819 unknown unit scale %c. +0040 LibertyReader.cc:822 unknown unit suffix %s. +0041 LibertyReader.cc:848 capacitive_load_units are not ff or pf. +0042 LibertyReader.cc:851 capacitive_load_units are not a string. +0043 LibertyReader.cc:854 capacitive_load_units missing suffix. +0044 LibertyReader.cc:857 capacitive_load_units scale is not a float. +0045 LibertyReader.cc:860 capacitive_load_units missing scale and suffix. +0046 LibertyReader.cc:863 capacitive_load_unit missing values suffix. +0047 LibertyReader.cc:881 delay_model %s not supported. +0048 LibertyReader.cc:885 delay_model %s not supported. +0049 LibertyReader.cc:889 delay_model %s not supported. +0050 LibertyReader.cc:894 delay_model %s not supported. . -0051 LibertyReader.cc:893 unknown delay_model %s +0051 LibertyReader.cc:897 unknown delay_model %s . -0052 LibertyReader.cc:912 unknown bus_naming_style format. -0053 LibertyReader.cc:590 library %s already exists. -0054 LibertyReader.cc:933 voltage_map voltage is not a float. -0055 LibertyReader.cc:936 voltage_map missing voltage. -0056 LibertyReader.cc:939 voltage_map supply name is not a string. -0057 LibertyReader.cc:942 voltage_map missing supply name and voltage. -0058 LibertyReader.cc:945 voltage_map missing values suffix. -0059 LibertyReader.cc:1163 default_wire_load_mode %s not found. -0060 LibertyReader.cc:679 default_operating_condition %s not found. -0061 LibertyReader.cc:1334 table template missing name. -0062 LibertyReader.cc:1379 missing variable_%d attribute. -0063 LibertyReader.cc:1422 axis type %s not supported. -0064 LibertyReader.cc:1482 bus type %s missing bit_from. -0065 LibertyReader.cc:1484 bus type %s missing bit_to. -0066 LibertyReader.cc:1488 type missing name. -0067 LibertyReader.cc:1515 scaling_factors do not have a name. -0068 LibertyReader.cc:1683 operating_conditions missing name. -0069 LibertyReader.cc:1753 wire_load missing name. -0070 LibertyReader.cc:1796 fanout_length is missing length and fanout. -0071 LibertyReader.cc:1811 wire_load_selection missing name. -0072 LibertyReader.cc:1842 wireload %s not found. -0074 LibertyReader.cc:1849 wire_load_from_area min not a float. -0075 LibertyReader.cc:1852 wire_load_from_area max not a float. -0076 LibertyReader.cc:1855 wire_load_from_area missing parameters. -0077 LibertyReader.cc:1858 wire_load_from_area missing parameters. -0078 LibertyReader.cc:1875 cell missing name. -0079 LibertyReader.cc:1898 cell %s ocv_derate_group %s not found. -0080 LibertyReader.cc:1934 port %s function size does not match port size. -0081 LibertyReader.cc:2002 %s %s bus width mismatch. -0082 LibertyReader.cc:2013 %s %s bus width mismatch. -0083 LibertyReader.cc:2023 clear -0084 LibertyReader.cc:2033 preset -0085 LibertyReader.cc:2069 latch enable function is non-unate for port %s. -0086 LibertyReader.cc:2074 latch enable function is unknown for port %s. -0087 LibertyReader.cc:2150 operating conditions %s not found. -0088 LibertyReader.cc:2153 scaled_cell missing operating condition. -0089 LibertyReader.cc:2156 scaled_cell cell %s has not been defined. -0090 LibertyReader.cc:2159 scaled_cell missing name. -0091 LibertyReader.cc:2185 scaled_cell %s, %s port functions do not match cell port functions. -0092 LibertyReader.cc:2190 scaled_cell ports do not match cell ports. -0093 LibertyReader.cc:2192 scaled_cell %s, %s timing does not match cell timing. -0094 LibertyReader.cc:2211 combinational timing to an input port. -0095 LibertyReader.cc:2302 missing %s_transition. -0096 LibertyReader.cc:2304 missing cell_%s. -0099 LibertyReader.cc:2821 scaling_factors %s not found. -0100 LibertyReader.cc:2864 pin name is not a string. -0101 LibertyReader.cc:2883 pin name is not a string. -0102 LibertyReader.cc:2899 pin name is not a string. -0103 LibertyReader.cc:2977 bus %s bus_type not found. -0104 LibertyReader.cc:3033 bus_type %s not found. -0105 LibertyReader.cc:3036 bus_type is not a string. -0106 LibertyReader.cc:3054 bundle %s member not found. -0107 LibertyReader.cc:3081 member is not a string. -0108 LibertyReader.cc:3088 members attribute is missing values. -0109 LibertyReader.cc:3139 unknown port direction. -0110 LibertyReader.cc:3507 pulse_latch unknown pulse type. -0111 LibertyReader.cc:3885 unknown timing_type %s. -0112 LibertyReader.cc:3905 unknown timing_sense %s. -0113 LibertyReader.cc:3945 mode value is not a string. -0114 LibertyReader.cc:3948 missing mode value. -0115 LibertyReader.cc:3951 mode name is not a string. -0116 LibertyReader.cc:3954 mode missing values. -0117 LibertyReader.cc:3957 mode missing mode name and value. -0118 LibertyReader.cc:2541 unsupported model axis. -0119 LibertyReader.cc:4060 unsupported model axis. -0120 LibertyReader.cc:4089 unsupported model axis. -0121 LibertyReader.cc:4124 unsupported model axis. -0122 LibertyReader.cc:4179 table template %s not found. -0123 LibertyReader.cc:4258 %s is missing values. -0124 LibertyReader.cc:4283 %s is not a list of floats. -0125 LibertyReader.cc:4285 table row has %u columns but axis has %d. -0126 LibertyReader.cc:4295 table has %u rows but axis has %d. -0127 LibertyReader.cc:4348 lut output is not a string. -0128 LibertyReader.cc:4390 mode definition missing name. -0129 LibertyReader.cc:4407 mode value missing name. -0130 LibertyReader.cc:4421 when attribute inside table model. -0131 LibertyReader.cc:4470 %s attribute is not a string. -0132 LibertyReader.cc:4473 %s is not a simple attribute. -0133 LibertyReader.cc:4496 %s is not a simple attribute. -0134 LibertyReader.cc:4509 %s is not a simple attribute. -0135 LibertyReader.cc:4533 %s value %s is not a float. -0136 LibertyReader.cc:4562 %s missing values. -0137 LibertyReader.cc:4566 %s missing values. -0138 LibertyReader.cc:4569 %s is not a complex attribute. -0139 LibertyReader.cc:4595 %s is not a float. -0140 LibertyReader.cc:4618 %s is missing values. -0141 LibertyReader.cc:4621 %s has more than one string. -0142 LibertyReader.cc:4630 %s is missing values. -0143 LibertyReader.cc:4655 %s attribute is not boolean. -0144 LibertyReader.cc:4658 %s attribute is not boolean. -0145 LibertyReader.cc:4661 %s is not a simple attribute. -0146 LibertyReader.cc:4677 attribute %s value %s not recognized. -0147 LibertyReader.cc:4707 unknown early/late value. -0148 LibertyReader.cc:4933 OCV derate group named %s not found. -0149 LibertyReader.cc:4949 ocv_derate missing name. -0150 LibertyReader.cc:5002 unknown rise/fall. -0151 LibertyReader.cc:5022 unknown derate type. -0152 LibertyReader.cc:5054 unsupported model axis. -0153 LibertyReader.cc:5086 unsupported model axis. -0154 LibertyReader.cc:5118 unsupported model axis. -0155 LibertyReader.cc:5189 unknown pg_type. -0156 LibertyReader.cc:5584 port %s subscript out of range. -0157 LibertyReader.cc:5588 port range %s of non-bus port %s. -0158 LibertyReader.cc:5602 port %s not found. -0159 LibertyReader.cc:5672 port %s not found. -0160 LibertyReader.cc:1030 default_max_transition is 0.0. -0161 LibertyReader.cc:3395 max_transition is 0.0. -0162 LibertyReader.cc:4493 %s attribute is not an integer. -0163 LibertyReader.cc:1135 default_fanout_load is 0.0. -0164 LibertyReader.cc:2324 timing group from output port. -0165 LibertyReader.cc:2334 timing group from output port. -0166 LibertyReader.cc:2344 timing group from output port. -0167 LibertyReader.cc:2362 timing group from output port. -0168 LibertyReader.cc:2377 timing group from output port. -0169 LibertyReader.cc:4365 cell %s test_cell redefinition. -0170 LibertyReader.cc:3804 timing group missing related_pin/related_bus_pin. -0179 SpefReader.cc:733 %s. +0052 LibertyReader.cc:916 unknown bus_naming_style format. +0053 LibertyReader.cc:594 library %s already exists. +0054 LibertyReader.cc:937 voltage_map voltage is not a float. +0055 LibertyReader.cc:940 voltage_map missing voltage. +0056 LibertyReader.cc:943 voltage_map supply name is not a string. +0057 LibertyReader.cc:946 voltage_map missing supply name and voltage. +0058 LibertyReader.cc:949 voltage_map missing values suffix. +0059 LibertyReader.cc:1167 default_wire_load_mode %s not found. +0060 LibertyReader.cc:683 default_operating_condition %s not found. +0061 LibertyReader.cc:1338 table template missing name. +0062 LibertyReader.cc:1383 missing variable_%d attribute. +0063 LibertyReader.cc:1426 axis type %s not supported. +0064 LibertyReader.cc:1486 bus type %s missing bit_from. +0065 LibertyReader.cc:1488 bus type %s missing bit_to. +0066 LibertyReader.cc:1492 type missing name. +0067 LibertyReader.cc:1519 scaling_factors do not have a name. +0068 LibertyReader.cc:1687 operating_conditions missing name. +0069 LibertyReader.cc:1757 wire_load missing name. +0070 LibertyReader.cc:1800 fanout_length is missing length and fanout. +0071 LibertyReader.cc:1815 wire_load_selection missing name. +0072 LibertyReader.cc:1846 wireload %s not found. +0074 LibertyReader.cc:1853 wire_load_from_area min not a float. +0075 LibertyReader.cc:1856 wire_load_from_area max not a float. +0076 LibertyReader.cc:1859 wire_load_from_area missing parameters. +0077 LibertyReader.cc:1862 wire_load_from_area missing parameters. +0078 LibertyReader.cc:1879 cell missing name. +0079 LibertyReader.cc:1902 cell %s ocv_derate_group %s not found. +0080 LibertyReader.cc:1938 port %s function size does not match port size. +0081 LibertyReader.cc:2006 %s %s bus width mismatch. +0082 LibertyReader.cc:2017 %s %s bus width mismatch. +0083 LibertyReader.cc:2027 clear +0084 LibertyReader.cc:2037 preset +0085 LibertyReader.cc:2073 latch enable function is non-unate for port %s. +0086 LibertyReader.cc:2078 latch enable function is unknown for port %s. +0087 LibertyReader.cc:2154 operating conditions %s not found. +0088 LibertyReader.cc:2157 scaled_cell missing operating condition. +0089 LibertyReader.cc:2160 scaled_cell cell %s has not been defined. +0090 LibertyReader.cc:2163 scaled_cell missing name. +0091 LibertyReader.cc:2189 scaled_cell %s, %s port functions do not match cell port functions. +0092 LibertyReader.cc:2194 scaled_cell ports do not match cell ports. +0093 LibertyReader.cc:2196 scaled_cell %s, %s timing does not match cell timing. +0094 LibertyReader.cc:2215 combinational timing to an input port. +0095 LibertyReader.cc:2306 missing %s_transition. +0096 LibertyReader.cc:2308 missing cell_%s. +0099 LibertyReader.cc:2900 scaling_factors %s not found. +0100 LibertyReader.cc:2943 pin name is not a string. +0101 LibertyReader.cc:2962 pin name is not a string. +0102 LibertyReader.cc:2978 pin name is not a string. +0103 LibertyReader.cc:3056 bus %s bus_type not found. +0104 LibertyReader.cc:3112 bus_type %s not found. +0105 LibertyReader.cc:3115 bus_type is not a string. +0106 LibertyReader.cc:3133 bundle %s member not found. +0107 LibertyReader.cc:3160 member is not a string. +0108 LibertyReader.cc:3167 members attribute is missing values. +0109 LibertyReader.cc:3218 unknown port direction. +0110 LibertyReader.cc:3586 pulse_latch unknown pulse type. +0111 LibertyReader.cc:3964 unknown timing_type %s. +0112 LibertyReader.cc:3984 unknown timing_sense %s. +0113 LibertyReader.cc:4024 mode value is not a string. +0114 LibertyReader.cc:4027 missing mode value. +0115 LibertyReader.cc:4030 mode name is not a string. +0116 LibertyReader.cc:4033 mode missing values. +0117 LibertyReader.cc:4036 mode missing mode name and value. +0118 LibertyReader.cc:2547 unsupported model axis. +0119 LibertyReader.cc:4139 unsupported model axis. +0120 LibertyReader.cc:4168 unsupported model axis. +0121 LibertyReader.cc:4203 unsupported model axis. +0122 LibertyReader.cc:4258 table template %s not found. +0123 LibertyReader.cc:4337 %s is missing values. +0124 LibertyReader.cc:4362 %s is not a list of floats. +0125 LibertyReader.cc:4364 table row has %u columns but axis has %d. +0126 LibertyReader.cc:4374 table has %u rows but axis has %d. +0127 LibertyReader.cc:4427 lut output is not a string. +0128 LibertyReader.cc:4469 mode definition missing name. +0129 LibertyReader.cc:4486 mode value missing name. +0130 LibertyReader.cc:4500 when attribute inside table model. +0131 LibertyReader.cc:4549 %s attribute is not a string. +0132 LibertyReader.cc:4552 %s is not a simple attribute. +0133 LibertyReader.cc:4575 %s is not a simple attribute. +0134 LibertyReader.cc:4588 %s is not a simple attribute. +0135 LibertyReader.cc:4612 %s value %s is not a float. +0136 LibertyReader.cc:4641 %s missing values. +0137 LibertyReader.cc:4645 %s missing values. +0138 LibertyReader.cc:4648 %s is not a complex attribute. +0139 LibertyReader.cc:4674 %s is not a float. +0140 LibertyReader.cc:4697 %s is missing values. +0141 LibertyReader.cc:4700 %s has more than one string. +0142 LibertyReader.cc:4709 %s is missing values. +0143 LibertyReader.cc:4734 %s attribute is not boolean. +0144 LibertyReader.cc:4737 %s attribute is not boolean. +0145 LibertyReader.cc:4740 %s is not a simple attribute. +0146 LibertyReader.cc:4756 attribute %s value %s not recognized. +0147 LibertyReader.cc:4786 unknown early/late value. +0148 LibertyReader.cc:5012 OCV derate group named %s not found. +0149 LibertyReader.cc:5028 ocv_derate missing name. +0150 LibertyReader.cc:5081 unknown rise/fall. +0151 LibertyReader.cc:5101 unknown derate type. +0152 LibertyReader.cc:5133 unsupported model axis. +0153 LibertyReader.cc:5165 unsupported model axis. +0154 LibertyReader.cc:5197 unsupported model axis. +0155 LibertyReader.cc:5268 unknown pg_type. +0156 LibertyReader.cc:5663 port %s subscript out of range. +0157 LibertyReader.cc:5667 port range %s of non-bus port %s. +0158 LibertyReader.cc:5681 port %s not found. +0159 LibertyReader.cc:5751 port %s not found. +0160 LibertyReader.cc:1034 default_max_transition is 0.0. +0161 LibertyReader.cc:3474 max_transition is 0.0. +0162 LibertyReader.cc:4572 %s attribute is not an integer. +0163 LibertyReader.cc:1139 default_fanout_load is 0.0. +0164 LibertyReader.cc:2328 timing group from output port. +0165 LibertyReader.cc:2338 timing group from output port. +0166 LibertyReader.cc:2348 timing group from output port. +0167 LibertyReader.cc:2366 timing group from output port. +0168 LibertyReader.cc:2381 timing group from output port. +0169 LibertyReader.cc:4444 cell %s test_cell redefinition. +0170 LibertyReader.cc:3883 timing group missing related_pin/related_bus_pin. +0179 SpefReader.cc:734 %s. 0190 VerilogReader.cc:1756 %s is not a verilog module. 0191 VerilogReader.cc:1761 %s is not a verilog module. -0201 StaTcl.i:117 no network has been linked. -0202 StaTcl.i:131 network does not support edits. -0204 StaTcl.i:4021 POCV support requires compilation with SSTA=1. +0201 StaTcl.i:118 no network has been linked. +0202 StaTcl.i:132 network does not support edits. +0204 StaTcl.i:4072 POCV support requires compilation with SSTA=1. 0206 LibertyExpr.cc:175 %s %s. 0207 GraphDelayCalc1.cc:738 port not found in cell 0208 Graph.cc:793 arc_delay_annotated array bounds exceeded @@ -184,20 +184,20 @@ 0252 PathEnumed.cc:135 enumerated path required time 0253 PathGroup.cc:399 unknown path end type 0254 PathVertexRep.cc:145 tag group missing tag -0255 ReportPath.cc:289 unsupported path type -0256 ReportPath.cc:310 unsupported path type -0257 ReportPath.cc:349 unsupported path type -0259 ReportPath.cc:2378 unsupported path type +0255 ReportPath.cc:287 unsupported path type +0256 ReportPath.cc:308 unsupported path type +0257 ReportPath.cc:347 unsupported path type +0259 ReportPath.cc:2376 unsupported path type 0260 Search.cc:2628 max tag group index exceeded 0261 Search.cc:2860 max tag index exceeded 0262 Search.cc:3551 unexpected filter path 0263 Search.cc:3719 tns incr existing vertex -0264 Sta.cc:4174 corresponding timing arc set not found in equiv cells +0264 Sta.cc:4180 corresponding timing arc set not found in equiv cells 0265 TagGroup.cc:297 tag group missing tag 0266 Sta.cc:2090 '%s' is not a valid endpoint. 0267 Sta.cc:2014 '%s' is not a valid start point. -0272 StaTcl.i:4007 unknown common clk pessimism mode. -0273 StaTcl.i:4963 unknown clock sense +0272 StaTcl.i:4058 unknown common clk pessimism mode. +0273 StaTcl.i:5003 unknown clock sense 0299 Power.tcl:241 activity cannot be set on clock ports. 0300 CmdUtil.tcl:44 no commands match '$pattern'. 0301 Power.tcl:218 activity should be 0.0 to 1.0 or 2.0 @@ -461,8 +461,8 @@ 0604 Sdc.tcl:281 unknown $unit prefix '$prefix'. 0605 Sdc.tcl:3547 wire load model '$model_name' not found. 0606 Property.tcl:77 get_property unsupported object type $object_type. -0607 StaTcl.i:4257 unknown report path field %s -0608 StaTcl.i:4269 unknown report path field %s +0607 StaTcl.i:4308 unknown report path field %s +0608 StaTcl.i:4320 unknown report path field %s 0609 Search.tcl:421 -all_violators is deprecated. Use -violators 0610 Search.tcl:501 -max_transition deprecated. Use -max_slew. 0611 Search.tcl:506 -min_transition deprecated. Use -min_slew. @@ -480,7 +480,7 @@ 0702 LibertyWriter.cc:436 3 axis table models not supported. 0703 LibertyWriter.cc:576 %s/%s/%s timing arc type %s not supported. 0704 LibertyWriter.cc:289 %s/%s bundled ports not supported. -0705 Liberty.cc:794 Liberty cell %s/%s for corner %s/%s not found. +0705 Liberty.cc:795 Liberty cell %s/%s for corner %s/%s not found. 0706 Parasitics.tcl:70 read_spef -increment is deprecated. 0710 LumpedCapDelayCalc.cc:169 gate delay input variable is NaN 0800 VcdReader.cc:109 unhandled vcd command. @@ -491,13 +491,15 @@ 0806 ReadVcdActivities.cc:247 clock %s vcd period %s differs from SDC clock period %s 0807 Sdc.tcl:394 only one of -cells, -data_pins, -clock_pins, -async_pins, -output_pins are suppported. 0810 MakeTimingModel.cc:189 clock %s pin %s is inside model block. -0900 LibertyReader.cc:2761 level_shifter_type must be HL, LH, or HL_LH -0901 LibertyReader.cc:2797 switch_cell_type must be coarse_grain or fine_grain -0902 LibertyReader.cc:2460 unsupported model axis. -0903 LibertyReader.cc:4140 %s group not in timing group. -0904 LibertyReader.cc:2443 receiver_capacitance group not in timing or pin group. -0906 LibertyReader.cc:4033 unsupported model axis. -0907 LibertyReader.cc:2486 output_current_%s group not in timing group. -0908 LibertyReader.cc:2575 vector reference_time not found. -0912 LibertyReader.cc:2573 vector index_1 and index_2 must have one value. -0913 LibertyReader.cc:2525 output current waveform %.2e %.2e not found. +0900 LibertyReader.cc:2840 level_shifter_type must be HL, LH, or HL_LH +0901 LibertyReader.cc:2876 switch_cell_type must be coarse_grain or fine_grain +0902 LibertyReader.cc:2464 unsupported model axis. +0903 LibertyReader.cc:4219 %s group not in timing group. +0904 LibertyReader.cc:2447 receiver_capacitance group not in timing or pin group. +0906 LibertyReader.cc:4112 unsupported model axis. +0907 LibertyReader.cc:2492 output_current_%s group not in timing group. +0908 LibertyReader.cc:2591 vector reference_time not found. +0912 LibertyReader.cc:2589 vector index_1 and index_2 must have exactly one value. +0913 LibertyReader.cc:2531 output current waveform %.2e %.2e not found. +0914 LibertyReader.cc:2624 normalized_driver_waveform variable_2 must be normalized_voltage +0915 LibertyReader.cc:2627 normalized_driver_waveform variable_1 must be input_net_transition diff --git a/power/Power.cc b/power/Power.cc index 2feac82c..773219ee 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -63,6 +63,7 @@ namespace sta { using std::abs; +using std::max; using std::isnormal; static bool @@ -302,12 +303,16 @@ public: virtual VertexVisitor *copy() const; virtual void visit(Vertex *vertex); InstanceSet &visitedRegs() { return visited_regs_; } + void init(); + float maxChange() const { return max_change_; } private: bool setActivityCheck(const Pin *pin, PwrActivity &activity); + static constexpr float change_tolerance_ = .01; InstanceSet visited_regs_; + float max_change_; Power *power_; BfsFwdIterator *bfs_; }; @@ -327,6 +332,12 @@ PropActivityVisitor::copy() const return new PropActivityVisitor(power_, bfs_); } +void +PropActivityVisitor::init() +{ + max_change_ = 0.0; +} + void PropActivityVisitor::visit(Vertex *vertex) { @@ -420,8 +431,12 @@ PropActivityVisitor::setActivityCheck(const Pin *pin, PwrActivity &activity) { PwrActivity &prev_activity = power_->activity(pin); - if (abs(activity.activity() - prev_activity.activity()) > .001 - || abs(activity.duty() - prev_activity.duty()) > .001) { + float activity_delta = abs(activity.activity() - prev_activity.activity()); + float duty_delta = abs(activity.duty() - prev_activity.duty()); + if (activity_delta > change_tolerance_ + || duty_delta > change_tolerance_) { + max_change_ = max(max_change_, activity_delta); + max_change_ = max(max_change_, duty_delta); power_->setActivity(pin, activity); return true; } @@ -556,8 +571,10 @@ Power::ensureActivities() bfs.visit(levelize_->maxLevel(), &visitor); // Propagate activiities through registers. InstanceSet regs = std::move(visitor.visitedRegs()); - while (!regs.empty()) { - InstanceSet::Iterator reg_iter(regs); + int pass = 1; + while (!regs.empty() && pass < max_activity_passes_) { + visitor.init(); + InstanceSet::Iterator reg_iter(regs); while (reg_iter.hasNext()) { const Instance *reg = reg_iter.next(); // Propagate activiities across register D->Q. @@ -567,6 +584,9 @@ Power::ensureActivities() // combinational logic. bfs.visit(levelize_->maxLevel(), &visitor); regs = std::move(visitor.visitedRegs()); + debugPrint(debug_, "power_activity", 1, "Pass %d change %.2f", + pass, visitor.maxChange()); + pass++; } activities_valid_ = true; } @@ -615,7 +635,7 @@ Power::seedRegOutputActivities(const Instance *inst, && func && (func->port() == seq->output() || func->port() == seq->outputInv())) { - debugPrint(debug_, "power_activity", 3, "enqueue reg output %s", + debugPrint(debug_, "power_reg", 1, "enqueue reg output %s", vertex->name(network_)); bfs.enqueue(vertex); } diff --git a/power/Power.hh b/power/Power.hh index b9296727..3cc5a1b3 100644 --- a/power/Power.hh +++ b/power/Power.hh @@ -196,6 +196,7 @@ private: PwrActivityMap activity_map_; PwrSeqActivityMap seq_activity_map_; bool activities_valid_; + static constexpr int max_activity_passes_ = 100; friend class PropActivityVisitor; }; diff --git a/search/Property.cc b/search/Property.cc index cdc39d11..8abc350c 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -48,6 +48,11 @@ pinSlewProperty(const Pin *pin, const MinMax *min_max, Sta *sta); static PropertyValue +pinArrivalProperty(const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + Sta *sta); +static PropertyValue pinSlackProperty(const Pin *pin, const MinMax *min_max, Sta *sta); @@ -882,6 +887,15 @@ getProperty(const Pin *pin, return PropertyValue(&activity); } + else if (stringEqual(property, "arrival_max_rise")) + return pinArrivalProperty(pin, RiseFall::rise(), MinMax::max(), sta); + else if (stringEqual(property, "arrival_max_fall")) + return pinArrivalProperty(pin, RiseFall::fall(), MinMax::max(), sta); + else if (stringEqual(property, "arrival_min_rise")) + return pinArrivalProperty(pin, RiseFall::rise(), MinMax::min(), sta); + else if (stringEqual(property, "arrival_min_fall")) + return pinArrivalProperty(pin, RiseFall::fall(), MinMax::min(), sta); + else if (stringEqual(property, "slack_max")) return pinSlackProperty(pin, MinMax::max(), sta); else if (stringEqual(property, "slack_max_fall")) @@ -912,6 +926,16 @@ getProperty(const Pin *pin, throw PropertyUnknown("pin", property); } +static PropertyValue +pinArrivalProperty(const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + Sta *sta) +{ + Arrival arrival = sta->pinArrival(pin, rf, min_max);; + return PropertyValue(delayPropertyValue(arrival, sta)); +} + static PropertyValue pinSlackProperty(const Pin *pin, const MinMax *min_max, diff --git a/search/Sta.cc b/search/Sta.cc index e7fb9e01..0e9d4904 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -2803,42 +2803,49 @@ Sta::vertexWorstSlackPath(Vertex *vertex, } Arrival -Sta::vertexArrival(Vertex *vertex, - const MinMax *min_max) +Sta::pinArrival(const Pin *pin, + const RiseFall *rf, + const MinMax *min_max) { - searchPreamble(); - search_->findArrivals(vertex->level()); - Arrival arrival = min_max->initValue(); - VertexPathIterator path_iter(vertex, this); - while (path_iter.hasNext()) { - Path *path = path_iter.next(); - const Arrival &path_arrival = path->arrival(this); - ClkInfo *clk_info = path->clkInfo(search_); - if (path->minMax(this) == min_max - && !clk_info->isGenClkSrcPath() - && delayGreater(path->arrival(this), arrival, min_max, this)) - arrival = path_arrival; + Vertex *vertex, *bidirect_vertex; + graph_->pinVertices(pin, vertex, bidirect_vertex); + Arrival arrival; + if (vertex) + arrival = vertexArrival(vertex, rf, clk_edge_wildcard, nullptr, min_max); + if (bidirect_vertex) { + Arrival arrival1 = vertexArrival(bidirect_vertex, rf, clk_edge_wildcard, + nullptr, min_max); + arrival = min_max->minMax(arrival, arrival1); } return arrival; } +Arrival +Sta::vertexArrival(Vertex *vertex, + const MinMax *min_max) +{ + return vertexArrival(vertex, nullptr, nullptr, nullptr, min_max); +} + Arrival Sta::vertexArrival(Vertex *vertex, const RiseFall *rf, const PathAnalysisPt *path_ap) { - return vertexArrival(vertex, rf, clk_edge_wildcard, path_ap); + return vertexArrival(vertex, rf, clk_edge_wildcard, path_ap, nullptr); } Arrival Sta::vertexArrival(Vertex *vertex, const RiseFall *rf, const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap) + const PathAnalysisPt *path_ap, + const MinMax *min_max) { searchPreamble(); search_->findArrivals(vertex->level()); - const MinMax *min_max = path_ap->pathMinMax(); + if (min_max == nullptr) + min_max = path_ap->pathMinMax(); Arrival arrival = min_max->initValue(); VertexPathIterator path_iter(vertex, rf, path_ap, this); while (path_iter.hasNext()) { diff --git a/search/WritePathSpice.cc b/search/WritePathSpice.cc index bba79426..97caf9ac 100644 --- a/search/WritePathSpice.cc +++ b/search/WritePathSpice.cc @@ -48,6 +48,8 @@ namespace sta { using std::string; using std::ofstream; using std::ifstream; +using std::max; +using std::set; typedef Map CellSpicePortNames; typedef int Stage; @@ -79,7 +81,7 @@ private: void writeHeader(); void writeStageInstances(); void writeInputSource(); - void writeStepVoltSource(const Pin *pin, + void writeRampVoltSource(const Pin *pin, const RiseFall *rf, float slew, float time, @@ -98,8 +100,8 @@ private: DcalcAPIndex dcalc_ap_index); void writeStageParasitics(Stage stage); void writeSubckts(); - void findPathCellnames(// Return values. - StringSet &path_cell_names); + set findPathCellnames(); + void findPathCellSubckts(set &path_cell_names); void recordSpicePortNames(const char *cell_name, StringVector &tokens); float maxTime(); @@ -135,6 +137,11 @@ private: void writeWaveformEdge(const RiseFall *rf, float time, float slew); + void writeWaveformVoltSource(const Pin *pin, + DriverWaveform *drvr_waveform, + const RiseFall *rf, + float slew, + int &volt_index); void writeClkedStepSource(const Pin *pin, const RiseFall *rf, const Clock *clk, @@ -162,6 +169,7 @@ private: int &volt_index); float slewAxisMinValue(TimingArc *arc); float pgPortVoltage(LibertyPgPort *pg_port); + void writePrintStmt(); // Stage "accessors". // @@ -321,6 +329,7 @@ WritePathSpice::writeSpice() // Find subckt port names as a side-effect of writeSubckts. writeSubckts(); writeHeader(); + writePrintStmt(); writeStageInstances(); writeMeasureStmts(); writeInputSource(); @@ -332,6 +341,18 @@ WritePathSpice::writeSpice() throw FileNotWritable(spice_filename_); } +// Use c++17 fs::path(filename).stem() +static string +filenameStem(const char *filename) +{ + string filename1 = filename; + const size_t last_slash_idx = filename1.find_last_of("\\/"); + if (last_slash_idx != std::string::npos) + return filename1.substr(last_slash_idx + 1); + else + return filename1; +} + void WritePathSpice::writeHeader() { @@ -348,12 +369,25 @@ WritePathSpice::writeHeader() float temp = pvt->temperature(); streamPrint(spice_stream_, ".temp %.1f\n", temp); streamPrint(spice_stream_, ".include \"%s\"\n", model_filename_); - streamPrint(spice_stream_, ".include \"%s\"\n", subckt_filename_); + string subckt_filename_stem = filenameStem(subckt_filename_); + streamPrint(spice_stream_, ".include \"%s\"\n", subckt_filename_stem.c_str()); float max_time = maxTime(); float time_step = max_time / 1e+3; streamPrint(spice_stream_, ".tran %.3g %.3g\n\n", time_step, max_time); + streamPrint(spice_stream_, ".options nomod\n"); +} + +void +WritePathSpice::writePrintStmt() +{ + streamPrint(spice_stream_, ".print tran"); + for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { + streamPrint(spice_stream_, " v(%s)", stageDrvrPinName(stage)); + streamPrint(spice_stream_, " v(%s)", stageLoadPinName(stage)); + } + streamPrint(spice_stream_, "\n\n"); } float @@ -373,9 +407,8 @@ WritePathSpice::maxTime() } else { float end_slew = findSlew(path_); - float max_time = delayAsFloat(input_slew - + path_->arrival(this) - + end_slew * 2) * 1.5; + float arrival = delayAsFloat(path_->arrival(this)); + float max_time = input_slew / 2.0 + arrival + end_slew; return max_time; } } @@ -463,11 +496,55 @@ WritePathSpice::writeInputWaveform() float time0 = slew0; int volt_index = 1; const Pin *drvr_pin = stageDrvrPin(input_stage); - writeStepVoltSource(drvr_pin, rf, slew0, time0, volt_index); + const Pin *load_pin = stageLoadPin(input_stage); + const LibertyPort *load_port = network_->libertyPort(load_pin); + DriverWaveform *drvr_waveform = nullptr; + if (load_port) + drvr_waveform = load_port->driverWaveform(rf); + if (drvr_waveform) + writeWaveformVoltSource(drvr_pin, drvr_waveform, + rf, slew0, volt_index); + else + writeRampVoltSource(drvr_pin, rf, slew0, time0, volt_index); } void -WritePathSpice::writeStepVoltSource(const Pin *pin, +WritePathSpice::writeWaveformVoltSource(const Pin *pin, + DriverWaveform *drvr_waveform, + const RiseFall *rf, + float slew, + int &volt_index) +{ + float volt0, volt1, volt_factor; + if (rf == RiseFall::rise()) { + volt0 = gnd_voltage_; + volt1 = power_voltage_; + volt_factor = power_voltage_; + } + else { + volt0 = power_voltage_; + volt1 = gnd_voltage_; + volt_factor = -power_voltage_; + } + streamPrint(spice_stream_, "v%d %s 0 pwl(\n", + volt_index, + network_->pathName(pin)); + streamPrint(spice_stream_, "+%.3e %.3e\n", 0.0, volt0); + Table1 waveform = drvr_waveform->waveform(slew); + TableAxisPtr time_axis = waveform.axis1(); + for (size_t time_index = 0; time_index < time_axis->size(); time_index++) { + float time = time_axis->axisValue(time_index); + float wave_volt = waveform.value(time_index); + float volt = volt0 + wave_volt * volt_factor; + streamPrint(spice_stream_, "+%.3e %.3e\n", time, volt); + } + streamPrint(spice_stream_, "+%.3e %.3e\n", maxTime(), volt1); + streamPrint(spice_stream_, "+)\n"); + volt_index++; +} + +void +WritePathSpice::writeRampVoltSource(const Pin *pin, const RiseFall *rf, float slew, float time, @@ -928,7 +1005,7 @@ WritePathSpice::writeClkedStepSource(const Pin *pin, Vertex *vertex = graph_->pinLoadVertex(pin); float slew = findSlew(vertex, rf, nullptr, dcalc_ap_index); float time = clkWaveformTImeOffset(clk) + clk->period() / 2.0; - writeStepVoltSource(pin, rf, slew, time, volt_index); + writeRampVoltSource(pin, rf, slew, time, volt_index); } void @@ -1342,8 +1419,8 @@ WritePathSpice::nodeName(ParasiticNode *node) void WritePathSpice::writeSubckts() { - StringSet path_cell_names; - findPathCellnames(path_cell_names); + set path_cell_names = findPathCellnames(); + findPathCellSubckts(path_cell_names); ifstream lib_subckts_stream(lib_subckt_filename_); if (lib_subckts_stream.is_open()) { @@ -1357,7 +1434,7 @@ WritePathSpice::writeSubckts() if (tokens.size() >= 2 && stringEqual(tokens[0].c_str(), ".subckt")) { const char *cell_name = tokens[1].c_str(); - if (path_cell_names.hasKey(cell_name)) { + if (path_cell_names.find(cell_name) != path_cell_names.end()) { subckts_stream << line << "\n"; bool found_ends = false; while (getline(lib_subckts_stream, line)) { @@ -1379,10 +1456,14 @@ WritePathSpice::writeSubckts() lib_subckts_stream.close(); if (!path_cell_names.empty()) { - report_->error(28, "The following subkcts are missing from %s", - lib_subckt_filename_); - for (const char *cell_name : path_cell_names) - report_->reportLine(" %s", cell_name); + string missing_cells; + for (const string &cell_name : path_cell_names) { + missing_cells += "\n"; + missing_cells += cell_name; + } + report_->error(28, "The subkct file %s is missing definitions for %s", + lib_subckt_filename_, + missing_cells.c_str()); } } else { @@ -1394,10 +1475,10 @@ WritePathSpice::writeSubckts() throw FileNotReadable(lib_subckt_filename_); } -void -WritePathSpice::findPathCellnames(// Return values. - StringSet &path_cell_names) +set +WritePathSpice::findPathCellnames() { + set path_cell_names; for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { TimingArc *arc = stageGateArc(stage); if (arc) { @@ -1420,6 +1501,47 @@ WritePathSpice::findPathCellnames(// Return values. delete pin_iter; } } + return path_cell_names; +} + +// Subckts can call subckts (asap7). +void +WritePathSpice::findPathCellSubckts(set &path_cell_names) +{ + ifstream lib_subckts_stream(lib_subckt_filename_); + if (lib_subckts_stream.is_open()) { + string line; + while (getline(lib_subckts_stream, line)) { + // .subckt [args..] + StringVector tokens; + split(line, " \t", tokens); + if (tokens.size() >= 2 + && stringEqual(tokens[0].c_str(), ".subckt")) { + const char *cell_name = tokens[1].c_str(); + if (path_cell_names.find(cell_name) != path_cell_names.end()) { + // Scan the subckt definition for subckt calls. + string stmt; + while (getline(lib_subckts_stream, line)) { + if (line[0] == '+') + stmt += line.substr(1); + else { + // Process previous statement. + if (tolower(stmt[0]) == 'x') { + split(stmt, " \t", tokens); + string &subckt_cell = tokens[tokens.size() - 1]; + path_cell_names.insert(subckt_cell); + } + stmt = line; + } + if (stringBeginEqual(line.c_str(), ".ends")) + break; + } + } + } + } + } + else + throw FileNotReadable(lib_subckt_filename_); } void diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index 067d34bd..1b37d6b8 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -49,6 +49,7 @@ #include "Transition.hh" #include "TimingRole.hh" #include "TimingArc.hh" +#include "TableModel.hh" #include "Liberty.hh" #include "LibertyWriter.hh" #include "EquivCells.hh" @@ -567,13 +568,13 @@ using namespace sta; %typemap(in) RiseFall* { int length; const char *arg = Tcl_GetStringFromObj($input, &length); - RiseFall *tr = RiseFall::find(arg); - if (tr == nullptr) { - Tcl_SetResult(interp,const_cast("Error: unknown transition name."), + RiseFall *rf = RiseFall::find(arg); + if (rf == nullptr) { + Tcl_SetResult(interp,const_cast("Error: unknown rise/fall edge."), TCL_STATIC); return TCL_ERROR; } - $1 = tr; + $1 = rf; } %typemap(out) RiseFall* { @@ -875,6 +876,44 @@ using namespace sta; $1 = ints; } +%typemap(out) Table1 { + Table1 &table = $1; + Tcl_Obj *list3 = Tcl_NewListObj(0, nullptr); + Tcl_Obj *list1 = Tcl_NewListObj(0, nullptr); + for (float f : *table.axis1()->values()) { + Tcl_Obj *obj = Tcl_NewDoubleObj(f); + Tcl_ListObjAppendElement(interp, list1, obj); + } + Tcl_Obj *list2 = Tcl_NewListObj(0, nullptr); + for (float f : *table.values()) { + Tcl_Obj *obj = Tcl_NewDoubleObj(f); + Tcl_ListObjAppendElement(interp, list2, obj); + } + Tcl_ListObjAppendElement(interp, list3, list1); + Tcl_ListObjAppendElement(interp, list3, list2); + Tcl_SetObjResult(interp, list3); +} + +%typemap(out) Table1* { + Table1 *table = $1; + Tcl_Obj *list3 = Tcl_NewListObj(0, nullptr); + if (table) { + Tcl_Obj *list1 = Tcl_NewListObj(0, nullptr); + for (float f : *table->axis1()->values()) { + Tcl_Obj *obj = Tcl_NewDoubleObj(f); + Tcl_ListObjAppendElement(interp, list1, obj); + } + Tcl_Obj *list2 = Tcl_NewListObj(0, nullptr); + for (float f : *table->values()) { + Tcl_Obj *obj = Tcl_NewDoubleObj(f); + Tcl_ListObjAppendElement(interp, list2, obj); + } + Tcl_ListObjAppendElement(interp, list3, list1); + Tcl_ListObjAppendElement(interp, list3, list2); + } + Tcl_SetObjResult(interp, list3); +} + %typemap(in) MinMax* { int length; char *arg = Tcl_GetStringFromObj($input, &length); @@ -3749,6 +3788,14 @@ format_voltage(const char *value, return Sta::sta()->units()->voltageUnit()->asString(value1, digits); } +const char * +format_current(const char *value, + int digits) +{ + float value1 = strtof(value, nullptr); + return Sta::sta()->units()->currentUnit()->asString(value1, digits); +} + const char * format_power(const char *value, int digits) @@ -4586,17 +4633,6 @@ find_clk_min_period(const Clock *clk, return sta->findClkMinPeriod(clk, ignore_port_paths); } -TmpString * -report_delay_calc_cmd(Edge *edge, - TimingArc *arc, - const Corner *corner, - const MinMax *min_max, - int digits) -{ - cmdLinkedNetwork(); - return Sta::sta()->reportDelayCalc(edge, arc, corner, min_max, digits); -} - //////////////////////////////////////////////////////////////// PinSeq @@ -5601,6 +5637,9 @@ full_name() to); } +TimingArcSeq & +timing_arcs() { return self->arcs(); } + } // TimingArcSet methods %extend TimingArc { @@ -5611,6 +5650,37 @@ const char *from_edge_name() { return self->fromEdge()->asRiseFall()->name(); } Transition *to_edge() { return self->toEdge(); } const char *to_edge_name() { return self->toEdge()->asRiseFall()->name(); } TimingRole *role() { return self->role(); } + +Table1 +voltage_waveform(float in_slew, + float load_cap) +{ + GateTableModel *gate_model = dynamic_cast(self->model()); + if (gate_model) { + OutputWaveforms *waveforms = gate_model->outputWaveforms(); + if (waveforms) { + Table1 waveform = waveforms->voltageWaveform(in_slew, load_cap); + return waveform; + } + } + return Table1(); +} + +Table1 +current_waveform(float in_slew, + float load_cap) +{ + GateTableModel *gate_model = dynamic_cast(self->model()); + if (gate_model) { + OutputWaveforms *waveforms = gate_model->outputWaveforms(); + if (waveforms) { + Table1 waveform = waveforms->currentWaveform(in_slew, load_cap); + return waveform; + } + } + return Table1(); +} + } // TimingArc methods %extend Instance { @@ -5847,7 +5917,7 @@ arrivals_clk(const RiseFall *rf, clk_edge = clk->edge(clk_rf); for (auto path_ap : sta->corners()->pathAnalysisPts()) { arrivals.push_back(delayAsFloat(sta->vertexArrival(self, rf, clk_edge, - path_ap))); + path_ap, nullptr))); } return arrivals; } @@ -5865,7 +5935,7 @@ arrivals_clk_delays(const RiseFall *rf, clk_edge = clk->edge(clk_rf); for (auto path_ap : sta->corners()->pathAnalysisPts()) { arrivals.push_back(delayAsString(sta->vertexArrival(self, rf, clk_edge, - path_ap), + path_ap, nullptr), sta, digits)); } return arrivals; diff --git a/util/Transition.cc b/util/Transition.cc index b5380006..b77b46ae 100644 --- a/util/Transition.cc +++ b/util/Transition.cc @@ -56,11 +56,13 @@ RiseFall::opposite() const } RiseFall * -RiseFall::find(const char *tr_str) +RiseFall::find(const char *rf_str) { - if (stringEq(tr_str, rise_.name())) + if (stringEq(rf_str, rise_.name()) + || stringEq(rf_str, rise_.shortName())) return &rise_; - else if (stringEq(tr_str, fall_.name())) + else if (stringEq(rf_str, fall_.name()) + || stringEq(rf_str, fall_.shortName())) return &fall_; else return nullptr;