diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index a713bfe4..7ef36e71 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -362,6 +362,7 @@ public: bool &extrapolated) const; float findValue(float axis_value1) const; float findValueClip(float axis_value1) const; + float findValueClipZero(float axis_value1) const; FloatSeq *values() const { return values_; } using Table::findValue; @@ -506,6 +507,9 @@ public: Table1 *ref_times); ~OutputWaveforms(); const RiseFall *rf() const { return rf_; } + TableAxisPtr slewAxis() const { return slew_axis_; } + TableAxisPtr capAxis() const { return cap_axis_; } + // Column. bool inBounds(float in_slew, float load_cap) const; Table1 voltageWaveform(float in_slew, @@ -515,6 +519,12 @@ public: float voltage); const Table1 *currentWaveform(float slew, float cap); + float timeCurrent(float slew, + float cap, + float time); + float voltageCurrent(float slew, + float cap, + float volt); float referenceTime(float slew); void setVdd(float vdd); static bool checkAxes(TableTemplate *tbl_template); @@ -525,6 +535,10 @@ private: float cap); FloatSeq *voltageTimes(size_t wave_index, float cap); + void findVoltages(size_t wave_index, + float cap); + const Table1 *voltageCurrents(size_t wave_index, + float cap); // Row. TableAxisPtr slew_axis_; @@ -532,6 +546,7 @@ private: TableAxisPtr cap_axis_; const RiseFall *rf_; Table1Seq current_waveforms_; + Table1Seq voltage_currents_; FloatTable voltage_times_; Table1 *ref_times_; float vdd_; diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 48db282a..8e97fa52 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -908,6 +908,27 @@ Table1::findValueClip(float axis_value1) const } } +float +Table1::findValueClipZero(float axis_value1) const +{ + if (axis1_->size() == 1) + return this->value(axis_value1); + else { + size_t axis_index1 = axis1_->findAxisIndex(axis_value1); + float x1 = axis_value1; + float x1l = axis1_->axisValue(axis_index1); + float x1u = axis1_->axisValue(axis_index1 + 1); + if (x1 < x1l || x1 > x1u) + return 0.0; + else { + float y1 = this->value(axis_index1); + float y2 = this->value(axis_index1 + 1); + float dx1 = (x1 - x1l) / (x1u - x1l); + return (1 - dx1) * y1 + dx1 * y2; + } + } +} + string Table1::reportValue(const char *result_name, const LibertyLibrary *library, @@ -1592,6 +1613,7 @@ OutputWaveforms::OutputWaveforms(TableAxisPtr slew_axis, cap_axis_(cap_axis), rf_(rf), current_waveforms_(current_waveforms), + voltage_currents_(current_waveforms.size()), voltage_times_(current_waveforms.size()), ref_times_(ref_times), vdd_(0.0) @@ -1601,6 +1623,7 @@ OutputWaveforms::OutputWaveforms(TableAxisPtr slew_axis, OutputWaveforms::~OutputWaveforms() { current_waveforms_.deleteContents(); + voltage_currents_.deleteContents(); voltage_times_.deleteContents(); delete ref_times_; } @@ -1640,6 +1663,48 @@ OutputWaveforms::currentWaveform(float slew, return current_waveforms_[wave_index]; } +float +OutputWaveforms::timeCurrent(float slew, + float cap, + float time) +{ + 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); + + const Table1 *waveform00 = current_waveforms_[wave_index00]; + const Table1 *waveform01 = current_waveforms_[wave_index01]; + const Table1 *waveform10 = current_waveforms_[wave_index10]; + const Table1 *waveform11 = current_waveforms_[wave_index11]; + + // Interpolate waveform samples at voltage 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); + + float y00 = waveform00->findValueClipZero(time); + float y01 = waveform01->findValueClipZero(time); + float y10 = waveform10->findValueClipZero(time); + float y11 = waveform11->findValueClipZero(time); + float current + = (1 - dx1) * (1 - dx2) * y00 + + dx1 * (1 - dx2) * y10 + + dx1 * dx2 * y11 + + (1 - dx1) * dx2 * y01; + return current; +} + float OutputWaveforms::referenceTime(float slew) { @@ -1652,6 +1717,23 @@ OutputWaveforms::setVdd(float vdd) vdd_ = vdd; } +Table1 +OutputWaveforms::voltageWaveform(float slew, + float cap) +{ + 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); +} + float OutputWaveforms::voltageTime(float slew, float cap, @@ -1691,23 +1773,6 @@ OutputWaveforms::voltageTime(float slew, return time; } -Table1 -OutputWaveforms::voltageWaveform(float slew, - float cap) -{ - 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); -} - float OutputWaveforms::voltageTime1(float voltage, size_t wave_index, @@ -1728,57 +1793,128 @@ OutputWaveforms::voltageTimes(size_t wave_index, { 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; - 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++) { - 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; - volts.push_back(voltage); - prev_time = time; - prev_current = current; - } - - // 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; + findVoltages(wave_index, cap); + voltage_times = voltage_times_[wave_index]; } return voltage_times; } +void +OutputWaveforms::findVoltages(size_t wave_index, + float cap) +{ + if (vdd_ == 0.0) + criticalError(239, "output waveform vdd = 0.0"); + // 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; + 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++) { + 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; + volts.push_back(voltage); + prev_time = time; + prev_current = current; + } + + // Make voltage -> current table. + FloatSeq *axis_volts = new FloatSeq(volts); + TableAxisPtr volt_axis = + make_shared(TableAxisVariable::input_voltage, axis_volts); + FloatSeq *currents1 = new FloatSeq(*currents->values()); + Table1 *volt_currents = new Table1(currents1, volt_axis); + voltage_currents_[wave_index] = volt_currents; + + // Sample the voltage waveform at uniform intervals to speed up + // voltage time lookup. + FloatSeq *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); + voltage_times->push_back(time3); + } + voltage_times_[wave_index] = voltage_times; +} + +float +OutputWaveforms::voltageCurrent(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); + + // Interpolate waveform samples at voltage 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); + + const Table1 *waveform00 = voltageCurrents(wave_index00, cap0); + const Table1 *waveform01 = voltageCurrents(wave_index01, cap1); + const Table1 *waveform10 = voltageCurrents(wave_index10, cap0); + const Table1 *waveform11 = voltageCurrents(wave_index11, cap1); + + float y00 = waveform00->findValueClipZero(volt); + float y01 = waveform01->findValueClipZero(volt); + float y10 = waveform10->findValueClipZero(volt); + float y11 = waveform11->findValueClipZero(volt); + float current + = (1 - dx1) * (1 - dx2) * y00 + + dx1 * (1 - dx2) * y10 + + dx1 * dx2 * y11 + + (1 - dx1) * dx2 * y01; + return current; +} + +const Table1 * +OutputWaveforms::voltageCurrents(size_t wave_index, + float cap) +{ + const Table1 *waveform = voltage_currents_[wave_index]; + if (waveform == nullptr) { + findVoltages(wave_index, cap); + waveform = voltage_currents_[wave_index]; + } + return waveform; +} + //////////////////////////////////////////////////////////////// DriverWaveform::DriverWaveform(const char *name, diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index 0dd0b32a..ac0fdcac 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -5725,6 +5725,23 @@ current_waveform(float in_slew, return nullptr; } +float +voltage_current(float in_slew, + float load_cap, + float voltage) +{ + GateTableModel *gate_model = dynamic_cast(self->model()); + if (gate_model) { + OutputWaveforms *waveforms = gate_model->outputWaveforms(); + if (waveforms) { + waveforms->setVdd(.7); + float current = waveforms->voltageCurrent(in_slew, load_cap, voltage); + return current; + } + } + return 0.0; +} + } // TimingArc methods %extend Instance {