diff --git a/CMakeLists.txt b/CMakeLists.txt index e2c6f77a..a6b33856 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,7 +73,7 @@ set(STA_SOURCE dcalc/LumpedCapDelayCalc.cc dcalc/NetCaps.cc dcalc/RCDelayCalc.cc - dcalc/SimpleRCDelayCalc.cc + dcalc/SlewDegradeDelayCalc.cc dcalc/UnitDelayCalc.cc graph/DelayFloat.cc diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index 6afcaf20..368740e0 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -439,6 +439,7 @@ ArnoldiDelayCalc::loadDelay(const Pin *load_pin, ArcDelay &wire_delay, Slew &load_slew) { + // This does not appear to handle input port parasitics correctly. wire_delay = 0.0; load_slew = drvr_slew_ * multi_drvr_slew_factor_; if (rcmodel_) { diff --git a/dcalc/DelayCalc.cc b/dcalc/DelayCalc.cc index f0a89735..2c5c1ba0 100644 --- a/dcalc/DelayCalc.cc +++ b/dcalc/DelayCalc.cc @@ -20,7 +20,7 @@ #include "StringUtil.hh" #include "UnitDelayCalc.hh" #include "LumpedCapDelayCalc.hh" -#include "SimpleRCDelayCalc.hh" +#include "SlewDegradeDelayCalc.hh" #include "DmpDelayCalc.hh" #include "ArnoldiDelayCalc.hh" @@ -35,7 +35,7 @@ registerDelayCalcs() { registerDelayCalc("unit", makeUnitDelayCalc); registerDelayCalc("lumped_cap", makeLumpedCapDelayCalc); - registerDelayCalc("simple_rc", makeSimpleRCDelayCalc); + registerDelayCalc("slew_degrade", makeSlewDegradeDelayCalc); registerDelayCalc("dmp_ceff_elmore", makeDmpCeffElmoreDelayCalc); registerDelayCalc("dmp_ceff_two_pole", makeDmpCeffTwoPoleDelayCalc); registerDelayCalc("arnoldi", makeArnoldiDelayCalc); diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index ff3a9f16..7ebdf5a6 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -601,6 +601,7 @@ DmpAlg::loadDelaySlew(const Pin *, slew = slew1; } catch (DmpError &error) { + fail(error.what()); delay = elmore_; slew = drvr_slew_; } @@ -620,7 +621,7 @@ DmpAlg::findVlCrossing(double vth) double DmpAlg::vlCrossingUpperBound() { - return voCrossingUpperBound() + elmore_; + return voCrossingUpperBound() + elmore_ * 2.0; } static void @@ -670,8 +671,8 @@ DmpAlg::showVl() void DmpAlg::fail(const char *reason) { - // Allow only failures to be reported with a unique debug flag. - if (debug_->check("dmp_ceff", 1) || debug_->check("dmp_ceff_fail", 1)) + // Report failures with a unique debug flag. + if (debug_->check("dmp_ceff", 1) || debug_->check("dcalc_error", 1)) report_->reportLine("delay_calc: DMP failed - %s c2=%s rpi=%s c1=%s rd=%s", reason, units_->capacitanceUnit()->asString(c2_), diff --git a/dcalc/DmpDelayCalc.cc b/dcalc/DmpDelayCalc.cc index 4d5d4b14..25fcd3f9 100644 --- a/dcalc/DmpDelayCalc.cc +++ b/dcalc/DmpDelayCalc.cc @@ -24,6 +24,7 @@ #include "DcalcAnalysisPt.hh" #include "GraphDelayCalc.hh" #include "DmpCeff.hh" +#include "Network.hh" namespace sta { @@ -125,30 +126,30 @@ class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc { public: DmpCeffTwoPoleDelayCalc(StaState *sta); - virtual ArcDelayCalc *copy(); - virtual Parasitic *findParasitic(const Pin *drvr_pin, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap); - virtual ReducedParasiticType reducedParasiticType() const; - virtual void inputPortDelay(const Pin *port_pin, - float in_slew, - const RiseFall *rf, - const Parasitic *parasitic, - const DcalcAnalysisPt *dcalc_ap); - virtual void gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *drvr_parasitic, - float related_out_cap, - const Pvt *pvt, - const DcalcAnalysisPt *dcalc_ap, - // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew); - virtual void loadDelay(const Pin *load_pin, - ArcDelay &wire_delay, - Slew &load_slew); + ArcDelayCalc *copy() override; + Parasitic *findParasitic(const Pin *drvr_pin, + const RiseFall *rf, + const DcalcAnalysisPt *dcalc_ap) override; + ReducedParasiticType reducedParasiticType() const override; + void inputPortDelay(const Pin *port_pin, + float in_slew, + const RiseFall *rf, + const Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap) override; + void gateDelay(const LibertyCell *drvr_cell, + const TimingArc *arc, + const Slew &in_slew, + float load_cap, + const Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) override; + void loadDelay(const Pin *load_pin, + ArcDelay &wire_delay, + Slew &load_slew) override; private: void loadDelay(Parasitic *pole_residue, @@ -302,7 +303,7 @@ DmpCeffTwoPoleDelayCalc::loadDelay(const Pin *load_pin, ArcDelay &wire_delay, Slew &load_slew) { - // NEED to handle PiElmore parasitic. + // Should handle PiElmore parasitic. ArcDelay wire_delay1 = 0.0; Slew load_slew1 = drvr_slew_; Parasitic *pole_residue = 0; diff --git a/dcalc/LumpedCapDelayCalc.hh b/dcalc/LumpedCapDelayCalc.hh index 0f502e33..ff92dea4 100644 --- a/dcalc/LumpedCapDelayCalc.hh +++ b/dcalc/LumpedCapDelayCalc.hh @@ -47,7 +47,7 @@ public: // Return values. ArcDelay &gate_delay, Slew &drvr_slew) override; - void setMultiDrvrSlewFactor(float) override; + void setMultiDrvrSlewFactor(float factor) override; float ceff(const LibertyCell *drvr_cell, const TimingArc *arc, const Slew &in_slew, diff --git a/dcalc/RCDelayCalc.cc b/dcalc/RCDelayCalc.cc index 3aec6224..5f852e14 100644 --- a/dcalc/RCDelayCalc.cc +++ b/dcalc/RCDelayCalc.cc @@ -62,16 +62,10 @@ RCDelayCalc::dspfWireDelaySlew(const Pin *, ArcDelay &wire_delay, Slew &load_slew) { - float vth = .5; - float vl = .2; - float vh = .8; - float slew_derate = 1.0; - if (drvr_library_) { - vth = drvr_library_->inputThreshold(drvr_rf_); - vl = drvr_library_->slewLowerThreshold(drvr_rf_); - vh = drvr_library_->slewUpperThreshold(drvr_rf_); - slew_derate = drvr_library_->slewDerateFromLibrary(); - } + float vth = drvr_library_->inputThreshold(drvr_rf_); + float vl = drvr_library_->slewLowerThreshold(drvr_rf_); + float vh = drvr_library_->slewUpperThreshold(drvr_rf_); + float slew_derate = drvr_library_->slewDerateFromLibrary(); wire_delay = static_cast(-elmore * log(1.0 - vth)); load_slew = (drvr_slew_ + elmore * log((1.0 - vl) / (1.0 - vh)) / slew_derate) * multi_drvr_slew_factor_; diff --git a/dcalc/SimpleRCDelayCalc.hh b/dcalc/SimpleRCDelayCalc.hh deleted file mode 100644 index aa2f4452..00000000 --- a/dcalc/SimpleRCDelayCalc.hh +++ /dev/null @@ -1,63 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2023, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#pragma once - -#include "RCDelayCalc.hh" - -namespace sta { - -// Liberty table model lumped capacitance arc delay calculator. -// Effective capacitance is the pi model total capacitance (C1+C2). -// Wire delays are elmore delays. -// Driver slews are degraded to loads by rise/fall transition_degradation -// tables. -class SimpleRCDelayCalc : public RCDelayCalc -{ -public: - SimpleRCDelayCalc(StaState *sta); - virtual ArcDelayCalc *copy(); - virtual void inputPortDelay(const Pin *port_pin, - float in_slew, - const RiseFall *rf, - const Parasitic *parasitic, - const DcalcAnalysisPt *dcalc_ap); - virtual void gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *drvr_parasitic, - float related_out_cap, - const Pvt *pvt, - const DcalcAnalysisPt *dcalc_ap, - // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew); - virtual void loadDelay(const Pin *load_pin, - ArcDelay &wire_delay, - Slew &load_slew); - - using RCDelayCalc::gateDelay; - using RCDelayCalc::reportGateDelay; - -private: - const Pvt *pvt_; -}; - -ArcDelayCalc * -makeSimpleRCDelayCalc(StaState *sta); - -} // namespace diff --git a/dcalc/SimpleRCDelayCalc.cc b/dcalc/SlewDegradeDelayCalc.cc similarity index 50% rename from dcalc/SimpleRCDelayCalc.cc rename to dcalc/SlewDegradeDelayCalc.cc index 394e10e5..1e61ebe0 100644 --- a/dcalc/SimpleRCDelayCalc.cc +++ b/dcalc/SlewDegradeDelayCalc.cc @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "SimpleRCDelayCalc.hh" +#include "SlewDegradeDelayCalc.hh" #include "TimingArc.hh" #include "Liberty.hh" @@ -25,46 +25,83 @@ namespace sta { -ArcDelayCalc * -makeSimpleRCDelayCalc(StaState *sta) +// Liberty table model lumped capacitance arc delay calculator. +// Effective capacitance is the pi model total capacitance (C1+C2). +// Wire delays are elmore delays. +// Driver slews are degraded to loads by rise/fall transition_degradation +// tables. +class SlewDegradeDelayCalc : public RCDelayCalc { - return new SimpleRCDelayCalc(sta); +public: + SlewDegradeDelayCalc(StaState *sta); + virtual ArcDelayCalc *copy(); + virtual void inputPortDelay(const Pin *port_pin, + float in_slew, + const RiseFall *rf, + const Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap); + virtual void gateDelay(const LibertyCell *drvr_cell, + const TimingArc *arc, + const Slew &in_slew, + float load_cap, + const Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew); + virtual void loadDelay(const Pin *load_pin, + ArcDelay &wire_delay, + Slew &load_slew); + + using RCDelayCalc::gateDelay; + using RCDelayCalc::reportGateDelay; + +private: + const Pvt *pvt_; +}; + +ArcDelayCalc * +makeSlewDegradeDelayCalc(StaState *sta) +{ + return new SlewDegradeDelayCalc(sta); } -SimpleRCDelayCalc::SimpleRCDelayCalc(StaState *sta) : +SlewDegradeDelayCalc::SlewDegradeDelayCalc(StaState *sta) : RCDelayCalc(sta) { } ArcDelayCalc * -SimpleRCDelayCalc::copy() +SlewDegradeDelayCalc::copy() { - return new SimpleRCDelayCalc(this); + return new SlewDegradeDelayCalc(this); } void -SimpleRCDelayCalc::inputPortDelay(const Pin *port_pin, - float in_slew, - const RiseFall *rf, - const Parasitic *parasitic, - const DcalcAnalysisPt *dcalc_ap) +SlewDegradeDelayCalc::inputPortDelay(const Pin *port_pin, + float in_slew, + const RiseFall *rf, + const Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap) { pvt_ = dcalc_ap->operatingConditions(); RCDelayCalc::inputPortDelay(port_pin, in_slew, rf, parasitic, dcalc_ap); } void -SimpleRCDelayCalc::gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *drvr_parasitic, - float related_out_cap, - const Pvt *pvt, - const DcalcAnalysisPt *dcalc_ap, - // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew) +SlewDegradeDelayCalc::gateDelay(const LibertyCell *drvr_cell, + const TimingArc *arc, + const Slew &in_slew, + float load_cap, + const Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) { input_port_ = false; drvr_parasitic_ = drvr_parasitic; @@ -79,9 +116,9 @@ SimpleRCDelayCalc::gateDelay(const LibertyCell *drvr_cell, } void -SimpleRCDelayCalc::loadDelay(const Pin *load_pin, - ArcDelay &wire_delay, - Slew &load_slew) +SlewDegradeDelayCalc::loadDelay(const Pin *load_pin, + ArcDelay &wire_delay, + Slew &load_slew) { ArcDelay wire_delay1 = 0.0; Slew load_slew1 = drvr_slew_; diff --git a/search/WriteSpice.hh b/dcalc/SlewDegradeDelayCalc.hh similarity index 62% rename from search/WriteSpice.hh rename to dcalc/SlewDegradeDelayCalc.hh index c6d7465c..97b39c5f 100644 --- a/search/WriteSpice.hh +++ b/dcalc/SlewDegradeDelayCalc.hh @@ -16,21 +16,11 @@ #pragma once +#include "RCDelayCalc.hh" + namespace sta { -// Write a spice deck for path. -// Throws FileNotReadable, FileNotWritable, SubcktEndsMissing -void -writeSpice(Path *path, - // Spice file written for path. - const char *spice_filename, - // Subckts used by path included in spice file. - const char *subckts_filename, - // File of all cell spice subckt definitions. - const char *lib_subckts_filename, - // Device model file included in spice file. - const char *models_filename, - StaState *sta); +ArcDelayCalc * +makeSlewDegradeDelayCalc(StaState *sta); } // namespace -#endif diff --git a/include/sta/ArcDelayCalc.hh b/include/sta/ArcDelayCalc.hh index 7eb117ef..420f5951 100644 --- a/include/sta/ArcDelayCalc.hh +++ b/include/sta/ArcDelayCalc.hh @@ -36,10 +36,11 @@ class DcalcAnalysisPt; // UnitDelayCalc // LumpedCapDelayCalc // RCDelayCalc -// SimpleRCDelayCalc +// SlewDegradeDelayCalc // DmpCeffDelayCalc // DmpCeffElmoreDelayCalc // DmpCeffTwoPoleDelayCalc +// ArnoldiDelayCalc // Abstract class to interface to a delay calculator primitive. class ArcDelayCalc : public StaState diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index ca75d838..a713bfe4 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -462,6 +462,7 @@ public: const char *variableString() const; const Unit *unit(const Units *units); size_t size() const { return values_->size(); } + bool inBounds(float value) const; float axisValue(size_t index) const { return (*values_)[index]; } // Find the index for value such that axis[index] <= value < axis[index+1]. size_t findAxisIndex(float value) const; @@ -505,16 +506,25 @@ public: Table1 *ref_times); ~OutputWaveforms(); const RiseFall *rf() const { return rf_; } + bool inBounds(float in_slew, + float load_cap) const; Table1 voltageWaveform(float in_slew, float load_cap); - Table1 currentWaveform(float slew, - float cap); + float voltageTime(float in_slew, + float load_cap, + float voltage); + const Table1 *currentWaveform(float slew, + float cap); float referenceTime(float slew); + void setVdd(float vdd); static bool checkAxes(TableTemplate *tbl_template); private: - Table1 *voltageWaveform(size_t wave_index, - float cap); + float voltageTime1(float voltage, + size_t wave_index, + float cap); + FloatSeq *voltageTimes(size_t wave_index, + float cap); // Row. TableAxisPtr slew_axis_; @@ -522,8 +532,10 @@ private: TableAxisPtr cap_axis_; const RiseFall *rf_; Table1Seq current_waveforms_; - Table1Seq voltage_waveforms_; + FloatTable voltage_times_; Table1 *ref_times_; + float vdd_; + static constexpr size_t voltage_waveform_step_count_ = 20; }; class DriverWaveform diff --git a/include/sta/Units.hh b/include/sta/Units.hh index b3fd6a19..d8b1b509 100644 --- a/include/sta/Units.hh +++ b/include/sta/Units.hh @@ -33,8 +33,10 @@ public: void operator=(const Unit &unit); float scale() const { return scale_; } void setScale(float scale); - const char *scaleAbreviation() const; + const char *scaleAbbreviation() const; const char *suffix() const { return suffix_; } + // scale abbreviation + suffix + const char *scaledSuffix() const { return scaled_suffix_; } void setSuffix(const char *suffix); int digits() const { return digits_; } void setDigits(int digits); @@ -46,8 +48,11 @@ public: int digits) const; private: + void setScaledSuffix(); + float scale_; // multiplier from user units to internal units const char *suffix_; // print suffix + const char *scaled_suffix_; int digits_; // print digits (after decimal pt) }; @@ -67,8 +72,6 @@ public: const Unit *voltageUnit() const { return &voltage_unit_; } Unit *resistanceUnit() { return &resistance_unit_; } const Unit *resistanceUnit() const { return &resistance_unit_; } - Unit *pullingResistanceUnit() { return &pulling_resistance_unit_; } - const Unit *pullingResistanceUnit() const {return &pulling_resistance_unit_;} Unit *currentUnit() { return ¤t_unit_; } const Unit *currentUnit() const { return ¤t_unit_; } Unit *powerUnit() { return &power_unit_; } @@ -80,10 +83,9 @@ public: private: Unit time_unit_; + Unit resistance_unit_; Unit capacitance_unit_; Unit voltage_unit_; - Unit resistance_unit_; - Unit pulling_resistance_unit_; Unit current_unit_; Unit power_unit_; Unit distance_unit_; diff --git a/include/sta/WritePathSpice.hh b/include/sta/WritePathSpice.hh index 6ab489e5..a54c9c95 100644 --- a/include/sta/WritePathSpice.hh +++ b/include/sta/WritePathSpice.hh @@ -16,8 +16,16 @@ #pragma once +#include +#include + namespace sta { +using std::string; +using std::set; + +typedef set StdStringSet; + class Path; class StaState; @@ -33,7 +41,9 @@ writePathSpice(Path *path, const char *lib_subckt_filename, // Device model file included in spice file. const char *model_filename, - const char *power_name, + // Nets off of path to include in the spice run. + StdStringSet *off_path_pin_names, + const char *power_name, const char *gnd_name, StaState *sta); diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 5e811226..ad0947eb 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -134,7 +134,6 @@ LibertyReader::readLibertyFile(const char *filename, mode_value_ = nullptr; ocv_derate_ = nullptr; pg_port_ = nullptr; - have_resistance_unit_ = false; default_operating_condition_ = nullptr; receiver_model_ = nullptr; @@ -648,12 +647,6 @@ LibertyReader::endLibrary(LibertyGroup *group) void LibertyReader::endLibraryAttrs(LibertyGroup *group) { - // Default resistance_unit to pulling_resistance_unit. - if (!have_resistance_unit_) { - Units *units = library_->units(); - *units->resistanceUnit() = *units->pullingResistanceUnit(); - } - // These attributes reference named groups in the library so // wait until the end of the library to resolve them. if (default_wireload_) { @@ -727,16 +720,14 @@ LibertyReader::visitPullingResistanceUnit(LibertyAttr *attr) { if (library_) parseUnits(attr, "ohm", res_scale_, - library_->units()->pullingResistanceUnit()); + library_->units()->resistanceUnit()); } void LibertyReader::visitResistanceUnit(LibertyAttr *attr) { - if (library_) { + if (library_) parseUnits(attr, "ohm", res_scale_, library_->units()->resistanceUnit()); - have_resistance_unit_ = true; - } } void @@ -2522,7 +2513,7 @@ LibertyReader::endOutputCurrentRiseFall(LibertyGroup *group) 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; + size_t index = slew_index * slew_axis->size() + cap_index; current_waveforms[index] = waveform->stealCurrents(); (*ref_times)[slew_index] = waveform->referenceTime(); } diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index 4a5cf678..eadc990b 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -624,7 +624,6 @@ protected: float power_scale_; float energy_scale_; float distance_scale_; - bool have_resistance_unit_; const char *default_operating_condition_; ReceiverModelPtr receiver_model_; OutputWaveformSeq output_currents_; diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc index 7b3216e0..cf04cb5d 100644 --- a/liberty/LibertyWriter.cc +++ b/liberty/LibertyWriter.cc @@ -123,26 +123,21 @@ LibertyWriter::writeHeader() fprintf(stream_, " delay_model : table_lookup;\n"); fprintf(stream_, " simulation : false;\n"); const Unit *cap_unit = library_->units()->capacitanceUnit(); - fprintf(stream_, " capacitive_load_unit (1,%s%s);\n", - cap_unit->scaleAbreviation(), - cap_unit->suffix()); + fprintf(stream_, " capacitive_load_unit (1,%s);\n", + cap_unit->scaledSuffix()); fprintf(stream_, " leakage_power_unit : 1pW;\n"); const Unit *current_unit = library_->units()->currentUnit(); - fprintf(stream_, " current_unit : \"1%s%s\";\n", - current_unit->scaleAbreviation(), - current_unit->suffix()); + fprintf(stream_, " current_unit : \"1%s\";\n", + current_unit->scaledSuffix()); const Unit *res_unit = library_->units()->resistanceUnit(); - fprintf(stream_, " pulling_resistance_unit : \"1%s%s\";\n", - res_unit->scaleAbreviation(), - res_unit->suffix()); + fprintf(stream_, " pulling_resistance_unit : \"1%s\";\n", + res_unit->scaledSuffix()); const Unit *time_unit = library_->units()->timeUnit(); - fprintf(stream_, " time_unit : \"1%s%s\";\n", - time_unit->scaleAbreviation(), - time_unit->suffix()); + fprintf(stream_, " time_unit : \"1%s\";\n", + time_unit->scaledSuffix()); const Unit *volt_unit = library_->units()->voltageUnit(); - fprintf(stream_, " voltage_unit : \"1%s%s\";\n", - volt_unit->scaleAbreviation(), - volt_unit->suffix()); + fprintf(stream_, " voltage_unit : \"1%s\";\n", + volt_unit->scaledSuffix()); fprintf(stream_, " library_features(report_delay_calculation);\n"); fprintf(stream_, "\n"); diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 8d6dd3fe..48db282a 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -1437,6 +1437,15 @@ TableAxis::~TableAxis() delete values_; } +bool +TableAxis::inBounds(float value) const +{ + size_t size = values_->size(); + return size > 1 + && value >= (*values_)[0] + && value <= (*values_)[size - 1]; +} + // Bisection search. size_t TableAxis::findAxisIndex(float value) const @@ -1572,9 +1581,6 @@ tableVariableUnit(TableAxisVariable variable, return nullptr; } -//////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////// OutputWaveforms::OutputWaveforms(TableAxisPtr slew_axis, @@ -1586,15 +1592,16 @@ OutputWaveforms::OutputWaveforms(TableAxisPtr slew_axis, cap_axis_(cap_axis), rf_(rf), current_waveforms_(current_waveforms), - voltage_waveforms_(current_waveforms.size()), - ref_times_(ref_times) + voltage_times_(current_waveforms.size()), + ref_times_(ref_times), + vdd_(0.0) { } OutputWaveforms::~OutputWaveforms() { current_waveforms_.deleteContents(); - voltage_waveforms_.deleteContents(); + voltage_times_.deleteContents(); delete ref_times_; } @@ -1615,43 +1622,52 @@ OutputWaveforms::checkAxes(TableTemplate *tbl_template) && axis3->variable() == TableAxisVariable::time); } -Table1 -OutputWaveforms::voltageWaveform(float slew, +bool +OutputWaveforms::inBounds(float in_slew, + float load_cap) const +{ + return slew_axis_->inBounds(in_slew) + && cap_axis_->inBounds(load_cap); +} + +const 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); + size_t wave_index = slew_index * cap_axis_->size() + cap_index; + return current_waveforms_[wave_index]; +} + +float +OutputWaveforms::referenceTime(float slew) +{ + return ref_times_->findValue(slew); +} + +void +OutputWaveforms::setVdd(float vdd) +{ + vdd_ = vdd; +} + +float +OutputWaveforms::voltageTime(float slew, + float cap, + float volt) +{ + size_t slew_index = slew_axis_->findAxisIndex(slew); + size_t cap_index = cap_axis_->findAxisIndex(cap); + size_t slew_count = slew_axis_->size(); + size_t wave_index00 = slew_index * slew_count + cap_index; + size_t wave_index01 = slew_index * slew_count + (cap_index + 1); + size_t wave_index10 = (slew_index + 1) * slew_count + cap_index; + size_t wave_index11 = (slew_index + 1) * slew_count + (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. + // Interpolate waveform samples at voltage steps. size_t index1 = slew_index; size_t index2 = cap_index; float x1 = slew; @@ -1662,49 +1678,65 @@ OutputWaveforms::voltageWaveform(float slew, float x2l = cap_axis_->axisValue(index2); float x2u = cap_axis_->axisValue(index2 + 1); float dx2 = (x2 - x2l) / (x2u - x2l); - FloatSeq *values = new FloatSeq; - float prev_value = 0.0; - constexpr float value_tol = .0001; - for (size_t i = 0; i <= time_step_count; i++) { - float time = time_min + time_step * i; - if (time > time_max) - break; - float y00 = values00->findValueClip(time); - float y10 = values10->findValueClip(time); - float y11 = values11->findValueClip(time); - float y01 = values01->findValueClip(time); - float value - = (1 - dx1) * (1 - dx2) * y00 + + float y00 = voltageTime1(volt, wave_index00, cap0); + float y01 = voltageTime1(volt, wave_index01, cap1); + float y10 = voltageTime1(volt, wave_index10, cap0); + float y11 = voltageTime1(volt, wave_index11, cap1); + float time + = (1 - dx1) * (1 - dx2) * y00 + dx1 * (1 - dx2) * y10 + dx1 * dx2 * y11 + (1 - dx1) * dx2 * y01; - if (i == 0 || abs(value - prev_value) > value_tol) { - time_values->push_back(time); - values->push_back(value); - } - prev_value = value; - } - return Table1(values, time_axis); + return time; } -Table1 * -OutputWaveforms::voltageWaveform(size_t wave_index, +Table1 +OutputWaveforms::voltageWaveform(float slew, 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; + float volt_step = vdd_ / voltage_waveform_step_count_; + FloatSeq *times = new FloatSeq; + FloatSeq *volts = new FloatSeq; + for (size_t v = 0; v <= voltage_waveform_step_count_; v++) { + float volt = v * volt_step; + float time = voltageTime(slew, cap, volt); + times->push_back(time); + volts->push_back(volt); + } + TableAxisPtr time_axis = make_shared(TableAxisVariable::time, times); + return Table1(volts, time_axis); +} - // i = C dv/dt +float +OutputWaveforms::voltageTime1(float voltage, + size_t wave_index, + float cap) +{ + FloatSeq *voltage_times = voltageTimes(wave_index, cap); + float volt_step = vdd_ / voltage_waveform_step_count_; + size_t volt_idx = voltage / volt_step; + float time0 = (*voltage_times)[volt_idx]; + float time1 = (*voltage_times)[volt_idx + 1]; + float time = time0 + (time1 - time0) * (voltage - volt_step * volt_idx); + return time; +} + +FloatSeq * +OutputWaveforms::voltageTimes(size_t wave_index, + float cap) +{ + FloatSeq *voltage_times = voltage_times_[wave_index]; + if (voltage_times == nullptr) { // Integrate current waveform to find voltage waveform. + // i = C dv/dt + FloatSeq volts; + Table1 *currents = current_waveforms_[wave_index]; 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); + volts.push_back(voltage); bool always_rise = true; bool invert = (always_rise && rf_ == RiseFall::fall()); for (size_t i = 1; i < time_axis->size(); i++) { @@ -1712,92 +1744,39 @@ OutputWaveforms::voltageWaveform(size_t wave_index, float current = currents->value(i); float dv = (current + prev_current) / 2.0 * (time - prev_time) / cap; voltage += invert ? -dv : dv; - voltages1->push_back(voltage); + volts.push_back(voltage); prev_time = time; prev_current = current; } - if (!always_rise && rf_ == RiseFall::fall()) { - for (size_t i = 0; i < voltages1->size(); i++) - (*voltages1)[i] -= voltage; + + // Sample the voltage waveform at uniform intervals to speed up + // voltage time lookup. + voltage_times = new FloatSeq; + float volt_step = vdd_ / voltage_waveform_step_count_; + size_t i = 0; + float time0 = time_axis->axisValue(i); + float volt0 = volts[i]; + i = 1; + float time1 = time_axis->axisValue(i); + float volt1 = volts[i]; + for (size_t v = 0; v <= voltage_waveform_step_count_; v++) { + float volt3 = v * volt_step; + while (volt3 > volt1 && i < volts.size() - 1) { + time0 = time1; + volt0 = volt1; + i++; + time1 = time_axis->axisValue(i); + volt1 = volts[i]; + } + float time3 = time0 + (time1 - time0) * (volt3 - volt0) / (volt1 - volt0); + if (time3 < 0.0) + printf("luse\n"); + //printf("%.2f %.2e\n", volt3, time3); + voltage_times->push_back(time3); } + voltage_times_[wave_index] = voltage_times; } - 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; - float prev_value = 0.0; - constexpr float value_tol = 1e-6; - for (size_t i = 0; i <= time_step_count; i++) { - float time = time_min + time_step * i; - if (time > time_max) - break; - float y00 = values00->findValueClip(time); - float y10 = values10->findValueClip(time); - float y11 = values11->findValueClip(time); - float y01 = values01->findValueClip(time); - float value - = (1 - dx1) * (1 - dx2) * y00 - + dx1 * (1 - dx2) * y10 - + dx1 * dx2 * y11 - + (1 - dx1) * dx2 * y01; - if (i == 0 || abs(value - prev_value) > value_tol) { - time_values->push_back(time); - values->push_back(value); - } - prev_value = value; - } - return Table1(values, time_axis); -} - -float -OutputWaveforms::referenceTime(float slew) -{ - return ref_times_->findValue(slew); + return voltage_times; } //////////////////////////////////////////////////////////////// diff --git a/liberty/Units.cc b/liberty/Units.cc index 3f2825e3..ecd17846 100644 --- a/liberty/Units.cc +++ b/liberty/Units.cc @@ -31,8 +31,10 @@ using std::abs; Unit::Unit(const char *suffix) : scale_(1.0), suffix_(stringCopy(suffix)), + scaled_suffix_(nullptr), digits_(3) { + setScaledSuffix(); } Unit::Unit(float scale, @@ -40,13 +42,23 @@ Unit::Unit(float scale, int digits) : scale_(scale), suffix_(stringCopy(suffix)), + scaled_suffix_(nullptr), digits_(digits) { + setScaledSuffix(); +} + +void +Unit::setScaledSuffix() +{ + stringDelete(scaled_suffix_); + scaled_suffix_ = stringPrint("%s%s", scaleAbbreviation(), suffix_); } Unit::~Unit() { stringDelete(suffix_); + stringDelete(scaled_suffix_); } void @@ -55,6 +67,8 @@ Unit::operator=(const Unit &unit) scale_ = unit.scale_; stringDelete(suffix_); suffix_ = stringCopy(unit.suffix_); + stringDelete(scaled_suffix_); + scaled_suffix_ = stringCopy(unit.scaled_suffix_); digits_ = unit.digits_; } @@ -74,10 +88,11 @@ void Unit::setScale(float scale) { scale_ = scale; + setScaledSuffix(); } const char * -Unit::scaleAbreviation() const +Unit::scaleAbbreviation() const { if (fuzzyEqual(scale_, 1E+6)) return "M"; @@ -104,6 +119,7 @@ Unit::setSuffix(const char *suffix) { stringDelete(suffix_); suffix_ = stringCopy(suffix); + setScaledSuffix(); } void @@ -150,10 +166,9 @@ Unit::asString(float value, Units::Units() : time_unit_("s"), + resistance_unit_("ohm"), capacitance_unit_("F"), voltage_unit_("v"), - resistance_unit_("ohm"), - pulling_resistance_unit_("ohm"), current_unit_("A"), power_unit_("W"), distance_unit_("m"), @@ -166,10 +181,10 @@ Units::find(const char *unit_name) { if (stringEq(unit_name, "time")) return &time_unit_; - else if (stringEq(unit_name, "capacitance")) - return &capacitance_unit_; else if (stringEq(unit_name, "resistance")) return &resistance_unit_; + else if (stringEq(unit_name, "capacitance")) + return &capacitance_unit_; else if (stringEq(unit_name, "voltage")) return &voltage_unit_; else if (stringEq(unit_name, "current")) @@ -186,8 +201,8 @@ void Units::operator=(const Units &units) { time_unit_ = *units.timeUnit(); - capacitance_unit_ = *units.capacitanceUnit(); resistance_unit_ = *units.resistanceUnit(); + capacitance_unit_ = *units.capacitanceUnit(); voltage_unit_ = *units.voltageUnit(); current_unit_ = *units.currentUnit(); power_unit_ = *units.powerUnit(); diff --git a/power/Vcd.hh b/power/Vcd.hh index 54e64cc2..2ebe6ee1 100644 --- a/power/Vcd.hh +++ b/power/Vcd.hh @@ -38,7 +38,24 @@ typedef int64_t VcdTime; typedef vector VcdScope; typedef map VcdNameMap; -enum class VcdVarType { wire, reg, parameter, real }; +enum class VcdVarType { + wire, + reg, + parameter, + integer, + real, + supply0, + supply1, + tri, + triand, + trior, + trireg, + tri0, + tri1, + wand, + wor, + unknown +}; class Vcd : public StaState { diff --git a/power/VcdReader.cc b/power/VcdReader.cc index 0e4e20b6..cd7e5e77 100644 --- a/power/VcdReader.cc +++ b/power/VcdReader.cc @@ -23,6 +23,7 @@ #include "Report.hh" #include "Error.hh" #include "StringUtil.hh" +#include "EnumNameMap.hh" namespace sta { @@ -160,6 +161,24 @@ VcdReader::setTimeUnit(const string &time_unit) vcd_->setTimeUnit(time_unit, time_unit_scale);; } +static EnumNameMap vcd_var_type_map = + {{VcdVarType::wire, "wire"}, + {VcdVarType::reg, "reg"}, + {VcdVarType::parameter, "parameter"}, + {VcdVarType::integer, "integer"}, + {VcdVarType::real, "real"}, + {VcdVarType::supply0, "supply0"}, + {VcdVarType::supply1, "supply1"}, + {VcdVarType::tri, "tri"}, + {VcdVarType::triand, "triand"}, + {VcdVarType::trior, "trior"}, + {VcdVarType::trireg, "trireg"}, + {VcdVarType::tri0, "tri0"}, + {VcdVarType::tri1, "tri1"}, + {VcdVarType::wand, "wand"}, + {VcdVarType::wor, "wor"} + }; + void VcdReader::parseVar() { @@ -167,34 +186,27 @@ VcdReader::parseVar() if (tokens.size() == 4 || tokens.size() == 5) { string type_name = tokens[0]; - VcdVarType type = VcdVarType::wire; - if (type_name == "wire") - type = VcdVarType::wire; - else if (type_name == "reg") - type = VcdVarType::reg; - else if (type_name == "parameter") - type = VcdVarType::parameter; - else if (type_name == "real") - type = VcdVarType::real; - else - report_->fileError(803, filename_, stmt_line_, - "Unknown variable type %s.", - type_name.c_str()); + VcdVarType type = vcd_var_type_map.find(type_name, VcdVarType::unknown); + if (type == VcdVarType::unknown) + report_->fileWarn(803, filename_, stmt_line_, + "Unknown variable type %s.", + type_name.c_str()); + else { + int width = stoi(tokens[1]); + string id = tokens[2]; + string name; - int width = stoi(tokens[1]); - string id = tokens[2]; - string name; + for (string &context : scope_) { + name += context; + name += '/'; + } + name += tokens[3]; + // iverilog separates bus base name from bit range. + if (tokens.size() == 5) + name += tokens[4]; - for (string &context : scope_) { - name += context; - name += '/'; + vcd_->makeVar(name, type, width, id); } - name += tokens[3]; - // iverilog separates bus base name from bit range. - if (tokens.size() == 5) - name += tokens[4]; - - vcd_->makeVar(name, type, width, id); } else report_->fileError(804, filename_, stmt_line_, "Variable syntax error."); diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 2cf82c33..b0c512c8 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -5697,6 +5697,8 @@ Sdc::disconnectPinBefore(const Pin *pin) } } } + for (int corner_index = 0; corner_index < corners_->count(); corner_index++) + drvr_pin_wire_cap_maps_[corner_index].erase(pin); } void diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index ecf8d0ea..fa168456 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -133,7 +133,6 @@ MakeTimingModel::makeLibrary() *library_->units()->capacitanceUnit() = *default_lib->units()->capacitanceUnit(); *library_->units()->voltageUnit() = *default_lib->units()->voltageUnit(); *library_->units()->resistanceUnit() = *default_lib->units()->resistanceUnit(); - *library_->units()->pullingResistanceUnit() = *default_lib->units()->pullingResistanceUnit(); *library_->units()->powerUnit() = *default_lib->units()->powerUnit(); *library_->units()->distanceUnit() = *default_lib->units()->distanceUnit(); diff --git a/search/WritePathSpice.cc b/search/WritePathSpice.cc index 99ec69df..dc95c363 100644 --- a/search/WritePathSpice.cc +++ b/search/WritePathSpice.cc @@ -45,11 +45,9 @@ namespace sta { -using std::string; using std::ofstream; using std::ifstream; using std::max; -using std::set; typedef Map CellSpicePortNames; typedef int Stage; @@ -71,6 +69,7 @@ public: const char *subckt_filename, const char *lib_subckt_filename, const char *model_filename, + StdStringSet *off_path_pin_names, const char *power_name, const char *gnd_name, const StaState *sta); @@ -99,12 +98,19 @@ private: const Clock *clk, DcalcAPIndex dcalc_ap_index); void writeStageParasitics(Stage stage); + void writeStageParasiticNetwork(Pin *drvr_pin, + Parasitic *parasitic, + ParasiticAnalysisPt *parasitic_ap); + void writeStagePiElmore(Pin *drvr_pin, + Parasitic *parasitic); + void writeNullParasitics(Pin *drvr_pin); void writeSubckts(); - set findPathCellnames(); - void findPathCellSubckts(set &path_cell_names); + StdStringSet findPathCellnames(); + void findPathCellSubckts(StdStringSet &path_cell_names); void recordSpicePortNames(const char *cell_name, StringVector &tokens); float maxTime(); + float pathMaxTime(); const char *nodeName(ParasiticNode *node); void initNodeMap(const char *net_name); const char *spiceTrans(const RiseFall *rf); @@ -209,12 +215,14 @@ private: const char *stageLoadPinName(Stage stage); LibertyCell *stageLibertyCell(Stage stage); Instance *stageInstance(Stage stage); + StdStringSet stageOffPathPinNames(Stage stage); Path *path_; const char *spice_filename_; const char *subckt_filename_; const char *lib_subckt_filename_; const char *model_filename_; + StdStringSet *off_path_pin_names_; const char *power_name_; const char *gnd_name_; @@ -270,13 +278,14 @@ writePathSpice(Path *path, const char *subckt_filename, const char *lib_subckt_filename, const char *model_filename, - const char *power_name, + StdStringSet *off_path_pin_names, + const char *power_name, const char *gnd_name, StaState *sta) { WritePathSpice writer(path, spice_filename, subckt_filename, lib_subckt_filename, model_filename, - power_name, gnd_name, sta); + off_path_pin_names, power_name, gnd_name, sta); writer.writeSpice(); } @@ -285,6 +294,7 @@ WritePathSpice::WritePathSpice(Path *path, const char *subckt_filename, const char *lib_subckt_filename, const char *model_filename, + StdStringSet *off_path_pin_names, const char *power_name, const char *gnd_name, const StaState *sta) : @@ -294,6 +304,7 @@ WritePathSpice::WritePathSpice(Path *path, subckt_filename_(subckt_filename), lib_subckt_filename_(lib_subckt_filename), model_filename_(model_filename), + off_path_pin_names_(off_path_pin_names), power_name_(power_name), gnd_name_(gnd_name), path_expanded_(sta), @@ -375,7 +386,7 @@ WritePathSpice::writeHeader() streamPrint(spice_stream_, ".include \"%s\"\n", subckt_filename_stem.c_str()); float max_time = maxTime(); - float time_step = max_time / 1e+3; + float time_step = 1e-13; streamPrint(spice_stream_, ".tran %.3g %.3g\n\n", time_step, max_time); streamPrint(spice_stream_, ".options nomod\n"); @@ -388,6 +399,9 @@ WritePathSpice::writePrintStmt() for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { streamPrint(spice_stream_, " v(%s)", stageDrvrPinName(stage)); streamPrint(spice_stream_, " v(%s)", stageLoadPinName(stage)); + StdStringSet off_path_names = stageOffPathPinNames(stage); + for (const string &off_path_name : off_path_names) + streamPrint(spice_stream_, " v(%s)", off_path_name.c_str()); } streamPrint(spice_stream_, "\n\n"); } @@ -397,9 +411,6 @@ WritePathSpice::maxTime() { Stage input_stage = stageFirst(); PathRef *input_path = stageDrvrPath(input_stage); - const RiseFall *rf = input_path->transition(this); - TimingArc *next_arc = stageGateArc(input_stage + 1); - float input_slew = findSlew(input_path, rf, next_arc); if (input_path->isClock(this)) { const Clock *clk = input_path->clock(this); float period = clk->period(); @@ -407,13 +418,36 @@ WritePathSpice::maxTime() float max_time = period * clk_cycle_count_ + first_edge_offset; return max_time; } - else { - float end_slew = findSlew(path_); - float arrival = delayAsFloat(path_->arrival(this)); - float max_time = railToRailSlew(input_slew, rf) + arrival - + railToRailSlew(end_slew, rf); - return max_time; + else + return pathMaxTime(); +} + +// Make sure run time is long enough to see side load transitions along the path. +float +WritePathSpice::pathMaxTime() +{ + float max_time = 0.0; + DcalcAPIndex dcalc_ap_index = path_->dcalcAnalysisPt(this)->index(); + for (size_t i = 0; i < path_expanded_.size(); i++) { + PathRef *path = path_expanded_.path(i); + const RiseFall *rf = path->transition(this); + Vertex *vertex = path->vertex(this); + float path_max_slew = railToRailSlew(findSlew(vertex,rf,nullptr,dcalc_ap_index),rf); + if (vertex->isDriver(network_)) { + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *load = edge->to(graph_); + float load_slew = railToRailSlew(findSlew(load, rf, nullptr, dcalc_ap_index),rf); + if (load_slew > path_max_slew) + path_max_slew = load_slew; + } + } + float path_max_time = delayAsFloat(path->arrival(this)) + path_max_slew * 2.0; + if (path_max_time > max_time) + max_time = path_max_time; } + return max_time; } float @@ -441,13 +475,17 @@ WritePathSpice::writeStageInstances() stageDrvrPinName(stage), stageLoadPinName(stage), stage_cname); - else - streamPrint(spice_stream_, "x%s %s %s %s %s\n", + else { + streamPrint(spice_stream_, "x%s %s %s %s", stage_cname, stageGateInputPinName(stage), stageDrvrPinName(stage), - stageLoadPinName(stage), - stage_cname); + stageLoadPinName(stage)); + StdStringSet off_path_names = stageOffPathPinNames(stage); + for (const string &off_path_name : off_path_names) + streamPrint(spice_stream_, " %s", off_path_name.c_str()); + streamPrint(spice_stream_, " %s\n", stage_cname); + } } streamPrint(spice_stream_, "\n"); } @@ -855,11 +893,16 @@ WritePathSpice::writeGateStage(Stage stage) const char *drvr_pin_name = stageDrvrPinName(stage); const Pin *load_pin = stageLoadPin(stage); const char *load_pin_name = stageLoadPinName(stage); - streamPrint(spice_stream_, ".subckt stage%d %s %s %s\n", + streamPrint(spice_stream_, ".subckt stage%d %s %s %s", stage, input_pin_name, drvr_pin_name, load_pin_name); + StdStringSet off_path_names = stageOffPathPinNames(stage); + for (const string &off_path_name : off_path_names) + streamPrint(spice_stream_, " %s", off_path_name.c_str()); + streamPrint(spice_stream_, "\n"); + // Driver subckt call. Instance *inst = stageInstance(stage); LibertyPort *input_port = stageGateInputPort(stage); @@ -1300,60 +1343,77 @@ WritePathSpice::writeStageParasitics(Stage stage) { PathRef *drvr_path = stageDrvrPath(stage); Pin *drvr_pin = stageDrvrPin(stage); + + Net *net = network_->net(drvr_pin); + const char *net_name = net ? network_->pathName(net) : network_->pathName(drvr_pin); + initNodeMap(net_name); + streamPrint(spice_stream_, "* Net %s\n", net_name); + DcalcAnalysisPt *dcalc_ap = drvr_path->dcalcAnalysisPt(this); ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + if (parasitic) + writeStageParasiticNetwork(drvr_pin, parasitic, parasitic_ap); + else { + const RiseFall *drvr_rf = drvr_path->transition(this); + parasitic = parasitics_->findPiElmore(drvr_pin, drvr_rf, parasitic_ap); + if (parasitic) + writeStagePiElmore(drvr_pin, parasitic); + else { + streamPrint(spice_stream_, "* No parasitics found for this net.\n"); + writeNullParasitics(drvr_pin); + } + } +} + +void +WritePathSpice::writeStageParasiticNetwork(Pin *drvr_pin, + Parasitic *parasitic, + ParasiticAnalysisPt *parasitic_ap) +{ Set reachable_pins; int res_index = 1; int cap_index = 1; - if (parasitic) { - Net *net = network_->net(drvr_pin); - const char *net_name = net ? network_->pathName(net) : network_->pathName(drvr_pin); - initNodeMap(net_name); - streamPrint(spice_stream_, "* Net %s\n", net_name); - // Sort devices for consistent regression results. - Vector devices; - ParasiticDeviceIterator *device_iter1 = parasitics_->deviceIterator(parasitic); - while (device_iter1->hasNext()) { - ParasiticDevice *device = device_iter1->next(); - devices.push_back(device); + // Sort devices for consistent regression results. + Vector devices; + ParasiticDeviceIterator *device_iter1 = parasitics_->deviceIterator(parasitic); + while (device_iter1->hasNext()) { + ParasiticDevice *device = device_iter1->next(); + devices.push_back(device); + } + delete device_iter1; + + sort(devices, ParasiticDeviceLess(parasitics_)); + + for (ParasiticDevice *device : devices) { + float resistance = parasitics_->value(device, parasitic_ap); + if (parasitics_->isResistor(device)) { + ParasiticNode *node1 = parasitics_->node1(device); + ParasiticNode *node2 = parasitics_->node2(device); + streamPrint(spice_stream_, "R%d %s %s %.3e\n", + res_index, + nodeName(node1), + nodeName(node2), + resistance); + res_index++; + + const Pin *pin1 = parasitics_->connectionPin(node1); + reachable_pins.insert(pin1); + const Pin *pin2 = parasitics_->connectionPin(node2); + reachable_pins.insert(pin2); } - delete device_iter1; - - sort(devices, ParasiticDeviceLess(parasitics_)); - - for (ParasiticDevice *device : devices) { - float resistance = parasitics_->value(device, parasitic_ap); - if (parasitics_->isResistor(device)) { - ParasiticNode *node1 = parasitics_->node1(device); - ParasiticNode *node2 = parasitics_->node2(device); - streamPrint(spice_stream_, "R%d %s %s %.3e\n", - res_index, - nodeName(node1), - nodeName(node2), - resistance); - res_index++; - - const Pin *pin1 = parasitics_->connectionPin(node1); - reachable_pins.insert(pin1); - const Pin *pin2 = parasitics_->connectionPin(node2); - reachable_pins.insert(pin2); - } - else if (parasitics_->isCouplingCap(device)) { - // Ground coupling caps for now. - ParasiticNode *node1 = parasitics_->node1(device); - float cap = parasitics_->value(device, parasitic_ap); - streamPrint(spice_stream_, "C%d %s 0 %.3e\n", - cap_index, - nodeName(node1), - cap); - cap_index++; - } + else if (parasitics_->isCouplingCap(device)) { + // Ground coupling caps for now. + ParasiticNode *node1 = parasitics_->node1(device); + float cap = parasitics_->value(device, parasitic_ap); + streamPrint(spice_stream_, "C%d %s 0 %.3e\n", + cap_index, + nodeName(node1), + cap); + cap_index++; } } - else - streamPrint(spice_stream_, "* No parasitics found for this net.\n"); // Add resistors from drvr to load for missing parasitic connections. auto pin_iter = network_->connectedPinIterator(drvr_pin); @@ -1373,30 +1433,107 @@ WritePathSpice::writeStageParasitics(Stage stage) } delete pin_iter; - if (parasitic) { - // Sort node capacitors for consistent regression results. - Vector nodes; - ParasiticNodeIterator *node_iter = parasitics_->nodeIterator(parasitic); - while (node_iter->hasNext()) { - ParasiticNode *node = node_iter->next(); - nodes.push_back(node); - } - - sort(nodes, ParasiticNodeLess(parasitics_)); - - for (ParasiticNode *node : nodes) { - float cap = parasitics_->nodeGndCap(node, parasitic_ap); - // Spice has a cow over zero value caps. - if (cap > 0.0) { - streamPrint(spice_stream_, "C%d %s 0 %.3e\n", - cap_index, - nodeName(node), - cap); - cap_index++; - } - } - delete node_iter; + // Sort node capacitors for consistent regression results. + Vector nodes; + ParasiticNodeIterator *node_iter = parasitics_->nodeIterator(parasitic); + while (node_iter->hasNext()) { + ParasiticNode *node = node_iter->next(); + nodes.push_back(node); } + + sort(nodes, ParasiticNodeLess(parasitics_)); + + for (ParasiticNode *node : nodes) { + float cap = parasitics_->nodeGndCap(node, parasitic_ap); + // Spice has a cow over zero value caps. + if (cap > 0.0) { + streamPrint(spice_stream_, "C%d %s 0 %.3e\n", + cap_index, + nodeName(node), + cap); + cap_index++; + } + } + delete node_iter; +} + +void +WritePathSpice::writeStagePiElmore(Pin *drvr_pin, + Parasitic *parasitic) +{ + float c2, rpi, c1; + parasitics_->piModel(parasitic, c2, rpi, c1); + const char *c1_node = "n1"; + streamPrint(spice_stream_, "RPI %s %s %.3e\n", + network_->pathName(drvr_pin), + c1_node, + rpi); + if (c2 > 0.0) + streamPrint(spice_stream_, "C2 %s 0 %.3e\n", + network_->pathName(drvr_pin), + c2); + if (c1 > 0.0) + streamPrint(spice_stream_, "C1 %s 0 %.3e\n", + c1_node, + c1); + + int load_index = 3; + auto pin_iter = network_->connectedPinIterator(drvr_pin); + while (pin_iter->hasNext()) { + const Pin *load_pin = pin_iter->next(); + if (load_pin != drvr_pin + && network_->isLoad(load_pin) + && !network_->isHierarchical(load_pin)) { + float elmore; + bool exists; + parasitics_->findElmore(parasitic, load_pin, elmore, exists); + if (exists) { + streamPrint(spice_stream_, "E%d el%d 0 %s 0 1.0\n", + load_index, + load_index, + network_->pathName(drvr_pin)); + streamPrint(spice_stream_, "R%d el%d %s 1.0\n", + load_index, + load_index, + network_->pathName(load_pin)); + streamPrint(spice_stream_, "C%d %s 0 %.3e\n", + load_index, + network_->pathName(load_pin), + elmore); + } + else + // Add resistor from drvr to load for missing elmore. + streamPrint(spice_stream_, "R%d %s %s %.3e\n", + load_index, + network_->pathName(drvr_pin), + network_->pathName(load_pin), + short_ckt_resistance_); + load_index++; + } + } + delete pin_iter; +} + +void +WritePathSpice::writeNullParasitics(Pin *drvr_pin) +{ + int res_index = 1; + // Add resistors from drvr to load for missing parasitic connections. + auto pin_iter = network_->connectedPinIterator(drvr_pin); + while (pin_iter->hasNext()) { + const Pin *load_pin = pin_iter->next(); + if (load_pin != drvr_pin + && network_->isLoad(load_pin) + && !network_->isHierarchical(load_pin)) { + streamPrint(spice_stream_, "R%d %s %s %.3e\n", + res_index, + network_->pathName(drvr_pin), + network_->pathName(load_pin), + short_ckt_resistance_); + res_index++; + } + } + delete pin_iter; } void @@ -1433,7 +1570,7 @@ WritePathSpice::nodeName(ParasiticNode *node) void WritePathSpice::writeSubckts() { - set path_cell_names = findPathCellnames(); + StdStringSet path_cell_names = findPathCellnames(); findPathCellSubckts(path_cell_names); ifstream lib_subckts_stream(lib_subckt_filename_); @@ -1489,10 +1626,10 @@ WritePathSpice::writeSubckts() throw FileNotReadable(lib_subckt_filename_); } -set +StdStringSet WritePathSpice::findPathCellnames() { - set path_cell_names; + StdStringSet path_cell_names; for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { TimingArc *arc = stageGateArc(stage); if (arc) { @@ -1520,7 +1657,7 @@ WritePathSpice::findPathCellnames() // Subckts can call subckts (asap7). void -WritePathSpice::findPathCellSubckts(set &path_cell_names) +WritePathSpice::findPathCellSubckts(StdStringSet &path_cell_names) { ifstream lib_subckts_stream(lib_subckt_filename_); if (lib_subckts_stream.is_open()) { @@ -1731,6 +1868,26 @@ WritePathSpice::stageLoadPinName(Stage stage) return network_->pathName(pin); } +StdStringSet +WritePathSpice::stageOffPathPinNames(Stage stage) +{ + StdStringSet pin_names; + if (off_path_pin_names_) { + const PathRef *path = stageDrvrPath(stage); + Vertex *drvr = path->vertex(this); + VertexOutEdgeIterator edge_iter(drvr, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *load = edge->to(graph_); + const Pin *load_pin = load->pin(); + string load_pin_name = network_->pathName(load_pin); + if (off_path_pin_names_->find(load_pin_name) != off_path_pin_names_->end()) + pin_names.insert(load_pin_name); + } + } + return pin_names; +} + Instance * WritePathSpice::stageInstance(Stage stage) { diff --git a/search/WriteSpice.cc b/search/WriteSpice.cc deleted file mode 100644 index 65bdb990..00000000 --- a/search/WriteSpice.cc +++ /dev/null @@ -1,927 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2023, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include -#include -#include -#include -#include "Machine.hh" -#include "Debug.hh" -#include "Error.hh" -#include "Report.hh" -#include "StringUtil.hh" -#include "FuncExpr.hh" -#include "Liberty.hh" -#include "TimingArc.hh" -#include "Network.hh" -#include "Graph.hh" -#include "Sdc.hh" -#include "DcalcAnalysisPt.hh" -#include "Parasitics.hh" -#include "PathAnalysisPt.hh" -#include "Path.hh" -#include "PathRef.hh" -#include "PathExpanded.hh" -#include "StaState.hh" -#include "WriteSpice.hh" - -namespace sta { - -using std::string; -using std::ofstream; -using std::ifstream; - -typedef Vector StringVector; -typedef Map CellSpicePortNames; -typedef int Stage; -typedef Map ParasiticNodeMap; - -void -split(const string &text, - const string &delims, - // Return values. - StringVector &tokens); -void -streamPrint(ofstream &stream, - const char *fmt, - ...) __attribute__((format (printf, 2, 3))); - -//////////////////////////////////////////////////////////////// - -class WriteSpice : public StaState -{ -public: - WriteSpice(Path *path, - const char *spice_filename, - const char *subckts_filename, - const char *lib_subckts_filename, - const char *models_filename, - const StaState *sta); - ~WriteSpice(); - void writeSpice();; - -private: - void writeHeader(); - void writeStageInstances(); - void writeInputSource(); - void writeStageSubckts(); - void writeInputStage(Stage stage); - void writeMeasureStmts(); - void writeGateStage(Stage stage); - void writeStageVoltageSources(LibertyCell *cell, - StringVector *spice_port_names, - const char *inst_name, - LibertyPort *from_port, - LibertyPort *drvr_port); - void writeStageParasitics(Stage stage); - void writeSubckts(); - void findPathCellnames(// Return values. - StringSet &path_cell_names); - void recordSpicePortNames(const char *cell_name, - StringVector &tokens); - float pgPortVoltage(const char *pg_port_name, - LibertyCell *cell); - float pgPortVoltage(LibertyPgPort *pg_port); - float maxTime(); - const char *nodeName(ParasiticNode *node); - void initNodeMap(const char *net_name); - - // Stage "accessors". - // Internally a stage index from stageFirst() to stageLast() - // is turned into an index into path_expanded_. - Stage stageFirst(); - Stage stageLast(); - string stageName(Stage stage); - int stageGateInputPathIndex(Stage stage); - int stageDrvrPathIndex(Stage stage); - int stageLoadPathIndex(Stage stage); - PathRef *stageGateInputPath(Stage stage); - PathRef *stageDrvrPath(Stage stage); - PathRef *stageLoadPath(Stage stage); - TimingArc *stageGateArc(Stage stage); - TimingArc *stageWireArc(Stage stage); - Edge *stageGateEdge(Stage stage); - Edge *stageWireEdge(Stage stage); - Pin *stageInputPin(Stage stage); - Pin *stageDrvrPin(Stage stage); - Pin *stageLoadPin(Stage stage); - const char *stageInputPinName(Stage stage); - const char *stageDrvrPinName(Stage stage); - const char *stageLoadPinName(Stage stage); - - Path *path_; - const char *spice_filename_; - const char *subckts_filename_; - const char *lib_subckts_filename_; - const char *models_filename_; - - ofstream spice_stream_; - PathExpanded path_expanded_; - CellSpicePortNames cell_spice_port_names_; - ParasiticNodeMap node_map_; - int next_node_index_; - const char *net_name_; - - // Resistance to use to simulate a short circuit between spice nodes. - static const float short_ckt_resistance_; -}; - -//////////////////////////////////////////////////////////////// - -class SubcktEndsMissing : public StaException -{ -public: - SubcktEndsMissing(const char *cell_name, - const char *subckt_filename);; - const char *what() const throw(); - -protected: - string what_; -}; - -SubcktEndsMissing::SubcktEndsMissing(const char *cell_name, - const char *subckt_filename) -{ - what_ = "Error: spice subckt for cell "; - what_ += cell_name; - what_ += " missing .ends in "; - what_ += subckt_filename; -} - -const char * -SubcktEndsMissing::what() const throw() -{ - return what_.c_str(); -} - -//////////////////////////////////////////////////////////////// - -void -writeSpice (Path *path, - const char *spice_filename, - const char *subckts_filename, - const char *lib_subckts_filename, - const char *models_filename, - StaState *sta) -{ - WriteSpice writer(path, spice_filename, subckts_filename, - lib_subckts_filename, models_filename, sta); - writer.writeSpice(); -} - -const float WriteSpice::short_ckt_resistance_ = .0001; - -WriteSpice::WriteSpice(Path *path, - const char *spice_filename, - const char *subckts_filename, - const char *lib_subckts_filename, - const char *models_filename, - const StaState *sta) : - StaState(sta), - path_(path), - spice_filename_(spice_filename), - subckts_filename_(subckts_filename), - lib_subckts_filename_(lib_subckts_filename), - models_filename_(models_filename), - path_expanded_(sta), - net_name_(NULL) -{ -} - -WriteSpice::~WriteSpice() -{ - cell_spice_port_names_.deleteContents(); -} - -void -WriteSpice::writeSpice() -{ - spice_stream_.open(spice_filename_); - if (spice_stream_.is_open()) { - path_expanded_.expand(path_, true); - // Find subckt port names as a side-effect of writeSubckts. - writeSubckts(); - writeHeader(); - writeStageInstances(); - writeInputSource(); - writeStageSubckts(); - streamPrint(spice_stream_, ".end\n"); - spice_stream_.close(); - } - else - throw FileNotWritable(spice_filename_); -} - -void -WriteSpice::writeHeader() -{ - const MinMax *min_max = path_->minMax(this); - const Pvt *pvt = sdc_->operatingConditions(min_max); - if (pvt == NULL) - pvt = network_->defaultLibertyLibrary()->defaultOperatingConditions(); - float temp = pvt->temperature(); - streamPrint(spice_stream_, ".temp %.1f\n", temp); - streamPrint(spice_stream_, ".include \"%s\"\n", models_filename_); - streamPrint(spice_stream_, ".include \"%s\"\n", subckts_filename_); - - float max_time = maxTime(); - float time_step = max_time / 1e+3; - streamPrint(spice_stream_, ".tran %.3g %.3g\n\n", - time_step, max_time); -} - -float -WriteSpice::maxTime() -{ - float end_slew = path_->slew(this); - float max_time = (path_->arrival(this) + end_slew * 2) * 1.5; - return max_time; -} - -void -WriteSpice::writeStageInstances() -{ - streamPrint(spice_stream_, "*****************\n"); - streamPrint(spice_stream_, "* Stage instances\n"); - streamPrint(spice_stream_, "*****************\n\n"); - - for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { - const char *stage_name = stageName(stage).c_str(); - if (stage == stageFirst()) - streamPrint(spice_stream_, "x%s %s %s %s\n", - stage_name, - stageDrvrPinName(stage), - stageLoadPinName(stage), - stage_name); - else - streamPrint(spice_stream_, "x%s %s %s %s %s\n", - stage_name, - stageInputPinName(stage), - stageDrvrPinName(stage), - stageLoadPinName(stage), - stage_name); - } - streamPrint(spice_stream_, "\n"); -} - -float -WriteSpice::pgPortVoltage(const char *pg_port_name, - LibertyCell *cell) -{ - auto pg_port = cell->findPgPort(pg_port_name); - return pgPortVoltage(pg_port); -} - -float -WriteSpice::pgPortVoltage(LibertyPgPort *pg_port) -{ - auto cell = pg_port->cell(); - auto voltage_name = pg_port->voltageName(); - auto lib = cell->libertyLibrary(); - float voltage = lib->supplyVoltage(voltage_name); - return voltage; -} - -void -WriteSpice::writeInputSource() -{ - streamPrint(spice_stream_, "**************\n"); - streamPrint(spice_stream_, "* Input source\n"); - streamPrint(spice_stream_, "**************\n\n"); - - Stage input_stage = stageFirst(); - streamPrint(spice_stream_, "v1 %s 0 pwl(\n", - stageDrvrPinName(input_stage)); - auto wire_arc = stageWireArc(input_stage); - auto load_pin = stageLoadPin(input_stage); - auto cell = network_->libertyCell(network_->instance(load_pin)); - auto load_port = network_->libertyPort(load_pin); - const char *pg_gnd_port_name = load_port->relatedGroundPin(); - const char *pg_pwr_port_name = load_port->relatedPowerPin(); - auto gnd_volt = pgPortVoltage(pg_gnd_port_name, cell); - auto pwr_volt = pgPortVoltage(pg_pwr_port_name, cell); - float volt0, volt1; - if (wire_arc->fromTrans()->asRiseFall() == TransRiseFall::rise()) { - volt0 = gnd_volt; - volt1 = pwr_volt; - } - else { - volt0 = pwr_volt; - volt1 = gnd_volt; - } - float time0 = .1e-9; - float time1 = .2e-9; - streamPrint(spice_stream_, "+%.3e %.3e\n", 0.0, volt0); - streamPrint(spice_stream_, "+%.3e %.3e\n", time0, volt0); - streamPrint(spice_stream_, "+%.3e %.3e\n", time1, volt1); - streamPrint(spice_stream_, "+%.3e %.3e\n", maxTime(), volt1); - streamPrint(spice_stream_, "+)\n\n"); -} - -void -WriteSpice::writeMeasureStmts() -{ - streamPrint(spice_stream_, "********************\n"); - streamPrint(spice_stream_, "* Measure statements\n"); - streamPrint(spice_stream_, "********************\n\n"); -} - -void -WriteSpice::writeStageSubckts() -{ - streamPrint(spice_stream_, "***************\n"); - streamPrint(spice_stream_, "* Stage subckts\n"); - streamPrint(spice_stream_, "***************\n\n"); - - for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { - if (stage == stageFirst()) - writeInputStage(stage); - else - writeGateStage(stage); - } -} - -// Input port to first gate input. -void -WriteSpice::writeInputStage(Stage stage) -{ - // Input arc. - // External driver not handled. - auto drvr_pin_name = stageDrvrPinName(stage); - auto load_pin_name = stageLoadPinName(stage); - streamPrint(spice_stream_, ".subckt %s %s %s\n", - stageName(stage).c_str(), - drvr_pin_name, - load_pin_name); - writeStageParasitics(stage); - streamPrint(spice_stream_, ".ends\n\n"); -} - -// Gate and load parasitics. -void -WriteSpice::writeGateStage(Stage stage) -{ - auto input_pin = stageInputPin(stage); - auto input_pin_name = stageInputPinName(stage); - auto drvr_pin = stageDrvrPin(stage); - auto drvr_pin_name = stageDrvrPinName(stage); - auto load_pin_name = stageLoadPinName(stage); - streamPrint(spice_stream_, ".subckt stage%d %s %s %s\n", - stage, - input_pin_name, - drvr_pin_name, - load_pin_name); - Instance *inst = network_->instance(input_pin); - const char *inst_name = network_->pathName(inst); - LibertyCell *cell = network_->libertyCell(inst); - const char *cell_name = cell->name(); - auto spice_port_names = cell_spice_port_names_[cell_name]; - - // Instance subckt call. - streamPrint(spice_stream_, "x%s", inst_name); - StringVector::Iterator port_iter(spice_port_names); - while (port_iter.hasNext()) { - const char *subckt_port_name = port_iter.next().c_str(); - auto pin = network_->findPin(inst, subckt_port_name); - auto pg_port = cell->findPgPort(subckt_port_name); - const char *pin_name; - if (pin) { - pin_name = network_->pathName(pin); - streamPrint(spice_stream_, " %s", pin_name); - } - else if (pg_port) - streamPrint(spice_stream_, " %s/%s", inst_name, subckt_port_name); - } - streamPrint(spice_stream_, " %s\n", cell_name); - - writeStageVoltageSources(cell, spice_port_names, - inst_name, - network_->libertyPort(input_pin), - network_->libertyPort(drvr_pin)); - writeStageParasitics(stage); - streamPrint(spice_stream_, ".ends\n\n"); -} - -typedef Map LibertyPortLogicValues; - -// Find the logic values for expression inputs to enable paths from_port. -void -sensitizationValues(FuncExpr *expr, - LibertyPort *from_port, - // Return values. - LibertyPortLogicValues &port_values) -{ - switch (expr->op()) { - case FuncExpr::op_port: { - break; - } - case FuncExpr::op_not: { - sensitizationValues(expr->left(), from_port, port_values); - break; - } - case FuncExpr::op_or: { - FuncExpr *left = expr->left(); - FuncExpr *right = expr->right(); - if (left->port() == from_port - && right->op() == FuncExpr::op_port) - port_values[right->port()] = logic_zero; - else if (right->port() == from_port - && left->op() == FuncExpr::op_port) - port_values[left->port()] = logic_zero; - break; - } - case FuncExpr::op_and: { - FuncExpr *left = expr->left(); - FuncExpr *right = expr->right(); - if (left->port() == from_port - && right->op() == FuncExpr::op_port) - port_values[right->port()] = logic_one; - else if (right->port() == from_port - && left->op() == FuncExpr::op_port) - port_values[left->port()] = logic_one; - break; - } - case FuncExpr::op_xor: { - // Need to know timing arc sense to get this right. - FuncExpr *left = expr->left(); - FuncExpr *right = expr->right(); - if (left->port() == from_port - && right->op() == FuncExpr::op_port) - port_values[right->port()] = logic_zero; - else if (right->port() == from_port - && left->op() == FuncExpr::op_port) - port_values[left->port()] = logic_zero; - break; - } - case FuncExpr::op_one: - case FuncExpr::op_zero: - break; - } -} - -// Power/ground and input voltage sources. -void -WriteSpice::writeStageVoltageSources(LibertyCell *cell, - StringVector *spice_port_names, - const char *inst_name, - LibertyPort *from_port, - LibertyPort *drvr_port) -{ - auto from_port_name = from_port->name(); - auto drvr_port_name = drvr_port->name(); - LibertyLibrary *lib = cell->libertyLibrary(); - LibertyPortLogicValues port_values; - sensitizationValues(drvr_port->function(), from_port, port_values); - int volt_source = 1; - debugPrint1(debug_, "write_spice", 2, "subckt %s\n", cell->name()); - StringVector::Iterator port_iter(spice_port_names); - while (port_iter.hasNext()) { - auto subckt_port_name = port_iter.next().c_str(); - auto pg_port = cell->findPgPort(subckt_port_name); - debugPrint2(debug_, "write_spice", 2, " port %s%s\n", - subckt_port_name, - pg_port ? " pwr/gnd" : ""); - if (pg_port) { - auto voltage = pgPortVoltage(pg_port); - streamPrint(spice_stream_, "v%d %s/%s 0 %.3f\n", - volt_source, - inst_name, subckt_port_name, - voltage); - volt_source++; - } else if (!(stringEq(subckt_port_name, from_port_name) - || stringEq(subckt_port_name, drvr_port_name))) { - // Input voltage to sensitize path from gate input to output. - LibertyPort *port = cell->findLibertyPort(subckt_port_name); - if (port) { - const char *pg_port_name = NULL; - bool port_has_value; - LogicValue port_value; - port_values.findKey(port, port_value, port_has_value); - if (port_has_value) { - switch (port_value) { - case logic_zero: - pg_port_name = port->relatedGroundPin(); - break; - case logic_one: - pg_port_name = port->relatedPowerPin(); - break; - default: - break; - } - if (pg_port_name) { - auto pg_port = cell->findPgPort(pg_port_name); - if (pg_port) { - auto voltage_name = pg_port->voltageName(); - if (voltage_name) { - float voltage = lib->supplyVoltage(voltage_name); - streamPrint(spice_stream_, "v%d %s/%s 0 %.3f\n", - volt_source, - inst_name, subckt_port_name, - voltage); - volt_source++; - } - else - report_->error("port %s %s voltage %s not found,\n", - subckt_port_name, - pg_port_name, - voltage_name); - } - else - report_->error("port %s %s not found,\n", - subckt_port_name, - pg_port_name); - } - } - } - } - } -} - -typedef Set ParasiticDeviceSet; -typedef Set ParasiticNodeSet; - -void -findParasiticDevicesNodes(ParasiticNode *node, - Parasitics *parasitics, - // Return values. - ParasiticNodeSet &nodes, - ParasiticDeviceSet &devices) -{ - nodes.insert(node); - auto device_iter = parasitics->deviceIterator(node); - while (device_iter->hasNext()) { - auto device = device_iter->next(); - if (!devices.hasKey(device)) { - devices.insert(device); - auto other_node = parasitics->otherNode(device, node); - findParasiticDevicesNodes(other_node, parasitics, nodes, devices); - } - } - delete device_iter; -} - -void -WriteSpice::writeStageParasitics(Stage stage) -{ - auto drvr_path = stageDrvrPath(stage); - auto drvr_pin = stageDrvrPin(stage); - auto load_pin = stageLoadPin(stage); - auto dcalc_ap = drvr_path->dcalcAnalysisPt(this); - auto parasitic_ap = dcalc_ap->parasiticAnalysisPt(); - auto parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); - int resistor_index = 1; - int cap_index = 1; - if (parasitic) { - Net *net = network_->net(drvr_pin); - auto net_name = - net ? network_->pathName(net) : network_->pathName(drvr_pin); - initNodeMap(net_name); - streamPrint(spice_stream_, "* Net %s\n", net_name); - auto node = parasitics_->findNode(parasitic, drvr_pin); - ParasiticNodeSet nodes; - ParasiticDeviceSet devices; - findParasiticDevicesNodes(node, parasitics_, nodes, devices); - ParasiticDeviceSet::Iterator device_iter(devices); - while (device_iter.hasNext()) { - auto device = device_iter.next(); - auto resistance = parasitics_->value(device, parasitic_ap); - if (parasitics_->isResistor(device)) { - ParasiticNode *node1, *node2; - parasitics_->resistorNodes(device, node1, node2); - streamPrint(spice_stream_, "R%d %s %s %.3e\n", - resistor_index, - nodeName(node1), - nodeName(node2), - resistance); - resistor_index++; - } - else if (parasitics_->isCouplingCap(device)) { - } - } - ParasiticNodeSet::Iterator node_iter(nodes); - while (node_iter.hasNext()) { - auto node = node_iter.next(); - auto cap = parasitics_->nodeGndCap(node, parasitic_ap); - streamPrint(spice_stream_, "C%d %s 0 %.3e\n", - cap_index, - nodeName(node), - cap); - cap_index++; - } - } - else - streamPrint(spice_stream_, "R1 %s %s %.3e\n", - network_->pathName(drvr_pin), - network_->pathName(load_pin), - short_ckt_resistance_); -} - -void -WriteSpice::initNodeMap(const char *net_name) -{ - stringDelete(net_name_); - node_map_.clear(); - next_node_index_ = 1; - net_name_ = stringCopy(net_name); -} - -const char * -WriteSpice::nodeName(ParasiticNode *node) -{ - auto pin = parasitics_->connectionPin(node); - if (pin) - return parasitics_->name(node); - else { - int node_index; - bool node_index_exists; - node_map_.findKey(node, node_index, node_index_exists); - if (!node_index_exists) { - node_index = next_node_index_++; - node_map_[node] = node_index; - } - return stringPrintTmp(strlen(net_name_) + 10, "%s/%d", - net_name_, node_index); - } -} - -//////////////////////////////////////////////////////////////// - -// Copy the subckt definition from lib_subckts_filename for -// each cell in path to path_subckts_filename. -void -WriteSpice::writeSubckts() -{ - StringSet path_cell_names; - findPathCellnames(path_cell_names); - - ifstream lib_subckts_stream(lib_subckts_filename_); - if (lib_subckts_stream.is_open()) { - ofstream subckts_stream(subckts_filename_); - if (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.hasKey(cell_name)) { - subckts_stream << line << "\n"; - bool found_ends = false; - while (getline(lib_subckts_stream, line)) { - subckts_stream << line << "\n"; - if (stringEqual(line.c_str(), ".ends")) { - subckts_stream << "\n"; - found_ends = true; - break; - } - } - if (!found_ends) - throw SubcktEndsMissing(cell_name, lib_subckts_filename_); - path_cell_names.eraseKey(cell_name); - } - recordSpicePortNames(cell_name, tokens); - } - } - subckts_stream.close(); - lib_subckts_stream.close(); - - if (!path_cell_names.empty()) { - StringSet::Iterator cell_iter(path_cell_names); - report_->error("The following subkcts are missing from %s\n", - lib_subckts_filename_); - while (cell_iter.hasNext()) { - const char *cell_name = cell_iter.next(); - report_->printError(" %s\n", cell_name); - } - } - } - else { - lib_subckts_stream.close(); - throw FileNotWritable(subckts_filename_); - } - } - else - throw FileNotReadable(lib_subckts_filename_); -} - -void -WriteSpice::findPathCellnames(// Return values. - StringSet &path_cell_names) -{ - for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { - auto arc = stageGateArc(stage); - if (arc) { - LibertyCell *cell = arc->set()->libertyCell(); - if (cell) { - debugPrint1(debug_, "write_spice", 2, "cell %s\n", cell->name()); - path_cell_names.insert(cell->name()); - } - } - } -} - -void -WriteSpice::recordSpicePortNames(const char *cell_name, - StringVector &tokens) -{ - auto cell = network_->findLibertyCell(cell_name); - auto spice_port_names = new StringVector; - for (int i = 2; i < tokens.size(); i++) { - const char *port_name = tokens[i].c_str(); - auto port = cell->findLibertyPort(port_name); - auto pg_port = cell->findPgPort(port_name); - if (port == NULL && pg_port == NULL) - report_->error("subckt %s port %s has no corresponding liberty port or pg_port.\n", - cell_name, port_name); - spice_port_names->push_back(port_name); - } - cell_spice_port_names_[cell_name] = spice_port_names; -} - -//////////////////////////////////////////////////////////////// - -Stage -WriteSpice::stageFirst() -{ - return 1; -} - -Stage -WriteSpice::stageLast() -{ - return (path_expanded_.size() + 1) / 2; -} - -string -WriteSpice::stageName(Stage stage) -{ - string name; - stringPrint(name, "stage%d", stage); - return name; -} - -int -WriteSpice::stageGateInputPathIndex(Stage stage) -{ - return stage * 2 - 3; -} - -int -WriteSpice::stageDrvrPathIndex(Stage stage) -{ - return stage * 2 - 2; -} - -int -WriteSpice::stageLoadPathIndex(Stage stage) -{ - return stage * 2 - 1; -} - -PathRef * -WriteSpice::stageGateInputPath(Stage stage) -{ - int path_index = stageGateInputPathIndex(stage); - return path_expanded_.path(path_index); -} - -PathRef * -WriteSpice::stageDrvrPath(Stage stage) -{ - int path_index = stageDrvrPathIndex(stage); - return path_expanded_.path(path_index); -} - -PathRef * -WriteSpice::stageLoadPath(Stage stage) -{ - int path_index = stageLoadPathIndex(stage); - return path_expanded_.path(path_index); -} - -TimingArc * -WriteSpice::stageGateArc(Stage stage) -{ - int path_index = stageDrvrPathIndex(stage); - if (path_index >= 0) - return path_expanded_.prevArc(path_index); - else - return NULL; -} - -TimingArc * -WriteSpice::stageWireArc(Stage stage) -{ - int path_index = stageLoadPathIndex(stage); - return path_expanded_.prevArc(path_index); -} - -Edge * -WriteSpice::stageGateEdge(Stage stage) -{ - PathRef *path = stageGateInputPath(stage); - TimingArc *arc = stageGateArc(stage); - return path->prevEdge(arc, this); -} - -Edge * -WriteSpice::stageWireEdge(Stage stage) -{ - PathRef *path = stageLoadPath(stage); - TimingArc *arc = stageWireArc(stage); - return path->prevEdge(arc, this); -} - -Pin * -WriteSpice::stageInputPin(Stage stage) -{ - PathRef *path = stageGateInputPath(stage); - return path->pin(this); -} - -Pin * -WriteSpice::stageDrvrPin(Stage stage) -{ - PathRef *path = stageDrvrPath(stage); - return path->pin(this); -} - -Pin * -WriteSpice::stageLoadPin(Stage stage) -{ - PathRef *path = stageLoadPath(stage); - return path->pin(this); -} - -const char * -WriteSpice::stageInputPinName(Stage stage) -{ - const Pin *pin = stageInputPin(stage); - return network_->pathName(pin); -} - -const char * -WriteSpice::stageDrvrPinName(Stage stage) -{ - Pin *pin = stageDrvrPin(stage); - return network_->pathName(pin); -} - -const char * -WriteSpice::stageLoadPinName(Stage stage) -{ - const Pin *pin = stageLoadPin(stage); - return network_->pathName(pin); -} - -//////////////////////////////////////////////////////////////// - -void -split(const string &text, - const string &delims, - // Return values. - StringVector &tokens) -{ - auto start = text.find_first_not_of(delims); - auto end = text.find_first_of(delims, start); - while (end != string::npos) { - tokens.push_back(text.substr(start, end - start)); - start = text.find_first_not_of(delims, end); - end = text.find_first_of(delims, start); - } - if (start != string::npos) - tokens.push_back(text.substr(start)); -} - -// fprintf for c++ streams. -// Yes, I hate formatted output to ostream THAT much. -void -streamPrint(ofstream &stream, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - char *result; - vasprintf(&result, fmt, args); - stream << result; - free(result); - va_end(args); -} - -} // namespace diff --git a/tcl/CmdUtil.tcl b/tcl/CmdUtil.tcl index fe62c4d6..8fe51a62 100644 --- a/tcl/CmdUtil.tcl +++ b/tcl/CmdUtil.tcl @@ -114,7 +114,7 @@ define_cmd_args "report_units" {} proc report_units { args } { check_argc_eq0 "report_units" $args foreach unit {"time" "capacitance" "resistance" "voltage" "current" "power" "distance"} { - report_line " $unit 1[unit_scale_abreviation $unit][unit_suffix $unit]" + report_line " $unit 1[unit_scaled_suffix $unit]" } } diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index 86ec7975..0dd0b32a 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -254,6 +254,26 @@ tclListSeqConstChar(Tcl_Obj *const source, return nullptr; } +StdStringSet * +tclListSetStdString(Tcl_Obj *const source, + Tcl_Interp *interp) +{ + int argc; + Tcl_Obj **argv; + + if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) { + StdStringSet *set = new StdStringSet; + for (int i = 0; i < argc; i++) { + int length; + const char *str = Tcl_GetStringFromObj(argv[i], &length); + set->insert(str); + } + return set; + } + else + return nullptr; +} + //////////////////////////////////////////////////////////////// // Sequence out to tcl list. @@ -425,6 +445,10 @@ using namespace sta; $1 = tclListSeqConstChar($input, interp); } +%typemap(in) StdStringSet* { + $1 = tclListSetStdString($input, interp); +} + %typemap(out) StringSeq* { StringSeq *strs = $1; Tcl_Obj *list = Tcl_NewListObj(0, nullptr); @@ -885,24 +909,26 @@ using namespace sta; %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); + if (table.axis1()) { + 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); } - 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; +%typemap(out) const Table1* { + const Table1 *table = $1; Tcl_Obj *list3 = Tcl_NewListObj(0, nullptr); if (table) { Tcl_Obj *list1 = Tcl_NewListObj(0, nullptr); @@ -3963,11 +3989,11 @@ set_cmd_unit_suffix(const char *unit_name, } const char * -unit_scale_abreviation(const char *unit_name) +unit_scale_abbreviation (const char *unit_name) { Unit *unit = Sta::sta()->units()->find(unit_name); if (unit) - return unit->scaleAbreviation(); + return unit->scaleAbbreviation(); else return ""; } @@ -3982,6 +4008,16 @@ unit_suffix(const char *unit_name) return ""; } +const char * +unit_scaled_suffix(const char *unit_name) +{ + Unit *unit = Sta::sta()->units()->find(unit_name); + if (unit) + return unit->scaledSuffix(); + else + return ""; +} + //////////////////////////////////////////////////////////////// VertexIterator * @@ -4865,12 +4901,13 @@ write_path_spice_cmd(PathRef *path, const char *subckt_filename, const char *lib_subckt_filename, const char *model_filename, + StdStringSet *off_path_pins, const char *power_name, const char *gnd_name) { Sta *sta = Sta::sta(); writePathSpice(path, spice_filename, subckt_filename, - lib_subckt_filename, model_filename, + lib_subckt_filename, model_filename, off_path_pins, power_name, gnd_name, sta); } @@ -5673,7 +5710,7 @@ voltage_waveform(float in_slew, return Table1(); } -Table1 +const Table1 * current_waveform(float in_slew, float load_cap) { @@ -5681,11 +5718,11 @@ current_waveform(float in_slew, if (gate_model) { OutputWaveforms *waveforms = gate_model->outputWaveforms(); if (waveforms) { - Table1 waveform = waveforms->currentWaveform(in_slew, load_cap); + const Table1 *waveform = waveforms->currentWaveform(in_slew, load_cap); return waveform; } } - return Table1(); + return nullptr; } } // TimingArc methods diff --git a/tcl/WritePathSpice.tcl b/tcl/WritePathSpice.tcl index a8cf9c5a..0ba7da65 100644 --- a/tcl/WritePathSpice.tcl +++ b/tcl/WritePathSpice.tcl @@ -89,7 +89,7 @@ proc write_path_spice { args } { set spice_file [file join $spice_dir "$path_name.sp"] set subckt_file [file join $spice_dir "$path_name.subckt"] write_path_spice_cmd $path $spice_file $subckt_file \ - $lib_subckt_file $model_file $power $ground + $lib_subckt_file $model_file {} $power $ground incr path_index } } diff --git a/verilog/VerilogWriter.cc b/verilog/VerilogWriter.cc index ca6a9478..7c407921 100644 --- a/verilog/VerilogWriter.cc +++ b/verilog/VerilogWriter.cc @@ -232,7 +232,7 @@ VerilogWriter::writeWireDcls(Instance *inst) { Cell *cell = network_->cell(inst); char escape = network_->pathEscape(); - Map bus_ranges; + Map> bus_ranges; NetIterator *net_iter = network_->netIterator(inst); while (net_iter->hasNext()) { Net *net = net_iter->next(); @@ -243,7 +243,7 @@ VerilogWriter::writeWireDcls(Instance *inst) string bus_name; int index; parseBusName(net_name, '[', ']', escape, is_bus, bus_name, index); - BusIndexRange &range = bus_ranges[bus_name.c_str()]; + BusIndexRange &range = bus_ranges[bus_name]; range.first = max(range.first, index); range.second = min(range.second, index); } @@ -256,7 +256,7 @@ VerilogWriter::writeWireDcls(Instance *inst) delete net_iter; for (auto name_range : bus_ranges) { - const char *bus_name = name_range.first; + const char *bus_name = name_range.first.c_str(); const BusIndexRange &range = name_range.second; string net_vname = netVerilogName(bus_name, network_->pathEscape()); fprintf(stream_, " wire [%d:%d] %s;\n",