// OpenSTA, Static Timing Analyzer // Copyright (c) 2020, 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 "TableModel.hh" #include #include "Error.hh" #include "EnumNameMap.hh" #include "Units.hh" #include "Liberty.hh" namespace sta { using std::string; static void deleteSigmaModels(TableModel *models[EarlyLate::index_count]); static void reportPvt(const LibertyLibrary *library, const Pvt *pvt, int digits, string *result); static void appendSpaces(string *result, int count); GateTableModel::GateTableModel(TableModel *delay_model, TableModel *delay_sigma_models[EarlyLate::index_count], TableModel *slew_model, TableModel *slew_sigma_models[EarlyLate::index_count]) : delay_model_(delay_model), slew_model_(slew_model) { for (auto el_index : EarlyLate::rangeIndex()) { slew_sigma_models_[el_index] = slew_sigma_models[el_index]; delay_sigma_models_[el_index] = delay_sigma_models[el_index]; } } GateTableModel::~GateTableModel() { delete delay_model_; delete slew_model_; deleteSigmaModels(slew_sigma_models_); deleteSigmaModels(delay_sigma_models_); } static void deleteSigmaModels(TableModel *models[EarlyLate::index_count]) { TableModel *early_model = models[EarlyLate::earlyIndex()]; TableModel *late_model = models[EarlyLate::lateIndex()]; if (early_model == late_model) delete early_model; else { delete early_model; delete late_model; } } void GateTableModel::setIsScaled(bool is_scaled) { if (delay_model_) delay_model_->setIsScaled(is_scaled); if (slew_model_) slew_model_->setIsScaled(is_scaled); } void GateTableModel::gateDelay(const LibertyCell *cell, const Pvt *pvt, float in_slew, float load_cap, float related_out_cap, bool pocv_enabled, // return values ArcDelay &gate_delay, Slew &drvr_slew) const { const LibertyLibrary *library = cell->libertyLibrary(); float delay = findValue(library, cell, pvt, delay_model_, in_slew, load_cap, related_out_cap); float sigma_early = 0.0; float sigma_late = 0.0; if (pocv_enabled && delay_sigma_models_[EarlyLate::earlyIndex()]) sigma_early = findValue(library, cell, pvt, delay_sigma_models_[EarlyLate::earlyIndex()], in_slew, load_cap, related_out_cap); if (pocv_enabled && delay_sigma_models_[EarlyLate::lateIndex()]) sigma_late = findValue(library, cell, pvt, delay_sigma_models_[EarlyLate::lateIndex()], in_slew, load_cap, related_out_cap); gate_delay = makeDelay(delay, sigma_early, sigma_late); float slew = findValue(library, cell, pvt, slew_model_, in_slew, load_cap, related_out_cap); if (pocv_enabled && slew_sigma_models_[EarlyLate::earlyIndex()]) sigma_early = findValue(library, cell, pvt, slew_sigma_models_[EarlyLate::earlyIndex()], in_slew, load_cap, related_out_cap); if (pocv_enabled && slew_sigma_models_[EarlyLate::lateIndex()]) sigma_late = findValue(library, cell, pvt, slew_sigma_models_[EarlyLate::lateIndex()], in_slew, load_cap, related_out_cap); // Clip negative slews to zero. if (slew < 0.0) slew = 0.0; drvr_slew = makeDelay(slew, sigma_early, sigma_late); } void GateTableModel::reportGateDelay(const LibertyCell *cell, const Pvt *pvt, float in_slew, float load_cap, float related_out_cap, bool pocv_enabled, int digits, string *result) const { const LibertyLibrary *library = cell->libertyLibrary(); reportPvt(library, pvt, digits, result); reportTableLookup("Delay", library, cell, pvt, delay_model_, in_slew, load_cap, related_out_cap, digits, result); if (pocv_enabled && delay_sigma_models_[EarlyLate::earlyIndex()]) reportTableLookup("Delay sigma(early)", library, cell, pvt, delay_sigma_models_[EarlyLate::earlyIndex()], in_slew, load_cap, related_out_cap, digits, result); if (pocv_enabled && delay_sigma_models_[EarlyLate::lateIndex()]) reportTableLookup("Delay sigma(late)", library, cell, pvt, delay_sigma_models_[EarlyLate::lateIndex()], in_slew, load_cap, related_out_cap, digits, result); *result += '\n'; reportTableLookup("Slew", library, cell, pvt, slew_model_, in_slew, load_cap, related_out_cap, digits, result); if (pocv_enabled && slew_sigma_models_[EarlyLate::earlyIndex()]) reportTableLookup("Slew sigma(early)", library, cell, pvt, slew_sigma_models_[EarlyLate::earlyIndex()], in_slew, load_cap, related_out_cap, digits, result); if (pocv_enabled && slew_sigma_models_[EarlyLate::lateIndex()]) reportTableLookup("Slew sigma(late)", library, cell, pvt, slew_sigma_models_[EarlyLate::lateIndex()], in_slew, load_cap, related_out_cap, digits, result); float drvr_slew = findValue(library, cell, pvt, slew_model_, in_slew, load_cap, related_out_cap); if (drvr_slew < 0.0) *result += "Negative slew clipped to 0.0\n"; } void GateTableModel::reportTableLookup(const char *result_name, const LibertyLibrary *library, const LibertyCell *cell, const Pvt *pvt, const TableModel *model, float in_slew, float load_cap, float related_out_cap, int digits, string *result) const { if (model) { float axis_value1, axis_value2, axis_value3; findAxisValues(model, in_slew, load_cap, related_out_cap, axis_value1, axis_value2, axis_value3); model->reportValue(result_name, library, cell, pvt, axis_value1, nullptr, axis_value2, axis_value3, digits, result); } } float GateTableModel::findValue(const LibertyLibrary *library, const LibertyCell *cell, const Pvt *pvt, const TableModel *model, float in_slew, float load_cap, float related_out_cap) const { if (model) { float axis_value1, axis_value2, axis_value3; findAxisValues(model, in_slew, load_cap, related_out_cap, axis_value1, axis_value2, axis_value3); return model->findValue(library, cell, pvt, axis_value1, axis_value2, axis_value3); } else return 0.0; } void GateTableModel::findAxisValues(const TableModel *model, float in_slew, float load_cap, float related_out_cap, // Return values. float &axis_value1, float &axis_value2, float &axis_value3) const { switch (model->order()) { case 0: axis_value1 = 0.0; axis_value2 = 0.0; axis_value3 = 0.0; break; case 1: axis_value1 = axisValue(model->axis1(), in_slew, load_cap, related_out_cap); axis_value2 = 0.0; axis_value3 = 0.0; break; case 2: axis_value1 = axisValue(model->axis1(), in_slew, load_cap, related_out_cap); axis_value2 = axisValue(model->axis2(), in_slew, load_cap, related_out_cap); axis_value3 = 0.0; break; case 3: axis_value1 = axisValue(model->axis1(), in_slew, load_cap, related_out_cap); axis_value2 = axisValue(model->axis2(), in_slew, load_cap, related_out_cap); axis_value3 = axisValue(model->axis3(), in_slew, load_cap, related_out_cap); break; default: criticalError(239, "unsupported table order"); } } // Use slew/Cload for the highest Cload, which approximates output // admittance as the "drive". float GateTableModel::driveResistance(const LibertyCell *cell, const Pvt *pvt) const { float slew, cap; maxCapSlew(cell, 0.0, pvt, slew, cap); return slew / cap; } void GateTableModel::maxCapSlew(const LibertyCell *cell, float in_slew, const Pvt *pvt, float &slew, float &cap) const { const LibertyLibrary *library = cell->libertyLibrary(); TableAxis *axis1 = slew_model_->axis1(); TableAxis *axis2 = slew_model_->axis2(); TableAxis *axis3 = slew_model_->axis3(); if (axis1 && axis1->variable() == TableAxisVariable::total_output_net_capacitance) { cap = axis1->axisValue(axis1->size() - 1); slew = findValue(library, cell, pvt, slew_model_, in_slew, cap, 0.0); } else if (axis2 && axis2->variable()==TableAxisVariable::total_output_net_capacitance) { cap = axis2->axisValue(axis2->size() - 1); slew = findValue(library, cell, pvt, slew_model_, in_slew, cap, 0.0); } else if (axis3 && axis3->variable()==TableAxisVariable::total_output_net_capacitance) { cap = axis3->axisValue(axis3->size() - 1); slew = findValue(library, cell, pvt, slew_model_, in_slew, cap, 0.0); } else { // Table not dependent on capacitance. cap = 1.0; slew = 0.0; } // Clip negative slews to zero. if (slew < 0.0) slew = 0.0; } float GateTableModel::axisValue(TableAxis *axis, float in_slew, float load_cap, float related_out_cap) const { TableAxisVariable var = axis->variable(); if (var == TableAxisVariable::input_transition_time || var == TableAxisVariable::input_net_transition) return in_slew; else if (var == TableAxisVariable::total_output_net_capacitance) return load_cap; else if (var == TableAxisVariable::related_out_total_output_net_capacitance) return related_out_cap; else { criticalError(240, "unsupported table axes"); return 0.0; } } bool GateTableModel::checkAxes(const Table *table) { TableAxis *axis1 = table->axis1(); TableAxis *axis2 = table->axis2(); TableAxis *axis3 = table->axis3(); bool axis_ok = true; if (axis1) axis_ok &= checkAxis(table->axis1()); if (axis2) axis_ok &= checkAxis(table->axis2()); if (axis3) axis_ok &= checkAxis(table->axis3()); return axis_ok; } bool GateTableModel::checkAxis(TableAxis *axis) { TableAxisVariable var = axis->variable(); return var == TableAxisVariable::total_output_net_capacitance || var == TableAxisVariable::input_transition_time || var == TableAxisVariable::input_net_transition || var == TableAxisVariable::related_out_total_output_net_capacitance; } //////////////////////////////////////////////////////////////// CheckTableModel::CheckTableModel(TableModel *model, TableModel *sigma_models[EarlyLate::index_count]) : model_(model) { for (auto el_index : EarlyLate::rangeIndex()) sigma_models_[el_index] = sigma_models[el_index]; } CheckTableModel::~CheckTableModel() { delete model_; deleteSigmaModels(sigma_models_); } void CheckTableModel::setIsScaled(bool is_scaled) { model_->setIsScaled(is_scaled); } void CheckTableModel::checkDelay(const LibertyCell *cell, const Pvt *pvt, float from_slew, float to_slew, float related_out_cap, bool pocv_enabled, // Return values. ArcDelay &margin) const { if (model_) { const LibertyLibrary *library = cell->libertyLibrary(); float mean = findValue(library, cell, pvt, model_, from_slew, to_slew, related_out_cap); float sigma_early = 0.0; float sigma_late = 0.0; if (pocv_enabled && sigma_models_[EarlyLate::earlyIndex()]) sigma_early = findValue(library, cell, pvt, sigma_models_[EarlyLate::earlyIndex()], from_slew, to_slew, related_out_cap); if (pocv_enabled && sigma_models_[EarlyLate::lateIndex()]) sigma_late = findValue(library, cell, pvt, sigma_models_[EarlyLate::lateIndex()], from_slew, to_slew, related_out_cap); margin = makeDelay(mean, sigma_early, sigma_late); } else margin = 0.0; } float CheckTableModel::findValue(const LibertyLibrary *library, const LibertyCell *cell, const Pvt *pvt, const TableModel *model, float from_slew, float to_slew, float related_out_cap) const { if (model) { float axis_value1, axis_value2, axis_value3; findAxisValues(from_slew, to_slew, related_out_cap, axis_value1, axis_value2, axis_value3); return model->findValue(library, cell, pvt, axis_value1, axis_value2, axis_value3); } else return 0.0; } void CheckTableModel::reportCheckDelay(const LibertyCell *cell, const Pvt *pvt, float from_slew, const char *from_slew_annotation, float to_slew, float related_out_cap, bool pocv_enabled, int digits, string *result) const { const LibertyLibrary *library = cell->libertyLibrary(); reportTableDelay("Check", library, cell, pvt, model_, from_slew, from_slew_annotation, to_slew, related_out_cap, digits, result); if (pocv_enabled && sigma_models_[EarlyLate::earlyIndex()]) reportTableDelay("Check sigma early", library, cell, pvt, sigma_models_[EarlyLate::earlyIndex()], from_slew, from_slew_annotation, to_slew, related_out_cap, digits, result); if (pocv_enabled && sigma_models_[EarlyLate::lateIndex()]) reportTableDelay("Check sigma late", library, cell, pvt, sigma_models_[EarlyLate::lateIndex()], from_slew, from_slew_annotation, to_slew, related_out_cap, digits, result); } void CheckTableModel::reportTableDelay(const char *result_name, const LibertyLibrary *library, const LibertyCell *cell, const Pvt *pvt, const TableModel *model, float from_slew, const char *from_slew_annotation, float to_slew, float related_out_cap, int digits, string *result) const { if (model) { float axis_value1, axis_value2, axis_value3; findAxisValues(from_slew, to_slew, related_out_cap, axis_value1, axis_value2, axis_value3); reportPvt(library, pvt, digits, result); model_->reportValue(result_name, library, cell, pvt, axis_value1, from_slew_annotation, axis_value2, axis_value3, digits, result); } } void CheckTableModel::findAxisValues(float from_slew, float to_slew, float related_out_cap, // Return values. float &axis_value1, float &axis_value2, float &axis_value3) const { switch (model_->order()) { case 0: axis_value1 = 0.0; axis_value2 = 0.0; axis_value3 = 0.0; break; case 1: axis_value1 = axisValue(model_->axis1(), from_slew, to_slew, related_out_cap); axis_value2 = 0.0; axis_value3 = 0.0; break; case 2: axis_value1 = axisValue(model_->axis1(), from_slew, to_slew, related_out_cap); axis_value2 = axisValue(model_->axis2(), from_slew, to_slew, related_out_cap); axis_value3 = 0.0; break; case 3: axis_value1 = axisValue(model_->axis1(), from_slew, to_slew, related_out_cap); axis_value2 = axisValue(model_->axis2(), from_slew, to_slew, related_out_cap); axis_value3 = axisValue(model_->axis3(), from_slew, to_slew, related_out_cap); break; default: criticalError(241, "unsupported table order"); } } float CheckTableModel::axisValue(TableAxis *axis, float from_slew, float to_slew, float related_out_cap) const { TableAxisVariable var = axis->variable(); if (var == TableAxisVariable::related_pin_transition) return from_slew; else if (var == TableAxisVariable::constrained_pin_transition) return to_slew; else if (var == TableAxisVariable::related_out_total_output_net_capacitance) return related_out_cap; else { criticalError(242, "unsupported table axes"); return 0.0; } } bool CheckTableModel::checkAxes(const Table *table) { TableAxis *axis1 = table->axis1(); TableAxis *axis2 = table->axis2(); TableAxis *axis3 = table->axis3(); bool axis_ok = true; if (axis1) axis_ok &= checkAxis(table->axis1()); if (axis2) axis_ok &= checkAxis(table->axis2()); if (axis3) axis_ok &= checkAxis(table->axis3()); return axis_ok; } bool CheckTableModel::checkAxis(TableAxis *axis) { TableAxisVariable var = axis->variable(); return var == TableAxisVariable::constrained_pin_transition || var == TableAxisVariable::related_pin_transition || var == TableAxisVariable::related_out_total_output_net_capacitance; } //////////////////////////////////////////////////////////////// TableModel::TableModel(Table *table, ScaleFactorType scale_factor_type, RiseFall *rf) : table_(table), scale_factor_type_(int(scale_factor_type)), tr_index_(rf->index()), is_scaled_(false) { } TableModel::~TableModel() { delete table_; } int TableModel::order() const { return table_->order(); } void TableModel::setScaleFactorType(ScaleFactorType type) { scale_factor_type_ = int(type); } void TableModel::setIsScaled(bool is_scaled) { is_scaled_ = is_scaled; } TableAxis * TableModel::axis1() const { return table_->axis1(); } TableAxis * TableModel::axis2() const { return table_->axis2(); } TableAxis * TableModel::axis3() const { return table_->axis3(); } float TableModel::findValue(const LibertyLibrary *library, const LibertyCell *cell, const Pvt *pvt, float value1, float value2, float value3) const { return table_->findValue(value1, value2, value3) * scaleFactor(library, cell, pvt); } float TableModel::scaleFactor(const LibertyLibrary *library, const LibertyCell *cell, const Pvt *pvt) const { if (is_scaled_) // Scaled tables are not derated because scale factors are wrt // nominal pvt. return 1.0F; else return library->scaleFactor(static_cast(scale_factor_type_), tr_index_, cell, pvt); } void TableModel::reportValue(const char *result_name, const LibertyLibrary *library, const LibertyCell *cell, const Pvt *pvt, float value1, const char *comment1, float value2, float value3, int digits, string *result) const { table_->reportValue("Table value", library, cell, pvt, value1, comment1, value2, value3, digits, result); reportPvtScaleFactor(library, cell, pvt, digits, result); const Units *units = library->units(); const Unit *table_unit = units->timeUnit(); *result += result_name; *result += " = "; *result += table_unit->asString(findValue(library, cell, pvt, value1, value2, value3), digits); *result += '\n'; } static void reportPvt(const LibertyLibrary *library, const Pvt *pvt, int digits, string *result) { if (pvt == nullptr) pvt = library->defaultOperatingConditions(); if (pvt) { string pvt_str; stringPrint(pvt_str, "P = %.*f V = %.*f T = %.*f\n", digits, pvt->process(), digits, pvt->voltage(), digits, pvt->temperature()); *result += pvt_str; } } void TableModel::reportPvtScaleFactor(const LibertyLibrary *library, const LibertyCell *cell, const Pvt *pvt, int digits, string *result) const { if (pvt == nullptr) pvt = library->defaultOperatingConditions(); if (pvt) { string scale_str; stringPrint(scale_str, "PVT scale factor = %.*f\n", digits, scaleFactor(library, cell, pvt)); *result += scale_str; } } //////////////////////////////////////////////////////////////// Table0::Table0(float value) : Table(), value_(value) { } float Table0::findValue(float, float, float) const { return value_; } void Table0::reportValue(const char *result_name, const LibertyLibrary *library, const LibertyCell *, const Pvt *, float value1, const char *comment1, float value2, float value3, int digits, string *result) const { const Units *units = library->units(); const Unit *table_unit = units->timeUnit(); *result += result_name; *result += " constant = "; *result += table_unit->asString(findValue(value1, value2, value3), digits); if (comment1) *result += comment1; *result += '\n'; } void Table0::report(const Units *units, Report *report) const { int digits = 4; const Unit *table_unit = units->timeUnit(); report->reportLine("%s", table_unit->asString(value_, digits)); } //////////////////////////////////////////////////////////////// Table1::Table1(FloatSeq *values, TableAxis *axis1, bool own_axis1) : Table(), values_(values), axis1_(axis1), own_axis1_(own_axis1) { } Table1::~Table1() { delete values_; if (own_axis1_) delete axis1_; } float Table1::tableValue(size_t index1) const { return (*values_)[index1]; } float Table1::findValue(float value1, float, float) const { if (axis1_->size() == 1) return tableValue(value1); else { size_t index1 = axis1_->findAxisIndex(value1); float x1 = value1; float x1l = axis1_->axisValue(index1); float x1u = axis1_->axisValue(index1 + 1); float y1 = tableValue(index1); float y2 = tableValue(index1 + 1); float dx1 = (x1 - x1l) / (x1u - x1l); return (1 - dx1) * y1 + dx1 * y2; } } void Table1::reportValue(const char *result_name, const LibertyLibrary *library, const LibertyCell *, const Pvt *, float value1, const char *comment1, float value2, float value3, int digits, string *result) const { TableAxisVariable var1 = axis1_->variable(); const Units *units = library->units(); const Unit *table_unit = units->timeUnit(); const Unit *unit1 = tableVariableUnit(var1, units); *result += "Table is indexed by\n"; *result += " "; *result += tableVariableString(var1); *result += " = "; *result += unit1->asString(value1, digits); if (comment1) *result += comment1; *result += '\n'; if (axis1_->size() != 1) { size_t index1 = axis1_->findAxisIndex(value1); *result += " "; *result += unit1->asString(axis1_->axisValue(index1), digits); *result += " "; *result += unit1->asString(axis1_->axisValue(index1 + 1), digits); *result += '\n'; *result += " --------------------\n"; *result += "| "; *result += table_unit->asString(tableValue(index1), digits); *result += " "; *result += table_unit->asString(tableValue(index1 + 1), digits); *result += '\n'; } *result += result_name; *result += " = "; *result += table_unit->asString(findValue(value1, value2, value3), digits); *result += '\n'; } void Table1::report(const Units *units, Report *report) const { int digits = 4; const Unit *unit1 = tableVariableUnit(axis1_->variable(), units); const Unit *table_unit = units->timeUnit(); report->reportLine("%s", tableVariableString(axis1_->variable())); report->reportLine("------------------------------"); string line; for (size_t index1 = 0; index1 < axis1_->size(); index1++) { line += unit1->asString(axis1_->axisValue(index1), digits); line += " "; } report->reportLineString(line); line.clear(); for (size_t index1 = 0; index1 < axis1_->size(); index1++) { line += table_unit->asString(tableValue(index1), digits); line += " "; } report->reportLineString(line); } //////////////////////////////////////////////////////////////// Table2::Table2(FloatTable *values, TableAxis *axis1, bool own_axis1, TableAxis *axis2, bool own_axis2) : Table(), values_(values), axis1_(axis1), own_axis1_(own_axis1), axis2_(axis2), own_axis2_(own_axis2) { } Table2::~Table2() { values_->deleteContents(); delete values_; if (own_axis1_) delete axis1_; if (own_axis2_) delete axis2_; } float Table2::tableValue(size_t index1, size_t index2) const { FloatSeq *row = (*values_)[index1]; return (*row)[index2]; } // Bilinear Interpolation. float Table2::findValue(float value1, float value2, float) const { size_t size1 = axis1_->size(); size_t size2 = axis2_->size(); if (size1 == 1) { if (size2 == 1) return tableValue(0, 0); else { size_t index2 = axis2_->findAxisIndex(value2); float x2 = value2; float y00 = tableValue(0, index2); float x2l = axis2_->axisValue(index2); float x2u = axis2_->axisValue(index2 + 1); float dx2 = (x2 - x2l) / (x2u - x2l); float y01 = tableValue(0, index2 + 1); float tbl_value = (1 - dx2) * y00 + dx2 * y01; return tbl_value; } } else if (size2 == 1) { size_t index1 = axis1_->findAxisIndex(value1); float x1 = value1; float y00 = tableValue(index1, 0); float x1l = axis1_->axisValue(index1); float x1u = axis1_->axisValue(index1 + 1); float dx1 = (x1 - x1l) / (x1u - x1l); float y10 = tableValue(index1 + 1, 0); float tbl_value = (1 - dx1) * y00 + dx1 * y10; return tbl_value; } else { size_t index1 = axis1_->findAxisIndex(value1); size_t index2 = axis2_->findAxisIndex(value2); float x1 = value1; float x2 = value2; float y00 = tableValue(index1, index2); float x1l = axis1_->axisValue(index1); float x1u = axis1_->axisValue(index1 + 1); float dx1 = (x1 - x1l) / (x1u - x1l); float y10 = tableValue(index1 + 1, index2); float y11 = tableValue(index1 + 1, index2 + 1); float x2l = axis2_->axisValue(index2); float x2u = axis2_->axisValue(index2 + 1); float dx2 = (x2 - x2l) / (x2u - x2l); float y01 = tableValue(index1, index2 + 1); float tbl_value = (1 - dx1) * (1 - dx2) * y00 + dx1 * (1 - dx2) * y10 + dx1 * dx2 * y11 + (1 - dx1) * dx2 * y01; return tbl_value; } } void Table2::reportValue(const char *result_name, const LibertyLibrary *library, const LibertyCell *, const Pvt *, float value1, const char *comment1, float value2, float value3, int digits, string *result) const { TableAxisVariable var1 = axis1_->variable(); TableAxisVariable var2 = axis2_->variable(); const Units *units = library->units(); const Unit *table_unit = units->timeUnit(); const Unit *unit1 = tableVariableUnit(var1, units); const Unit *unit2 = tableVariableUnit(var2, units); *result += "------- "; *result += tableVariableString(var1), *result += " = "; *result += unit1->asString(value1, digits); if (comment1) *result += comment1; *result += '\n'; *result += "| "; *result += tableVariableString(var2); *result += " = "; *result += unit2->asString(value2, digits); *result += '\n'; size_t index1 = axis1_->findAxisIndex(value1); size_t index2 = axis2_->findAxisIndex(value2); *result += "| "; *result += unit2->asString(axis2_->axisValue(index2), digits); if (axis2_->size() != 1) { *result += " "; *result += unit2->asString(axis2_->axisValue(index2 + 1), digits); } *result += '\n'; *result += "v --------------------\n"; *result += unit1->asString(axis1_->axisValue(index1), digits); *result += " | "; *result += table_unit->asString(tableValue(index1, index2), digits); if (axis2_->size() != 1) { *result += " "; *result += table_unit->asString(tableValue(index1, index2 + 1), digits); } *result += '\n'; if (axis1_->size() != 1) { *result += unit1->asString(axis1_->axisValue(index1 + 1), digits); *result += " | "; *result += table_unit->asString(tableValue(index1 + 1, index2), digits); if (axis2_->size() != 1) { *result += " "; *result +=table_unit->asString(tableValue(index1 + 1, index2 + 1),digits); } } *result += '\n'; *result += result_name; *result += " = "; *result += table_unit->asString(findValue(value1, value2, value3), digits); *result += '\n'; } void Table2::report(const Units *units, Report *report) const { int digits = 4; const Unit *table_unit = units->timeUnit(); const Unit *unit1 = tableVariableUnit(axis1_->variable(), units); const Unit *unit2 = tableVariableUnit(axis2_->variable(), units); report->reportLine("%s", tableVariableString(axis2_->variable())); report->reportLine(" ------------------------------"); string line = " "; for (size_t index2 = 0; index2 < axis2_->size(); index2++) { line += unit2->asString(axis2_->axisValue(index2), digits); line += " "; } report->reportLineString(line); for (size_t index1 = 0; index1 < axis1_->size(); index1++) { line = unit1->asString(axis1_->axisValue(index1), digits); line += " |"; for (size_t index2 = 0; index2 < axis2_->size(); index2++) { line += table_unit->asString(tableValue(index1, index2), digits); line += " "; } report->reportLineString(line); } } //////////////////////////////////////////////////////////////// Table3::Table3(FloatTable *values, TableAxis *axis1, bool own_axis1, TableAxis *axis2, bool own_axis2, TableAxis *axis3, bool own_axis3) : Table2(values, axis1, own_axis1, axis2, own_axis2), axis3_(axis3), own_axis3_(own_axis3) { } Table3::~Table3() { if (own_axis3_) delete axis3_; } float Table3::tableValue(size_t index1, size_t index2, size_t index3) const { size_t row = index1 * axis2_->size() + index2; return values_->operator[](row)->operator[](index3); } // Bilinear Interpolation. float Table3::findValue(float value1, float value2, float value3) const { size_t index1 = axis1_->findAxisIndex(value1); size_t index2 = axis2_->findAxisIndex(value2); size_t index3 = axis3_->findAxisIndex(value3); float x1 = value1; float x2 = value2; float x3 = value3; float dx1 = 0.0; float dx2 = 0.0; float dx3 = 0.0; float y000 = tableValue(index1, index2, index3); float y001 = 0.0; float y010 = 0.0; float y011 = 0.0; float y100 = 0.0; float y101 = 0.0; float y110 = 0.0; float y111 = 0.0; if (axis1_->size() != 1) { float x1l = axis1_->axisValue(index1); float x1u = axis1_->axisValue(index1 + 1); dx1 = (x1 - x1l) / (x1u - x1l); y100 = tableValue(index1 + 1, index2, index3); if (axis3_->size() != 1) y101 = tableValue(index1 + 1, index2, index3 + 1); if (axis2_->size() != 1) { y110 = tableValue(index1 + 1, index2 + 1, index3); if (axis3_->size() != 1) y111 = tableValue(index1 + 1, index2 + 1, index3 + 1); } } if (axis2_->size() != 1) { float x2l = axis2_->axisValue(index2); float x2u = axis2_->axisValue(index2 + 1); dx2 = (x2 - x2l) / (x2u - x2l); y010 = tableValue(index1, index2 + 1, index3); if (axis3_->size() != 1) y011 = tableValue(index1, index2 + 1, index3 + 1); } if (axis3_->size() != 1) { float x3l = axis3_->axisValue(index3); float x3u = axis3_->axisValue(index3 + 1); dx3 = (x3 - x3l) / (x3u - x3l); y001 = tableValue(index1, index2, index3 + 1); } float tbl_value = (1 - dx1) * (1 - dx2) * (1 - dx3) * y000 + (1 - dx1) * (1 - dx2) * dx3 * y001 + (1 - dx1) * dx2 * (1 - dx3) * y010 + (1 - dx1) * dx2 * dx3 * y011 + dx1 * (1 - dx2) * (1 - dx3) * y100 + dx1 * (1 - dx2) * dx3 * y101 + dx1 * dx2 * (1 - dx3) * y110 + dx1 * dx2 * dx3 * y111; return tbl_value; } // Sample output. // // --------- input_net_transition = 0.00 // | ---- total_output_net_capacitance = 0.20 // | | related_out_total_output_net_capacitance = 0.10 // | | 0.00 0.30 // v | -------------------- // 0.01 v / 0.23 0.25 // 0.00 0.20 | 0.10 0.20 // |/ 0.30 0.32 // 0.40 | 0.20 0.30 void Table3::reportValue(const char *result_name, const LibertyLibrary *library, const LibertyCell *, const Pvt *, float value1, const char *comment1, float value2, float value3, int digits, string *result) const { TableAxisVariable var1 = axis1_->variable(); TableAxisVariable var2 = axis2_->variable(); TableAxisVariable var3 = axis3_->variable(); const Units *units = library->units(); const Unit *table_unit = units->timeUnit(); const Unit *unit1 = tableVariableUnit(var1, units); const Unit *unit2 = tableVariableUnit(var2, units); const Unit *unit3 = tableVariableUnit(var3, units); *result += " --------- "; *result += tableVariableString(var1), *result += " = "; *result += unit1->asString(value1, digits); if (comment1) *result += comment1; *result += '\n'; *result += " | ---- "; *result += tableVariableString(var2), *result += " = "; *result += unit2->asString(value2, digits); *result += '\n'; *result += " | | "; *result += tableVariableString(var3); *result += " = "; *result += unit3->asString(value3, digits); *result += '\n'; size_t index1 = axis1_->findAxisIndex(value1); size_t index2 = axis2_->findAxisIndex(value2); size_t index3 = axis3_->findAxisIndex(value3); *result += " | | "; *result += unit3->asString(axis3_->axisValue(index3), digits); if (axis3_->size() != 1) { *result += " "; *result += unit3->asString(axis3_->axisValue(index3 + 1), digits); } *result += '\n'; *result += " v | --------------------\n"; if (axis1_->size() != 1) { *result += " "; *result += unit1->asString(axis1_->axisValue(index1+1), digits); *result += " v / "; *result += table_unit->asString(tableValue(index1+1,index2,index3), digits); if (axis3_->size() != 1) { *result += " "; *result += table_unit->asString(tableValue(index1+1,index2,index3+1), digits); } } else { appendSpaces(result, digits+3); *result += " v / "; } *result += '\n'; *result += unit1->asString(axis1_->axisValue(index1), digits); *result += " "; *result += unit2->asString(axis2_->axisValue(index2), digits); *result += " | "; *result += table_unit->asString(tableValue(index1, index2, index3), digits); if (axis3_->size() != 1) { *result += " "; *result += table_unit->asString(tableValue(index1, index2, index3+1), digits); } *result += '\n'; *result += " |/ "; if (axis1_->size() != 1 && axis2_->size() != 1) { *result += table_unit->asString(tableValue(index1+1,index2+1,index3), digits); if (axis3_->size() != 1) { *result += " "; *result +=table_unit->asString(tableValue(index1+1,index2+1,index3+1), digits); } } *result += '\n'; *result += " "; *result += unit2->asString(axis2_->axisValue(index2 + 1), digits); *result += " | "; if (axis2_->size() != 1) { *result += table_unit->asString(tableValue(index1, index2+1, index3), digits); if (axis3_->size() != 1) { *result += " "; *result +=table_unit->asString(tableValue(index1, index2+1,index3+1), digits); } } *result += '\n'; *result += result_name; *result += " = "; *result += table_unit->asString(findValue(value1, value2, value3), digits); *result += '\n'; } static void appendSpaces(string *result, int count) { while (count--) *result += ' '; } void Table3::report(const Units *units, Report *report) const { int digits = 4; const Unit *table_unit = units->timeUnit(); const Unit *unit1 = tableVariableUnit(axis1_->variable(), units); const Unit *unit2 = tableVariableUnit(axis2_->variable(), units); const Unit *unit3 = tableVariableUnit(axis3_->variable(), units); for (size_t index1 = 0; index1 < axis1_->size(); index1++) { report->reportLine("%s %s", tableVariableString(axis1_->variable()), unit1->asString(axis1_->axisValue(index1), digits)); report->reportLine("%s", tableVariableString(axis3_->variable())); report->reportLine(" ------------------------------"); string line = " "; for (size_t index3 = 0; index3 < axis3_->size(); index3++) { line += unit3->asString(axis3_->axisValue(index3), digits); line += " "; } report->reportLineString(line); for (size_t index2 = 0; index2 < axis2_->size(); index2++) { line = unit2->asString(axis2_->axisValue(index2),digits); line += " |"; for (size_t index3 = 0; index3 < axis3_->size(); index3++) { line += table_unit->asString(tableValue(index1, index2, index3), digits); line += " "; } report->reportLineString(line); } } } //////////////////////////////////////////////////////////////// TableAxis::TableAxis(TableAxisVariable variable, FloatSeq *values) : variable_(variable), values_(values) { } TableAxis::~TableAxis() { delete values_; } // Bisection search. size_t TableAxis::findAxisIndex(float value) const { int max = static_cast(values_->size()) - 1; if (value <= (*values_)[0] || max == 0) return 0; else if (value >= (*values_)[max]) // Return max-1 for value too large so interpolation pts are index,index+1. return max - 1; else { int lower = -1; int upper = max + 1; bool ascend = ((*values_)[max] >= (*values_)[0]); while (upper - lower > 1) { int mid = (upper + lower) >> 1; if ((value >= (*values_)[mid]) == ascend) lower = mid; else upper = mid; } return lower; } } //////////////////////////////////////////////////////////////// static EnumNameMap table_axis_variable_map = {{TableAxisVariable::total_output_net_capacitance, "total_output_net_capacitance"}, {TableAxisVariable::equal_or_opposite_output_net_capacitance, "equal_or_opposite_output_net_capacitance"}, {TableAxisVariable::input_net_transition, "input_net_transition"}, {TableAxisVariable::input_transition_time, "input_transition_time"}, {TableAxisVariable::related_pin_transition, "related_pin_transition"}, {TableAxisVariable::constrained_pin_transition, "constrained_pin_transition"}, {TableAxisVariable::output_pin_transition, "output_pin_transition"}, {TableAxisVariable::connect_delay, "connect_delay"}, {TableAxisVariable::related_out_total_output_net_capacitance, "related_out_total_output_net_capacitance"}, {TableAxisVariable::time, "time"}, {TableAxisVariable::iv_output_voltage, "iv_output_voltage"}, {TableAxisVariable::input_noise_width, "input_noise_width"}, {TableAxisVariable::input_noise_height, "input_noise_height"}, {TableAxisVariable::input_voltage, "input_voltage"}, {TableAxisVariable::output_voltage, "output_voltage"}, {TableAxisVariable::path_depth, "path_depth"}, {TableAxisVariable::path_distance, "path_distance"}, {TableAxisVariable::normalized_voltage, "normalized_voltage"} }; TableAxisVariable stringTableAxisVariable(const char *variable) { return table_axis_variable_map.find(variable, TableAxisVariable::unknown); } const char * tableVariableString(TableAxisVariable variable) { return table_axis_variable_map.find(variable); } const Unit * tableVariableUnit(TableAxisVariable variable, const Units *units) { switch (variable) { case TableAxisVariable::total_output_net_capacitance: case TableAxisVariable::related_out_total_output_net_capacitance: case TableAxisVariable::equal_or_opposite_output_net_capacitance: return units->capacitanceUnit(); case TableAxisVariable::input_net_transition: case TableAxisVariable::input_transition_time: case TableAxisVariable::related_pin_transition: case TableAxisVariable::constrained_pin_transition: case TableAxisVariable::output_pin_transition: case TableAxisVariable::connect_delay: case TableAxisVariable::time: case TableAxisVariable::input_noise_height: return units->timeUnit(); case TableAxisVariable::input_voltage: case TableAxisVariable::output_voltage: case TableAxisVariable::iv_output_voltage: case TableAxisVariable::input_noise_width: return units->voltageUnit(); case TableAxisVariable::path_distance: return units->distanceUnit(); case TableAxisVariable::path_depth: case TableAxisVariable::normalized_voltage: case TableAxisVariable::unknown: return units->scalarUnit(); } // Prevent warnings from lame compilers. return nullptr; } } // namespace