// OpenSTA, Static Timing Analyzer // Copyright (c) 2025, 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 . // // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. // // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // This notice may not be removed or altered from any source distribution. #include "LibertyReader.hh" #include #include #include #include #include "ContainerHelpers.hh" #include "EnumNameMap.hh" #include "Report.hh" #include "Debug.hh" #include "Units.hh" #include "Transition.hh" #include "FuncExpr.hh" #include "TimingArc.hh" #include "TableModel.hh" #include "LeakagePower.hh" #include "InternalPower.hh" #include "LinearModel.hh" #include "Wireload.hh" #include "EquivCells.hh" #include "LibExprReader.hh" #include "Liberty.hh" #include "LibertyBuilder.hh" #include "LibertyReaderPvt.hh" #include "PortDirection.hh" #include "ParseBus.hh" #include "Network.hh" extern int LibertyParse_debug; namespace sta { static void scaleFloats(FloatSeq &floats, float scale); LibertyLibrary * readLibertyFile(const char *filename, bool infer_latches, Network *network) { LibertyReader reader(filename, infer_latches, network); return reader.readLibertyFile(filename); } LibertyReader::LibertyReader(const char *filename, bool infer_latches, Network *network) : LibertyGroupVisitor(), filename_(filename), infer_latches_(infer_latches), report_(network->report()), debug_(network->debug()), network_(network), builder_(debug_, report_), library_(nullptr), first_cell_(true) { defineVisitors(); } LibertyLibrary * LibertyReader::readLibertyFile(const char *filename) { //::LibertyParse_debug = 1; parseLibertyFile(filename, this, report_); return library_; } void LibertyReader::defineGroupVisitor(const char *type, LibraryGroupVisitor begin_visitor, LibraryGroupVisitor end_visitor) { if (begin_visitor) group_begin_map_[type] = begin_visitor; if (end_visitor) group_end_map_[type] = end_visitor; } void LibertyReader::defineVisitors() { defineGroupVisitor("library", &LibertyReader::beginLibrary, &LibertyReader::endLibrary); defineGroupVisitor("cell", nullptr, &LibertyReader::endCell); defineGroupVisitor("scaled_cell", nullptr, &LibertyReader::endScaledCell); } void LibertyReader::visitAttr(const LibertySimpleAttr *) { } void LibertyReader::visitAttr(const LibertyComplexAttr *) { } void LibertyReader::begin(const LibertyGroup *group, LibertyGroup *parent_group) { LibraryGroupVisitor *visitor = findKeyValuePtr(group_begin_map_, group->type()); if (visitor) (this->**visitor)(group, parent_group); } void LibertyReader::end(const LibertyGroup *group, LibertyGroup *parent_group) { LibraryGroupVisitor *visitor = findKeyValuePtr(group_end_map_, group->type()); if (visitor) (this->**visitor)(group, parent_group); } void LibertyReader::beginLibrary(const LibertyGroup *library_group, LibertyGroup *) { makeLibrary(library_group); } void LibertyReader::endLibrary(const LibertyGroup *group, LibertyGroup *) { // If a library hasno cells endCell is not called. if (first_cell_) readLibraryAttributes(group); delete group; } //////////////////////////////////////////////////////////////// void LibertyReader::endCell(const LibertyGroup *cell_group, LibertyGroup *library_group) { // Read library groups defined since the last cell was read. // Normally they are all defined by the first cell, but there // are libraries that define table templates and bus tyupes // between cells. if (first_cell_) readLibraryAttributes(library_group); else { readTableTemplates(library_group); readBusTypes(nullptr, library_group); } const char *name = cell_group->firstName(); if (name) { debugPrint(debug_, "liberty", 1, "cell %s", name); LibertyCell *cell = builder_.makeCell(library_, name, filename_); readCell(cell, cell_group); } else libWarn(1193, cell_group, "cell missing name."); library_group->clear(); first_cell_ = false; } void LibertyReader::endScaledCell(const LibertyGroup *scaled_cell_group, LibertyGroup *library_group) { readLibraryAttributes(library_group); readScaledCell(scaled_cell_group); library_group->deleteSubgroup(scaled_cell_group); } //////////////////////////////////////////////////////////////// void LibertyReader::readLibraryAttributes(const LibertyGroup *library_group) { readTechnology(library_group); readLibraryUnits(library_group); readThresholds(library_group); readDelayModel(library_group); readBusStyle(library_group); readBusTypes(nullptr, library_group); readTableTemplates(library_group); readVoltateMaps(library_group); readWireloads(library_group); readWireloadSelection(library_group); readDefaultWireLoad(library_group); readDefaultWireLoadMode(library_group); readDefaultWireLoadSelection(library_group); readOperatingConds(library_group); readScaleFactors(library_group); readOcvDerateFactors(nullptr, library_group); readDefaultOcvDerateGroup(library_group); readGroupAttrFloat("ocv_arc_depth", library_group, [this](float v) { library_->setOcvArcDepth(v); }); readNormalizedDriverWaveform(library_group); readSlewDegradations(library_group); readLibAttrFloat(library_group, "nom_temperature", &LibertyLibrary::setNominalTemperature, 1.0F); readLibAttrFloat(library_group, "nom_voltage", &LibertyLibrary::setNominalVoltage, volt_scale_); readLibAttrFloat(library_group, "nom_process", &LibertyLibrary::setNominalProcess, 1.0F); readLibAttrFloat(library_group, "default_inout_pin_cap", &LibertyLibrary::setDefaultBidirectPinCap, cap_scale_); readLibAttrFloat(library_group, "default_input_pin_cap", &LibertyLibrary::setDefaultInputPinCap, cap_scale_); readLibAttrFloat(library_group, "default_output_pin_cap", &LibertyLibrary::setDefaultOutputPinCap, cap_scale_); readLibAttrFloatWarnZero(library_group, "default_max_transition", &LibertyLibrary::setDefaultMaxSlew, time_scale_); readLibAttrFloatWarnZero(library_group, "default_max_fanout", &LibertyLibrary::setDefaultMaxFanout, 1.0F); readLibAttrFloat(library_group, "default_intrinsic_rise", &LibertyLibrary::setDefaultIntrinsic, RiseFall::rise(), time_scale_); readLibAttrFloat(library_group, "default_intrinsic_fall", &LibertyLibrary::setDefaultIntrinsic, RiseFall::fall(), time_scale_); readLibAttrFloat(library_group, "default_inout_pin_rise_res", &LibertyLibrary::setDefaultBidirectPinRes, RiseFall::rise(), res_scale_); readLibAttrFloat(library_group, "default_inout_pin_fall_res", &LibertyLibrary::setDefaultBidirectPinRes, RiseFall::fall(), res_scale_); readLibAttrFloat(library_group, "default_output_pin_rise_res", &LibertyLibrary::setDefaultOutputPinRes, RiseFall::rise(), res_scale_); readLibAttrFloat(library_group, "default_output_pin_fall_res", &LibertyLibrary::setDefaultOutputPinRes, RiseFall::fall(), res_scale_); readLibAttrFloatWarnZero(library_group, "default_fanout_load", &LibertyLibrary::setDefaultFanoutLoad, 1.0F); readLibAttrFloat(library_group, "slew_derate_from_library", &LibertyLibrary::setSlewDerateFromLibrary, 1.0F); } void LibertyReader::makeLibrary(const LibertyGroup *libary_group) { const char *name = libary_group->firstName(); if (name) { LibertyLibrary *library = network_->findLiberty(name); if (library) libWarn(1140, libary_group, "library %s already exists.", name); // Make a new library even if a library with the same name exists. // Both libraries may be accessed by min/max analysis points. library_ = network_->makeLibertyLibrary(name, filename_); // 1ns default time_scale_ = 1E-9F; // 1ohm default res_scale_ = 1.0F; // pF default cap_scale_ = 1E-12F; // 1v default volt_scale_ = 1; // Default is 1mA. current_scale_ = 1E-3F; // Default is 1; power_scale_ = 1; // Default is 1 micron. distance_scale_ = 1e-6; library_->units()->timeUnit()->setScale(time_scale_); library_->units()->capacitanceUnit()->setScale(cap_scale_); library_->units()->resistanceUnit()->setScale(res_scale_); library_->units()->voltageUnit()->setScale(volt_scale_); library_->units()->currentUnit()->setScale(current_scale_); library_->units()->distanceUnit()->setScale(distance_scale_); library_->setDelayModelType(DelayModelType::cmos_linear); } else libError(1141, libary_group, "library missing name."); } // Energy scale is derived from other units. float LibertyReader::energyScale() { return volt_scale_ * volt_scale_ * cap_scale_; } void LibertyReader::readTechnology(const LibertyGroup *library_group) { const LibertyComplexAttr *tech_attr = library_group->findComplexAttr("technology"); if (tech_attr) { const LibertyAttrValue *tech_value = tech_attr->firstValue(); if (tech_value) { const std::string &tech = tech_value->stringValue(); if (tech == "fpga") library_->setDelayModelType(DelayModelType::cmos_linear); } } } void LibertyReader::readLibraryUnits(const LibertyGroup *library_group) { readUnit("time_unit", "s", time_scale_, library_->units()->timeUnit(), library_group); readUnit("pulling_resistance_unit", "ohm", res_scale_, library_->units()->resistanceUnit(), library_group); readUnit("voltage_unit", "V", volt_scale_, library_->units()->voltageUnit(), library_group); readUnit("current_unit", "A", current_scale_, library_->units()->currentUnit(), library_group); readUnit("leakage_power_unit", "W", power_scale_, library_->units()->powerUnit(), library_group); readUnit("distance_unit", "m", distance_scale_, library_->units()->distanceUnit(), library_group); const LibertyComplexAttr *cap_attr = library_group->findComplexAttr("capacitive_load_unit"); if (cap_attr) { const LibertyAttrValueSeq &values = cap_attr->values(); if (values.size() == 2) { LibertyAttrValue *value = values[0]; bool valid = false; float scale; if (value->isFloat()) { scale = value->floatValue(); valid = true; } else if (value->isString()) { try { scale = std::stof(value->stringValue()); valid = true; } catch (...) { valid = false; } } if (valid) { value = values[1]; if (value->isString()) { const std::string suffix = value->stringValue(); if (stringEqual(suffix.c_str(), "ff")) cap_scale_ = scale * 1E-15F; else if (stringEqual(suffix.c_str(), "pf")) cap_scale_ = scale * 1E-12F; else libWarn(1154, cap_attr, "capacitive_load_units are not ff or pf."); } else libWarn(1155, cap_attr, "capacitive_load_units are not a string."); } else libWarn(1157, cap_attr, "capacitive_load_units scale is not a float."); } else if (values.size() == 1) libWarn(1156, cap_attr, "capacitive_load_units missing suffix."); else libWarn(1158, cap_attr, "capacitive_load_units missing scale and suffix."); library_->units()->capacitanceUnit()->setScale(cap_scale_); } } void LibertyReader::readUnit(const char *unit_attr_name, const char *unit_suffix, float &scale_var, Unit *unit, const LibertyGroup *library_group) { const LibertySimpleAttr *unit_attr = library_group->findSimpleAttr(unit_attr_name); if (unit_attr) { const std::string *units = unit_attr->stringValue(); if (units) { // Unit format is . // Find the multiplier digits. std::string units1 = *units; size_t mult_end = units1.find_first_not_of("0123456789"); float mult = 1.0F; std::string scale_suffix; if (mult_end != units1.npos) { std::string unit_mult = units1.substr(0, mult_end); scale_suffix = units1.substr(mult_end); if (unit_mult == "1") mult = 1.0F; else if (unit_mult == "10") mult = 10.0F; else if (unit_mult == "100") mult = 100.0F; else libWarn(1150, unit_attr, "unknown unit multiplier %s.", unit_mult.c_str()); } else scale_suffix = *units; float scale_mult = 1.0F; if (scale_suffix.size() == strlen(unit_suffix) + 1) { std::string suffix = scale_suffix.substr(1); if (stringEqual(suffix.c_str(), unit_suffix)) { char scale_char = tolower(scale_suffix[0]); if (scale_char == 'k') scale_mult = 1E+3F; else if (scale_char == 'm') scale_mult = 1E-3F; else if (scale_char == 'u') scale_mult = 1E-6F; else if (scale_char == 'n') scale_mult = 1E-9F; else if (scale_char == 'p') scale_mult = 1E-12F; else if (scale_char == 'f') scale_mult = 1E-15F; else libWarn(1151, unit_attr, "unknown unit scale %c.", scale_char); } else libWarn(1152, unit_attr, "unknown unit suffix %s.", suffix.c_str()); } else if (!stringEqual(scale_suffix.c_str(), unit_suffix)) libWarn(1153, unit_attr, "unknown unit suffix %s.", scale_suffix.c_str()); scale_var = scale_mult * mult; unit->setScale(scale_var); } } } void LibertyReader::readDelayModel(const LibertyGroup *library_group) { const std::string *type_name = library_group->findAttrString("delay_model"); if (type_name) { if (*type_name == "table_lookup") library_->setDelayModelType(DelayModelType::table); else if (*type_name == "generic_cmos") library_->setDelayModelType(DelayModelType::cmos_linear); else if (*type_name == "piecewise_cmos") { library_->setDelayModelType(DelayModelType::cmos_pwl); libWarn(1160, library_group, "delay_model %s not supported.", type_name->c_str()); } else if (*type_name == "cmos2") { library_->setDelayModelType(DelayModelType::cmos2); libWarn(1161, library_group, "delay_model %s not supported.", type_name->c_str()); } else if (*type_name == "polynomial") { library_->setDelayModelType(DelayModelType::polynomial); libWarn(1162, library_group, "delay_model %s not supported.", type_name->c_str()); } // Evil IBM garbage. else if (*type_name == "dcm") { library_->setDelayModelType(DelayModelType::dcm); libWarn(1163, library_group, "delay_model %s not supported..", type_name->c_str()); } else libWarn(1164, library_group, "unknown delay_model %s.", type_name->c_str()); } } void LibertyReader::readBusStyle(const LibertyGroup *library_group) { const std::string *bus_style = library_group->findAttrString("bus_naming_style"); if (bus_style) { // Assume bus style is of the form "%s[%d]". if (bus_style->size() == 6 && (*bus_style)[0] == '%' && (*bus_style)[1] == 's' && (*bus_style)[3] == '%' && (*bus_style)[4] == 'd') library_->setBusBrkts((*bus_style)[2], (*bus_style)[5]); else libWarn(1165, library_group, "unknown bus_naming_style format."); } } void LibertyReader::readBusTypes(LibertyCell *cell, const LibertyGroup *group) { for (const LibertyGroup *type_group : group->findSubgroups("type")) { const char *name = type_group->firstName(); if (name) { int from, to; bool from_exists, to_exists; type_group->findAttrInt("bit_from", from, from_exists); type_group->findAttrInt("bit_to", to, to_exists); if (from_exists && to_exists) { if (cell) cell->makeBusDcl(name, from, to); else library_->makeBusDcl(name, from, to); } else if (!from_exists) libWarn(1179, type_group, "bus type missing bit_from."); else if (!to_exists) libWarn(1180, type_group, "bus type missing bit_to."); } } } void LibertyReader::readThresholds(const LibertyGroup *library_group) { for (const RiseFall *rf : RiseFall::range()) { std::string suffix = rf->to_string_long(); readLibAttrFloat(library_group, ("input_threshold_pct_" + suffix).c_str(), &LibertyLibrary::setInputThreshold, rf, 0.01F); if (library_->inputThreshold(rf) == 0.0) libWarn(1145, library_group, "input_threshold_pct_%s not found.", rf->name()); readLibAttrFloat(library_group, ("output_threshold_pct_" + suffix).c_str(), &LibertyLibrary::setOutputThreshold, rf, 0.01F); if (library_->outputThreshold(rf) == 0.0) libWarn(1146, library_group, "output_threshold_pct_%s not found.", rf->name()); readLibAttrFloat(library_group, ("slew_lower_threshold_pct_" + suffix).c_str(), &LibertyLibrary::setSlewLowerThreshold, rf, 0.01F); if (library_->slewLowerThreshold(rf) == 0.0) libWarn(1147, library_group, "slew_lower_threshold_pct_%s not found.", rf->name()); readLibAttrFloat(library_group, ("slew_upper_threshold_pct_" + suffix).c_str(), &LibertyLibrary::setSlewUpperThreshold, rf, 0.01F); if (library_->slewUpperThreshold(rf) == 0.0) libWarn(1148, library_group, "slew_upper_threshold_pct_%s not found.", rf->name()); } } void LibertyReader::readTableTemplates(const LibertyGroup *library_group) { readTableTemplates(library_group, "lu_table_template", TableTemplateType::delay); readTableTemplates(library_group, "output_current_template", TableTemplateType::output_current); readTableTemplates(library_group, "power_lut_template", TableTemplateType::power); readTableTemplates(library_group, "ocv_table_template", TableTemplateType::ocv); } void LibertyReader::readTableTemplates(const LibertyGroup *library_group, const char *group_name, TableTemplateType type) { for (const LibertyGroup *template_group : library_group->findSubgroups(group_name)) { const char *name = template_group->firstName(); if (name) { TableTemplate *tbl_template = library_->makeTableTemplate(name, type); TableAxisPtr axis1 = makeTableTemplateAxis(template_group, 1); if (axis1) tbl_template->setAxis1(axis1); TableAxisPtr axis2 = makeTableTemplateAxis(template_group, 2); if (axis2) tbl_template->setAxis2(axis2); TableAxisPtr axis3 = makeTableTemplateAxis(template_group, 3); if (axis3) tbl_template->setAxis3(axis3); } else libWarn(1175, template_group, "table template missing name."); } } TableAxisPtr LibertyReader::makeTableTemplateAxis(const LibertyGroup *template_group, int axis_index) { std::string var_attr_name = "variable_" + std::to_string(axis_index); const std::string *var_name = template_group->findAttrString(var_attr_name); if (var_name) { TableAxisVariable axis_var = stringTableAxisVariable(var_name->c_str()); if (axis_var == TableAxisVariable::unknown) libWarn(1297, template_group, "axis type %s not supported.", var_name->c_str()); else { std::string index_attr_name = "index_" + std::to_string(axis_index); const LibertyComplexAttr *index_attr = template_group->findComplexAttr(index_attr_name); FloatSeq axis_values; if (index_attr) { axis_values = readFloatSeq(index_attr, 1.0F); if (!axis_values.empty()) { float prev = axis_values[0]; for (size_t i = 1; i < axis_values.size(); i++) { float value = axis_values[i]; if (value <= prev) { libWarn(1178, template_group, "non-increasing table index values."); break; } prev = value; } } } const Units *units = library_->units(); float scale = tableVariableUnit(axis_var, units)->scale(); scaleFloats(axis_values, scale); return make_shared(axis_var, std::move(axis_values)); } } return nullptr; } static void scaleFloats(FloatSeq &floats, float scale) { size_t count = floats.size(); for (size_t i = 0; i < count; i++) floats[i] *= scale; } void LibertyReader::readVoltateMaps(const LibertyGroup *library_group) { for (const LibertyComplexAttr *volt_attr : library_group->findComplexAttrs("voltage_map")) { const LibertyAttrValueSeq &values = volt_attr->values(); if (values.size() == 2) { const std::string &volt_name = values[0]->stringValue(); float volt; bool valid; values[1]->floatValue(volt, valid); if (valid) library_->addSupplyVoltage(volt_name.c_str(), volt); else libWarn(1166, volt_attr, "voltage_map voltage is not a float."); } } } void LibertyReader::readOperatingConds(const LibertyGroup *library_group) { for (const LibertyGroup *opcond_group : library_group->findSubgroups("operating_conditions")) { const char *name = opcond_group->firstName(); if (name) { OperatingConditions *op_cond = library_->makeOperatingConditions(name); float value; bool exists; opcond_group->findAttrFloat("process", value, exists); if (exists) op_cond->setProcess(value); opcond_group->findAttrFloat("temperature", value, exists); if (exists) op_cond->setTemperature(value); opcond_group->findAttrFloat("voltage", value, exists); if (exists) op_cond->setVoltage(value); const std::string *tree_type = opcond_group->findAttrString("tree_type"); if (tree_type) { WireloadTree wireload_tree = stringWireloadTree(tree_type->c_str()); op_cond->setWireloadTree(wireload_tree); } } } const std::string *default_op_cond = library_group->findAttrString("default_operating_conditions"); if (default_op_cond) { OperatingConditions *op_cond = library_->findOperatingConditions(default_op_cond->c_str()); if (op_cond) library_->setDefaultOperatingConditions(op_cond); else libWarn(1144, library_group, "default_operating_condition %s not found.", default_op_cond->c_str()); } } void LibertyReader::readScaleFactors(const LibertyGroup *library_group) { // Top level scale factors. ScaleFactors *scale_factors = library_->makeScaleFactors(""); library_->setScaleFactors(scale_factors); readScaleFactors(library_group, scale_factors); // Named scale factors. for (const LibertyGroup *scale_group : library_group->findSubgroups("scaling_factors")){ const char *name = scale_group->firstName(); if (name) { ScaleFactors *scale_factors = library_->makeScaleFactors(name); readScaleFactors(scale_group, scale_factors); } } } void LibertyReader::readScaleFactors(const LibertyGroup *scale_group, ScaleFactors *scale_factors) { // Skip unknown type. for (int type_index = 0; type_index < scale_factor_type_count - 1; type_index++) { ScaleFactorType type = static_cast(type_index); const char *type_name = scaleFactorTypeName(type); // Skip unknown pvt. for (int pvt_index = 0; pvt_index < scale_factor_pvt_count - 1; pvt_index++) { ScaleFactorPvt pvt = static_cast(pvt_index); const std::string pvt_name = scaleFactorPvtName(pvt); std::string attr_name; for (const RiseFall *rf : RiseFall::range()) { if (scaleFactorTypeRiseFallSuffix(type)) { const std::string rf_name = (rf == RiseFall::rise()) ? "rise" : "fall"; attr_name = "k_" + pvt_name + "_" + type_name + "_" + rf_name; } else if (scaleFactorTypeRiseFallPrefix(type)) { const char *rf_name = (rf == RiseFall::rise()) ? "rise" : "fall"; attr_name = "k_" + pvt_name + "_" + rf_name + "_" + type_name; } else if (scaleFactorTypeLowHighSuffix(type)) { const char *rf_name = (rf == RiseFall::rise()) ? "high":"low"; attr_name = "k_" + pvt_name + "_" + type_name + "_" + rf_name; } else attr_name = "k_" + pvt_name + "_" + type_name; float value; bool exists; scale_group->findAttrFloat(attr_name, value, exists); if (exists) scale_factors->setScale(type, pvt, rf, value); } } } } void LibertyReader::readWireloads(const LibertyGroup *library_group) { for (const LibertyGroup *wl_group : library_group->findSubgroups("wire_load")) { const char *name = wl_group->firstName(); if (name) { Wireload *wireload = library_->makeWireload(name); float value; bool exists; wl_group->findAttrFloat("resistance", value, exists); if (exists) wireload->setResistance(value * res_scale_); wl_group->findAttrFloat("capacitance", value, exists); if (exists) wireload->setCapacitance(value * cap_scale_); wl_group->findAttrFloat("slope", value, exists); if (exists) wireload->setSlope(value); for (const LibertyComplexAttr *fanout_attr : wl_group->findComplexAttrs("fanout_length")) { float fanout, length; bool exists; getAttrFloat2(fanout_attr, fanout, length, exists); if (exists) wireload->addFanoutLength(fanout, length); else libWarn(1185, fanout_attr, "fanout_length is missing length and fanout."); } } else libWarn(1184, wl_group, "wire_load missing name."); } } void LibertyReader::readWireloadSelection(const LibertyGroup *library_group) { const LibertyGroup *sel_group = library_group->findSubgroup("wire_load_selection"); if (sel_group) { const char *name = sel_group->firstName(); if (name == nullptr) name = ""; WireloadSelection *wireload_selection = library_->makeWireloadSelection(name); for (const LibertyComplexAttr *area_attr : sel_group->findComplexAttrs("wire_load_from_area")) { const LibertyAttrValueSeq &values = area_attr->values(); if (values.size() == 3) { LibertyAttrValue *value = values[0]; if (value->isFloat()) { float min_area = value->floatValue(); value = values[1]; if (value->isFloat()) { float max_area = value->floatValue(); value = values[2]; if (value->isString()) { const std::string &wireload_name = value->stringValue(); const Wireload *wireload = library_->findWireload(wireload_name.c_str()); if (wireload) wireload_selection->addWireloadFromArea(min_area, max_area, wireload); else libWarn(1187, area_attr, "wireload %s not found.", wireload_name.c_str()); } else libWarn(1188, area_attr, "wire_load_from_area wireload name not a string."); } else libWarn(1189, area_attr, "wire_load_from_area min not a float."); } else libWarn(1190, area_attr, "wire_load_from_area max not a float."); } else libWarn(1191, area_attr, "wire_load_from_area missing parameters."); } } } void LibertyReader::readDefaultWireLoad(const LibertyGroup *library_group) { const std::string *wireload_name = library_group->findAttrString("default_wire_load"); if (wireload_name) { const Wireload *wireload = library_->findWireload(wireload_name->c_str()); if (wireload) library_->setDefaultWireload(wireload); else libWarn(1142, library_group, "default_wire_load %s not found.", wireload_name->c_str()); } } void LibertyReader::readDefaultWireLoadMode(const LibertyGroup *library_group) { const std::string *wire_load_mode = library_group->findAttrString("default_wire_load_mode"); if (wire_load_mode) { WireloadMode mode = stringWireloadMode(wire_load_mode->c_str()); if (mode != WireloadMode::unknown) library_->setDefaultWireloadMode(mode); else libWarn(1174, library_group, "default_wire_load_mode %s not found.", wire_load_mode->c_str()); } } void LibertyReader::readDefaultWireLoadSelection(const LibertyGroup *library_group) { const std::string *selection_name = library_group->findAttrString("default_wire_load_selection"); if (selection_name) { const WireloadSelection *selection = library_->findWireloadSelection(selection_name->c_str()); if (selection) library_->setDefaultWireloadSelection(selection); else libWarn(1143, library_group, "default_wire_selection %s not found.", selection_name->c_str()); } } void LibertyReader::readModeDefs(LibertyCell *cell, const LibertyGroup *cell_group) { for (const LibertyGroup *mode_group : cell_group->findSubgroups("mode_definition")) { const char *name = mode_group->firstName(); if (name) { ModeDef *mode_def = cell->makeModeDef(name); for (const LibertyGroup *value_group : mode_group->findSubgroups("mode_value")) { const char *value_name = value_group->firstName(); if (value_name) { ModeValueDef *mode_value = mode_def->defineValue(value_name, nullptr, nullptr); const std::string *sdf_cond = value_group->findAttrString("sdf_cond"); if (sdf_cond) mode_value->setSdfCond(sdf_cond->c_str()); const std::string *when = value_group->findAttrString("when"); if (when) { // line FuncExpr *when_expr = parseFunc(when->c_str(), "when", cell, value_group->line()); mode_value->setCond(when_expr); } } else libWarn(1264, value_group, "mode value missing name."); } } else libWarn(1263, mode_group, "mode definition missing name."); } } void LibertyReader::readSlewDegradations(const LibertyGroup *library_group) { for (const RiseFall *rf : RiseFall::range()) { const std::string group_name = rf->to_string_long() + "_transition_degradation"; const LibertyGroup *degradation_group = library_group->findSubgroup(group_name.c_str()); if (degradation_group) { TableModel *table_model = readTableModel(degradation_group, rf, TableTemplateType::delay, time_scale_, ScaleFactorType::transition); if (LibertyLibrary::checkSlewDegradationAxes(table_model)) library_->setWireSlewDegradationTable(table_model, rf); else libWarn(1254, degradation_group, "unsupported model axis."); } } } void LibertyReader::readLibAttrFloat(const LibertyGroup *library_group, const char *attr_name, void (LibertyLibrary::*set_func)(float value), float scale) { float value; bool exists; library_group->findAttrFloat(attr_name, value, exists); if (exists) (library_->*set_func)(value * scale); } void LibertyReader::readLibAttrFloat(const LibertyGroup *library_group, const char *attr_name, void (LibertyLibrary::*set_func)(const RiseFall *rf, float value), const RiseFall *rf, float scale) { float value; bool exists; library_group->findAttrFloat(attr_name, value, exists); if (exists) (library_->*set_func)(rf, value * scale); } void LibertyReader::readLibAttrFloatWarnZero(const LibertyGroup *library_group, const char *attr_name, void (LibertyLibrary::*set_func)(float value), float scale) { float value; bool exists; library_group->findAttrFloat(attr_name, value, exists); if (exists) { if (value == 0.0F) { const LibertySimpleAttr *attr = library_group->findSimpleAttr(attr_name); if (attr) libWarn(1171, attr, "%s is 0.0.", attr_name); else libWarn(1172, library_group, "%s is 0.0.", attr_name); } (library_->*set_func)(value * scale); } } //////////////////////////////////////////////////////////////// void LibertyReader::readCell(LibertyCell *cell, const LibertyGroup *cell_group) { readBusTypes(cell, cell_group); // Make ports first because they are referenced by functions, timing arcs, etc. LibertyPortGroupMap port_group_map = makeCellPorts(cell, cell_group); // Make ff/latch output ports. makeSequentials(cell, cell_group); readCellAttributes(cell, cell_group); // Set port directions before making timing arcs etc. for (auto const &[port_group, ports] : port_group_map) readPortDir(ports, port_group); for (auto const &[port_group, ports] : port_group_map) { readPortAttributes(cell, ports, port_group); makePortFuncs(cell, ports, port_group); makeTimingArcs(cell, ports, port_group); readInternalPowerGroups(cell, ports, port_group); } readTestCell(cell, cell_group); cell->finish(infer_latches_, report_, debug_); } void LibertyReader::readScaledCell(const LibertyGroup *scaled_cell_group) { const char *name = scaled_cell_group->firstName(); if (name) { LibertyCell *owner = library_->findLibertyCell(name); if (owner) { const char *op_cond_name = scaled_cell_group->secondName(); if (op_cond_name) { OperatingConditions *op_cond = library_->findOperatingConditions(op_cond_name); if (op_cond) { debugPrint(debug_, "liberty", 1, "scaled cell %s %s", name, op_cond_name); LibertyCell *scaled_cell = library_->makeScaledCell(name, filename_); readCell(scaled_cell, scaled_cell_group); checkScaledCell(scaled_cell, owner, scaled_cell_group, op_cond_name); // Add scaled cell AFTER ports and timing arcs are defined. owner->addScaledCell(op_cond, scaled_cell); } else libWarn(1202, scaled_cell_group, "operating conditions %s not found.", op_cond_name); } else libWarn(1203, scaled_cell_group, "scaled_cell missing operating condition."); } else libWarn(1204, scaled_cell_group, "scaled_cell cell %s has not been defined.", name); } else libWarn(1205, scaled_cell_group, "scaled_cell missing name."); } // Minimal check that is not very specific about where the discrepancies are. void LibertyReader::checkScaledCell(LibertyCell *scaled_cell, LibertyCell *owner, const LibertyGroup *scaled_cell_group, const char *op_cond_name) { if (equivCellPorts(scaled_cell, owner)) { if (!equivCellPorts(scaled_cell, owner)) libWarn(1206, scaled_cell_group, "scaled_cell %s, %s ports do not match cell ports", scaled_cell->name(), op_cond_name); if (!equivCellFuncs(scaled_cell, owner)) libWarn(1206, scaled_cell_group, "scaled_cell %s, %s port functions do not match cell port functions.", scaled_cell->name(), op_cond_name); } else libWarn(1207, scaled_cell_group, "scaled_cell ports do not match cell ports."); if (!equivCellTimingArcSets(scaled_cell, owner)) libWarn(1208, scaled_cell_group, "scaled_cell %s, %s timing does not match cell timing.", scaled_cell->name(), op_cond_name); } LibertyPortGroupMap LibertyReader::makeCellPorts(LibertyCell *cell, const LibertyGroup *cell_group) { LibertyPortGroupMap port_group_map; for (const LibertyGroup *subgroup : cell_group->subgroups()) { const std::string &type = subgroup->type(); if (type == "pin") makePinPort(cell, subgroup, port_group_map); else if (type == "bus") makeBusPort(cell, subgroup, port_group_map); else if (type == "bundle") makeBundlePort(cell, subgroup, port_group_map); else if (type == "pg_pin") makePgPinPort(cell, subgroup); } return port_group_map; } void LibertyReader::makePinPort(LibertyCell *cell, const LibertyGroup *pin_group, LibertyPortGroupMap &port_group_map) { for (const LibertyAttrValue *port_value : pin_group->params()) { const std::string &port_name = port_value->stringValue(); LibertyPort *port = makePort(cell, port_name.c_str()); port_group_map[pin_group].push_back(port); } } void LibertyReader::makeBusPort(LibertyCell *cell, const LibertyGroup *bus_group, LibertyPortGroupMap &port_group_map) { for (const LibertyAttrValue *port_value : bus_group->params()) { const std::string &port_name = port_value->stringValue(); const LibertySimpleAttr *bus_type_attr = bus_group->findSimpleAttr("bus_type"); if (bus_type_attr) { const std::string *bus_type = bus_type_attr->stringValue(); if (bus_type) { // Look for bus dcl local to cell first. BusDcl *bus_dcl = cell->findBusDcl(bus_type->c_str()); if (bus_dcl == nullptr) bus_dcl = library_->findBusDcl(bus_type->c_str()); if (bus_dcl) { debugPrint(debug_, "liberty", 1, " bus %s", port_name.c_str()); LibertyPort *bus_port = makeBusPort(cell, port_name.c_str(), bus_dcl->from(), bus_dcl->to(), bus_dcl); port_group_map[bus_group].push_back(bus_port); // Make ports for pin groups inside the bus group. makeBusPinPorts(cell, bus_group, port_group_map); } else libWarn(1235, bus_type_attr, "bus_type %s not found.", bus_type->c_str()); } } else libWarn(1236, bus_type_attr, "bus_type not found."); } } void LibertyReader::makeBusPinPorts(LibertyCell *cell, const LibertyGroup *bus_group, LibertyPortGroupMap &port_group_map) { for (const LibertyGroup *pin_group : bus_group->findSubgroups("pin")) { for (const LibertyAttrValue *param : pin_group->params()) { if (param->isString()) { const std::string pin_name = param->stringValue(); debugPrint(debug_, "liberty", 1, " bus pin port %s", pin_name.c_str()); // Expand foo[3:0] port names. PortNameBitIterator name_iter(cell, pin_name.c_str(), this, pin_group->line()); while (name_iter.hasNext()) { LibertyPort *pin_port = name_iter.next(); if (pin_port) { port_group_map[pin_group].push_back(pin_port); } else libWarn(1232, pin_group, "pin %s not found.", pin_name.c_str()); } } else libWarn(1233, pin_group, "pin name is not a string."); } } } void LibertyReader::makeBundlePort(LibertyCell *cell, const LibertyGroup *bundle_group, LibertyPortGroupMap &port_group_map) { const std::string &bundle_name = bundle_group->firstName(); debugPrint(debug_, "liberty", 1, " bundle %s", bundle_name.c_str()); const LibertyComplexAttr *member_attr = bundle_group->findComplexAttr("members"); ConcretePortSeq *members = new ConcretePortSeq; for (const LibertyAttrValue *member_value : member_attr->values()) { if (member_value->isString()) { const char *member_name = member_value->stringValue().c_str(); LibertyPort *member = cell->findLibertyPort(member_name); if (member == nullptr) member = makePort(cell, member_name); members->push_back(member); } } LibertyPort *bundle_port = builder_.makeBundlePort(cell, bundle_name.c_str(), members); port_group_map[bundle_group].push_back(bundle_port); // Make ports for pin groups inside the bundle group. makeBundlePinPorts(cell, bundle_group, port_group_map); } void LibertyReader::makeBundlePinPorts(LibertyCell *cell, const LibertyGroup *bundle_group, LibertyPortGroupMap &port_group_map) { for (const LibertyGroup *pin_group : bundle_group->findSubgroups("pin")) { for (LibertyAttrValue *param : pin_group->params()) { if (param->isString()) { const std::string pin_name = param->stringValue(); debugPrint(debug_, "liberty", 1, " bundle pin port %s", pin_name.c_str()); LibertyPort *pin_port = cell->findLibertyPort(pin_name.c_str()); if (pin_port == nullptr) pin_port = makePort(cell, pin_name.c_str()); port_group_map[pin_group].push_back(pin_port); } else libWarn(1234, pin_group, "pin name is not a string."); } } } void LibertyReader::makePgPinPort(LibertyCell *cell, const LibertyGroup *pg_pin_group) { const std::string &port_name = pg_pin_group->firstName(); LibertyPort *pg_port = makePort(cell, port_name.c_str()); const std::string *type_name = pg_pin_group->findAttrString("pg_type"); if (type_name) { PwrGndType type = findPwrGndType(type_name->c_str()); PortDirection *dir = PortDirection::unknown(); switch (type) { case PwrGndType::primary_ground: case PwrGndType::backup_ground: case PwrGndType::internal_ground: dir = PortDirection::ground(); break; case PwrGndType::primary_power: case PwrGndType::backup_power: case PwrGndType::internal_power: dir = PortDirection::power(); break; case PwrGndType::none: libError(1291, pg_pin_group, "unknown pg_type."); break; default: break; } pg_port->setPwrGndType(type); pg_port->setDirection(dir); } const std::string *voltate_name = pg_pin_group->findAttrString("voltage_name"); if (voltate_name) pg_port->setVoltageName(voltate_name->c_str()); } //////////////////////////////////////////////////////////////// void LibertyReader::readPortAttributes(LibertyCell *cell, const LibertyPortSeq &ports, const LibertyGroup *port_group) { readCapacitance(ports, port_group); readMinPulseWidth(cell, ports, port_group); readPortAttrFloat("min_period", &LibertyPort::setMinPeriod, ports, port_group, time_scale_); readPortAttrBool("clock", &LibertyPort::setIsClock, ports, port_group); readPortAttrFloat("fanout_load", &LibertyPort::setFanoutLoad, ports, port_group, 1.0F); readPortAttrFloatMinMax("max_fanout", &LibertyPort::setFanoutLimit, ports, port_group, MinMax::max(), 1.0F); readPortAttrFloatMinMax("min_fanout", &LibertyPort::setFanoutLimit, ports, port_group, MinMax::min(), 1.0F); readPulseClock(ports, port_group); readPortAttrBool("clock_gate_clock_pin", &LibertyPort::setIsClockGateClock, ports, port_group); readPortAttrBool("clock_gate_enable_pin", &LibertyPort::setIsClockGateEnable, ports, port_group); readPortAttrBool("clock_gate_out_pin", &LibertyPort::setIsClockGateOut, ports, port_group); readPortAttrBool("is_pll_feedback_pin", &LibertyPort::setIsPllFeedback, ports, port_group); readSignalType(cell, ports, port_group); readPortAttrBool("isolation_cell_data_pin", &LibertyPort::setIsolationCellData, ports, port_group); readPortAttrBool("isolation_cell_enable_pin", &LibertyPort::setIsolationCellEnable, ports, port_group); readPortAttrBool("level_shifter_data_pin", &LibertyPort::setLevelShifterData, ports, port_group); readPortAttrBool("switch_pin", &LibertyPort::setIsSwitch, ports, port_group); readPortAttrString("related_ground_pin", &LibertyPort::setRelatedGroundPin, ports, port_group); readPortAttrString("related_power_pin", &LibertyPort::setRelatedPowerPin, ports, port_group); readDriverWaveform(ports, port_group); } void LibertyReader::readDriverWaveform(const LibertyPortSeq &ports, const LibertyGroup *port_group) { for (const RiseFall *rf : RiseFall::range()) { const char *attr_name = rf == RiseFall::rise() ? "driver_waveform_rise" : "driver_waveform_fall"; const std::string *name = port_group->findAttrString(attr_name); if (name) { DriverWaveform *waveform = library_->findDriverWaveform(name->c_str()); if (waveform) { for (LibertyPort *port : ports) port->setDriverWaveform(waveform, rf); } } } } void LibertyReader::readPortAttrString(const char *attr_name, void (LibertyPort::*set_func)(const char *value), const LibertyPortSeq &ports, const LibertyGroup *group) { const std::string *value = group->findAttrString(attr_name); if (value) { for (LibertyPort *port : ports) (port->*set_func)(value->c_str()); } } void LibertyReader::readPortAttrFloat(const char *attr_name, void (LibertyPort::*set_func)(float value), const LibertyPortSeq &ports, const LibertyGroup *group, float scale) { float value; bool exists; group->findAttrFloat(attr_name, value, exists); if (exists) { for (LibertyPort *port : ports) (port->*set_func)(value * scale); } } void LibertyReader::readPortAttrBool(const char *attr_name, void (LibertyPort::*set_func)(bool value), const LibertyPortSeq &ports, const LibertyGroup *group) { const LibertySimpleAttr *attr = group->findSimpleAttr(attr_name); if (attr) { const LibertyAttrValue &attr_value = attr->value(); if (attr_value.isString()) { const std::string &value = attr_value.stringValue(); if (stringEqual(value.c_str(), "true")) { for (LibertyPort *port : ports) (port->*set_func)(true); } else if (stringEqual(value.c_str(), "false")) { for (LibertyPort *port : ports) (port->*set_func)(false); } else libWarn(1238, attr, "%s attribute is not boolean.", attr_name); } else libWarn(1239, attr, "%s attribute is not boolean.", attr_name); } } void LibertyReader::readPortAttrFloatMinMax(const char *attr_name, void (LibertyPort::*set_func)(float value, const MinMax *min_max), const LibertyPortSeq &ports, const LibertyGroup *group, const MinMax *min_max, float scale) { float value; bool exists; group->findAttrFloat(attr_name, value, exists); if (exists) { for (LibertyPort *port : ports) (port->*set_func)(value * scale, min_max); } } void LibertyReader::readPulseClock(const LibertyPortSeq &ports, const LibertyGroup *port_group) { const std::string *pulse_clk = port_group->findAttrString("pulse_clock"); if (pulse_clk) { const RiseFall *trigger = nullptr; const RiseFall *sense = nullptr; if (*pulse_clk == "rise_triggered_high_pulse") { trigger = RiseFall::rise(); sense = RiseFall::rise(); } else if (*pulse_clk == "rise_triggered_low_pulse") { trigger = RiseFall::rise(); sense = RiseFall::fall(); } else if (*pulse_clk == "fall_triggered_high_pulse") { trigger = RiseFall::fall(); sense = RiseFall::rise(); } else if (*pulse_clk == "fall_triggered_low_pulse") { trigger = RiseFall::fall(); sense = RiseFall::fall(); } else libWarn(1242, port_group, "pulse_latch unknown pulse type."); if (trigger) { for (LibertyPort *port : ports) port->setPulseClk(trigger, sense); } } } void LibertyReader::readSignalType(LibertyCell *cell, const LibertyPortSeq &ports, const LibertyGroup *port_group) { if (!dynamic_cast(cell)) return; const std::string *type = port_group->findAttrString("signal_type"); if (!type) return; ScanSignalType signal_type = ScanSignalType::none; if (*type == "test_scan_enable") signal_type = ScanSignalType::enable; else if (*type == "test_scan_enable_inverted") signal_type = ScanSignalType::enable_inverted; else if (*type == "test_scan_clock") signal_type = ScanSignalType::clock; else if (*type == "test_scan_clock_a") signal_type = ScanSignalType::clock_a; else if (*type == "test_scan_clock_b") signal_type = ScanSignalType::clock_b; else if (*type == "test_scan_in") signal_type = ScanSignalType::input; else if (*type == "test_scan_in_inverted") signal_type = ScanSignalType::input_inverted; else if (*type == "test_scan_out") signal_type = ScanSignalType::output; else if (*type == "test_scan_out_inverted") signal_type = ScanSignalType::output_inverted; else { libWarn(1299, port_group, "unknown signal_type %s.", type->c_str()); return; } for (LibertyPort *port : ports) port->setScanSignalType(signal_type); } void LibertyReader::readPortDir(const LibertyPortSeq &ports, const LibertyGroup *port_group) { const LibertySimpleAttr *dir_attr = port_group->findSimpleAttr("direction"); // Note missing direction attribute is not an error because a bus group // can have pin groups for the bus bits that have direcitons. if (dir_attr) { const std::string *dir = dir_attr->stringValue(); if (dir) { PortDirection *port_dir = PortDirection::unknown(); if (*dir == "input") port_dir = PortDirection::input(); else if (*dir == "output") port_dir = PortDirection::output(); else if (*dir == "inout") port_dir = PortDirection::bidirect(); else if (*dir == "internal") port_dir = PortDirection::internal(); else libWarn(1240, dir_attr, "unknown port direction."); for (LibertyPort *port : ports) port->setDirection(port_dir); } } } void LibertyReader::readCapacitance(const LibertyPortSeq &ports, const LibertyGroup *port_group) { // capacitance readPortAttrFloat("capacitance", &LibertyPort::setCapacitance, ports, port_group, cap_scale_); for (LibertyPort *port : ports) { // rise/fall_capacitance for (const RiseFall *rf : RiseFall::range()) { std::string attr_name = rf->to_string_long() + "_capacitance"; float cap; bool exists; port_group->findAttrFloat(attr_name, cap, exists); if (exists) { for (const MinMax *min_max : MinMax::range()) port->setCapacitance(rf, min_max, cap * cap_scale_); } // rise/fall_capacitance_range(min_cap, max_cap); attr_name = rf->to_string_long() + "_capacitance_range"; const LibertyComplexAttrSeq &range_attrs = port_group->findComplexAttrs(attr_name); if (!range_attrs.empty()) { const LibertyComplexAttr *attr = range_attrs[0]; const LibertyAttrValueSeq &values = attr->values(); if (values.size() == 2) { float cap_min = values[0]->floatValue(); float cap_max = values[1]->floatValue(); port->setCapacitance(rf, MinMax::min(), cap_min * cap_scale_); port->setCapacitance(rf, MinMax::max(), cap_max * cap_scale_); } } } if (!(port->isBus() || port->isBundle())) setPortCapDefault(port); for (const MinMax *min_max : MinMax::range()) { // min/max_capacitance std::string attr_name = min_max->to_string() + "_capacitance"; float limit; bool exists; port_group->findAttrFloat(attr_name, limit, exists); if (exists) port->setCapacitanceLimit(limit * cap_scale_, min_max); // min/max_transition attr_name = min_max->to_string() + "_transition"; port_group->findAttrFloat(attr_name, limit, exists); if (exists) port->setSlewLimit(limit * time_scale_, min_max); } // Default capacitance. if (port->isBus() || port->isBundle()) { // Do not clobber member port capacitances by setting the capacitance // on a bus or bundle. LibertyPortMemberIterator member_iter(port); while (member_iter.hasNext()) { LibertyPort *member = member_iter.next(); setPortCapDefault(member); } } else setPortCapDefault(port); } } void LibertyReader::setPortCapDefault(LibertyPort *port) { for (const MinMax *min_max : MinMax::range()) { for (const RiseFall *rf : RiseFall::range()) { float cap; bool exists; port->capacitance(rf, min_max, cap, exists); if (!exists) port->setCapacitance(rf, min_max, defaultCap(port)); } } } void LibertyReader::readMinPulseWidth(LibertyCell *cell, const LibertyPortSeq &ports, const LibertyGroup *port_group) { for (LibertyPort *port : ports) { TimingArcAttrsPtr timing_attrs = nullptr; for (const RiseFall *rf : RiseFall::range()) { const char *mpw_attr_name = rf == RiseFall::rise() ? "min_pulse_width_high" : "min_pulse_width_low"; float mpw; bool exists; port_group->findAttrFloat(mpw_attr_name, mpw, exists); if (exists) { mpw *= time_scale_; port->setMinPulseWidth(rf, mpw); // Make timing arcs for the port min_pulse_width_low/high attributes. // This is redundant but makes sdf annotation consistent. if (timing_attrs == nullptr) { timing_attrs = std::make_shared(); timing_attrs->setTimingType(TimingType::min_pulse_width); } TimingModel *check_model = makeScalarCheckModel(cell, mpw, ScaleFactorType::min_pulse_width, rf); timing_attrs->setModel(rf, check_model); } } if (timing_attrs) builder_.makeTimingArcs(cell, port, port, nullptr, timing_attrs, port_group->line()); } } void LibertyReader::makePortFuncs(LibertyCell *cell, const LibertyPortSeq &ports, const LibertyGroup *port_group) { const LibertySimpleAttr *func_attr = port_group->findSimpleAttr("function"); if (func_attr) { const std::string *func = func_attr->stringValue(); if (func) { FuncExpr *func_expr = parseFunc(func->c_str(), "function", cell, func_attr->line()); for (LibertyPort *port : ports) { port->setFunction(func_expr); if (func_expr->checkSize(port)) { libWarn(1195, func_attr->line(), "port %s function size does not match port size.", port->name()); } } } } const LibertySimpleAttr *tri_attr = port_group->findSimpleAttr("three_state"); if (tri_attr) { const std::string *tri_disable = tri_attr->stringValue(); if (tri_disable) { FuncExpr *tri_disable_expr = parseFunc(tri_disable->c_str(), "three_state", cell, tri_attr->line()); FuncExpr *tri_enable_expr = tri_disable_expr->invert(); for (LibertyPort *port : ports) { port->setTristateEnable(tri_enable_expr); if (port->direction() == PortDirection::output()) port->setDirection(PortDirection::tristate()); } } } } //////////////////////////////////////////////////////////////// void LibertyReader::makeSequentials(LibertyCell *cell, const LibertyGroup *cell_group) { makeSequentials(cell, cell_group, true, "ff", "clocked_on", "next_state"); makeSequentials(cell, cell_group, true, "ff_bank", "clocked_on", "next_state"); makeSequentials(cell, cell_group, false, "latch", "enable", "data_in"); makeSequentials(cell, cell_group, false, "latch_bank", "enable", "data_in"); const LibertyGroup *lut_group = cell_group->findSubgroup("lut");; if (lut_group) { LibertyPort *out_port = nullptr; LibertyPort *out_port_inv = nullptr; size_t size; makeSeqPorts(cell, lut_group, out_port, out_port_inv, size); } } void LibertyReader::makeSequentials(LibertyCell *cell, const LibertyGroup *cell_group, bool is_register, const char *seq_group_name, const char *clk_attr_name, const char *data_attr_name) { for (const LibertyGroup *seq_group : cell_group->findSubgroups(seq_group_name)) { LibertyPort *out_port = nullptr; LibertyPort *out_port_inv = nullptr; size_t size; makeSeqPorts(cell, seq_group, out_port, out_port_inv, size); FuncExpr *clk_expr = makeSeqFunc(cell, seq_group, clk_attr_name, size); FuncExpr *data_expr = makeSeqFunc(cell, seq_group, data_attr_name, size); FuncExpr *clr_expr = makeSeqFunc(cell, seq_group, "clear", size); FuncExpr *preset_expr = makeSeqFunc(cell, seq_group, "preset", size); LogicValue clr_preset_var1 = LogicValue::unknown; const LibertySimpleAttr *var1 = seq_group->findSimpleAttr("clear_preset_var1"); if (var1) clr_preset_var1 = getAttrLogicValue(var1); LogicValue clr_preset_var2 = LogicValue::unknown; const LibertySimpleAttr *var2 = seq_group->findSimpleAttr("clear_preset_var2"); if (var2) clr_preset_var2 = getAttrLogicValue(var2); cell->makeSequential(size, is_register, clk_expr, data_expr, clr_expr, preset_expr, clr_preset_var1, clr_preset_var2, out_port, out_port_inv); } } FuncExpr * LibertyReader::makeSeqFunc(LibertyCell *cell, const LibertyGroup *seq_group, const char *attr_name, int size) { FuncExpr *expr = nullptr; const std::string *attr = seq_group->findAttrString(attr_name); if (attr) { expr = parseFunc(attr->c_str(), attr_name, cell, seq_group->line()); if (expr && expr->checkSize(size)) { libWarn(1196, seq_group, "%s %s bus width mismatch.", seq_group->type().c_str(), attr_name); delete expr; expr = nullptr; } } return expr; } void LibertyReader::makeSeqPorts(LibertyCell *cell, const LibertyGroup *seq_group, // Return values. LibertyPort *&out_port, LibertyPort *&out_port_inv, size_t &size) { const char *out_name, *out_inv_name; bool has_size; seqPortNames(seq_group, out_name, out_inv_name, has_size, size); if (out_name) { if (has_size) out_port = makeBusPort(cell, out_name, size - 1, 0, nullptr); else out_port = makePort(cell, out_name); out_port->setDirection(PortDirection::internal()); } if (out_inv_name) { if (has_size) out_port_inv = makeBusPort(cell, out_inv_name, size - 1, 0, nullptr); else out_port_inv = makePort(cell, out_inv_name); out_port_inv->setDirection(PortDirection::internal()); } } void LibertyReader::seqPortNames(const LibertyGroup *group, const char *&out_name, const char *&out_inv_name, bool &has_size, size_t &size) { out_name = nullptr; out_inv_name = nullptr; if (group->params().size() == 1) { // out_port out_name = group->firstName(); size = 1; has_size = false; } if (group->params().size() == 2) { // out_port, out_port_inv out_name = group->firstName(); out_inv_name = group->secondName(); size = 1; has_size = false; } else if (group->params().size() == 3) { LibertyAttrValue *third_value = group->params()[2]; if (third_value->isFloat()) { // out_port, out_port_inv, bus_size out_name = group->firstName(); out_inv_name = group->secondName(); size = static_cast(third_value->floatValue()); has_size = true; } else { // in_port (ignored), out_port, out_port_inv out_name = group->secondName(); out_inv_name = third_value->stringValue().c_str(); has_size = true; size = 1; } } } //////////////////////////////////////////////////////////////// void LibertyReader::readCellAttributes(LibertyCell *cell, const LibertyGroup *cell_group) { readCellAttrFloat("area", &LibertyCell::setArea, cell, cell_group, 1.0); readCellAttrString("cell_footprint", &LibertyCell::setFootprint, cell, cell_group); readCellAttrBool("dont_use", &LibertyCell::setDontUse, cell, cell_group); readCellAttrBool("is_macro_cell", &LibertyCell::setIsMacro, cell, cell_group); readCellAttrBool("is_pad", &LibertyCell::setIsPad, cell, cell_group); readCellAttrBool("is_level_shifter", &LibertyCell::setIsLevelShifter, cell, cell_group); readCellAttrBool("is_clock_cell", &LibertyCell::setIsClockCell, cell, cell_group); readCellAttrBool("is_isolation_cell", &LibertyCell::setIsIsolationCell,cell,cell_group); readCellAttrBool("always_on", &LibertyCell::setAlwaysOn,cell,cell_group); readCellAttrBool("interface_timing", &LibertyCell::setInterfaceTiming,cell,cell_group); readCellAttrFloat("cell_leakage_power", &LibertyCell::setLeakagePower, cell, cell_group, power_scale_); readCellAttrBool("is_memory", &LibertyCell::setIsMemory, cell, cell_group); if (cell_group->findSubgroup("memory")) cell->setIsMemory(true); readCellAttrBool("pad_cell", &LibertyCell::setIsPad, cell, cell_group); readLevelShifterType(cell, cell_group); readSwitchCellType(cell, cell_group); readCellAttrString("user_function_class", &LibertyCell::setUserFunctionClass, cell, cell_group); readOcvDerateFactors(cell, cell_group); readCellOcvDerateGroup(cell, cell_group); readGroupAttrFloat("ocv_arc_depth", cell_group, [cell](float v) { cell->setOcvArcDepth(v); }); const std::string *clock_gate_type = cell_group->findAttrString("clock_gating_integrated_cell"); if (clock_gate_type) { if (stringBeginEqual(clock_gate_type->c_str(), "latch_posedge")) cell->setClockGateType(ClockGateType::latch_posedge); else if (stringBeginEqual(clock_gate_type->c_str(), "latch_negedge")) cell->setClockGateType(ClockGateType::latch_negedge); else cell->setClockGateType(ClockGateType::other); } readScaleFactors(cell, cell_group); readLeagageGrouops(cell, cell_group); readStatetable(cell, cell_group); readModeDefs(cell, cell_group); } void LibertyReader::readScaleFactors(LibertyCell *cell, const LibertyGroup *cell_group) { const std::string *scale_factors_name = cell_group->findAttrString("scaling_factors"); if (scale_factors_name) { ScaleFactors *scale_factors = library_->findScaleFactors(scale_factors_name->c_str()); if (scale_factors) cell->setScaleFactors(scale_factors); else libWarn(1230, cell_group, "scaling_factors %s not found.", scale_factors_name->c_str()); } } void LibertyReader::readCellAttrString(const char *attr_name, void (LibertyCell::*set_func)(const char *value), LibertyCell *cell, const LibertyGroup *group) { const std::string *value = group->findAttrString(attr_name); if (value) (cell->*set_func)(value->c_str()); } void LibertyReader::readCellAttrFloat(const char *attr_name, void (LibertyCell::*set_func)(float value), LibertyCell *cell, const LibertyGroup *group, float scale) { float value; bool exists; group->findAttrFloat(attr_name, value, exists); if (exists) (cell->*set_func)(value * scale); } void LibertyReader::readCellAttrBool(const char *attr_name, void (LibertyCell::*set_func)(bool value), LibertyCell *cell, const LibertyGroup *group) { const LibertySimpleAttr *attr = group->findSimpleAttr(attr_name); if (attr) { const LibertyAttrValue &attr_value = attr->value(); if (attr_value.isString()) { const std::string &value = attr_value.stringValue(); if (stringEqual(value.c_str(), "true")) (cell->*set_func)(true); else if (stringEqual(value.c_str(), "false")) (cell->*set_func)(false); else libWarn(1279, attr, "%s attribute is not boolean.", attr_name); } else libWarn(1280, attr, "%s attribute is not boolean.", attr_name); } } //////////////////////////////////////////////////////////////// void LibertyReader::makeTimingArcs(LibertyCell *cell, const LibertyPortSeq &ports, const LibertyGroup *port_group) { for (const LibertyGroup *timing_group : port_group->findSubgroups("timing")) { TimingArcAttrsPtr timing_attrs = std::make_shared(); readTimingArcAttrs(cell, timing_group, timing_attrs); makeTimingModels(cell, timing_group, timing_attrs); LibertyPort *related_output_port = findLibertyPort(cell, timing_group, "related_output_pin"); StdStringSeq related_port_names = findAttributStrings(timing_group, "related_pin"); StdStringSeq related_bus_names=findAttributStrings(timing_group,"related_bus_pins"); TimingType timing_type = timing_attrs->timingType(); for (LibertyPort *to_port : ports) { if (timing_type == TimingType::combinational && to_port->direction()->isInput()) libWarn(1209, timing_group, "combinational timing to an input port."); if (related_port_names.size() || related_bus_names.size()) { for (const std::string &from_port_name : related_port_names) { debugPrint(debug_, "liberty", 2, " timing %s -> %s", from_port_name.c_str(), to_port->name()); makeTimingArcs(cell, from_port_name, to_port, related_output_port, true, timing_attrs, timing_group->line()); } for (const std::string &from_port_name : related_bus_names) { debugPrint(debug_, "liberty", 2, " timing %s -> %s", from_port_name.c_str(), to_port->name()); makeTimingArcs(cell, from_port_name, to_port, related_output_port, false, timing_attrs, timing_group->line()); } } else if (!(timing_type == TimingType::min_pulse_width || timing_type == TimingType::minimum_period || timing_type == TimingType::min_clock_tree_path || timing_type == TimingType::max_clock_tree_path)) libWarn(1243, timing_group, "timing group missing related_pin/related_bus_pin."); else makeTimingArcs(cell, to_port, related_output_port, timing_attrs, timing_group->line()); } } } void LibertyReader::readTimingArcAttrs(LibertyCell *cell, const LibertyGroup *timing_group, TimingArcAttrsPtr timing_attrs) { readTimingSense(timing_group, timing_attrs); readTimingType(timing_group, timing_attrs); readTimingWhen(cell, timing_group, timing_attrs); readTimingMode(timing_group, timing_attrs); readGroupAttrFloat("ocv_arc_depth", timing_group, [timing_attrs](float v) { timing_attrs->setOcvArcDepth(v); }); } void LibertyReader::readGroupAttrFloat(const char *attr_name, const LibertyGroup *group, const std::function &set_func, float scale) { float value; bool exists; group->findAttrFloat(attr_name, value, exists); if (exists) set_func(value * scale); } void LibertyReader::readTimingSense(const LibertyGroup *timing_group, TimingArcAttrsPtr timing_attrs) { const LibertySimpleAttr *sense_attr = timing_group->findSimpleAttr("timing_sense"); if (sense_attr) { const std::string *sense_name = sense_attr->stringValue(); if (sense_name) { if (*sense_name == "non_unate") timing_attrs->setTimingSense(TimingSense::non_unate); else if (*sense_name == "positive_unate") timing_attrs->setTimingSense(TimingSense::positive_unate); else if (*sense_name == "negative_unate") timing_attrs->setTimingSense(TimingSense::negative_unate); else libWarn(1245, timing_group, "unknown timing_sense %s.", sense_name->c_str()); } } } void LibertyReader::readTimingType(const LibertyGroup *timing_group, TimingArcAttrsPtr timing_attrs) { TimingType type = TimingType::combinational; const LibertySimpleAttr *type_attr = timing_group->findSimpleAttr("timing_type"); if (type_attr) { const std::string *type_name = type_attr->stringValue(); if (type_name) { type = findTimingType(type_name->c_str()); if (type == TimingType::unknown) { libWarn(1244, type_attr, "unknown timing_type %s.", type_name->c_str()); type = TimingType::combinational; } } } timing_attrs->setTimingType(type); } void LibertyReader::readTimingWhen(const LibertyCell *cell, const LibertyGroup *timing_group, TimingArcAttrsPtr timing_attrs) { const LibertySimpleAttr *when_attr = timing_group->findSimpleAttr("when"); if (when_attr) { const std::string *when = when_attr->stringValue(); if (when) { FuncExpr *when_expr = parseFunc(when->c_str(), "when", cell, when_attr->line()); timing_attrs->setCond(when_expr); } } const LibertySimpleAttr *cond_attr = timing_group->findSimpleAttr("sdf_cond"); if (cond_attr) { const std::string *cond = cond_attr->stringValue(); if (cond) timing_attrs->setSdfCond(cond->c_str()); } cond_attr = timing_group->findSimpleAttr("sdf_cond_start"); if (cond_attr) { const std::string *cond = cond_attr->stringValue(); if (cond) timing_attrs->setSdfCondStart(cond->c_str()); } cond_attr = timing_group->findSimpleAttr("sdf_cond_end"); if (cond_attr) { const std::string *cond = cond_attr->stringValue(); if (cond) timing_attrs->setSdfCondEnd(cond->c_str()); } } void LibertyReader::readTimingMode(const LibertyGroup *timing_group, TimingArcAttrsPtr timing_attrs) { const LibertyComplexAttrSeq &mode_attrs = timing_group->findComplexAttrs("mode"); if (!mode_attrs.empty()) { const LibertyComplexAttr *mode_attr = mode_attrs[0]; const LibertyAttrValueSeq &mode_values = mode_attr->values(); if (mode_values.size() == 2) { LibertyAttrValue *value = mode_values[0]; if (value->isString()) timing_attrs->setModeName(value->stringValue()); else libWarn(1248, mode_attr, "mode name is not a string."); value = mode_values[1]; if (value->isString()) timing_attrs->setModeValue(value->stringValue()); else libWarn(1246, mode_attr, "mode value is not a string."); } else libWarn(1249, mode_attr, "mode requirees 2 values."); } } void LibertyReader::makeTimingModels(LibertyCell *cell, const LibertyGroup *timing_group, TimingArcAttrsPtr timing_attrs) { switch (cell->libertyLibrary()->delayModelType()) { case DelayModelType::cmos_linear: makeLinearModels(cell, timing_group, timing_attrs); break; case DelayModelType::table: makeTableModels(cell, timing_group, timing_attrs); break; case DelayModelType::cmos_pwl: case DelayModelType::cmos2: case DelayModelType::polynomial: case DelayModelType::dcm: break; } } void LibertyReader::makeLinearModels(LibertyCell *cell, const LibertyGroup *timing_group, TimingArcAttrsPtr timing_attrs) { LibertyLibrary *library = cell->libertyLibrary(); for (const RiseFall *rf : RiseFall::range()) { std::string intr_attr_name = "intrinsic_" + rf->to_string_long(); float intr = 0.0; bool intr_exists; timing_group->findAttrFloat(intr_attr_name, intr, intr_exists); if (intr_exists) intr *= time_scale_; else library->defaultIntrinsic(rf, intr, intr_exists); TimingModel *model = nullptr; if (intr_exists) { if (timingTypeIsCheck(timing_attrs->timingType())) model = new CheckLinearModel(cell, intr); else { std::string res_attr_name = rf->to_string_long() + "_resistance"; float res = 0.0; bool res_exists; timing_group->findAttrFloat(res_attr_name, res, res_exists); if (res_exists) res *= res_scale_; else library->defaultPinResistance(rf, PortDirection::output(), res, res_exists); model = new GateLinearModel(cell, intr, res); } timing_attrs->setModel(rf, model); } } } void LibertyReader::makeTableModels(LibertyCell *cell, const LibertyGroup *timing_group, TimingArcAttrsPtr timing_attrs) { for (const RiseFall *rf : RiseFall::range()) { std::string delay_attr_name = "cell_" + rf->to_string_long(); TableModel *delay = readGateTableModel(timing_group, delay_attr_name.c_str(), rf, TableTemplateType::delay, time_scale_, ScaleFactorType::cell); std::string transition_attr_name = rf->to_string_long() + "_transition"; TableModel *transition = readGateTableModel(timing_group, transition_attr_name.c_str(), rf, TableTemplateType::delay, time_scale_, ScaleFactorType::transition); if (delay || transition) { std::string delay_sigma_attr_name = "ocv_sigma_cell_" + rf->to_string_long(); TableModelsEarlyLate delay_sigmas = readEarlyLateTableModels(timing_group, delay_sigma_attr_name.c_str(), rf, TableTemplateType::delay, time_scale_, ScaleFactorType::unknown); std::string slew_sigma_attr_name = "ocv_sigma_" + rf->to_string_long() + "_transition"; TableModelsEarlyLate slew_sigmas = readEarlyLateTableModels(timing_group, slew_sigma_attr_name.c_str(), rf, TableTemplateType::delay, time_scale_, ScaleFactorType::unknown); ReceiverModelPtr receiver_model = readReceiverCapacitance(timing_group, rf); OutputWaveforms *output_waveforms = readOutputWaveforms(timing_group, rf); timing_attrs->setModel(rf, new GateTableModel(cell, delay, std::move(delay_sigmas), transition, std::move(slew_sigmas), receiver_model, output_waveforms)); TimingType timing_type = timing_attrs->timingType(); if (isGateTimingType(timing_type)) { if (transition == nullptr) libWarn(1210, timing_group, "missing %s_transition.", rf->name()); if (delay == nullptr) libWarn(1211, timing_group, "missing cell_%s.", rf->name()); } } std::string constraint_attr_name = rf->to_string_long() + "_constraint"; ScaleFactorType scale_factor_type = timingTypeScaleFactorType(timing_attrs->timingType()); TableModel *constraint = readCheckTableModel(timing_group, constraint_attr_name.c_str(), rf, TableTemplateType::delay, time_scale_, scale_factor_type); if (constraint) { std::string constraint_sigma_attr_name = "ocv_sigma_" + rf->to_string_long() + "_constraint"; TableModelsEarlyLate constraint_sigmas = readEarlyLateTableModels(timing_group, constraint_sigma_attr_name.c_str(), rf, TableTemplateType::delay, time_scale_, ScaleFactorType::unknown); timing_attrs->setModel(rf, new CheckTableModel(cell, constraint, std::move(constraint_sigmas))); } } } bool LibertyReader::isGateTimingType(TimingType timing_type) { return timing_type == TimingType::clear || timing_type == TimingType::combinational || timing_type == TimingType::combinational_fall || timing_type == TimingType::combinational_rise || timing_type == TimingType::falling_edge || timing_type == TimingType::preset || timing_type == TimingType::rising_edge || timing_type == TimingType::three_state_disable || timing_type == TimingType::three_state_disable_rise || timing_type == TimingType::three_state_disable_fall || timing_type == TimingType::three_state_enable || timing_type == TimingType::three_state_enable_fall || timing_type == TimingType::three_state_enable_rise; } TableModel * LibertyReader::readGateTableModel(const LibertyGroup *timing_group, const char *table_group_name, const RiseFall *rf, TableTemplateType template_type, float scale, ScaleFactorType scale_factor_type) { const LibertyGroup *table_group = timing_group->findSubgroup(table_group_name); if (table_group) { TableModel *model = readTableModel(table_group, rf, template_type, scale, scale_factor_type); if (model && !GateTableModel::checkAxes(model)) libWarn(1251, table_group, "unsupported model axis."); return model; } return nullptr; } TableModel * LibertyReader::readCheckTableModel(const LibertyGroup *timing_group, const char *table_group_name, const RiseFall *rf, TableTemplateType template_type, float scale, ScaleFactorType scale_factor_type) { const LibertyGroup *table_group = timing_group->findSubgroup(table_group_name); if (table_group) { TableModel *model = readTableModel(table_group, rf, template_type, scale, scale_factor_type); if (model && !CheckTableModel::checkAxes(model)) libWarn(1252, table_group, "unsupported model axis."); return model; } return nullptr; } TableModelsEarlyLate LibertyReader::readEarlyLateTableModels(const LibertyGroup *timing_group, const char *table_group_name, const RiseFall *rf, TableTemplateType template_type, float scale, ScaleFactorType scale_factor_type) { TableModelsEarlyLate models{}; for (const LibertyGroup *table_group : timing_group->findSubgroups(table_group_name)) { TableModel *model = readTableModel(table_group, rf, template_type, scale, scale_factor_type); const std::string *early_late = table_group->findAttrString("sigma_type"); if (early_late == nullptr || *early_late == "early_and_late") { models[EarlyLate::early()->index()] = model; models[EarlyLate::late()->index()] = model; } else if (*early_late == "early") models[EarlyLate::early()->index()] = model; else if (*early_late == "late") models[EarlyLate::late()->index()] = model; //if (model && !GateTableModel::checkAxes(model)) // libWarn(1182, table_group, "unsupported model axis."); } return models; } ReceiverModelPtr LibertyReader::readReceiverCapacitance(const LibertyGroup *timing_group, const RiseFall *rf) { ReceiverModelPtr receiver_model = nullptr; readReceiverCapacitance(timing_group, "receiver_capacitance", 0, rf, receiver_model); readReceiverCapacitance(timing_group, "receiver_capacitance1", 0, rf, receiver_model); readReceiverCapacitance(timing_group, "receiver_capacitance2", 1, rf, receiver_model); return receiver_model; } void LibertyReader::readReceiverCapacitance(const LibertyGroup *timing_group, const char *cap_group_name, int index, const RiseFall *rf, ReceiverModelPtr &receiver_model) { std::string cap_group_name1 = cap_group_name; cap_group_name1 += "_" + rf->to_string_long(); const LibertyGroup *cap_group = timing_group->findSubgroup(cap_group_name1); if (cap_group) { const LibertySimpleAttr *segment_attr = cap_group->findSimpleAttr("segment"); if (segment_attr) { // For receiver_capacitance groups with mulitiple segments this // overrides the index passed in beginReceiverCapacitance1Rise/Fall. int segment; bool exists; getAttrInt(segment_attr, segment, exists); if (exists) index = segment; } TableModel *model = readTableModel(cap_group, rf, TableTemplateType::delay, cap_scale_, ScaleFactorType::pin_cap); if (ReceiverModel::checkAxes(model)) { if (receiver_model == nullptr) receiver_model = std::make_shared(); receiver_model->setCapacitanceModel(std::move(*model), index, rf); } else libWarn(1219, cap_group, "unsupported model axis."); delete model; } } OutputWaveforms * LibertyReader::readOutputWaveforms(const LibertyGroup *timing_group, const RiseFall *rf) { const std::string current_group_name = "output_current_" + rf->to_string_long(); const LibertyGroup *current_group = timing_group->findSubgroup(current_group_name); if (current_group) { OutputWaveformSeq output_currents; for (const LibertyGroup *vector_group : current_group->findSubgroups("vector")) { float ref_time; bool ref_time_exists; vector_group->findAttrFloat("reference_time", ref_time, ref_time_exists); if (ref_time_exists) { ref_time *= time_scale_; TableModel *table = readTableModel(vector_group, rf, TableTemplateType::output_current, current_scale_, ScaleFactorType::unknown); if (table) { TableTemplate *tbl_template = table->tblTemplate(); const TableAxis *slew_axis, *cap_axis; // Canonicalize axis order. if (tbl_template->axis1()->variable()==TableAxisVariable::input_net_transition){ slew_axis = table->axis1(); cap_axis = table->axis2(); } else { slew_axis = table->axis2(); cap_axis = table->axis1(); } if (slew_axis->size() == 1 && cap_axis->size() == 1) { // Convert 1x1xN Table (order 3) to 1D Table. float slew = slew_axis->axisValue(0); float cap = cap_axis->axisValue(0); TablePtr table_ptr = table->table(); FloatTable *values3 = table_ptr->values3(); FloatSeq row = std::move((*values3)[0]); values3->erase(values3->begin()); Table *table1 = new Table(std::move(row), table->table()->axis3ptr()); output_currents.emplace_back(slew, cap, table1, ref_time); } else libWarn(1223, vector_group, "vector index_1 and index_2 must have exactly one value."); } delete table; } else libWarn(1224, vector_group, "vector reference_time not found."); } if (!output_currents.empty()) return makeOutputWaveforms(current_group, output_currents, rf); } return nullptr; } OutputWaveforms * LibertyReader::makeOutputWaveforms(const LibertyGroup *current_group, OutputWaveformSeq &output_currents, const RiseFall *rf) { std::set slew_set, cap_set; FloatSeq slew_values; FloatSeq cap_values; for (const OutputWaveform &waveform : output_currents) { float slew = waveform.slew(); // Filter duplilcate slews and capacitances. if (!slew_set.contains(slew)) { slew_set.insert(slew); slew_values.push_back(slew); } float cap = waveform.cap(); if (!cap_set.contains(cap)) { cap_set.insert(cap); cap_values.push_back(cap); } } sort(slew_values, std::less()); sort(cap_values, std::less()); size_t slew_size = slew_values.size(); size_t cap_size = cap_values.size(); TableAxisPtr slew_axis = make_shared(TableAxisVariable::input_net_transition, std::move(slew_values)); TableAxisPtr cap_axis = make_shared(TableAxisVariable::total_output_net_capacitance, std::move(cap_values)); FloatSeq ref_times(slew_size); Table1Seq current_waveforms(slew_size * cap_size); for (OutputWaveform &waveform : output_currents) { size_t slew_index, cap_index; bool slew_exists, cap_exists; slew_axis->findAxisIndex(waveform.slew(), slew_index, slew_exists); cap_axis->findAxisIndex(waveform.cap(), cap_index, cap_exists); if (slew_exists && cap_exists) { size_t index = slew_index * cap_axis->size() + cap_index; current_waveforms[index] = waveform.releaseCurrents(); ref_times[slew_index] = waveform.referenceTime(); } else libWarn(1221, current_group, "output current waveform %.2e %.2e not found.", waveform.slew(), waveform.cap()); } Table ref_time_tbl(std::move(ref_times), slew_axis); OutputWaveforms *output_current = new OutputWaveforms(slew_axis, cap_axis, rf, current_waveforms, std::move(ref_time_tbl)); return output_current; } TableModel * LibertyReader::readTableModel(const LibertyGroup *table_group, const RiseFall *rf, TableTemplateType template_type, float scale, ScaleFactorType scale_factor_type) { const char *template_name = table_group->firstName(); if (library_ && template_name) { TableTemplate *tbl_template = library_->findTableTemplate(template_name, template_type); if (tbl_template) { TablePtr table = readTableModel(table_group, tbl_template, scale); if (table) { TableModel *table_model = new TableModel(table, tbl_template, scale_factor_type, rf); return table_model; } } else libWarn(1253, table_group, "table template %s not found.", template_name); } return nullptr; } TablePtr LibertyReader::readTableModel(const LibertyGroup *table_group, const TableTemplate *tbl_template, float scale) { const LibertyComplexAttr *values_attr = table_group->findComplexAttr("values"); if (values_attr) { TableAxisPtr axis1 = makeTableAxis(table_group, "index_1", tbl_template->axis1ptr()); TableAxisPtr axis2 = makeTableAxis(table_group, "index_2", tbl_template->axis2ptr()); TableAxisPtr axis3 = makeTableAxis(table_group, "index_3", tbl_template->axis3ptr()); if (axis1 && axis2 && axis3) { // 3D table FloatTable float_table = makeFloatTable(values_attr, table_group, axis1->size() * axis2->size(), axis3->size(), scale); return make_shared(std::move(float_table), axis1, axis2, axis3); } else if (axis1 && axis2) { FloatTable float_table = makeFloatTable(values_attr, table_group, axis1->size(), axis2->size(), scale); return make_shared
(std::move(float_table), axis1, axis2); } else if (axis1) { FloatTable table = makeFloatTable(values_attr, table_group, 1, axis1->size(), scale); return make_shared
(std::move(table[0]), axis1); } else if (axis1 == nullptr && axis2 == nullptr && axis3 == nullptr) { FloatTable table = makeFloatTable(values_attr, table_group, 1, 1, scale); float value = table[0][0]; return std::make_shared
(value); } } else libWarn(1257, table_group, "%s is missing values.", table_group->type().c_str()); return nullptr; } TableAxisPtr LibertyReader::makeTableAxis(const LibertyGroup *table_group, const char *index_attr_name, TableAxisPtr template_axis) { const LibertyComplexAttr *index_attr = table_group->findComplexAttr(index_attr_name); if (index_attr) { FloatSeq axis_values = readFloatSeq(index_attr, 1.0F); if (axis_values.empty()) libWarn(1177, index_attr, "missing table index values."); else { // Check monotonicity of the values. float prev = axis_values[0]; for (size_t i = 1; i < axis_values.size(); i++) { float value = axis_values[i]; if (value <= prev) libWarn(1173, index_attr, "non-increasing table index values."); prev = value; } TableAxisVariable axis_var = template_axis->variable(); const Units *units = library_->units(); float scale = tableVariableUnit(axis_var, units)->scale(); scaleFloats(axis_values, scale); return make_shared(axis_var, std::move(axis_values)); } } return template_axis; } //////////////////////////////////////////////////////////////// void LibertyReader::makeTimingArcs(LibertyCell *cell, const std::string &from_port_name, LibertyPort *to_port, LibertyPort *related_out_port, bool one_to_one, TimingArcAttrsPtr timing_attrs, int timing_line) { PortNameBitIterator from_port_iter(cell, from_port_name.c_str(), this, timing_line); if (from_port_iter.size() == 1 && !to_port->hasMembers()) { // one -> one if (from_port_iter.hasNext()) { LibertyPort *from_port = from_port_iter.next(); if (from_port->direction()->isOutput()) libWarn(1212, timing_line, "timing group from output port."); builder_.makeTimingArcs(cell, from_port, to_port, related_out_port, timing_attrs, timing_line); } } else if (from_port_iter.size() > 1 && !to_port->hasMembers()) { // bus -> one while (from_port_iter.hasNext()) { LibertyPort *from_port = from_port_iter.next(); if (from_port->direction()->isOutput()) libWarn(1213, timing_line, "timing group from output port."); builder_.makeTimingArcs(cell, from_port, to_port, related_out_port, timing_attrs, timing_line); } } else if (from_port_iter.size() == 1 && to_port->hasMembers()) { // one -> bus if (from_port_iter.hasNext()) { LibertyPort *from_port = from_port_iter.next(); if (from_port->direction()->isOutput()) libWarn(1214, timing_line, "timing group from output port."); LibertyPortMemberIterator bit_iter(to_port); while (bit_iter.hasNext()) { LibertyPort *to_port_bit = bit_iter.next(); builder_.makeTimingArcs(cell, from_port, to_port_bit, related_out_port, timing_attrs, timing_line); } } } else { // bus -> bus if (one_to_one) { int from_size = from_port_iter.size(); int to_size = to_port->size(); LibertyPortMemberIterator to_port_iter(to_port); // warn about different sizes if (from_size != to_size) libWarn(1216, timing_line, "timing port %s and related port %s are different sizes.", from_port_name.c_str(), to_port->name()); // align to/from iterators for one-to-one mapping while (from_size > to_size) { from_size--; from_port_iter.next(); } while (to_size > from_size) { to_size--; to_port_iter.next(); } // make timing arcs while (from_port_iter.hasNext() && to_port_iter.hasNext()) { LibertyPort *from_port_bit = from_port_iter.next(); LibertyPort *to_port_bit = to_port_iter.next(); if (from_port_bit->direction()->isOutput()) libWarn(1215, timing_line, "timing group from output port."); builder_.makeTimingArcs(cell, from_port_bit, to_port_bit, related_out_port, timing_attrs, timing_line); } } else { // cross product while (from_port_iter.hasNext()) { LibertyPort *from_port_bit = from_port_iter.next(); LibertyPortMemberIterator to_port_iter(to_port); while (to_port_iter.hasNext()) { LibertyPort *to_port_bit = to_port_iter.next(); builder_.makeTimingArcs(cell, from_port_bit, to_port_bit, related_out_port, timing_attrs, timing_line); } } } } } void LibertyReader::makeTimingArcs(LibertyCell *cell, LibertyPort *to_port, LibertyPort *related_out_port, TimingArcAttrsPtr timing_attrs, int timing_line) { if (to_port->hasMembers()) { LibertyPortMemberIterator bit_iter(to_port); while (bit_iter.hasNext()) { LibertyPort *to_port_bit = bit_iter.next(); builder_.makeTimingArcs(cell, nullptr, to_port_bit, related_out_port, timing_attrs, timing_line); } } else builder_.makeTimingArcs(cell, nullptr, to_port, related_out_port, timing_attrs, timing_line); } //////////////////////////////////////////////////////////////// void LibertyReader::readLeagageGrouops(LibertyCell *cell, const LibertyGroup *cell_group) { for (const LibertyGroup *leak_group : cell_group->findSubgroups("leakage_power")) { FuncExpr *when = readFuncExpr(cell, leak_group, "when"); float power; bool exists; leak_group->findAttrFloat("value", power, exists); if (exists) { LibertyPort *related_pg_port = findLibertyPort(cell, leak_group, "related_pg_pin"); cell->makeLeakagePower(related_pg_port, when, power * power_scale_); } else libWarn(1307, leak_group, "leakage_power missing value."); } } void LibertyReader::readInternalPowerGroups(LibertyCell *cell, const LibertyPortSeq &ports, const LibertyGroup *port_group) { for (LibertyPort *port : ports) { for (const LibertyGroup *ipwr_group : port_group->findSubgroups("internal_power")) { LibertyPortSeq related_ports = findLibertyPorts(cell, ipwr_group, "related_pin"); LibertyPort *related_pg_port = findLibertyPort(cell, ipwr_group, "related_pg_pin"); std::shared_ptr when; FuncExpr *when1 = readFuncExpr(cell, ipwr_group, "when"); if (when1) when = std::shared_ptr(when1); InternalPowerModels models; // rise/fall_power group for (const RiseFall *rf : RiseFall::range()) { std::string pwr_attr_name = rf->to_string_long() + "_power"; const LibertyGroup *pwr_group = ipwr_group->findSubgroup(pwr_attr_name); if (pwr_group) { TableModel *model = readTableModel(pwr_group, rf, TableTemplateType::power, energyScale(), ScaleFactorType::internal_power); models[rf->index()] = std::make_shared(model); } } // power group (rise/fall power are the same) const LibertyGroup *pwr_group = ipwr_group->findSubgroup("power"); if (pwr_group) { TableModel *model = readTableModel(pwr_group, RiseFall::rise(), TableTemplateType::power, energyScale(), ScaleFactorType::internal_power); auto pwr_model = std::make_shared(model); for (const RiseFall *rf : RiseFall::range()) models[rf->index()] = pwr_model; } if (related_ports.empty()) cell->makeInternalPower(port, nullptr, related_pg_port, when, models); else { for (LibertyPort *related_port : related_ports) cell->makeInternalPower(port, related_port, related_pg_port, when, models); } } } } //////////////////////////////////////////////////////////////// FuncExpr * LibertyReader::readFuncExpr(LibertyCell *cell, const LibertyGroup *group, const char *attr_name) { const std::string *attr = group->findAttrString(attr_name); if (attr) return parseFunc(attr->c_str(), attr_name, cell, group->line()); else return nullptr; } LibertyPort * LibertyReader::findLibertyPort(LibertyCell *cell, const LibertyGroup *group, const char *port_name_attr) { const LibertySimpleAttr *attr = group->findSimpleAttr(port_name_attr); if (attr) { const std::string *port_name = attr->stringValue(); if (port_name) { LibertyPort *port = cell->findLibertyPort(port_name->c_str()); if (port) return port; else libWarn(1290, attr, "port %s not found.", port_name->c_str()); } } return nullptr; } StdStringSeq LibertyReader::findAttributStrings(const LibertyGroup *group, const char *name_attr) { const LibertySimpleAttr *attr = group->findSimpleAttr(name_attr); if (attr) { const std::string *strings = attr->stringValue(); if (strings) { return parseTokens(*strings, ' '); } } return StdStringSeq(); } LibertyPortSeq LibertyReader::findLibertyPorts(LibertyCell *cell, const LibertyGroup *group, const char *port_name_attr) { LibertyPortSeq ports; StdStringSeq port_names = findAttributStrings(group, port_name_attr); for (const std::string &port_name : port_names) { LibertyPort *port = findPort(cell, port_name.c_str()); if (port) ports.push_back(port); else libWarn(1306, group, "port %s not found.", port_name.c_str()); } return ports; } //////////////////////////////////////////////////////////////// TimingModel * LibertyReader::makeScalarCheckModel(LibertyCell *cell, float value, ScaleFactorType scale_factor_type, const RiseFall *rf) { TablePtr table = std::make_shared
(value); TableTemplate *tbl_template = library_->findTableTemplate("scalar", TableTemplateType::delay); TableModel *table_model = new TableModel(table, tbl_template, scale_factor_type, rf); TableModelsEarlyLate sigmas{}; CheckTableModel *check_model = new CheckTableModel(cell, table_model, std::move(sigmas)); return check_model; } void LibertyReader::checkLatchEnableSense(FuncExpr *enable_func, int line) { LibertyPortSet enable_ports = enable_func->ports(); for (LibertyPort *enable_port : enable_ports) { TimingSense enable_sense = enable_func->portTimingSense(enable_port); switch (enable_sense) { case TimingSense::positive_unate: case TimingSense::negative_unate: break; case TimingSense::non_unate: libWarn(1200, line, "latch enable function is non-unate for port %s.", enable_port->name()); break; case TimingSense::none: case TimingSense::unknown: libWarn(1201, line, "latch enable function is unknown for port %s.", enable_port->name()); break; } } } //////////////////////////////////////////////////////////////// void LibertyReader::readNormalizedDriverWaveform(const LibertyGroup *library_group) { for (const LibertyGroup *waveform_group : library_group->findSubgroups("normalized_driver_waveform")) { const char *template_name = waveform_group->firstName(); if (template_name) { TableTemplate *tbl_template = library_->findTableTemplate(template_name, TableTemplateType::delay); if (!tbl_template) { libWarn(1256, waveform_group, "table template %s not found.", template_name); continue; } TablePtr table = readTableModel(waveform_group, tbl_template, time_scale_); if (!table) continue; if (table->axis1()->variable() != TableAxisVariable::input_net_transition) { libWarn(1265, waveform_group, "normalized_driver_waveform variable_1 must be input_net_transition"); continue; } if (table->axis2()->variable() != TableAxisVariable::normalized_voltage) { libWarn(1225, waveform_group, "normalized_driver_waveform variable_2 must be normalized_voltage"); continue; } std::string driver_waveform_name; const std::string *name_attr = waveform_group->findAttrString("driver_waveform_name"); if (name_attr) driver_waveform_name = *name_attr; library_->makeDriverWaveform(driver_waveform_name, table); } else libWarn(1227, waveform_group, "normalized_driver_waveform missing template."); } } //////////////////////////////////////////////////////////////// void LibertyReader::readLevelShifterType(LibertyCell *cell, const LibertyGroup *cell_group) { const std::string *level_shifter_type = cell_group->findAttrString("level_shifter_type"); if (level_shifter_type) { if (*level_shifter_type == "HL") cell->setLevelShifterType(LevelShifterType::HL); else if (*level_shifter_type == "LH") cell->setLevelShifterType(LevelShifterType::LH); else if (*level_shifter_type == "HL_LH") cell->setLevelShifterType(LevelShifterType::HL_LH); else libWarn(1228, cell_group, "level_shifter_type must be HL, LH, or HL_LH"); } } void LibertyReader::readSwitchCellType(LibertyCell *cell, const LibertyGroup *cell_group) { const std::string *switch_cell_type = cell_group->findAttrString("switch_cell_type"); if (switch_cell_type) { if (*switch_cell_type == "coarse_grain") cell->setSwitchCellType(SwitchCellType::coarse_grain); else if (*switch_cell_type == "fine_grain") cell->setSwitchCellType(SwitchCellType::fine_grain); else libWarn(1229, cell_group, "switch_cell_type must be coarse_grain or fine_grain"); } } void LibertyReader::readCellOcvDerateGroup(LibertyCell *cell, const LibertyGroup *cell_group) { const std::string *derate_name = cell_group->findAttrString("ocv_derate_group"); if (derate_name) { OcvDerate *derate = cell->findOcvDerate(derate_name->c_str()); if (derate == nullptr) derate = library_->findOcvDerate(derate_name->c_str()); if (derate) cell->setOcvDerate(derate); else libWarn(1237, cell_group, "OCV derate group named %s not found.", derate_name->c_str()); } } void LibertyReader::readStatetable(LibertyCell *cell, const LibertyGroup *cell_group) { for (const LibertyGroup *statetable_group : cell_group->findSubgroups("statetable")) { const char *input_ports_arg = statetable_group->firstName(); const char *internal_ports_arg = statetable_group->params().size() >= 2 ? statetable_group->secondName() : nullptr; StdStringSeq input_ports; if (input_ports_arg) input_ports = parseTokens(input_ports_arg, ' '); StdStringSeq internal_ports; if (internal_ports_arg) internal_ports = parseTokens(internal_ports_arg, ' '); const LibertySimpleAttr *table_attr = statetable_group->findSimpleAttr("table"); if (table_attr) { const std::string *table_str = table_attr->stringValue(); StdStringSeq table_rows = parseTokens(table_str->c_str(), ','); size_t input_count = input_ports.size(); size_t internal_count = internal_ports.size(); StatetableRows table; for (const std::string &row : table_rows) { const StdStringSeq row_groups = parseTokens(row, ':'); if (row_groups.size() != 3) { libWarn(1300, table_attr, "table row must have 3 groups separated by ':'."); break; } StdStringSeq inputs = parseTokens(row_groups[0], ' '); if (inputs.size() != input_count) { libWarn(1301,table_attr,"table row has %zu input values but %zu are required.", inputs.size(), input_count); break; } StdStringSeq currents = parseTokens(row_groups[1], ' '); if (currents.size() != internal_count) { libWarn(1302,table_attr, "table row has %zu current values but %zu are required.", currents.size(), internal_count); break; } StdStringSeq nexts = parseTokens(row_groups[2], ' '); if (nexts.size() != internal_count) { libWarn(1303, table_attr, "table row has %zu next values but %zu are required.", nexts.size(), internal_count); break; } StateInputValues input_values = parseStateInputValues(inputs, table_attr); StateInternalValues current_values=parseStateInternalValues(currents,table_attr); StateInternalValues next_values = parseStateInternalValues(nexts, table_attr); table.emplace_back(input_values, current_values, next_values); } LibertyPortSeq input_port_ptrs; for (const std::string &input : input_ports) { LibertyPort *port = cell->findLibertyPort(input.c_str()); if (port) input_port_ptrs.push_back(port); else libWarn(1298, statetable_group, "statetable input port %s not found.", input.c_str()); } LibertyPortSeq internal_port_ptrs; for (const std::string &internal : internal_ports) { LibertyPort *port = cell->findLibertyPort(internal.c_str()); if (port == nullptr) port = makePort(cell, internal.c_str()); internal_port_ptrs.push_back(port); } cell->makeStatetable(input_port_ptrs, internal_port_ptrs, table); } } } void LibertyReader::readTestCell(LibertyCell *cell, const LibertyGroup *cell_group) { const LibertyGroup *test_cell_group = cell_group->findSubgroup("test_cell"); if (test_cell_group) { if (cell->testCell()) libWarn(1262, test_cell_group, "cell %s test_cell redefinition.", cell->name()); else { std::string test_cell_name = std::string(cell->name()) + "/test_cell"; TestCell *test_cell = new TestCell(cell->libertyLibrary(), std::move(test_cell_name), cell->filename()); cell->setTestCell(test_cell); readCell(test_cell, test_cell_group); } } } //////////////////////////////////////////////////////////////// LibertyPort * LibertyReader::makePort(LibertyCell *cell, const char *port_name) { std::string sta_name = portLibertyToSta(port_name); return builder_.makePort(cell, sta_name.c_str()); } LibertyPort * LibertyReader::makeBusPort(LibertyCell *cell, const char *bus_name, int from_index, int to_index, BusDcl *bus_dcl) { std::string sta_name = portLibertyToSta(bus_name); return builder_.makeBusPort(cell, bus_name, from_index, to_index, bus_dcl); } // Also used by LibExprParser::makeFuncExprPort. LibertyPort * libertyReaderFindPort(const LibertyCell *cell, const char *port_name) { LibertyPort *port = cell->findLibertyPort(port_name); if (port == nullptr) { const LibertyLibrary *library = cell->libertyLibrary(); char brkt_left = library->busBrktLeft(); char brkt_right = library->busBrktRight(); const char escape = '\\'; // Pins at top level with bus names have escaped brackets. std::string escaped_port_name = escapeChars(port_name, brkt_left, brkt_right, escape); port = cell->findLibertyPort(escaped_port_name.c_str()); } return port; } LibertyPort * LibertyReader::findPort(LibertyCell *cell, const char *port_name) { return libertyReaderFindPort(cell, port_name); } float LibertyReader::defaultCap(LibertyPort *port) { PortDirection *dir = port->direction(); float cap = 0.0; if (dir->isInput()) cap = library_->defaultInputPinCap(); else if (dir->isOutput() || dir->isTristate()) cap = library_->defaultOutputPinCap(); else if (dir->isBidirect()) cap = library_->defaultBidirectPinCap(); return cap; } //////////////////////////////////////////////////////////////// static EnumNameMap state_input_value_name_map = {{StateInputValue::low, "L"}, {StateInputValue::high, "H"}, {StateInputValue::dont_care, "-"}, {StateInputValue::low_high, "L/H"}, {StateInputValue::high_low, "H/L"}, {StateInputValue::rise, "R"}, {StateInputValue::fall, "F"}, {StateInputValue::not_rise, "~R"}, {StateInputValue::not_fall, "~F"} }; static EnumNameMap state_internal_value_name_map = {{StateInternalValue::low, "L"}, {StateInternalValue::high, "H"}, {StateInternalValue::unspecified, "-"}, {StateInternalValue::low_high, "L/H"}, {StateInternalValue::high_low, "H/L"}, {StateInternalValue::unknown, "X"}, {StateInternalValue::hold, "N"} }; StateInputValues LibertyReader::parseStateInputValues(StdStringSeq &inputs, const LibertySimpleAttr *attr) { StateInputValues input_values; for (std::string input : inputs) { bool exists; StateInputValue value; state_input_value_name_map.find(input.c_str(), value, exists); if (!exists) { libWarn(1304, attr, "table input value '%s' not recognized.", input.c_str()); value = StateInputValue::dont_care; } input_values.push_back(value); } return input_values; } StateInternalValues LibertyReader::parseStateInternalValues(StdStringSeq &states, const LibertySimpleAttr *attr) { StateInternalValues state_values; for (std::string state : states) { bool exists; StateInternalValue value; state_internal_value_name_map.find(state.c_str(), value, exists); if (!exists) { libWarn(1305, attr, "table internal value '%s' not recognized.", state.c_str()); value = StateInternalValue::unknown; } state_values.push_back(value); } return state_values; } //////////////////////////////////////////////////////////////// FloatTable LibertyReader::makeFloatTable(const LibertyComplexAttr *values_attr, const LibertyGroup *table_group, size_t rows, size_t cols, float scale) { FloatTable table; table.reserve(rows); for (const LibertyAttrValue *value : values_attr->values()) { FloatSeq row; row.reserve(cols); if (value->isString()) row = parseStringFloatList(value->stringValue(), scale, values_attr); else if (value->isFloat()) row.push_back(value->floatValue() * scale); else libWarn(1258, values_attr, "%s is not a list of floats.", values_attr->name().c_str()); if (row.size() != cols) { libWarn(1259, values_attr, "%s row has %zu columns but axis has %zu.", table_group->type().c_str(), row.size(), cols); for (size_t c = row.size(); c < cols; c++) row.push_back(0.0); } table.push_back(std::move(row)); } if (table.size() != rows) { if (rows == 0) libWarn(1260, values_attr, "%s missing axis values.", table_group->type().c_str()); else libWarn(1261, values_attr, "%s has %zu rows but axis has %zu.", table_group->type().c_str(), table.size(), rows); for (size_t r = table.size(); r < rows; r++) { FloatSeq row(cols, 0.0); table.push_back(std::move(row)); } } return table; } //////////////////////////////////////////////////////////////// void LibertyReader::getAttrInt(const LibertySimpleAttr *attr, // Return values. int &value, bool &exists) { value = 0; exists = false; const LibertyAttrValue &attr_value = attr->value(); if (attr_value.isFloat()) { float float_val = attr_value.floatValue(); value = static_cast(float_val); exists = true; } else libWarn(1268, attr, "%s attribute is not an integer.",attr->name().c_str()); } // Get two floats in a complex attribute. // attr(float1, float2); void LibertyReader::getAttrFloat2(const LibertyComplexAttr *attr, // Return values. float &value1, float &value2, bool &exists) { exists = false; const LibertyAttrValueSeq &values = attr->values(); if (values.size() == 2) { LibertyAttrValue *value = values[0]; getAttrFloat(attr, value, value1, exists); if (!exists) libWarn(1272, attr, "%s is not a float.", attr->name().c_str()); value = values[1]; getAttrFloat(attr, value, value2, exists); if (!exists) libWarn(1273, attr, "%s is not a float.", attr->name().c_str()); } else libWarn(1274, attr, "%s requires 2 valules.", attr->name().c_str()); } void LibertyReader::getAttrFloat(const LibertyComplexAttr *attr, const LibertyAttrValue *attr_value, // Return values. float &value, bool &valid) { if (attr_value->isFloat()) { valid = true; value = attr_value->floatValue(); } else if (attr_value->isString()) { const std::string &str = attr_value->stringValue(); variableValue(str.c_str(), value, valid); if (!valid) { char *end; value = strtof(str.c_str(), &end); if ((*end && !isspace(*end)) || str == "inf") libWarn(1183, attr->line(), "%s value %s is not a float.", attr->name().c_str(), str.c_str()); valid = true; } } } // Parse string of comma separated floats. // Note that some brain damaged vendors (that used to "Think") are not // consistent about including the delimiters. FloatSeq LibertyReader::parseStringFloatList(const std::string &float_list, float scale, const LibertySimpleAttr *attr) { FloatSeq values; values.reserve(std::max(10, float_list.size() / 5)); const char *token = float_list.c_str(); while (*token != '\0') { // Some (brain dead) libraries enclose floats in brackets. if (*token == '{') token++; char *end; float value = strtof(token, &end) * scale; if (end == token || !(*end == '\0' || isspace(*end) || *end == ',' || *end == '}')) { std::string token_end = token; if (end != token) { token_end.clear(); for (const char *t = token; t <= end; t++) token_end += *t; } libWarn(1310, attr, "%s is not a float.", token_end.c_str()); token += token_end.size(); } else { values.push_back(value); token = end; } while (*token == ',' || *token == ' ' || *token == '}') token++; } return values; } FloatSeq LibertyReader::parseStringFloatList(const std::string &float_list, float scale, const LibertyComplexAttr *attr) { FloatSeq values; values.reserve(std::max(10, float_list.size() / 5)); const char *token = float_list.c_str(); while (*token != '\0') { if (*token == '{') token++; char *end; float value = strtof(token, &end) * scale; if (end == token || !(*end == '\0' || isspace(*end) || *end == ',' || *end == '}')) { std::string token_end = token; if (end != token) { token_end.clear(); for (const char *t = token; t <= end; t++) token_end += *t; } libWarn(1275, attr, "%s is not a float.", token_end.c_str()); token += token_end.size(); } else { values.push_back(value); token = end; } while (*token == ',' || *token == ' ' || *token == '}') token++; } return values; } FloatSeq LibertyReader::readFloatSeq(const LibertyComplexAttr *attr, float scale) { FloatSeq values; const LibertyAttrValueSeq &attr_values = attr->values(); if (attr_values.size() == 1) { LibertyAttrValue *value = attr_values[0]; if (value->isString()) { values = parseStringFloatList(value->stringValue(), scale, attr); } else if (value->isFloat()) { values.push_back(value->floatValue() * scale); } else libWarn(1276, attr, "%s is missing values.", attr->name().c_str()); } else if (attr_values.size() > 1) { for (LibertyAttrValue *val : attr_values) { if (val->isFloat()) values.push_back(val->floatValue() * scale); else if (val->isString()) { FloatSeq parsed = parseStringFloatList(val->stringValue(), scale, attr); values.insert(values.end(), parsed.begin(), parsed.end()); } } } else libWarn(1277, attr, "%s has no values.", attr->name().c_str()); return values; } //////////////////////////////////////////////////////////////// void LibertyReader::getAttrBool(const LibertySimpleAttr *attr, // Return values. bool &value, bool &exists) { exists = false; const LibertyAttrValue &val = attr->value(); if (val.isString()) { const std::string &str = val.stringValue(); if (stringEqual(str.c_str(), "true")) { value = true; exists = true; } else if (stringEqual(str.c_str(), "false")) { value = false; exists = true; } else libWarn(1288, attr, "%s attribute is not boolean.", attr->name().c_str()); } else libWarn(1289, attr, "%s attribute is not boolean.", attr->name().c_str()); } // Read L/H/X string attribute values as bool. LogicValue LibertyReader::getAttrLogicValue(const LibertySimpleAttr *attr) { const std::string *str = attr->stringValue(); if (str) { if (*str == "L") return LogicValue::zero; else if (*str == "H") return LogicValue::one; else if (*str == "X") return LogicValue::unknown; else libWarn(1282, attr, "attribute %s value %s not recognized.", attr->name().c_str(), str->c_str()); // fall thru } return LogicValue::unknown; } const EarlyLateAll * LibertyReader::getAttrEarlyLate(const LibertySimpleAttr *attr) { const std::string *value = attr->stringValue(); if (*value == "early") return EarlyLateAll::early(); else if (*value == "late") return EarlyLateAll::late(); else if (*value == "early_and_late") return EarlyLateAll::all(); else { libWarn(1283, attr, "unknown early/late value."); return EarlyLateAll::all(); } } //////////////////////////////////////////////////////////////// FuncExpr * LibertyReader::parseFunc(const char *func, const char *attr_name, const LibertyCell *cell, int line) { std::string error_msg; stringPrint(error_msg, "%s, line %d %s", filename_, line, attr_name); return parseFuncExpr(func, cell, error_msg.c_str(), report_); } //////////////////////////////////////////////////////////////// void LibertyReader::visitVariable(LibertyVariable *var) { const std::string &var_name = var->variable(); float value; bool exists; findKeyValue(var_map_, var_name, value, exists); var_map_[var_name] = var->value(); } void LibertyReader::variableValue(const char *var, float &value, bool &exists) { findKeyValue(var_map_, var, value, exists); } //////////////////////////////////////////////////////////////// void LibertyReader::libWarn(int id, const LibertyGroup *obj, const char *fmt, ...) { va_list args; va_start(args, fmt); report_->vfileWarn(id, filename_, obj->line(), fmt, args); va_end(args); } void LibertyReader::libWarn(int id, const LibertySimpleAttr *obj, const char *fmt, ...) { va_list args; va_start(args, fmt); report_->vfileWarn(id, filename_, obj->line(), fmt, args); va_end(args); } void LibertyReader::libWarn(int id, const LibertyComplexAttr *obj, const char *fmt, ...) { va_list args; va_start(args, fmt); report_->vfileWarn(id, filename_, obj->line(), fmt, args); va_end(args); } void LibertyReader::libWarn(int id, int line, const char *fmt, ...) { va_list args; va_start(args, fmt); report_->vfileWarn(id, filename_, line, fmt, args); va_end(args); } void LibertyReader::libError(int id, const LibertyGroup *obj, const char *fmt, ...) { va_list args; va_start(args, fmt); report_->vfileError(id, filename_, obj->line(), fmt, args); va_end(args); } void LibertyReader::libError(int id, const LibertySimpleAttr *obj, const char *fmt, ...) { va_list args; va_start(args, fmt); report_->vfileError(id, filename_, obj->line(), fmt, args); va_end(args); } void LibertyReader::libError(int id, const LibertyComplexAttr *obj, const char *fmt, ...) { va_list args; va_start(args, fmt); report_->vfileError(id, filename_, obj->line(), fmt, args); va_end(args); } //////////////////////////////////////////////////////////////// void LibertyReader::readDefaultOcvDerateGroup(const LibertyGroup *library_group) { const std::string *derate_name = library_group->findAttrString("default_ocv_derate_group"); if (derate_name) { OcvDerate *derate = library_->findOcvDerate(derate_name->c_str()); if (derate) library_->setDefaultOcvDerate(derate); else libWarn(1284, library_group, "OCV derate group named %s not found.", derate_name->c_str()); } } // Read cell or library level ocv_derate groups. void LibertyReader::readOcvDerateFactors(LibertyCell *cell, const LibertyGroup *parent_group) { for (const LibertyGroup *ocv_derate_group : parent_group->findSubgroups("ocv_derate")) { const char *name = ocv_derate_group->firstName(); if (name) { OcvDerate *ocv_derate = cell ? cell->makeOcvDerate(name) : library_->makeOcvDerate(name); for (const LibertyGroup *factors_group : ocv_derate_group->findSubgroups("ocv_derate_factors")) { const RiseFallBoth *rf_type = RiseFallBoth::riseFall(); const std::string *rf_attr = factors_group->findAttrString("rf_type"); if (rf_attr) { if (*rf_attr == "rise") rf_type = RiseFallBoth::rise(); else if (*rf_attr == "fall") rf_type = RiseFallBoth::fall(); else if (*rf_attr == "rise_and_fall") rf_type = RiseFallBoth::riseFall(); else libError(1286, factors_group, "unknown rise/fall."); } const EarlyLateAll *derate_type = EarlyLateAll::all(); const std::string *derate_attr = factors_group->findAttrString("derate_type"); if (derate_attr) { if (*derate_attr == "early") derate_type = EarlyLateAll::early(); else if (*derate_attr == "late") derate_type = EarlyLateAll::late(); else if (*derate_attr == "early_and_late") derate_type = EarlyLateAll::all(); else { libWarn(1309, factors_group, "unknown early/late value."); } } PathType path_type = PathType::clk_and_data; const std::string *path_attr = factors_group->findAttrString("path_type"); if (path_attr) { if (*path_attr == "clock") path_type = PathType::clk; else if (*path_attr == "data") path_type = PathType::data; else if (*path_attr == "clock_and_data") path_type = PathType::clk_and_data; else libWarn(1287, factors_group, "unknown derate type."); } const char *template_name = factors_group->firstName(); if (template_name) { TableTemplate *tbl_template = library_->findTableTemplate(template_name, TableTemplateType::ocv); if (tbl_template) { TablePtr table = readTableModel(factors_group, tbl_template, 1.0F); if (table) { for (const EarlyLate *early_late : derate_type->range()) { for (const RiseFall *rf : rf_type->range()) { if (path_type == PathType::clk_and_data) { ocv_derate->setDerateTable(rf, early_late, PathType::clk, table); ocv_derate->setDerateTable(rf, early_late, PathType::data, table); } else ocv_derate->setDerateTable(rf, early_late, path_type, table); } } } } else libWarn(1308, factors_group, "table template %s not found.", template_name); } } } else libWarn(1285, ocv_derate_group, "ocv_derate missing name."); } } //////////////////////////////////////////////////////////////// PortNameBitIterator::PortNameBitIterator(LibertyCell *cell, const char *port_name, LibertyReader *visitor, int line) : cell_(cell), visitor_(visitor), line_(line), port_(nullptr), bit_iterator_(nullptr), range_bus_port_(nullptr), range_name_next_(nullptr), size_(0) { init(port_name); } void PortNameBitIterator::init(const char *port_name) { LibertyPort *port = visitor_->findPort(cell_, port_name); if (port) { if (port->isBus()) bit_iterator_ = new LibertyPortMemberIterator(port); else port_ = port; size_ = port->size(); } else { // Check for bus range. LibertyLibrary *library = visitor_->library(); bool is_bus, is_range, subscript_wild; std::string bus_name; int from, to; parseBusName(port_name, library->busBrktLeft(), library->busBrktRight(), '\\', is_bus, is_range, bus_name, from, to, subscript_wild); if (is_range) { port = visitor_->findPort(cell_, port_name); if (port) { if (port->isBus()) { if (port->busIndexInRange(from) && port->busIndexInRange(to)) { range_bus_port_ = port; range_from_ = from; range_to_ = to; range_bit_ = from; } else visitor_->libWarn(1292, line_, "port %s subscript out of range.", port_name); } else visitor_->libWarn(1293, line_, "port range %s of non-bus port %s.", port_name, bus_name.c_str()); } else { range_bus_name_ = bus_name; range_from_ = from; range_to_ = to; range_bit_ = from; findRangeBusNameNext(); } size_ = abs(from - to) + 1; } else visitor_->libWarn(1294, line_, "port %s not found.", port_name); } } PortNameBitIterator::~PortNameBitIterator() { delete bit_iterator_; } bool PortNameBitIterator::hasNext() { return port_ || (bit_iterator_ && bit_iterator_->hasNext()) || (range_bus_port_ && ((range_from_ > range_to_) ? range_bit_ >= range_to_ : range_bit_ <= range_from_)) || (!range_bus_name_.empty() && range_name_next_); } LibertyPort * PortNameBitIterator::next() { if (port_) { LibertyPort *next = port_; port_ = nullptr; return next; } else if (bit_iterator_) return bit_iterator_->next(); else if (range_bus_port_) { LibertyPort *next = range_bus_port_->findLibertyBusBit(range_bit_); if (range_from_ > range_to_) range_bit_--; else range_bit_++; return next; } else if (!range_bus_name_.empty()) { LibertyPort *next = range_name_next_; findRangeBusNameNext(); return next; } else return nullptr; } void PortNameBitIterator::findRangeBusNameNext() { if ((range_from_ > range_to_) ? range_bit_ >= range_to_ : range_bit_ <= range_to_) { LibertyLibrary *library = visitor_->library(); std::string bus_bit_name = range_bus_name_ + library->busBrktLeft() + std::to_string(range_bit_) + library->busBrktRight(); range_name_next_ = visitor_->findPort(cell_, bus_bit_name.c_str()); if (range_name_next_) { if (range_from_ > range_to_) range_bit_--; else range_bit_++; } else visitor_->libWarn(1295, line_, "port %s not found.", bus_bit_name.c_str()); } else range_name_next_ = nullptr; } //////////////////////////////////////////////////////////////// OutputWaveform::OutputWaveform(float slew, float cap, Table *currents, float reference_time) : slew_(slew), cap_(cap), currents_(currents), reference_time_(reference_time) { } Table * OutputWaveform::releaseCurrents() { return currents_.release(); } } // namespace