OutputWaveform::voltageCurrent
Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
parent
9b4315eb64
commit
e56baf44ad
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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<TableAxis>(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<TableAxis>(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<TableAxis>(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,
|
||||
|
|
|
|||
17
tcl/StaTcl.i
17
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<GateTableModel*>(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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue