// OpenSTA, Static Timing Analyzer // Copyright (c) 2024, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "LibertyReader.hh" #include #include #include "EnumNameMap.hh" #include "Report.hh" #include "Debug.hh" #include "TokenParser.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 "LibertyExpr.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 { using std::make_shared; using std::string; 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() { init(filename, infer_latches, network); defineVisitors(); } void LibertyReader::init(const char *filename, bool infer_latches, Network *network) { filename_ = filename; infer_latches_ = infer_latches; report_ = network->report(); debug_ = network->debug(); network_ = network; var_map_ = nullptr; library_ = nullptr; wireload_ = nullptr; wireload_selection_ = nullptr; default_wireload_ = nullptr; default_wireload_selection_ = nullptr; scale_factors_ = nullptr; save_scale_factors_ = nullptr; tbl_template_ = nullptr; cell_ = nullptr; save_cell_ = nullptr; scaled_cell_owner_ = nullptr; test_cell_ = nullptr; ocv_derate_name_ = nullptr; op_cond_ = nullptr; ports_ = nullptr; port_ = nullptr; test_port_ = nullptr; port_group_ = nullptr; saved_ports_ = nullptr; saved_port_group_ = nullptr; in_bus_ = false; in_bundle_ = false; sequential_ = nullptr; statetable_ = nullptr; timing_ = nullptr; internal_power_ = nullptr; leakage_power_ = nullptr; table_ = nullptr; rf_ = nullptr; index_ = 0; table_model_scale_ = 1.0; mode_def_ = nullptr; mode_value_ = nullptr; ocv_derate_ = nullptr; pg_port_ = nullptr; default_operating_condition_ = nullptr; receiver_model_ = nullptr; builder_.init(debug_, report_); for (auto rf_index : RiseFall::rangeIndex()) { have_input_threshold_[rf_index] = false; have_output_threshold_[rf_index] = false; have_slew_lower_threshold_[rf_index] = false; have_slew_upper_threshold_[rf_index] = false; } } LibertyReader::~LibertyReader() { if (var_map_) { LibertyVariableMap::Iterator iter(var_map_); while (iter.hasNext()) { const char *var; float value; iter.next(var, value); stringDelete(var); } delete var_map_; } // Scaling factor attribute names are allocated, so delete them. LibraryAttrMap::Iterator attr_iter(attr_visitor_map_); while (attr_iter.hasNext()) { const char *attr_name; LibraryAttrVisitor visitor; attr_iter.next(attr_name, visitor); stringDelete(attr_name); } } 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) { group_begin_map_[type] = begin_visitor; group_end_map_[type] = end_visitor; } void LibertyReader::defineAttrVisitor(const char *attr_name, LibraryAttrVisitor visitor) { attr_visitor_map_[stringCopy(attr_name)] = visitor; } void LibertyReader::defineVisitors() { // Library defineGroupVisitor("library", &LibertyReader::beginLibrary, &LibertyReader::endLibrary); defineAttrVisitor("time_unit", &LibertyReader::visitTimeUnit); defineAttrVisitor("pulling_resistance_unit", &LibertyReader::visitPullingResistanceUnit); defineAttrVisitor("resistance_unit", &LibertyReader::visitResistanceUnit); defineAttrVisitor("capacitive_load_unit", &LibertyReader::visitCapacitiveLoadUnit); defineAttrVisitor("voltage_unit", &LibertyReader::visitVoltageUnit); defineAttrVisitor("current_unit", &LibertyReader::visitCurrentUnit); defineAttrVisitor("leakage_power_unit", &LibertyReader::visitPowerUnit); defineAttrVisitor("distance_unit", &LibertyReader::visitDistanceUnit); defineAttrVisitor("delay_model", &LibertyReader::visitDelayModel); defineAttrVisitor("bus_naming_style", &LibertyReader::visitBusStyle); defineAttrVisitor("voltage_map", &LibertyReader::visitVoltageMap); defineAttrVisitor("nom_temperature", &LibertyReader::visitNomTemp); defineAttrVisitor("nom_voltage", &LibertyReader::visitNomVolt); defineAttrVisitor("nom_process", &LibertyReader::visitNomProc); defineAttrVisitor("default_inout_pin_cap", &LibertyReader::visitDefaultInoutPinCap); defineAttrVisitor("default_input_pin_cap", &LibertyReader::visitDefaultInputPinCap); defineAttrVisitor("default_output_pin_cap", &LibertyReader::visitDefaultOutputPinCap); defineAttrVisitor("default_max_transition", &LibertyReader::visitDefaultMaxTransition); defineAttrVisitor("default_max_fanout", &LibertyReader::visitDefaultMaxFanout); defineAttrVisitor("default_intrinsic_rise", &LibertyReader::visitDefaultIntrinsicRise); defineAttrVisitor("default_intrinsic_fall", &LibertyReader::visitDefaultIntrinsicFall); defineAttrVisitor("default_inout_pin_rise_res", &LibertyReader::visitDefaultInoutPinRiseRes); defineAttrVisitor("default_inout_pin_fall_res", &LibertyReader::visitDefaultInoutPinFallRes); defineAttrVisitor("default_output_pin_rise_res", &LibertyReader::visitDefaultOutputPinRiseRes); defineAttrVisitor("default_output_pin_fall_res", &LibertyReader::visitDefaultOutputPinFallRes); defineAttrVisitor("default_fanout_load", &LibertyReader::visitDefaultFanoutLoad); defineAttrVisitor("default_wire_load", &LibertyReader::visitDefaultWireLoad); defineAttrVisitor("default_wire_load_mode", &LibertyReader::visitDefaultWireLoadMode); defineAttrVisitor("default_wire_load_selection", &LibertyReader::visitDefaultWireLoadSelection); defineAttrVisitor("default_operating_conditions", &LibertyReader::visitDefaultOperatingConditions); defineAttrVisitor("input_threshold_pct_fall", &LibertyReader::visitInputThresholdPctFall); defineAttrVisitor("input_threshold_pct_rise", &LibertyReader::visitInputThresholdPctRise); defineAttrVisitor("output_threshold_pct_fall", &LibertyReader::visitOutputThresholdPctFall); defineAttrVisitor("output_threshold_pct_rise", &LibertyReader::visitOutputThresholdPctRise); defineAttrVisitor("slew_lower_threshold_pct_fall", &LibertyReader::visitSlewLowerThresholdPctFall); defineAttrVisitor("slew_lower_threshold_pct_rise", &LibertyReader::visitSlewLowerThresholdPctRise); defineAttrVisitor("slew_upper_threshold_pct_fall", &LibertyReader::visitSlewUpperThresholdPctFall); defineAttrVisitor("slew_upper_threshold_pct_rise", &LibertyReader::visitSlewUpperThresholdPctRise); defineAttrVisitor("slew_derate_from_library", &LibertyReader::visitSlewDerateFromLibrary); defineGroupVisitor("lu_table_template", &LibertyReader::beginTableTemplateDelay, &LibertyReader::endTableTemplate); defineGroupVisitor("output_current_template", &LibertyReader::beginTableTemplateOutputCurrent, &LibertyReader::endTableTemplate); defineAttrVisitor("variable_1", &LibertyReader::visitVariable1); defineAttrVisitor("variable_2", &LibertyReader::visitVariable2); defineAttrVisitor("variable_3", &LibertyReader::visitVariable3); defineAttrVisitor("index_1", &LibertyReader::visitIndex1); defineAttrVisitor("index_2", &LibertyReader::visitIndex2); defineAttrVisitor("index_3", &LibertyReader::visitIndex3); defineGroupVisitor("technology", &LibertyReader::beginTechnology, &LibertyReader::endTechnology); defineGroupVisitor("rise_transition_degradation", &LibertyReader::beginRiseTransitionDegredation, &LibertyReader::endRiseFallTransitionDegredation); defineGroupVisitor("fall_transition_degradation", &LibertyReader::beginFallTransitionDegredation, &LibertyReader::endRiseFallTransitionDegredation); defineGroupVisitor("type", &LibertyReader::beginType, &LibertyReader::endType); defineAttrVisitor("bit_from", &LibertyReader::visitBitFrom); defineAttrVisitor("bit_to", &LibertyReader::visitBitTo); defineGroupVisitor("scaling_factors", &LibertyReader::beginScalingFactors, &LibertyReader::endScalingFactors); defineScalingFactorVisitors(); defineGroupVisitor("operating_conditions", &LibertyReader::beginOpCond, &LibertyReader::endOpCond); defineAttrVisitor("process", &LibertyReader::visitProc); defineAttrVisitor("voltage", &LibertyReader::visitVolt); defineAttrVisitor("temperature", &LibertyReader::visitTemp); defineAttrVisitor("tree_type", &LibertyReader::visitTreeType); defineGroupVisitor("wire_load", &LibertyReader::beginWireload, &LibertyReader::endWireload); defineAttrVisitor("resistance", &LibertyReader::visitResistance); defineAttrVisitor("slope", &LibertyReader::visitSlope); defineAttrVisitor("fanout_length", &LibertyReader::visitFanoutLength); defineGroupVisitor("wire_load_selection", &LibertyReader::beginWireloadSelection, &LibertyReader::endWireloadSelection); defineAttrVisitor("wire_load_from_area", &LibertyReader::visitWireloadFromArea); // Cells defineGroupVisitor("cell", &LibertyReader::beginCell, &LibertyReader::endCell); defineGroupVisitor("scaled_cell", &LibertyReader::beginScaledCell, &LibertyReader::endScaledCell); defineAttrVisitor("clock_gating_integrated_cell", &LibertyReader::visitClockGatingIntegratedCell); defineAttrVisitor("area", &LibertyReader::visitArea); defineAttrVisitor("dont_use", &LibertyReader::visitDontUse); defineAttrVisitor("is_macro_cell", &LibertyReader::visitIsMacro); defineAttrVisitor("is_memory", &LibertyReader::visitIsMemory); defineAttrVisitor("pad_cell", &LibertyReader::visitIsPadCell); defineAttrVisitor("is_pad", &LibertyReader::visitIsPad); defineAttrVisitor("is_clock_cell", &LibertyReader::visitIsClockCell); defineAttrVisitor("is_level_shifter", &LibertyReader::visitIsLevelShifter); defineAttrVisitor("level_shifter_type", &LibertyReader::visitLevelShifterType); defineAttrVisitor("is_isolation_cell", &LibertyReader::visitIsIsolationCell); defineAttrVisitor("always_on", &LibertyReader::visitAlwaysOn); defineAttrVisitor("switch_cell_type", &LibertyReader::visitSwitchCellType); defineAttrVisitor("interface_timing", &LibertyReader::visitInterfaceTiming); defineAttrVisitor("scaling_factors", &LibertyReader::visitScalingFactors); defineAttrVisitor("cell_footprint", &LibertyReader::visitCellFootprint); defineAttrVisitor("user_function_class", &LibertyReader::visitCellUserFunctionClass); // Pins defineGroupVisitor("pin", &LibertyReader::beginPin,&LibertyReader::endPin); defineGroupVisitor("bus", &LibertyReader::beginBus,&LibertyReader::endBus); defineGroupVisitor("bundle", &LibertyReader::beginBundle, &LibertyReader::endBundle); defineAttrVisitor("direction", &LibertyReader::visitDirection); defineAttrVisitor("clock", &LibertyReader::visitClock); defineAttrVisitor("bus_type", &LibertyReader::visitBusType); defineAttrVisitor("members", &LibertyReader::visitMembers); defineAttrVisitor("function", &LibertyReader::visitFunction); defineAttrVisitor("three_state", &LibertyReader::visitThreeState); defineAttrVisitor("capacitance", &LibertyReader::visitCapacitance); defineAttrVisitor("rise_capacitance", &LibertyReader::visitRiseCap); defineAttrVisitor("fall_capacitance", &LibertyReader::visitFallCap); defineAttrVisitor("rise_capacitance_range", &LibertyReader::visitRiseCapRange); defineAttrVisitor("fall_capacitance_range", &LibertyReader::visitFallCapRange); defineAttrVisitor("fanout_load", &LibertyReader::visitFanoutLoad); defineAttrVisitor("max_fanout", &LibertyReader::visitMaxFanout); defineAttrVisitor("min_fanout", &LibertyReader::visitMinFanout); defineAttrVisitor("max_transition", &LibertyReader::visitMaxTransition); defineAttrVisitor("min_transition", &LibertyReader::visitMinTransition); defineAttrVisitor("max_capacitance", &LibertyReader::visitMaxCapacitance); defineAttrVisitor("min_capacitance", &LibertyReader::visitMinCapacitance); defineAttrVisitor("min_period", &LibertyReader::visitMinPeriod); defineAttrVisitor("min_pulse_width_low", &LibertyReader::visitMinPulseWidthLow); defineAttrVisitor("min_pulse_width_high", &LibertyReader::visitMinPulseWidthHigh); defineAttrVisitor("pulse_clock", &LibertyReader::visitPulseClock); defineAttrVisitor("clock_gate_clock_pin", &LibertyReader::visitClockGateClockPin); defineAttrVisitor("clock_gate_enable_pin", &LibertyReader::visitClockGateEnablePin); defineAttrVisitor("clock_gate_out_pin", &LibertyReader::visitClockGateOutPin); defineAttrVisitor("is_pll_feedback_pin", &LibertyReader::visitIsPllFeedbackPin); defineAttrVisitor("signal_type", &LibertyReader::visitSignalType); defineAttrVisitor("isolation_cell_data_pin", &LibertyReader::visitIsolationCellDataPin); defineAttrVisitor("isolation_cell_enable_pin", &LibertyReader::visitIsolationCellEnablePin); defineAttrVisitor("level_shifter_data_pin", &LibertyReader::visitLevelShifterDataPin); defineAttrVisitor("switch_pin", &LibertyReader::visitSwitchPin); // Memory defineGroupVisitor("memory", &LibertyReader::beginMemory, &LibertyReader::endMemory); // Register/latch defineGroupVisitor("ff", &LibertyReader::beginFF, &LibertyReader::endFF); defineGroupVisitor("ff_bank", &LibertyReader::beginFFBank, &LibertyReader::endFFBank); defineGroupVisitor("latch", &LibertyReader::beginLatch, &LibertyReader::endLatch); defineGroupVisitor("latch_bank", &LibertyReader::beginLatchBank, &LibertyReader::endLatchBank); defineAttrVisitor("clocked_on", &LibertyReader::visitClockedOn); defineAttrVisitor("enable", &LibertyReader::visitClockedOn); defineAttrVisitor("data_in", &LibertyReader::visitDataIn); defineAttrVisitor("next_state", &LibertyReader::visitDataIn); defineAttrVisitor("clear", &LibertyReader::visitClear); defineAttrVisitor("preset", &LibertyReader::visitPreset); defineAttrVisitor("clear_preset_var1", &LibertyReader::visitClrPresetVar1); defineAttrVisitor("clear_preset_var2", &LibertyReader::visitClrPresetVar2); // Statetable defineGroupVisitor("statetable", &LibertyReader::beginStatetable, &LibertyReader::endStatetable); defineAttrVisitor("table", &LibertyReader::visitTable); defineGroupVisitor("timing", &LibertyReader::beginTiming, &LibertyReader::endTiming); defineAttrVisitor("related_pin", &LibertyReader::visitRelatedPin); defineAttrVisitor("related_bus_pins", &LibertyReader::visitRelatedBusPins); defineAttrVisitor("related_output_pin", &LibertyReader::visitRelatedOutputPin); defineAttrVisitor("timing_type", &LibertyReader::visitTimingType); defineAttrVisitor("timing_sense", &LibertyReader::visitTimingSense); defineAttrVisitor("sdf_cond_start", &LibertyReader::visitSdfCondStart); defineAttrVisitor("sdf_cond_end", &LibertyReader::visitSdfCondEnd); defineAttrVisitor("mode", &LibertyReader::visitMode); defineAttrVisitor("intrinsic_rise", &LibertyReader::visitIntrinsicRise); defineAttrVisitor("intrinsic_fall", &LibertyReader::visitIntrinsicFall); defineAttrVisitor("rise_resistance", &LibertyReader::visitRiseResistance); defineAttrVisitor("fall_resistance", &LibertyReader::visitFallResistance); defineGroupVisitor("cell_rise", &LibertyReader::beginCellRise, &LibertyReader::endCellRiseFall); defineGroupVisitor("cell_fall", &LibertyReader::beginCellFall, &LibertyReader::endCellRiseFall); defineGroupVisitor("rise_transition", &LibertyReader::beginRiseTransition, &LibertyReader::endRiseFallTransition); defineGroupVisitor("fall_transition", &LibertyReader::beginFallTransition, &LibertyReader::endRiseFallTransition); defineGroupVisitor("rise_constraint", &LibertyReader::beginRiseConstraint, &LibertyReader::endRiseFallConstraint); defineGroupVisitor("fall_constraint", &LibertyReader::beginFallConstraint, &LibertyReader::endRiseFallConstraint); defineAttrVisitor("value", &LibertyReader::visitValue); defineAttrVisitor("values", &LibertyReader::visitValues); defineGroupVisitor("lut", &LibertyReader::beginLut,&LibertyReader::endLut); defineGroupVisitor("test_cell", &LibertyReader::beginTestCell, &LibertyReader::endTestCell); defineGroupVisitor("mode_definition", &LibertyReader::beginModeDef, &LibertyReader::endModeDef); defineGroupVisitor("mode_value", &LibertyReader::beginModeValue, &LibertyReader::endModeValue); defineAttrVisitor("when", &LibertyReader::visitWhen); defineAttrVisitor("sdf_cond", &LibertyReader::visitSdfCond); // Power attributes. defineGroupVisitor("power_lut_template", &LibertyReader::beginTableTemplatePower, &LibertyReader::endTableTemplate); defineGroupVisitor("leakage_power", &LibertyReader::beginLeakagePower, &LibertyReader::endLeakagePower); defineGroupVisitor("internal_power", &LibertyReader::beginInternalPower, &LibertyReader::endInternalPower); // power group for both rise/fall defineGroupVisitor("power", &LibertyReader::beginRisePower, &LibertyReader::endPower); defineGroupVisitor("fall_power", &LibertyReader::beginFallPower, &LibertyReader::endRiseFallPower); defineGroupVisitor("rise_power", &LibertyReader::beginRisePower, &LibertyReader::endRiseFallPower); defineAttrVisitor("related_ground_pin",&LibertyReader::visitRelatedGroundPin); defineAttrVisitor("related_power_pin", &LibertyReader::visitRelatedPowerPin); defineAttrVisitor("related_pg_pin", &LibertyReader::visitRelatedPgPin); // AOCV attributes. defineAttrVisitor("ocv_arc_depth", &LibertyReader::visitOcvArcDepth); defineAttrVisitor("default_ocv_derate_group", &LibertyReader::visitDefaultOcvDerateGroup); defineAttrVisitor("ocv_derate_group", &LibertyReader::visitOcvDerateGroup); defineGroupVisitor("ocv_table_template", &LibertyReader::beginTableTemplateOcv, &LibertyReader::endTableTemplate); defineGroupVisitor("ocv_derate", &LibertyReader::beginOcvDerate, &LibertyReader::endOcvDerate); defineGroupVisitor("ocv_derate_factors", &LibertyReader::beginOcvDerateFactors, &LibertyReader::endOcvDerateFactors); defineAttrVisitor("rf_type", &LibertyReader::visitRfType); defineAttrVisitor("derate_type", &LibertyReader::visitDerateType); defineAttrVisitor("path_type", &LibertyReader::visitPathType); // POCV attributes. defineGroupVisitor("ocv_sigma_cell_rise", &LibertyReader::beginOcvSigmaCellRise, &LibertyReader::endOcvSigmaCell); defineGroupVisitor("ocv_sigma_cell_fall", &LibertyReader::beginOcvSigmaCellFall, &LibertyReader::endOcvSigmaCell); defineGroupVisitor("ocv_sigma_rise_transition", &LibertyReader::beginOcvSigmaRiseTransition, &LibertyReader::endOcvSigmaTransition); defineGroupVisitor("ocv_sigma_fall_transition", &LibertyReader::beginOcvSigmaFallTransition, &LibertyReader::endOcvSigmaTransition); defineGroupVisitor("ocv_sigma_rise_constraint", &LibertyReader::beginOcvSigmaRiseConstraint, &LibertyReader::endOcvSigmaConstraint); defineGroupVisitor("ocv_sigma_fall_constraint", &LibertyReader::beginOcvSigmaFallConstraint, &LibertyReader::endOcvSigmaConstraint); defineAttrVisitor("sigma_type", &LibertyReader::visitSigmaType); defineAttrVisitor("cell_leakage_power", &LibertyReader::visitCellLeakagePower); defineGroupVisitor("pg_pin", &LibertyReader::beginPgPin, &LibertyReader::endPgPin); defineAttrVisitor("pg_type", &LibertyReader::visitPgType); defineAttrVisitor("voltage_name", &LibertyReader::visitVoltageName); // ccs receiver capacitance defineGroupVisitor("receiver_capacitance", &LibertyReader::beginReceiverCapacitance, &LibertyReader::endReceiverCapacitance); defineGroupVisitor("receiver_capacitance_rise", &LibertyReader::beginReceiverCapacitance1Rise, &LibertyReader::endReceiverCapacitanceRiseFall); defineGroupVisitor("receiver_capacitance_fall", &LibertyReader::beginReceiverCapacitance1Fall, &LibertyReader::endReceiverCapacitanceRiseFall); defineAttrVisitor("segment", &LibertyReader::visitSegement); defineGroupVisitor("receiver_capacitance1_rise", &LibertyReader::beginReceiverCapacitance1Rise, &LibertyReader::endReceiverCapacitanceRiseFall); defineGroupVisitor("receiver_capacitance1_fall", &LibertyReader::beginReceiverCapacitance1Fall, &LibertyReader::endReceiverCapacitanceRiseFall); defineGroupVisitor("receiver_capacitance2_rise", &LibertyReader::beginReceiverCapacitance2Rise, &LibertyReader::endReceiverCapacitanceRiseFall); defineGroupVisitor("receiver_capacitance2_fall", &LibertyReader::beginReceiverCapacitance2Fall, &LibertyReader::endReceiverCapacitanceRiseFall); // ccs defineGroupVisitor("output_current_rise", &LibertyReader::beginOutputCurrentRise, &LibertyReader::endOutputCurrentRiseFall); defineGroupVisitor("output_current_fall", &LibertyReader::beginOutputCurrentFall, &LibertyReader::endOutputCurrentRiseFall); defineGroupVisitor("vector", &LibertyReader::beginVector, &LibertyReader::endVector); defineAttrVisitor("reference_time", &LibertyReader::visitReferenceTime); defineGroupVisitor("normalized_driver_waveform", &LibertyReader::beginNormalizedDriverWaveform, &LibertyReader::endNormalizedDriverWaveform); defineAttrVisitor("driver_waveform_name", &LibertyReader::visitDriverWaveformName); defineAttrVisitor("driver_waveform_rise", &LibertyReader::visitDriverWaveformRise); defineAttrVisitor("driver_waveform_fall", &LibertyReader::visitDriverWaveformFall); } void LibertyReader::defineScalingFactorVisitors() { for (int type_index = 0; type_index < scale_factor_type_count; type_index++) { ScaleFactorType type = static_cast(type_index); const char *type_name = scaleFactorTypeName(type); for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) { ScaleFactorPvt pvt = static_cast(pvt_index); const char *pvt_name = scaleFactorPvtName(pvt); if (scaleFactorTypeRiseFallSuffix(type)) { for (auto tr : RiseFall::range()) { const char *tr_name = (tr == RiseFall::rise()) ? "rise":"fall"; string attr_name; stringPrint(attr_name, "k_%s_%s_%s", pvt_name, type_name, tr_name); defineAttrVisitor(attr_name.c_str() ,&LibertyReader::visitScaleFactorSuffix); } } else if (scaleFactorTypeRiseFallPrefix(type)) { for (auto tr : RiseFall::range()) { const char *tr_name = (tr == RiseFall::rise()) ? "rise":"fall"; string attr_name; stringPrint(attr_name, "k_%s_%s_%s", pvt_name, tr_name, type_name); defineAttrVisitor(attr_name.c_str(),&LibertyReader::visitScaleFactorPrefix); } } else if (scaleFactorTypeLowHighSuffix(type)) { for (auto tr : RiseFall::range()) { const char *tr_name = (tr == RiseFall::rise()) ? "high":"low"; string attr_name; stringPrint(attr_name, "k_%s_%s_%s", pvt_name, tr_name, type_name); defineAttrVisitor(attr_name.c_str(),&LibertyReader::visitScaleFactorHiLow); } } else { string attr_name; stringPrint(attr_name, "k_%s_%s", pvt_name, type_name); defineAttrVisitor(attr_name.c_str(),&LibertyReader::visitScaleFactor); } } } } void LibertyReader::visitAttr(LibertyAttr *attr) { LibraryAttrVisitor visitor = attr_visitor_map_.findKey(attr->name()); if (visitor) (this->*visitor)(attr); } void LibertyReader::begin(LibertyGroup *group) { LibraryGroupVisitor visitor = group_begin_map_.findKey(group->type()); if (visitor) (this->*visitor)(group); } void LibertyReader::end(LibertyGroup *group) { LibraryGroupVisitor visitor = group_end_map_.findKey(group->type()); if (visitor) (this->*visitor)(group); } void LibertyReader::beginLibrary(LibertyGroup *group) { const char *name = group->firstName(); if (name) { LibertyLibrary *library = network_->findLiberty(name); if (library) libWarn(1140, 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 fJ. setEnergyScale(); // 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); scale_factors_ = new ScaleFactors(""); library_->setScaleFactors(scale_factors_); } else libError(1141, group, "library missing name."); } // Energy scale is derived. void LibertyReader::setEnergyScale() { energy_scale_ = volt_scale_ * volt_scale_ * cap_scale_; } void LibertyReader::endLibrary(LibertyGroup *group) { endLibraryAttrs(group); } void LibertyReader::endLibraryAttrs(LibertyGroup *group) { // These attributes reference named groups in the library so // wait until the end of the library to resolve them. if (default_wireload_) { Wireload *wireload = library_->findWireload(default_wireload_); if (wireload) library_->setDefaultWireload(wireload); else libWarn(1142, group, "default_wire_load %s not found.", default_wireload_); stringDelete(default_wireload_); default_wireload_ = nullptr; } if (default_wireload_selection_) { WireloadSelection *selection = library_->findWireloadSelection(default_wireload_selection_); if (selection) library_->setDefaultWireloadSelection(selection); else libWarn(1143, group, "default_wire_selection %s not found.", default_wireload_selection_); stringDelete(default_wireload_selection_); default_wireload_selection_ = nullptr; } if (default_operating_condition_) { OperatingConditions *op_cond = library_->findOperatingConditions(default_operating_condition_); if (op_cond) library_->setDefaultOperatingConditions(op_cond); else libWarn(1144, group, "default_operating_condition %s not found.", default_operating_condition_); stringDelete(default_operating_condition_); default_operating_condition_ = nullptr; } bool missing_threshold = false; for (auto rf : RiseFall::range()) { int rf_index = rf->index(); if (!have_input_threshold_[rf_index]) { libWarn(1145, group, "input_threshold_pct_%s not found.", rf->name()); missing_threshold = true; } if (!have_output_threshold_[rf_index]) { libWarn(1146, group, "output_threshold_pct_%s not found.", rf->name()); missing_threshold = true; } if (!have_slew_lower_threshold_[rf_index]) { libWarn(1147, group, "slew_lower_threshold_pct_%s not found.", rf->name()); missing_threshold = true; } if (!have_slew_upper_threshold_[rf_index]) { libWarn(1148, group, "slew_upper_threshold_pct_%s not found.", rf->name()); missing_threshold = true; } } if (missing_threshold) libError(1149, group, "Library %s is missing one or more thresholds.", library_->name()); } void LibertyReader::visitTimeUnit(LibertyAttr *attr) { if (library_) parseUnits(attr, "s", time_scale_, library_->units()->timeUnit()); } void LibertyReader::visitPullingResistanceUnit(LibertyAttr *attr) { if (library_) parseUnits(attr, "ohm", res_scale_, library_->units()->resistanceUnit()); } void LibertyReader::visitResistanceUnit(LibertyAttr *attr) { if (library_) parseUnits(attr, "ohm", res_scale_, library_->units()->resistanceUnit()); } void LibertyReader::visitCurrentUnit(LibertyAttr *attr) { if (library_) parseUnits(attr, "A", current_scale_, library_->units()->currentUnit()); } void LibertyReader::visitVoltageUnit(LibertyAttr *attr) { if (library_) parseUnits(attr, "V", volt_scale_, library_->units()->voltageUnit()); setEnergyScale(); } void LibertyReader::visitPowerUnit(LibertyAttr *attr) { if (library_) parseUnits(attr, "W", power_scale_, library_->units()->powerUnit()); } void LibertyReader::visitDistanceUnit(LibertyAttr *attr) { if (library_) parseUnits(attr, "m", distance_scale_, library_->units()->distanceUnit()); } void LibertyReader::parseUnits(LibertyAttr *attr, const char *unit_suffix, float &scale_var, Unit *unit) { string units = getAttrString(attr); if (!units.empty()) { // Unit format is . // Find the multiplier digits. string units = getAttrString(attr); size_t mult_end = units.find_first_not_of("0123456789"); float mult = 1.0F; string scale_suffix; if (mult_end != units.npos) { string unit_mult = units.substr(0, mult_end); scale_suffix = units.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, 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) { 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, attr, "unknown unit scale %c.", scale_char); } else libWarn(1152, attr, "unknown unit suffix %s.", suffix.c_str()); } else if (!stringEqual(scale_suffix.c_str(), unit_suffix)) libWarn(1153, attr, "unknown unit suffix %s.", scale_suffix.c_str()); scale_var = scale_mult * mult; unit->setScale(scale_var); } } void LibertyReader::visitCapacitiveLoadUnit(LibertyAttr *attr) { if (library_) { if (attr->isComplex()) { LibertyAttrValueIterator value_iter(attr->values()); if (value_iter.hasNext()) { LibertyAttrValue *value = value_iter.next(); if (value->isFloat()) { float scale = value->floatValue(); if (value_iter.hasNext()) { value = value_iter.next(); if (value->isString()) { const char *suffix = value->stringValue(); if (stringEqual(suffix, "ff")) cap_scale_ = scale * 1E-15F; else if (stringEqual(suffix, "pf")) cap_scale_ = scale * 1E-12F; else libWarn(1154, attr, "capacitive_load_units are not ff or pf."); } else libWarn(1155, attr, "capacitive_load_units are not a string."); } else libWarn(1156, attr, "capacitive_load_units missing suffix."); } else libWarn(1157, attr, "capacitive_load_units scale is not a float."); } else libWarn(1158, attr, "capacitive_load_units missing scale and suffix."); } else libWarn(1159, attr, "capacitive_load_unit missing values suffix."); library_->units()->capacitanceUnit()->setScale(cap_scale_); setEnergyScale(); } } void LibertyReader::visitDelayModel(LibertyAttr *attr) { if (library_) { const char *type_name = getAttrString(attr); if (type_name) { if (stringEq(type_name, "table_lookup")) library_->setDelayModelType(DelayModelType::table); else if (stringEq(type_name, "generic_cmos")) library_->setDelayModelType(DelayModelType::cmos_linear); else if (stringEq(type_name, "piecewise_cmos")) { library_->setDelayModelType(DelayModelType::cmos_pwl); libWarn(1160, attr, "delay_model %s not supported.", type_name); } else if (stringEq(type_name, "cmos2")) { library_->setDelayModelType(DelayModelType::cmos2); libWarn(1161, attr, "delay_model %s not supported.", type_name); } else if (stringEq(type_name, "polynomial")) { library_->setDelayModelType(DelayModelType::polynomial); libWarn(1162, attr, "delay_model %s not supported.", type_name); } // Evil IBM garbage. else if (stringEq(type_name, "dcm")) { library_->setDelayModelType(DelayModelType::dcm); libWarn(1163, attr, "delay_model %s not supported..", type_name); } else libWarn(1164, attr, "unknown delay_model %s.", type_name); } } } void LibertyReader::visitBusStyle(LibertyAttr *attr) { if (library_) { const char *bus_style = getAttrString(attr); // Assume bus style is of the form "%s[%d]". if (bus_style && strlen(bus_style) == 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, attr, "unknown bus_naming_style format."); } } void LibertyReader::visitVoltageMap(LibertyAttr *attr) { if (library_) { if (attr->isComplex()) { LibertyAttrValueIterator value_iter(attr->values()); if (value_iter.hasNext()) { LibertyAttrValue *value = value_iter.next(); if (value->isString()) { const char *supply_name = value->stringValue(); if (value_iter.hasNext()) { value = value_iter.next(); if (value->isFloat()) { float voltage = value->floatValue(); library_->addSupplyVoltage(supply_name, voltage); } else libWarn(1166, attr, "voltage_map voltage is not a float."); } else libWarn(1167, attr, "voltage_map missing voltage."); } else libWarn(1168, attr, "voltage_map supply name is not a string."); } else libWarn(1169, attr, "voltage_map missing supply name and voltage."); } else libWarn(1170, attr, "voltage_map missing values suffix."); } } void LibertyReader::visitNomTemp(LibertyAttr *attr) { if (library_) { float value; bool valid; getAttrFloat(attr, value, valid); if (valid) library_->setNominalTemperature(value); } } void LibertyReader::visitNomProc(LibertyAttr *attr) { if (library_) { float value; bool valid; getAttrFloat(attr, value, valid); if (valid) library_->setNominalProcess(value); } } void LibertyReader::visitNomVolt(LibertyAttr *attr) { if (library_) { float value; bool valid; getAttrFloat(attr, value, valid); if (valid) library_->setNominalVoltage(value); } } void LibertyReader::visitDefaultInoutPinCap(LibertyAttr *attr) { if (library_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) library_->setDefaultBidirectPinCap(value * cap_scale_); } } void LibertyReader::visitDefaultInputPinCap(LibertyAttr *attr) { if (library_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) library_->setDefaultInputPinCap(value * cap_scale_); } } void LibertyReader::visitDefaultOutputPinCap(LibertyAttr *attr) { if (library_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) library_->setDefaultOutputPinCap(value * cap_scale_); } } void LibertyReader::visitDefaultMaxTransition(LibertyAttr *attr) { if (library_){ float value; bool exists; getAttrFloat(attr, value, exists); if (exists) { if (value == 0.0) libWarn(1171, attr, "default_max_transition is 0.0."); library_->setDefaultMaxSlew(value * time_scale_); } } } void LibertyReader::visitDefaultMaxFanout(LibertyAttr *attr) { if (library_){ float value; bool exists; getAttrFloat(attr, value, exists); if (exists) { if (value == 0.0) libWarn(1172, attr, "default_max_fanout is 0.0."); library_->setDefaultMaxFanout(value); } } } void LibertyReader::visitDefaultIntrinsicRise(LibertyAttr *attr) { visitDefaultIntrinsic(attr, RiseFall::rise()); } void LibertyReader::visitDefaultIntrinsicFall(LibertyAttr *attr) { visitDefaultIntrinsic(attr, RiseFall::fall()); } void LibertyReader::visitDefaultIntrinsic(LibertyAttr *attr, RiseFall *rf) { if (library_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) library_->setDefaultIntrinsic(rf, value * time_scale_); } } void LibertyReader::visitDefaultInoutPinRiseRes(LibertyAttr *attr) { visitDefaultInoutPinRes(attr, RiseFall::rise()); } void LibertyReader::visitDefaultInoutPinFallRes(LibertyAttr *attr) { visitDefaultInoutPinRes(attr, RiseFall::fall()); } void LibertyReader::visitDefaultInoutPinRes(LibertyAttr *attr, RiseFall *rf) { if (library_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) library_->setDefaultBidirectPinRes(rf, value * res_scale_); } } void LibertyReader::visitDefaultOutputPinRiseRes(LibertyAttr *attr) { visitDefaultOutputPinRes(attr, RiseFall::rise()); } void LibertyReader::visitDefaultOutputPinFallRes(LibertyAttr *attr) { visitDefaultOutputPinRes(attr, RiseFall::fall()); } void LibertyReader::visitDefaultOutputPinRes(LibertyAttr *attr, RiseFall *rf) { if (library_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) library_->setDefaultOutputPinRes(rf, value * res_scale_); } } void LibertyReader::visitDefaultFanoutLoad(LibertyAttr *attr) { if (library_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) { if (value == 0.0) libWarn(1173, attr, "default_fanout_load is 0.0."); library_->setDefaultFanoutLoad(value); } } } void LibertyReader::visitDefaultWireLoad(LibertyAttr *attr) { if (library_) { const char *value = getAttrString(attr); if (value) { stringDelete(default_wireload_); default_wireload_ = stringCopy(value); } } } void LibertyReader::visitDefaultWireLoadMode(LibertyAttr *attr) { if (library_) { const char *wire_load_mode = getAttrString(attr); if (wire_load_mode) { WireloadMode mode = stringWireloadMode(wire_load_mode); if (mode != WireloadMode::unknown) library_->setDefaultWireloadMode(mode); else libWarn(1174, attr, "default_wire_load_mode %s not found.", wire_load_mode); } } } void LibertyReader::visitDefaultWireLoadSelection(LibertyAttr *attr) { if (library_) { const char *value = getAttrString(attr); if (value) { stringDelete(default_wireload_selection_); default_wireload_selection_ = stringCopy(value); } } } void LibertyReader::visitDefaultOperatingConditions(LibertyAttr *attr) { if (library_) { const char *value = getAttrString(attr); if (value) { stringDelete(default_operating_condition_); default_operating_condition_ = stringCopy(value); } } } void LibertyReader::visitInputThresholdPctFall(LibertyAttr *attr) { visitInputThresholdPct(attr, RiseFall::fall()); } void LibertyReader::visitInputThresholdPctRise(LibertyAttr *attr) { visitInputThresholdPct(attr, RiseFall::rise()); } void LibertyReader::visitInputThresholdPct(LibertyAttr *attr, RiseFall *rf) { if (library_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) library_->setInputThreshold(rf, value / 100.0F); } have_input_threshold_[rf->index()] = true; } void LibertyReader::visitOutputThresholdPctFall(LibertyAttr *attr) { visitOutputThresholdPct(attr, RiseFall::fall()); } void LibertyReader::visitOutputThresholdPctRise(LibertyAttr *attr) { visitOutputThresholdPct(attr, RiseFall::rise()); } void LibertyReader::visitOutputThresholdPct(LibertyAttr *attr, RiseFall *rf) { if (library_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) library_->setOutputThreshold(rf, value / 100.0F); } have_output_threshold_[rf->index()] = true; } void LibertyReader::visitSlewLowerThresholdPctFall(LibertyAttr *attr) { visitSlewLowerThresholdPct(attr, RiseFall::fall()); } void LibertyReader::visitSlewLowerThresholdPctRise(LibertyAttr *attr) { visitSlewLowerThresholdPct(attr, RiseFall::rise()); } void LibertyReader::visitSlewLowerThresholdPct(LibertyAttr *attr, RiseFall *rf) { if (library_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) library_->setSlewLowerThreshold(rf, value / 100.0F); } have_slew_lower_threshold_[rf->index()] = true; } void LibertyReader::visitSlewUpperThresholdPctFall(LibertyAttr *attr) { visitSlewUpperThresholdPct(attr, RiseFall::fall()); } void LibertyReader::visitSlewUpperThresholdPctRise(LibertyAttr *attr) { visitSlewUpperThresholdPct(attr, RiseFall::rise()); } void LibertyReader::visitSlewUpperThresholdPct(LibertyAttr *attr, RiseFall *rf) { if (library_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) library_->setSlewUpperThreshold(rf, value / 100.0F); } have_slew_upper_threshold_[rf->index()] = true; } void LibertyReader::visitSlewDerateFromLibrary(LibertyAttr *attr) { if (library_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) library_->setSlewDerateFromLibrary(value); } } //////////////////////////////////////////////////////////////// void LibertyReader::beginTechnology(LibertyGroup *group) { if (library_) { const char *tech = group->firstName(); if (stringEq(tech, "fpga")) library_->setDelayModelType(DelayModelType::cmos_linear); } } void LibertyReader::endTechnology(LibertyGroup *) { } void LibertyReader::beginTableTemplateDelay(LibertyGroup *group) { beginTableTemplate(group, TableTemplateType::delay); } void LibertyReader::beginTableTemplateOutputCurrent(LibertyGroup *group) { beginTableTemplate(group, TableTemplateType::output_current); } void LibertyReader::beginTableTemplate(LibertyGroup *group, TableTemplateType type) { if (library_) { const char *name = group->firstName(); if (name) { tbl_template_ = new TableTemplate(name); library_->addTableTemplate(tbl_template_, type); } else libWarn(1175, group, "table template missing name."); axis_var_[0] = axis_var_[1] = axis_var_[2] = TableAxisVariable::unknown; clearAxisValues(); } } void LibertyReader::clearAxisValues() { axis_values_[0] = axis_values_[1] = axis_values_[2] = nullptr; } void LibertyReader::endTableTemplate(LibertyGroup *group) { if (tbl_template_) { TableAxisPtr axis1 = makeAxis(0, group); if (axis1) tbl_template_->setAxis1(axis1); TableAxisPtr axis2 = makeAxis(1, group); if (axis2) tbl_template_->setAxis2(axis2); TableAxisPtr axis3 = makeAxis(2, group); if (axis3) tbl_template_->setAxis3(axis3); tbl_template_ = nullptr; axis_var_[0] = axis_var_[1] = axis_var_[2] = TableAxisVariable::unknown; } } TableAxisPtr LibertyReader::makeAxis(int index, LibertyGroup *group) { TableAxisVariable axis_var = axis_var_[index]; FloatSeq *axis_values = axis_values_[index]; if (axis_var != TableAxisVariable::unknown) { if (axis_values) { const Units *units = library_->units(); float scale = tableVariableUnit(axis_var, units)->scale(); scaleFloats(axis_values, scale); } return make_shared(axis_var, axis_values); } else if (axis_values) { libWarn(1176, group, "missing variable_%d attribute.", index + 1); delete axis_values; axis_values_[index] = nullptr; } // No warning for missing index_xx attributes because they are not required. 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::visitVariable1(LibertyAttr *attr) { visitVariable(0, attr); } void LibertyReader::visitVariable2(LibertyAttr *attr) { visitVariable(1, attr); } void LibertyReader::visitVariable3(LibertyAttr *attr) { visitVariable(2, attr); } void LibertyReader::visitVariable(int index, LibertyAttr *attr) { if (tbl_template_) { const char *type = getAttrString(attr); TableAxisVariable var = stringTableAxisVariable(type); if (var == TableAxisVariable::unknown) libWarn(1297, attr, "axis type %s not supported.", type); else axis_var_[index] = var; } } void LibertyReader::visitIndex1(LibertyAttr *attr) { visitIndex(0, attr); } void LibertyReader::visitIndex2(LibertyAttr *attr) { visitIndex(1, attr); } void LibertyReader::visitIndex3(LibertyAttr *attr) { visitIndex(2, attr); } void LibertyReader::visitIndex(int index, LibertyAttr *attr) { if (tbl_template_ // Ignore index_xx in ecsm_waveform groups. && !stringEq(libertyGroup()->type(), "ecsm_waveform")) { FloatSeq *axis_values = readFloatSeq(attr, 1.0F); if (axis_values) { if (axis_values->empty()) libWarn(1177, attr, "missing table index values."); else { 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, attr, "non-increasing table index values."); prev = value; } } axis_values_[index] = axis_values; } } } //////////////////////////////////////////////////////////////// void LibertyReader::beginType(LibertyGroup *) { type_bit_from_exists_ = false; type_bit_to_exists_ = false; } void LibertyReader::endType(LibertyGroup *group) { const char *name = group->firstName(); if (name) { if (type_bit_from_exists_ && type_bit_to_exists_) { BusDcl *bus_dcl = new BusDcl(name, type_bit_from_, type_bit_to_); if (cell_) cell_->addBusDcl(bus_dcl); else if (library_) library_->addBusDcl(bus_dcl); } else { if (!type_bit_from_exists_) libWarn(1179, group, "bus type %s missing bit_from.", name); if (!type_bit_to_exists_) libWarn(1180, group, "bus type %s missing bit_to.", name); } } else libWarn(1181, group, "type missing name."); } void LibertyReader::visitBitFrom(LibertyAttr *attr) { getAttrInt(attr, type_bit_from_, type_bit_from_exists_); } void LibertyReader::visitBitTo(LibertyAttr *attr) { getAttrInt(attr, type_bit_to_, type_bit_to_exists_); } //////////////////////////////////////////////////////////////// void LibertyReader::beginScalingFactors(LibertyGroup *group) { const char *name = group->firstName(); if (name) { save_scale_factors_ = scale_factors_; scale_factors_ = new ScaleFactors(name); library_->addScaleFactors(scale_factors_); } else libWarn(1182, group, "scaling_factors do not have a name."); } void LibertyReader::endScalingFactors(LibertyGroup *) { scale_factors_ = save_scale_factors_; } void LibertyReader::visitScaleFactorSuffix(LibertyAttr *attr) { if (scale_factors_) { ScaleFactorPvt pvt = ScaleFactorPvt::unknown; ScaleFactorType type = ScaleFactorType::unknown; RiseFall *rf = nullptr; // Parse the attribute name. TokenParser parser(attr->name(), "_"); if (parser.hasNext()) parser.next(); if (parser.hasNext()) { const char *pvt_name = parser.next(); pvt = findScaleFactorPvt(pvt_name); } if (parser.hasNext()) { const char *type_name = parser.next(); type = findScaleFactorType(type_name); } if (parser.hasNext()) { const char *tr_name = parser.next(); if (stringEq(tr_name, "rise")) rf = RiseFall::rise(); else if (stringEq(tr_name, "fall")) rf = RiseFall::fall(); } if (pvt != ScaleFactorPvt::unknown && type != ScaleFactorType::unknown && rf) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) scale_factors_->setScale(type, pvt, rf, value); } } } void LibertyReader::visitScaleFactorPrefix(LibertyAttr *attr) { if (scale_factors_) { ScaleFactorPvt pvt = ScaleFactorPvt::unknown; ScaleFactorType type = ScaleFactorType::unknown; RiseFall *rf = nullptr; // Parse the attribute name. TokenParser parser(attr->name(), "_"); if (parser.hasNext()) parser.next(); if (parser.hasNext()) { const char *pvt_name = parser.next(); pvt = findScaleFactorPvt(pvt_name); } if (parser.hasNext()) { const char *tr_name = parser.next(); if (stringEq(tr_name, "rise")) rf = RiseFall::rise(); else if (stringEq(tr_name, "fall")) rf = RiseFall::fall(); } if (parser.hasNext()) { const char *type_name = parser.next(); type = findScaleFactorType(type_name); } if (pvt != ScaleFactorPvt::unknown && type != ScaleFactorType::unknown && rf) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) scale_factors_->setScale(type, pvt, rf, value); } } } void LibertyReader::visitScaleFactorHiLow(LibertyAttr *attr) { if (scale_factors_) { ScaleFactorPvt pvt = ScaleFactorPvt::unknown; ScaleFactorType type = ScaleFactorType::unknown; RiseFall *rf = nullptr; const char *pvt_name = nullptr; const char *type_name = nullptr; const char *tr_name = nullptr; // Parse the attribute name. TokenParser parser(attr->name(), "_"); if (parser.hasNext()) parser.next(); if (parser.hasNext()) { pvt_name = parser.next(); pvt = findScaleFactorPvt(pvt_name); } if (parser.hasNext()) { type_name = parser.next(); type = findScaleFactorType(type_name); } if (parser.hasNext()) { tr_name = parser.next(); if (stringEq(tr_name, "high")) rf = RiseFall::rise(); else if (stringEq(tr_name, "low")) rf = RiseFall::fall(); } if (pvt != ScaleFactorPvt::unknown && type != ScaleFactorType::unknown && rf) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) scale_factors_->setScale(type, pvt, rf, value); } } } void LibertyReader::visitScaleFactor(LibertyAttr *attr) { if (scale_factors_) { ScaleFactorPvt pvt = ScaleFactorPvt::unknown; ScaleFactorType type = ScaleFactorType::unknown; const char *pvt_name = nullptr; const char *type_name = nullptr; // Parse the attribute name. TokenParser parser(attr->name(), " "); if (parser.hasNext()) parser.next(); if (parser.hasNext()) { pvt_name = parser.next(); pvt = findScaleFactorPvt(pvt_name); } if (parser.hasNext()) { type_name = parser.next(); type = findScaleFactorType(type_name); } if (pvt != ScaleFactorPvt::unknown && type != ScaleFactorType::unknown) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) scale_factors_->setScale(type, pvt, value); } } } //////////////////////////////////////////////////////////////// void LibertyReader::beginOpCond(LibertyGroup *group) { if (library_) { const char *name = group->firstName(); if (name) { op_cond_ = new OperatingConditions(name); library_->addOperatingConditions(op_cond_); } else libWarn(1183, group, "operating_conditions missing name."); } } void LibertyReader::visitProc(LibertyAttr *attr) { if (op_cond_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) op_cond_->setProcess(value); } } void LibertyReader::visitVolt(LibertyAttr *attr) { if (op_cond_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) op_cond_->setVoltage(value * volt_scale_); } } void LibertyReader::visitTemp(LibertyAttr *attr) { if (op_cond_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) op_cond_->setTemperature(value); } } void LibertyReader::visitTreeType(LibertyAttr *attr) { if (op_cond_) { const char *tree_type = getAttrString(attr); if (tree_type) { WireloadTree wire_load_tree = stringWireloadTree(tree_type); op_cond_->setWireloadTree(wire_load_tree); } } } void LibertyReader::endOpCond(LibertyGroup *) { op_cond_ = nullptr; } //////////////////////////////////////////////////////////////// void LibertyReader::beginWireload(LibertyGroup *group) { if (library_) { const char *name = group->firstName(); if (name) { wireload_ = new Wireload(name, library_); library_->addWireload(wireload_); } } else libWarn(1184, group, "wire_load missing name."); } void LibertyReader::endWireload(LibertyGroup *) { wireload_ = nullptr; } void LibertyReader::visitResistance(LibertyAttr *attr) { if (wireload_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) wireload_->setResistance(value * res_scale_); } } void LibertyReader::visitSlope(LibertyAttr *attr) { if (wireload_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) wireload_->setSlope(value); } } void LibertyReader::visitFanoutLength(LibertyAttr *attr) { if (wireload_) { float fanout, length; bool exists; getAttrFloat2(attr, fanout, length, exists); if (exists) wireload_->addFanoutLength(fanout, length); else libWarn(1185, attr, "fanout_length is missing length and fanout."); } } void LibertyReader::beginWireloadSelection(LibertyGroup *group) { if (library_) { const char *name = group->firstName(); if (name) { wireload_selection_ = new WireloadSelection(name); library_->addWireloadSelection(wireload_selection_); } } else libWarn(1186, group, "wire_load_selection missing name."); } void LibertyReader::endWireloadSelection(LibertyGroup *) { wireload_selection_ = nullptr; } void LibertyReader::visitWireloadFromArea(LibertyAttr *attr) { if (wireload_selection_) { if (attr->isComplex()) { LibertyAttrValueIterator value_iter(attr->values()); if (value_iter.hasNext()) { LibertyAttrValue *value = value_iter.next(); if (value->isFloat()) { float min_area = value->floatValue(); value = value_iter.next(); if (value->isFloat()) { float max_area = value->floatValue(); value = value_iter.next(); if (value->isString()) { const char *wireload_name = value->stringValue(); const Wireload *wireload = library_->findWireload(wireload_name); if (wireload) wireload_selection_->addWireloadFromArea(min_area, max_area, wireload); else libWarn(1187, attr, "wireload %s not found.", wireload_name); } else libWarn(1188, attr, "wire_load_from_area wireload name not a string."); } else libWarn(1189, attr, "wire_load_from_area min not a float."); } else libWarn(1190, attr, "wire_load_from_area max not a float."); } else libWarn(1191, attr, "wire_load_from_area missing parameters."); } else libWarn(1192, attr, "wire_load_from_area missing parameters."); } } //////////////////////////////////////////////////////////////// void LibertyReader::beginCell(LibertyGroup *group) { const char *name = group->firstName(); if (name) { debugPrint(debug_, "liberty", 1, "cell %s", name); if (library_) { cell_ = builder_.makeCell(library_, name, filename_); in_bus_ = false; in_bundle_ = false; } } else libWarn(1193, group, "cell missing name."); } void LibertyReader::endCell(LibertyGroup *group) { if (cell_) { // Sequentials and leakage powers reference expressions outside of port definitions // so they do not require LibertyFunc's. makeCellSequentials(); makeStatetable(); // Parse functions defined inside of port groups that reference other ports // and replace the references with the parsed expressions. parseCellFuncs(); makeLeakagePowers(); finishPortGroups(); if (ocv_derate_name_) { OcvDerate *derate = cell_->findOcvDerate(ocv_derate_name_); if (derate == nullptr) derate = library_->findOcvDerate(ocv_derate_name_); if (derate) cell_->setOcvDerate(derate); else libWarn(1194, group, "cell %s ocv_derate_group %s not found.", cell_->name(), ocv_derate_name_); stringDelete(ocv_derate_name_); ocv_derate_name_ = nullptr; } cell_->finish(infer_latches_, report_, debug_); cell_ = nullptr; } } void LibertyReader::finishPortGroups() { for (PortGroup *port_group : cell_port_groups_) { int line = port_group->line(); for (LibertyPort *port : *port_group->ports()) { checkPort(port, line); makeMinPulseWidthArcs(port, line); } makeTimingArcs(port_group); makeInternalPowers(port_group); delete port_group; } cell_port_groups_.clear(); } void LibertyReader::checkPort(LibertyPort *port, int line) { FuncExpr *func_expr = port->function(); if (func_expr) { if (func_expr->checkSize(port)) { libWarn(1195, line, "port %s function size does not match port size.", port->name()); } } if (port->tristateEnable() && port->direction() == PortDirection::output()) port->setDirection(PortDirection::tristate()); } // Make timing arcs for the port min_pulse_width_low/high attributes. // This is redundant but makes sdf annotation consistent. void LibertyReader::makeMinPulseWidthArcs(LibertyPort *port, int line) { TimingArcAttrsPtr attrs = nullptr; for (auto hi_low : RiseFall::range()) { float min_width; bool exists; port->minPulseWidth(hi_low, min_width, exists); if (exists) { if (attrs == nullptr) { attrs = make_shared(); attrs->setTimingType(TimingType::min_pulse_width); } // rise/fall_constraint model is on the trailing edge of the pulse. const RiseFall *model_rf = hi_low->opposite(); TimingModel *check_model = makeScalarCheckModel(min_width, ScaleFactorType::min_pulse_width, model_rf); attrs->setModel(model_rf, check_model); } } if (attrs) builder_.makeTimingArcs(cell_, port, port, nullptr, attrs, line); } TimingModel * LibertyReader::makeScalarCheckModel(float value, ScaleFactorType scale_factor_type, const RiseFall *rf) { TablePtr table = make_shared(value); TableTemplate *tbl_template = library_->findTableTemplate("scalar", TableTemplateType::delay); TableModel *table_model = new TableModel(table, tbl_template, scale_factor_type, rf); CheckTableModel *check_model = new CheckTableModel(cell_, table_model, nullptr); return check_model; } void LibertyReader::makeTimingArcs(PortGroup *port_group) { for (TimingGroup *timing : port_group->timingGroups()) { timing->makeTimingModels(cell_, this); for (LibertyPort *port : *port_group->ports()) makeTimingArcs(port, timing); } } void LibertyReader::makeInternalPowers(PortGroup *port_group) { for (InternalPowerGroup *power_group : port_group->internalPowerGroups()) { for (LibertyPort *port : *port_group->ports()) makeInternalPowers(port, power_group); cell_->addInternalPowerAttrs(power_group); } } void LibertyReader::makeCellSequentials() { for (SequentialGroup *seq : cell_sequentials_) { makeCellSequential(seq); delete seq; } cell_sequentials_.clear(); } void LibertyReader::makeCellSequential(SequentialGroup *seq) { int line = seq->line(); int size = seq->size(); bool is_register = seq->isRegister(); bool is_bank = seq->isBank(); const char *type = is_register ? (is_bank ? "ff_bank" : "ff") : (is_bank ? "latch_bank" : "latch"); const char *clk = seq->clock(); FuncExpr *clk_expr = nullptr; if (clk) { const char *clk_attr = is_register ? "clocked_on" : "enable"; clk_expr = parseFunc(clk, clk_attr, line); if (clk_expr && clk_expr->checkSize(size)) { libWarn(1196, line, "%s %s bus width mismatch.", type, clk_attr); clk_expr->deleteSubexprs(); clk_expr = nullptr; } } const char *data = seq->data(); FuncExpr *data_expr = nullptr; if (data) { const char *data_attr = is_register ? "next_state" : "data_in"; data_expr = parseFunc(data, data_attr, line); if (data_expr && data_expr->checkSize(size)) { libWarn(1197, line, "%s %s bus width mismatch.", type, data_attr); data_expr->deleteSubexprs(); data_expr = nullptr; } } const char *clr = seq->clear(); FuncExpr *clr_expr = nullptr; if (clr) { clr_expr = parseFunc(clr, "clear", line); if (clr_expr && clr_expr->checkSize(size)) { libWarn(1198, line, "%s %s bus width mismatch.", type, "clear"); clr_expr->deleteSubexprs(); clr_expr = nullptr; } } const char *preset = seq->preset(); FuncExpr *preset_expr = nullptr; if (preset) { preset_expr = parseFunc(preset, "preset", line); if (preset_expr && preset_expr->checkSize(size)) { libWarn(1199, line, "%s %s bus width mismatch.", type, "preset"); preset_expr->deleteSubexprs(); preset_expr = nullptr; } } cell_->makeSequential(size, is_register, clk_expr, data_expr, clr_expr, preset_expr, seq->clrPresetVar1(), seq->clrPresetVar2(), seq->outPort(), seq->outInvPort()); if (!is_register) checkLatchEnableSense(clk_expr, line); // The expressions used in the sequentials are copied by bitSubExpr. if (clk_expr) clk_expr->deleteSubexprs(); if (data_expr) data_expr->deleteSubexprs(); if (clr_expr) clr_expr->deleteSubexprs(); if (preset_expr) preset_expr->deleteSubexprs(); } void LibertyReader::checkLatchEnableSense(FuncExpr *enable_func, int line) { FuncExprPortIterator enable_iter(enable_func); while (enable_iter.hasNext()) { LibertyPort *enable_port = enable_iter.next(); 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::makeStatetable() { if (statetable_) { LibertyPortSeq input_ports; for (const string &input : statetable_->inputPorts()) { LibertyPort *port = cell_->findLibertyPort(input.c_str()); if (port) input_ports.push_back(port); else libWarn(1298, statetable_->line(), "statetable input port %s not found.", input.c_str()); } LibertyPortSeq internal_ports; for (const string &internal : statetable_->internalPorts()) { LibertyPort *port = cell_->findLibertyPort(internal.c_str()); if (port == nullptr) port = builder_.makePort(cell_, internal.c_str()); internal_ports.push_back(port); } cell_->makeStatetable(input_ports, internal_ports, statetable_->table()); delete statetable_; statetable_ = nullptr; } } //////////////////////////////////////////////////////////////// void LibertyReader::makeLeakagePowers() { for (LeakagePowerGroup *power_group : leakage_powers_) { builder_.makeLeakagePower(cell_, power_group); delete power_group; } leakage_powers_.clear(); } // Record a reference to a function that will be parsed at the end of // the cell definition when all of the ports are defined. void LibertyReader::makeLibertyFunc(const char *expr, FuncExpr *&func_ref, bool invert, const char *attr_name, LibertyStmt *stmt) { LibertyFunc *func = new LibertyFunc(expr, func_ref, invert, attr_name, stmt->line()); cell_funcs_.push_back(func); } void LibertyReader::parseCellFuncs() { for (LibertyFunc *func : cell_funcs_) { FuncExpr *expr = parseFunc(func->expr(), func->attrName(), func->line()); if (func->invert() && expr) { if (expr->op() == FuncExpr::op_not) { FuncExpr *inv = expr; expr = expr->left(); delete inv; } else expr = FuncExpr::makeNot(expr); } if (expr) { FuncExpr *prev_func = func->funcRef(); if (prev_func) prev_func->deleteSubexprs(); func->funcRef() = expr; } delete func; } cell_funcs_.clear(); } void LibertyReader::beginScaledCell(LibertyGroup *group) { const char *name = group->firstName(); if (name) { scaled_cell_owner_ = library_->findLibertyCell(name); if (scaled_cell_owner_) { const char *op_cond_name = group->secondName(); if (op_cond_name) { op_cond_ = library_->findOperatingConditions(op_cond_name); if (op_cond_) { debugPrint(debug_, "liberty", 1, "scaled cell %s %s", name, op_cond_name); cell_ = library_->makeScaledCell(name, filename_); } else libWarn(1202, group, "operating conditions %s not found.", op_cond_name); } else libWarn(1203, group, "scaled_cell missing operating condition."); } else libWarn(1204, group, "scaled_cell cell %s has not been defined.", name); } else libWarn(1205, group, "scaled_cell missing name."); } void LibertyReader::endScaledCell(LibertyGroup *group) { if (cell_) { makeCellSequentials(); parseCellFuncs(); finishPortGroups(); cell_->finish(infer_latches_, report_, debug_); checkScaledCell(group); // Add scaled cell AFTER ports and timing arcs are defined. scaled_cell_owner_->addScaledCell(op_cond_, cell_); cell_ = nullptr; scaled_cell_owner_ = nullptr; op_cond_ = nullptr; } } // Minimal check that is not very specific about where the discrepancies are. void LibertyReader::checkScaledCell(LibertyGroup *group) { if (equivCellPorts(cell_, scaled_cell_owner_)) { if (!equivCellPortsAndFuncs(cell_, scaled_cell_owner_)) libWarn(1206, group, "scaled_cell %s, %s port functions do not match cell port functions.", cell_->name(), op_cond_->name()); } else libWarn(1207, group, "scaled_cell ports do not match cell ports."); if (!equivCellTimingArcSets(cell_, scaled_cell_owner_)) libWarn(1208, group, "scaled_cell %s, %s timing does not match cell timing.", cell_->name(), op_cond_->name()); } void LibertyReader::makeTimingArcs(LibertyPort *to_port, TimingGroup *timing) { LibertyPort *related_out_port = nullptr; const char *related_out_port_name = timing->relatedOutputPortName(); if (related_out_port_name) related_out_port = findPort(related_out_port_name); int line = timing->line(); PortDirection *to_port_dir = to_port->direction(); // Checks should be more comprehensive (timing checks on inputs, etc). TimingType type = timing->attrs()->timingType(); if (type == TimingType::combinational && to_port_dir->isInput()) libWarn(1209, line, "combinational timing to an input port."); if (timing->relatedPortNames()) { for (const char *from_port_name : *timing->relatedPortNames()) { PortNameBitIterator from_port_iter(cell_, from_port_name, this, line); if (from_port_iter.hasNext()) { debugPrint(debug_, "liberty", 2, " timing %s -> %s", from_port_name, to_port->name()); makeTimingArcs(from_port_name, from_port_iter, to_port, related_out_port, timing); } } } else makeTimingArcs(to_port, related_out_port, timing); } void TimingGroup::makeTimingModels(LibertyCell *cell, LibertyReader *visitor) { switch (cell->libertyLibrary()->delayModelType()) { case DelayModelType::cmos_linear: makeLinearModels(cell); break; case DelayModelType::table: makeTableModels(cell, visitor); break; case DelayModelType::cmos_pwl: case DelayModelType::cmos2: case DelayModelType::polynomial: case DelayModelType::dcm: break; } } void TimingGroup::makeLinearModels(LibertyCell *cell) { LibertyLibrary *library = cell->libertyLibrary(); for (auto rf : RiseFall::range()) { int rf_index = rf->index(); float intr = intrinsic_[rf_index]; bool intr_exists = intrinsic_exists_[rf_index]; if (!intr_exists) library->defaultIntrinsic(rf, intr, intr_exists); TimingModel *model = nullptr; if (timingTypeIsCheck(attrs_->timingType())) { if (intr_exists) model = new CheckLinearModel(cell, intr); } else { float res = resistance_[rf_index]; bool res_exists = resistance_exists_[rf_index]; if (!res_exists) library->defaultPinResistance(rf, PortDirection::output(), res, res_exists); if (!res_exists) res = 0.0F; if (intr_exists) model = new GateLinearModel(cell, intr, res); } attrs_->setModel(rf, model); } } void TimingGroup::makeTableModels(LibertyCell *cell, LibertyReader *reader) { for (auto rf : RiseFall::range()) { int rf_index = rf->index(); TableModel *delay = cell_[rf_index]; TableModel *transition = transition_[rf_index]; TableModel *constraint = constraint_[rf_index]; if (delay || transition) { attrs_->setModel(rf, new GateTableModel(cell, delay, delay_sigma_[rf_index], transition, slew_sigma_[rf_index], receiver_model_, output_waveforms_[rf_index])); TimingType timing_type = attrs_->timingType(); if (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) { if (transition == nullptr) reader->libWarn(1210, line_, "missing %s_transition.", rf->name()); if (delay == nullptr) reader->libWarn(1211, line_, "missing cell_%s.", rf->name()); } } else if (constraint) attrs_->setModel(rf, new CheckTableModel(cell, constraint, constraint_sigma_[rf_index])); } } void LibertyReader::makeTimingArcs(const char *from_port_name, PortNameBitIterator &from_port_iter, LibertyPort *to_port, LibertyPort *related_out_port, TimingGroup *timing) { 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 (timing->isOneToOne()) { 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, 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 { while (from_port_iter.hasNext()) { LibertyPort *from_port_bit = from_port_iter.next(); if (from_port_bit->direction()->isOutput()) libWarn(1217, timing->line(), "timing group from output port."); LibertyPortMemberIterator to_iter(to_port); while (to_iter.hasNext()) { LibertyPort *to_port_bit = to_iter.next(); builder_.makeTimingArcs(cell_, from_port_bit, to_port_bit, related_out_port, timing->attrs(), timing->line()); } } } } } void LibertyReader::makeTimingArcs(LibertyPort *to_port, LibertyPort *related_out_port, TimingGroup *timing) { 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()); } //////////////////////////////////////////////////////////////// // Group that encloses receiver_capacitance1/2 etc groups. void LibertyReader::beginReceiverCapacitance(LibertyGroup *) { receiver_model_ = make_shared(); } void LibertyReader::endReceiverCapacitance(LibertyGroup *) { if (ports_) { for (LibertyPort *port : *ports_) port->setReceiverModel(receiver_model_); } receiver_model_ = nullptr; } // For receiver_capacitance groups with mulitiple segments this // overrides the index passed in beginReceiverCapacitance1Rise/Fall. void LibertyReader::visitSegement(LibertyAttr *attr) { if (receiver_model_) { int segment; bool exists; getAttrInt(attr, segment, exists); if (exists) index_ = segment; } } void LibertyReader::beginReceiverCapacitance1Rise(LibertyGroup *group) { beginReceiverCapacitance(group, 0, RiseFall::rise()); } void LibertyReader::beginReceiverCapacitance1Fall(LibertyGroup *group) { beginReceiverCapacitance(group, 0, RiseFall::fall()); } void LibertyReader::beginReceiverCapacitance2Rise(LibertyGroup *group) { beginReceiverCapacitance(group, 1, RiseFall::rise()); } void LibertyReader::beginReceiverCapacitance2Fall(LibertyGroup *group) { beginReceiverCapacitance(group, 1, RiseFall::fall()); } void LibertyReader::beginReceiverCapacitance(LibertyGroup *group, int index, RiseFall *rf) { if (timing_ || ports_) { beginTableModel(group, TableTemplateType::delay, rf, 1.0, ScaleFactorType::pin_cap); index_ = index; } else libWarn(1218, group, "receiver_capacitance group not in timing or pin group."); } void LibertyReader::endReceiverCapacitanceRiseFall(LibertyGroup *group) { if (table_) { if (ReceiverModel::checkAxes(table_)) { TableModel *table_model = new TableModel(table_, tbl_template_, scale_factor_type_, rf_); if (receiver_model_ == nullptr) { receiver_model_ = make_shared(); if (timing_) timing_->setReceiverModel(receiver_model_); } receiver_model_->setCapacitanceModel(table_model, index_, rf_); } else libWarn(1219, group, "unsupported model axis."); endTableModel(); } } //////////////////////////////////////////////////////////////// void LibertyReader::beginOutputCurrentRise(LibertyGroup *group) { beginOutputCurrent(RiseFall::rise(), group); } void LibertyReader::beginOutputCurrentFall(LibertyGroup *group) { beginOutputCurrent(RiseFall::fall(), group); } void LibertyReader::beginOutputCurrent(RiseFall *rf, LibertyGroup *group) { if (timing_) { rf_ = rf; output_currents_.clear(); } else libWarn(1220, group, "output_current_%s group not in timing group.", rf->name()); } void LibertyReader::endOutputCurrentRiseFall(LibertyGroup *group) { if (timing_) { Set slew_set, cap_set; FloatSeq *slew_values = new FloatSeq; FloatSeq *cap_values = new FloatSeq; for (OutputWaveform *waveform : output_currents_) { float slew = waveform->slew(); if (!slew_set.hasKey(slew)) { slew_set.insert(slew); slew_values->push_back(slew); } float cap = waveform->cap(); if (!cap_set.hasKey(cap)) { cap_set.insert(cap); cap_values->push_back(cap); } } sort(slew_values, std::less()); sort(cap_values, std::less()); TableAxisPtr slew_axis = make_shared(TableAxisVariable::input_net_transition, slew_values); TableAxisPtr cap_axis = make_shared(TableAxisVariable::total_output_net_capacitance, cap_values); FloatSeq *ref_times = new FloatSeq(slew_values->size()); Table1Seq current_waveforms(slew_axis->size() * cap_axis->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->stealCurrents(); (*ref_times)[slew_index] = waveform->referenceTime(); } else libWarn(1221, group, "output current waveform %.2e %.2e not found.", waveform->slew(), waveform->cap()); } Table1 *ref_time_tbl = new Table1(ref_times, slew_axis); OutputWaveforms *output_current = new OutputWaveforms(slew_axis, cap_axis, rf_, current_waveforms, ref_time_tbl); timing_->setOutputWaveforms(rf_, output_current); output_currents_.deleteContentsClear(); } } void LibertyReader::beginVector(LibertyGroup *group) { if (timing_) { beginTable(group, TableTemplateType::output_current, current_scale_); scale_factor_type_ = ScaleFactorType::unknown; reference_time_exists_ = false; if (tbl_template_ && !OutputWaveforms::checkAxes(tbl_template_)) libWarn(1222, group, "unsupported model axis."); } } void LibertyReader::visitReferenceTime(LibertyAttr *attr) { getAttrFloat(attr, reference_time_, reference_time_exists_); if (reference_time_exists_) reference_time_ *= time_scale_; } void LibertyReader::endVector(LibertyGroup *group) { if (timing_ && tbl_template_) { FloatSeq *slew_values = axis_values_[0]; FloatSeq *cap_values = axis_values_[1]; // Canonicalize axis order. if (tbl_template_->axis1()->variable() == TableAxisVariable::input_net_transition) { slew_values = axis_values_[0]; cap_values = axis_values_[1]; } else { slew_values = axis_values_[1]; cap_values = axis_values_[0]; } if (slew_values->size() == 1 && cap_values->size() == 1) { // Convert 1x1xN Table3 to Table1. float slew = (*slew_values)[0]; float cap = (*cap_values)[0]; Table3 *table3 = dynamic_cast(table_.get()); FloatTable *values3 = table3->values3(); // Steal the values. FloatSeq *values = (*values3)[0]; (*values3)[0] = nullptr; Table1 *table1 = new Table1(values, axis_[2]); OutputWaveform *waveform = new OutputWaveform(slew, cap, table1, reference_time_); output_currents_.push_back(waveform); } else libWarn(1223,group->line(), "vector index_1 and index_2 must have exactly one value."); if (!reference_time_exists_) libWarn(1224, group->line(), "vector reference_time not found."); reference_time_exists_ = false; } } /////////////////////////////////////////////////////////////// void LibertyReader::beginNormalizedDriverWaveform(LibertyGroup *group) { beginTable(group, TableTemplateType::delay, time_scale_); driver_waveform_name_ = nullptr; } void LibertyReader::visitDriverWaveformName(LibertyAttr *attr) { driver_waveform_name_ = stringCopy(getAttrString(attr)); } void LibertyReader::endNormalizedDriverWaveform(LibertyGroup *group) { if (table_) { if (table_->axis1()->variable() == TableAxisVariable::input_net_transition) { if (table_->axis2()->variable() == TableAxisVariable::normalized_voltage) { // Null driver_waveform_name_ means it is the default unnamed waveform. DriverWaveform *driver_waveform = new DriverWaveform(driver_waveform_name_, table_); library_->addDriverWaveform(driver_waveform); } else libWarn(1225, group, "normalized_driver_waveform variable_2 must be normalized_voltage"); } else libWarn(1226, group, "normalized_driver_waveform variable_1 must be input_net_transition"); } endTableModel(); } void LibertyReader::visitDriverWaveformRise(LibertyAttr *attr) { visitDriverWaveformRiseFall(attr, RiseFall::rise()); } void LibertyReader::visitDriverWaveformFall(LibertyAttr *attr) { visitDriverWaveformRiseFall(attr, RiseFall::fall()); } void LibertyReader::visitDriverWaveformRiseFall(LibertyAttr *attr, const RiseFall *rf) { if (ports_) { const char *driver_waveform_name = getAttrString(attr); DriverWaveform *driver_waveform = library_->findDriverWaveform(driver_waveform_name); if (driver_waveform) { for (LibertyPort *port : *ports_) port->setDriverWaveform(driver_waveform, rf); } } } /////////////////////////////////////////////////////////////// void LibertyReader::makeInternalPowers(LibertyPort *port, InternalPowerGroup *power_group) { int line = power_group->line(); StringSeq *related_port_names = power_group->relatedPortNames(); if (related_port_names) { for (const char *related_port_name : *related_port_names) { PortNameBitIterator related_port_iter(cell_, related_port_name, this, line); if (related_port_iter.hasNext()) { debugPrint(debug_, "liberty", 2, " power %s -> %s", related_port_name, port->name()); makeInternalPowers(port, related_port_name, related_port_iter, power_group); } } } else { if (port->hasMembers()) { LibertyPortMemberIterator bit_iter(port); while (bit_iter.hasNext()) { LibertyPort *port_bit = bit_iter.next(); builder_.makeInternalPower(cell_, port_bit, nullptr, power_group); } } else builder_.makeInternalPower(cell_, port, nullptr, power_group); } } void LibertyReader::makeInternalPowers(LibertyPort *port, const char *related_port_name, PortNameBitIterator &related_port_iter, InternalPowerGroup *power_group) { if (related_port_iter.size() == 1 && !port->hasMembers()) { // one -> one if (related_port_iter.hasNext()) { LibertyPort *related_port = related_port_iter.next(); builder_.makeInternalPower(cell_, port, related_port, power_group); } } else if (related_port_iter.size() > 1 && !port->hasMembers()) { // bus -> one while (related_port_iter.hasNext()) { LibertyPort *related_port = related_port_iter.next(); builder_.makeInternalPower(cell_, port, related_port, power_group); } } else if (related_port_iter.size() == 1 && port->hasMembers()) { // one -> bus if (related_port_iter.hasNext()) { LibertyPort *related_port = related_port_iter.next(); LibertyPortMemberIterator bit_iter(port); while (bit_iter.hasNext()) { LibertyPort *port_bit = bit_iter.next(); builder_.makeInternalPower(cell_, port_bit, related_port, power_group); } } } else { // bus -> bus if (power_group->isOneToOne()) { if (static_cast(related_port_iter.size()) == port->size()) { LibertyPortMemberIterator to_iter(port); while (related_port_iter.hasNext() && to_iter.hasNext()) { LibertyPort *related_port_bit = related_port_iter.next(); LibertyPort *port_bit = to_iter.next(); builder_.makeInternalPower(cell_, port_bit, related_port_bit, power_group); } } else libWarn(1227, power_group->line(), "internal_power port %s and related port %s are different sizes.", related_port_name, port->name()); } else { while (related_port_iter.hasNext()) { LibertyPort *related_port_bit = related_port_iter.next(); LibertyPortMemberIterator to_iter(port); while (to_iter.hasNext()) { LibertyPort *port_bit = to_iter.next(); builder_.makeInternalPower(cell_, port_bit, related_port_bit, power_group); } } } } } //////////////////////////////////////////////////////////////// void LibertyReader::visitArea(LibertyAttr *attr) { if (cell_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) cell_->setArea(value); } if (wireload_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) wireload_->setArea(value); } } void LibertyReader::visitDontUse(LibertyAttr *attr) { if (cell_) { bool dont_use, exists; getAttrBool(attr, dont_use, exists); if (exists) cell_->setDontUse(dont_use); } } void LibertyReader::visitIsMacro(LibertyAttr *attr) { if (cell_) { bool is_macro, exists; getAttrBool(attr, is_macro, exists); if (exists) cell_->setIsMacro(is_macro); } } void LibertyReader::visitIsMemory(LibertyAttr *attr) { if (cell_) { bool is_memory, exists; getAttrBool(attr, is_memory, exists); if (exists) cell_->setIsMemory(is_memory); } } void LibertyReader::visitIsPadCell(LibertyAttr *attr) { if (cell_) { bool pad_cell, exists; getAttrBool(attr, pad_cell, exists); if (exists) cell_->setIsPad(pad_cell); } } void LibertyReader::visitIsClockCell(LibertyAttr *attr) { if (cell_) { bool is_clock_cell, exists; getAttrBool(attr, is_clock_cell, exists); if (exists) cell_->setIsClockCell(is_clock_cell); } } void LibertyReader::visitIsLevelShifter(LibertyAttr *attr) { if (cell_) { bool is_level_shifter, exists; getAttrBool(attr, is_level_shifter, exists); if (exists) cell_->setIsLevelShifter(is_level_shifter); } } void LibertyReader::visitLevelShifterType(LibertyAttr *attr) { if (cell_) { const char *level_shifter_type = getAttrString(attr); if (stringEq(level_shifter_type, "HL")) cell_->setLevelShifterType(LevelShifterType::HL); else if (stringEq(level_shifter_type, "LH")) cell_->setLevelShifterType(LevelShifterType::LH); else if (stringEq(level_shifter_type, "HL_LH")) cell_->setLevelShifterType(LevelShifterType::HL_LH); else libWarn(1228, attr, "level_shifter_type must be HL, LH, or HL_LH"); } } void LibertyReader::visitIsIsolationCell(LibertyAttr *attr) { if (cell_) { bool is_isolation_cell, exists; getAttrBool(attr, is_isolation_cell, exists); if (exists) cell_->setIsIsolationCell(is_isolation_cell); } } void LibertyReader::visitAlwaysOn(LibertyAttr *attr) { if (cell_) { bool always_on, exists; getAttrBool(attr, always_on, exists); if (exists) cell_->setAlwaysOn(always_on); } } void LibertyReader::visitSwitchCellType(LibertyAttr *attr) { if (cell_) { const char *switch_cell_type = getAttrString(attr); if (stringEq(switch_cell_type, "coarse_grain")) cell_->setSwitchCellType(SwitchCellType::coarse_grain); else if (stringEq(switch_cell_type, "fine_grain")) cell_->setSwitchCellType(SwitchCellType::fine_grain); else libWarn(1229, attr, "switch_cell_type must be coarse_grain or fine_grain"); } } void LibertyReader::visitInterfaceTiming(LibertyAttr *attr) { if (cell_) { bool value, exists; getAttrBool(attr, value, exists); if (exists) cell_->setInterfaceTiming(value); } } void LibertyReader::visitScalingFactors(LibertyAttr *attr) { if (cell_) { const char *scale_factors_name = getAttrString(attr); ScaleFactors *scales = library_->findScaleFactors(scale_factors_name); if (scales) cell_->setScaleFactors(scales); else libWarn(1230, attr, "scaling_factors %s not found.", scale_factors_name); } } void LibertyReader::visitClockGatingIntegratedCell(LibertyAttr *attr) { if (cell_) { const char *clock_gate_type = getAttrString(attr); if (clock_gate_type) { if (stringBeginEqual(clock_gate_type, "latch_posedge")) cell_->setClockGateType(ClockGateType::latch_posedge); else if (stringBeginEqual(clock_gate_type, "latch_negedge")) cell_->setClockGateType(ClockGateType::latch_negedge); else cell_->setClockGateType(ClockGateType::other); } } } void LibertyReader::visitCellFootprint(LibertyAttr *attr) { if (cell_) { const char *footprint = getAttrString(attr); if (footprint) cell_->setFootprint(footprint); } } void LibertyReader::visitCellUserFunctionClass(LibertyAttr *attr) { if (cell_) { const char *user_function_class = getAttrString(attr); if (user_function_class) cell_->setUserFunctionClass(user_function_class); } } //////////////////////////////////////////////////////////////// void LibertyReader::beginPin(LibertyGroup *group) { if (cell_) { if (in_bus_) { saved_ports_ = ports_; saved_port_group_ = port_group_; ports_ = new LibertyPortSeq; for (LibertyAttrValue *param : *group->params()) { if (param->isString()) { const char *port_name = param->stringValue(); debugPrint(debug_, "liberty", 1, " port %s", port_name); PortNameBitIterator port_iter(cell_, port_name, this, group->line()); while (port_iter.hasNext()) { LibertyPort *port = port_iter.next(); ports_->push_back(port); } } else libWarn(1231, group, "pin name is not a string."); } } else if (in_bundle_) { saved_ports_ = ports_; saved_port_group_ = port_group_; ports_ = new LibertyPortSeq; for (LibertyAttrValue *param : *group->params()) { if (param->isString()) { const char *name = param->stringValue(); debugPrint(debug_, "liberty", 1, " port %s", name); LibertyPort *port = findPort(name); if (port == nullptr) port = builder_.makePort(cell_, name); ports_->push_back(port); } else libWarn(1232, group, "pin name is not a string."); } } else { ports_ = new LibertyPortSeq; // Multiple port names can share group def. for (LibertyAttrValue *param : *group->params()) { if (param->isString()) { const char *name = param->stringValue(); debugPrint(debug_, "liberty", 1, " port %s", name); LibertyPort *port = builder_.makePort(cell_, name); ports_->push_back(port); } else libWarn(1233, group, "pin name is not a string."); } } port_group_ = new PortGroup(ports_, group->line()); cell_port_groups_.push_back(port_group_); } if (test_cell_) { const char *pin_name = group->firstName(); if (pin_name) { port_ = findPort(save_cell_, pin_name); test_port_ = findPort(test_cell_, pin_name); } } } void LibertyReader::endPin(LibertyGroup *) { if (cell_) { endPorts(); if (in_bus_ || in_bundle_) { ports_ = saved_ports_; port_group_ = saved_port_group_; } } port_ = nullptr; test_port_ = nullptr; } void LibertyReader::endPorts() { // Capacitances default based on direction so wait until the end // of the pin group to set them. if (ports_) { for (LibertyPort *port : *ports_) { if (in_bus_ || in_bundle_) { // 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); } ports_ = nullptr; port_group_ = nullptr; } } void LibertyReader::setPortCapDefault(LibertyPort *port) { for (auto min_max : MinMax::range()) { for (auto tr : RiseFall::range()) { float cap; bool exists; port->capacitance(tr, min_max, cap, exists); if (!exists) port->setCapacitance(tr, min_max, defaultCap(port)); } } } void LibertyReader::beginBus(LibertyGroup *group) { if (cell_) { beginBusOrBundle(group); in_bus_ = true; } } void LibertyReader::endBus(LibertyGroup *group) { if (cell_) { if (ports_->empty()) libWarn(1234, group, "bus %s bus_type not found.", group->firstName()); endBusOrBundle(); in_bus_ = false; } } void LibertyReader::beginBusOrBundle(LibertyGroup *group) { // Multiple port names can share group def. for (LibertyAttrValue *param : *group->params()) { if (param->isString()) { const char *name = param->stringValue(); if (name) bus_names_.push_back(stringCopy(name)); } } ports_ = new LibertyPortSeq; port_group_ = new PortGroup(ports_, group->line()); cell_port_groups_.push_back(port_group_); } void LibertyReader::endBusOrBundle() { endPorts(); deleteContents(&bus_names_); bus_names_.clear(); ports_ = nullptr; port_group_ = nullptr; } // Bus port are not made until the bus_type is specified. void LibertyReader::visitBusType(LibertyAttr *attr) { if (cell_) { const char *bus_type = getAttrString(attr); if (bus_type) { // Look for bus dcl local to cell first. BusDcl *bus_dcl = cell_->findBusDcl(bus_type); if (bus_dcl == nullptr) bus_dcl = library_->findBusDcl(bus_type); if (bus_dcl) { for (const char *name : bus_names_) { debugPrint(debug_, "liberty", 1, " bus %s", name); LibertyPort *port = builder_.makeBusPort(cell_, name, bus_dcl->from(), bus_dcl->to(), bus_dcl); ports_->push_back(port); } } else libWarn(1235, attr, "bus_type %s not found.", bus_type); } else libWarn(1236, attr, "bus_type is not a string."); } } void LibertyReader::beginBundle(LibertyGroup *group) { if (cell_) { beginBusOrBundle(group); in_bundle_ = true; } } void LibertyReader::endBundle(LibertyGroup *group) { if (cell_) { if (ports_ && ports_->empty()) libWarn(1237, group, "bundle %s member not found.", group->firstName()); endBusOrBundle(); in_bundle_ = false; } } void LibertyReader::visitMembers(LibertyAttr *attr) { if (cell_) { if (attr->isComplex()) { for (const char *name : bus_names_) { debugPrint(debug_, "liberty", 1, " bundle %s", name); ConcretePortSeq *members = new ConcretePortSeq; for (LibertyAttrValue *value : *attr->values()) { if (value->isString()) { const char *port_name = value->stringValue(); LibertyPort *port = findPort(port_name); if (port == nullptr) port = builder_.makePort(cell_, port_name); members->push_back(port); } else libWarn(1238, attr, "member is not a string."); } LibertyPort *port = builder_.makeBundlePort(cell_, name, members); ports_->push_back(port); } } else libWarn(1239, attr,"members attribute is missing values."); } } LibertyPort * LibertyReader::findPort(const char *port_name) { return findPort(cell_, port_name); } // Also used by LibExprParser::makeFuncExprPort. LibertyPort * libertyReaderFindPort(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. 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); } void LibertyReader::visitDirection(LibertyAttr *attr) { if (ports_) { const char *dir = getAttrString(attr); if (dir) { PortDirection *port_dir = PortDirection::unknown(); if (stringEq(dir, "input")) port_dir = PortDirection::input(); else if (stringEq(dir, "output")) port_dir = PortDirection::output(); else if (stringEq(dir, "inout")) port_dir = PortDirection::bidirect(); else if (stringEq(dir, "internal")) port_dir = PortDirection::internal(); else libWarn(1240, attr, "unknown port direction."); for (LibertyPort *port : *ports_) { // Tristate enable function sets direction to tristate; don't // clobber it. if (!port->direction()->isTristate()) port->setDirection(port_dir); } } } } void LibertyReader::visitFunction(LibertyAttr *attr) { if (ports_) { const char *func = getAttrString(attr); if (func) { for (LibertyPort *port : *ports_) makeLibertyFunc(func, port->functionRef(), false, "function", attr); } } } void LibertyReader::visitThreeState(LibertyAttr *attr) { if (ports_) { const char *three_state = getAttrString(attr); if (three_state) { for (LibertyPort *port : *ports_) makeLibertyFunc(three_state, port->tristateEnableRef(), true, "three_state", attr); } } } void LibertyReader::visitPorts(std::function func) { for (LibertyPort *port : *ports_) { func(port); LibertyPortMemberIterator member_iter(port); while (member_iter.hasNext()) { LibertyPort *member = member_iter.next(); func(member); } } } void LibertyReader::visitClock(LibertyAttr *attr) { if (ports_) { bool is_clk, exists; getAttrBool(attr, is_clk, exists); if (exists) { for (LibertyPort *port : *ports_) port->setIsClock(is_clk); } } } void LibertyReader::visitIsPad(LibertyAttr *attr) { if (ports_) { bool is_pad, exists; getAttrBool(attr, is_pad, exists); if (exists) { for (LibertyPort *port : *ports_) port->setIsPad(is_pad); } } } void LibertyReader::visitCapacitance(LibertyAttr *attr) { if (ports_) { float cap; bool exists; getAttrFloat(attr, cap, exists); if (exists) { cap *= cap_scale_; for (LibertyPort *port : *ports_) port->setCapacitance(cap); } } if (wireload_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) wireload_->setCapacitance(value * cap_scale_); } } void LibertyReader::visitRiseCap(LibertyAttr *attr) { if (ports_) { float cap; bool exists; getAttrFloat(attr, cap, exists); if (exists) { cap *= cap_scale_; for (LibertyPort *port : *ports_) { port->setCapacitance(RiseFall::rise(), MinMax::min(), cap); port->setCapacitance(RiseFall::rise(), MinMax::max(), cap); } } } } void LibertyReader::visitFallCap(LibertyAttr *attr) { if (ports_) { float cap; bool exists; getAttrFloat(attr, cap, exists); if (exists) { cap *= cap_scale_; for (LibertyPort *port : *ports_) { port->setCapacitance(RiseFall::fall(), MinMax::min(), cap); port->setCapacitance(RiseFall::fall(), MinMax::max(), cap); } } } } void LibertyReader::visitRiseCapRange(LibertyAttr *attr) { if (ports_) { bool exists; float min, max; getAttrFloat2(attr, min, max, exists); if (exists) { min *= cap_scale_; max *= cap_scale_; for (LibertyPort *port : *ports_) { port->setCapacitance(RiseFall::rise(), MinMax::min(), min); port->setCapacitance(RiseFall::rise(), MinMax::max(), max); } } } } void LibertyReader::visitFallCapRange(LibertyAttr *attr) { if (ports_) { bool exists; float min, max; getAttrFloat2(attr, min, max, exists); if (exists) { min *= cap_scale_; max *= cap_scale_; for (LibertyPort *port : *ports_) { port->setCapacitance(RiseFall::fall(), MinMax::min(), min); port->setCapacitance(RiseFall::fall(), MinMax::max(), max); } } } } 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; } void LibertyReader::visitFanoutLoad(LibertyAttr *attr) { if (ports_) { float fanout; bool exists; getAttrFloat(attr, fanout, exists); if (exists) { visitPorts([&] (LibertyPort *port) { port->setFanoutLoad(fanout); }); } } } void LibertyReader::visitMaxFanout(LibertyAttr *attr) { visitFanout(attr, MinMax::max()); } void LibertyReader::visitMinFanout(LibertyAttr *attr) { visitFanout(attr, MinMax::min()); } void LibertyReader::visitFanout(LibertyAttr *attr, MinMax *min_max) { if (ports_) { float fanout; bool exists; getAttrFloat(attr, fanout, exists); if (exists) { visitPorts([&] (LibertyPort *port) { port->setFanoutLimit(fanout, min_max); }); } } } void LibertyReader::visitMaxTransition(LibertyAttr *attr) { visitMinMaxTransition(attr, MinMax::max()); } void LibertyReader::visitMinTransition(LibertyAttr *attr) { visitMinMaxTransition(attr, MinMax::min()); } void LibertyReader::visitMinMaxTransition(LibertyAttr *attr, MinMax *min_max) { if (cell_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) { if (min_max == MinMax::max() && value == 0.0) libWarn(1241, attr, "max_transition is 0.0."); value *= time_scale_; visitPorts([&] (LibertyPort *port) { port->setSlewLimit(value, min_max); }); } } } void LibertyReader::visitMaxCapacitance(LibertyAttr *attr) { visitMinMaxCapacitance(attr, MinMax::max()); } void LibertyReader::visitMinCapacitance(LibertyAttr *attr) { visitMinMaxCapacitance(attr, MinMax::min()); } void LibertyReader::visitMinMaxCapacitance(LibertyAttr *attr, MinMax *min_max) { if (cell_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) { value *= cap_scale_; LibertyPortSeq::Iterator port_iter(ports_); visitPorts([&] (LibertyPort *port) { port->setCapacitanceLimit(value, min_max); }); } } } void LibertyReader::visitMinPeriod(LibertyAttr *attr) { if (cell_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) { for (LibertyPort *port : *ports_) port->setMinPeriod(value * time_scale_); } } } void LibertyReader::visitMinPulseWidthLow(LibertyAttr *attr) { visitMinPulseWidth(attr, RiseFall::fall()); } void LibertyReader::visitMinPulseWidthHigh(LibertyAttr *attr) { visitMinPulseWidth(attr, RiseFall::rise()); } void LibertyReader::visitMinPulseWidth(LibertyAttr *attr, const RiseFall *rf) { if (cell_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) { value *= time_scale_; for (LibertyPort *port : *ports_) port->setMinPulseWidth(rf, value); } } } void LibertyReader::visitPulseClock(LibertyAttr *attr) { if (cell_) { const char *pulse_clk = getAttrString(attr); if (pulse_clk) { RiseFall *trigger = nullptr; RiseFall *sense = nullptr; if (stringEq(pulse_clk, "rise_triggered_high_pulse")) { trigger = RiseFall::rise(); sense = RiseFall::rise(); } else if (stringEq(pulse_clk, "rise_triggered_low_pulse")) { trigger = RiseFall::rise(); sense = RiseFall::fall(); } else if (stringEq(pulse_clk, "fall_triggered_high_pulse")) { trigger = RiseFall::fall(); sense = RiseFall::rise(); } else if (stringEq(pulse_clk, "fall_triggered_low_pulse")) { trigger = RiseFall::fall(); sense = RiseFall::fall(); } else libWarn(1242,attr, "pulse_latch unknown pulse type."); if (trigger) { for (LibertyPort *port : *ports_) port->setPulseClk(trigger, sense); } } } } void LibertyReader::visitClockGateClockPin(LibertyAttr *attr) { visitPortBoolAttr(attr, &LibertyPort::setIsClockGateClock); } void LibertyReader::visitClockGateEnablePin(LibertyAttr *attr) { visitPortBoolAttr(attr, &LibertyPort::setIsClockGateEnable); } void LibertyReader::visitClockGateOutPin(LibertyAttr *attr) { visitPortBoolAttr(attr, &LibertyPort::setIsClockGateOut); } void LibertyReader::visitIsPllFeedbackPin(LibertyAttr *attr) { visitPortBoolAttr(attr, &LibertyPort::setIsPllFeedback); } void LibertyReader::visitSignalType(LibertyAttr *attr) { if (test_cell_ && ports_) { const char *type = getAttrString(attr); if (type) { ScanSignalType signal_type = ScanSignalType::none; if (stringEq(type, "test_scan_enable")) signal_type = ScanSignalType::enable; else if (stringEq(type, "test_scan_enable_inverted")) signal_type = ScanSignalType::enable_inverted; else if (stringEq(type, "test_scan_clock")) signal_type = ScanSignalType::clock; else if (stringEq(type, "test_scan_clock_a")) signal_type = ScanSignalType::clock_a; else if (stringEq(type, "test_scan_clock_b")) signal_type = ScanSignalType::clock_b; else if (stringEq(type, "test_scan_in")) signal_type = ScanSignalType::input; else if (stringEq(type, "test_scan_in_inverted")) signal_type = ScanSignalType::input_inverted; else if (stringEq(type, "test_scan_out")) signal_type = ScanSignalType::output; else if (stringEq(type, "test_scan_out_inverted")) signal_type = ScanSignalType::output_inverted; else { libWarn(1299, attr, "unknown signal_type %s.", type); return; } if (port_) port_->setScanSignalType(signal_type); if (test_port_) test_port_->setScanSignalType(signal_type); for (LibertyPort *port : *ports_) port->setScanSignalType(signal_type); } } } void LibertyReader::visitIsolationCellDataPin(LibertyAttr *attr) { visitPortBoolAttr(attr, &LibertyPort::setIsolationCellData); } void LibertyReader::visitIsolationCellEnablePin(LibertyAttr *attr) { visitPortBoolAttr(attr, &LibertyPort::setIsolationCellEnable); } void LibertyReader::visitLevelShifterDataPin(LibertyAttr *attr) { visitPortBoolAttr(attr, &LibertyPort::setLevelShifterData); } void LibertyReader::visitSwitchPin(LibertyAttr *attr) { visitPortBoolAttr(attr, &LibertyPort::setIsSwitch); } void LibertyReader::visitPortBoolAttr(LibertyAttr *attr, LibertyPortBoolSetter setter) { if (cell_) { bool value, exists; getAttrBool(attr, value, exists); if (exists) { for (LibertyPort *port : *ports_) (port->*setter)(value); } } } //////////////////////////////////////////////////////////////// void LibertyReader::beginMemory(LibertyGroup *) { if (cell_) { cell_->setIsMemory(true); } } void LibertyReader::endMemory(LibertyGroup *) { } //////////////////////////////////////////////////////////////// void LibertyReader::beginFF(LibertyGroup *group) { beginSequential(group, true, false); } void LibertyReader::endFF(LibertyGroup *) { sequential_ = nullptr; } void LibertyReader::beginFFBank(LibertyGroup *group) { beginSequential(group, true, true); } void LibertyReader::endFFBank(LibertyGroup *) { sequential_ = nullptr; } void LibertyReader::beginLatch(LibertyGroup *group) { beginSequential(group, false, false); } void LibertyReader::endLatch(LibertyGroup *) { sequential_ = nullptr; } void LibertyReader::beginLatchBank(LibertyGroup *group) { beginSequential(group, false, true); } void LibertyReader::endLatchBank(LibertyGroup *) { sequential_ = nullptr; } void LibertyReader::beginSequential(LibertyGroup *group, bool is_register, bool is_bank) { if (cell_) { // Define ff/latch state variables as internal ports. const char *out_name, *out_inv_name; int size; bool has_size; seqPortNames(group, out_name, out_inv_name, has_size, size); LibertyPort *out_port = nullptr; LibertyPort *out_port_inv = nullptr; if (out_name) { if (has_size) out_port = builder_.makeBusPort(cell_, out_name, size - 1, 0, nullptr); else out_port = builder_.makePort(cell_, out_name); out_port->setDirection(PortDirection::internal()); } if (out_inv_name) { if (has_size) out_port_inv = builder_.makeBusPort(cell_, out_inv_name, size - 1, 0, nullptr); else out_port_inv = builder_.makePort(cell_, out_inv_name); out_port_inv->setDirection(PortDirection::internal()); } sequential_ = new SequentialGroup(is_register, is_bank, out_port, out_port_inv, size, group->line()); cell_sequentials_.push_back(sequential_); } } void LibertyReader::seqPortNames(LibertyGroup *group, const char *&out_name, const char *&out_inv_name, bool &has_size, int &size) { out_name = nullptr; out_inv_name = nullptr; size = 1; has_size = false; if (group->params()->size() == 2) { // out_port, out_port_inv out_name = group->firstName(); out_inv_name = group->secondName(); } 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(); } } } void LibertyReader::visitClockedOn(LibertyAttr *attr) { if (sequential_) { const char *func = getAttrString(attr); if (func) sequential_->setClock(stringCopy(func)); } } void LibertyReader::visitDataIn(LibertyAttr *attr) { if (sequential_) { const char *func = getAttrString(attr); if (func) sequential_->setData(stringCopy(func)); } } void LibertyReader::visitClear(LibertyAttr *attr) { if (sequential_) { const char *func = getAttrString(attr); if (func) sequential_->setClear(stringCopy(func)); } } void LibertyReader::visitPreset(LibertyAttr *attr) { if (sequential_) { const char *func = getAttrString(attr); if (func) sequential_->setPreset(stringCopy(func)); } } void LibertyReader::visitClrPresetVar1(LibertyAttr *attr) { if (sequential_) { LogicValue var = getAttrLogicValue(attr); sequential_->setClrPresetVar1(var); } } void LibertyReader::visitClrPresetVar2(LibertyAttr *attr) { if (sequential_) { LogicValue var = getAttrLogicValue(attr); sequential_->setClrPresetVar2(var); } } //////////////////////////////////////////////////////////////// void LibertyReader::beginStatetable(LibertyGroup *group) { if (cell_) { const char *input_ports_arg = group->firstName(); StdStringSeq input_ports; if (input_ports_arg) input_ports = parseTokenList(input_ports_arg, ' '); const char *internal_ports_arg = group->secondName(); StdStringSeq internal_ports; if (internal_ports_arg) internal_ports = parseTokenList(internal_ports_arg, ' '); statetable_ = new StatetableGroup(input_ports, internal_ports, group->line()); } } void LibertyReader::visitTable(LibertyAttr *attr) { if (statetable_) { const char *table_str = getAttrString(attr); StdStringSeq table_rows = parseTokenList(table_str, ','); size_t input_count = statetable_->inputPorts().size(); size_t internal_count = statetable_->internalPorts().size(); for (string row : table_rows) { StdStringSeq row_groups = parseTokenList(row.c_str(), ':'); if (row_groups.size() != 3) { libWarn(1300, attr, "table row must have 3 groups separated by ':'."); break; } StdStringSeq inputs = parseTokenList(row_groups[0].c_str(), ' '); if (inputs.size() != input_count) { libWarn(1301, attr, "table row has %zu input values but %zu are required.", inputs.size(), input_count); break; } StdStringSeq currents = parseTokenList(row_groups[1].c_str(), ' '); if (currents.size() != internal_count) { libWarn(1302, attr, "table row has %zu current values but %zu are required.", currents.size(), internal_count); break; } StdStringSeq nexts = parseTokenList(row_groups[2].c_str(), ' '); if (nexts.size() != internal_count) { libWarn(1303, attr, "table row has %zu next values but %zu are required.", nexts.size(), internal_count); break; } StateInputValues input_values = parseStateInputValues(inputs, attr); StateInternalValues current_values=parseStateInternalValues(currents,attr); StateInternalValues next_values = parseStateInternalValues(nexts, attr); statetable_->addRow(input_values, current_values, next_values); } } } 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, LibertyAttr *attr) { StateInputValues input_values; for (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, LibertyAttr *attr) { StateInternalValues state_values; for (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; } void LibertyReader::endStatetable(LibertyGroup *) { } //////////////////////////////////////////////////////////////// void LibertyReader::beginTiming(LibertyGroup *group) { if (port_group_) { timing_ = new TimingGroup(group->line()); port_group_->addTimingGroup(timing_); } } void LibertyReader::endTiming(LibertyGroup *group) { if (timing_) { // Set scale factor type in constraint tables. for (auto rf : RiseFall::range()) { TableModel *model = timing_->constraint(rf); if (model) { ScaleFactorType type=timingTypeScaleFactorType(timing_->attrs()->timingType()); model->setScaleFactorType(type); } } TimingType timing_type = timing_->attrs()->timingType(); if (timing_->relatedPortNames() == nullptr && !(timing_type == TimingType::min_pulse_width || timing_type == TimingType::min_clock_tree_path || timing_type == TimingType::max_clock_tree_path)) libWarn(1243, group, "timing group missing related_pin/related_bus_pin."); } timing_ = nullptr; receiver_model_ = nullptr; } void LibertyReader::visitRelatedPin(LibertyAttr *attr) { if (timing_) visitRelatedPin(attr, timing_); if (internal_power_) visitRelatedPin(attr, internal_power_); } void LibertyReader::visitRelatedPin(LibertyAttr *attr, RelatedPortGroup *group) { const char *port_names = getAttrString(attr); if (port_names) { group->setRelatedPortNames(parseNameList(port_names)); group->setIsOneToOne(true); } } StringSeq * LibertyReader::parseNameList(const char *name_list) { StringSeq *names = new StringSeq; // Parse space separated list of names. TokenParser parser(name_list, " "); while (parser.hasNext()) { char *token = parser.next(); // Skip extra spaces. if (token[0] != '\0') { const char *name = token; names->push_back(stringCopy(name)); } } return names; } StdStringSeq LibertyReader::parseTokenList(const char *token_str, const char separator) { StdStringSeq tokens; // Parse space separated list of names. char separators[2] = {separator, '\0'}; TokenParser parser(token_str, separators); while (parser.hasNext()) { char *token = parser.next(); // Skip extra spaces. if (token[0] != '\0') { tokens.push_back(token); } } return tokens; } void LibertyReader::visitRelatedBusPins(LibertyAttr *attr) { if (timing_) visitRelatedBusPins(attr, timing_); if (internal_power_) visitRelatedBusPins(attr, internal_power_); } void LibertyReader::visitRelatedBusPins(LibertyAttr *attr, RelatedPortGroup *group) { const char *port_names = getAttrString(attr); if (port_names) { group->setRelatedPortNames(parseNameList(port_names)); group->setIsOneToOne(false); } } void LibertyReader::visitRelatedOutputPin(LibertyAttr *attr) { if (timing_) { const char *pin_name = getAttrString(attr); if (pin_name) timing_->setRelatedOutputPortName(pin_name); } } void LibertyReader::visitTimingType(LibertyAttr *attr) { if (timing_) { const char *type_name = getAttrString(attr); if (type_name) { TimingType type = findTimingType(type_name); if (type == TimingType::unknown) libWarn(1244, attr, "unknown timing_type %s.", type_name); else timing_->attrs()->setTimingType(type); } } } void LibertyReader::visitTimingSense(LibertyAttr *attr) { if (timing_) { const char *sense_name = getAttrString(attr); if (sense_name) { if (stringEq(sense_name, "non_unate")) timing_->attrs()->setTimingSense(TimingSense::non_unate); else if (stringEq(sense_name, "positive_unate")) timing_->attrs()->setTimingSense(TimingSense::positive_unate); else if (stringEq(sense_name, "negative_unate")) timing_->attrs()->setTimingSense(TimingSense::negative_unate); else libWarn(1245, attr, "unknown timing_sense %s.", sense_name); } } } void LibertyReader::visitSdfCondStart(LibertyAttr *attr) { if (timing_) { const char *cond = getAttrString(attr); if (cond) timing_->attrs()->setSdfCondStart(cond); } } void LibertyReader::visitSdfCondEnd(LibertyAttr *attr) { if (timing_) { const char *cond = getAttrString(attr); if (cond) timing_->attrs()->setSdfCondEnd(cond); } } void LibertyReader::visitMode(LibertyAttr *attr) { if (timing_) { if (attr->isComplex()) { LibertyAttrValueIterator value_iter(attr->values()); if (value_iter.hasNext()) { LibertyAttrValue *value = value_iter.next(); if (value->isString()) { timing_->attrs()->setModeName(value->stringValue()); if (value_iter.hasNext()) { value = value_iter.next(); if (value->isString()) timing_->attrs()->setModeValue(value->stringValue()); else libWarn(1246, attr, "mode value is not a string."); } else libWarn(1247, attr, "missing mode value."); } else libWarn(1248, attr, "mode name is not a string."); } else libWarn(1249, attr, "mode missing values."); } else libWarn(1250, attr, "mode missing mode name and value."); } } void LibertyReader::visitIntrinsicRise(LibertyAttr *attr) { visitIntrinsic(attr, RiseFall::rise()); } void LibertyReader::visitIntrinsicFall(LibertyAttr *attr) { visitIntrinsic(attr, RiseFall::fall()); } void LibertyReader::visitIntrinsic(LibertyAttr *attr, RiseFall *rf) { if (timing_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) timing_->setIntrinsic(rf, value * time_scale_); } } void LibertyReader::visitRiseResistance(LibertyAttr *attr) { visitRiseFallResistance(attr, RiseFall::rise()); } void LibertyReader::visitFallResistance(LibertyAttr *attr) { visitRiseFallResistance(attr, RiseFall::fall()); } void LibertyReader::visitRiseFallResistance(LibertyAttr *attr, RiseFall *rf) { if (timing_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) timing_->setResistance(rf, value * res_scale_); } } void LibertyReader::beginCellRise(LibertyGroup *group) { beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::cell); } void LibertyReader::beginCellFall(LibertyGroup *group) { beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::cell); } void LibertyReader::endCellRiseFall(LibertyGroup *group) { if (table_) { if (GateTableModel::checkAxes(table_)) { TableModel *table_model = new TableModel(table_, tbl_template_, scale_factor_type_, rf_); timing_->setCell(rf_, table_model); } else libWarn(1251, group, "unsupported model axis."); } endTableModel(); } void LibertyReader::beginRiseTransition(LibertyGroup *group) { beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::transition); } void LibertyReader::beginFallTransition(LibertyGroup *group) { beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::transition); } void LibertyReader::endRiseFallTransition(LibertyGroup *group) { if (table_) { if (GateTableModel::checkAxes(table_)) { TableModel *table_model = new TableModel(table_, tbl_template_, scale_factor_type_, rf_); timing_->setTransition(rf_, table_model); } else libWarn(1252, group, "unsupported model axis."); } endTableModel(); } void LibertyReader::beginRiseConstraint(LibertyGroup *group) { // Scale factor depends on timing_type, which may follow this stmt. beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::unknown); } void LibertyReader::beginFallConstraint(LibertyGroup *group) { // Scale factor depends on timing_type, which may follow this stmt. beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::unknown); } void LibertyReader::endRiseFallConstraint(LibertyGroup *group) { if (table_) { if (CheckTableModel::checkAxes(table_)) { TableModel *table_model = new TableModel(table_, tbl_template_, scale_factor_type_, rf_); timing_->setConstraint(rf_, table_model); } else libWarn(1253, group, "unsupported model axis."); } endTableModel(); } //////////////////////////////////////////////////////////////// void LibertyReader::beginRiseTransitionDegredation(LibertyGroup *group) { if (library_) beginTableModel(group, TableTemplateType::delay, RiseFall::rise(), time_scale_, ScaleFactorType::transition); } void LibertyReader::beginFallTransitionDegredation(LibertyGroup *group) { if (library_) beginTableModel(group, TableTemplateType::delay, RiseFall::fall(), time_scale_, ScaleFactorType::transition); } void LibertyReader::endRiseFallTransitionDegredation(LibertyGroup *group) { if (table_) { if (LibertyLibrary::checkSlewDegradationAxes(table_)) { TableModel *table_model = new TableModel(table_, tbl_template_, scale_factor_type_, rf_); library_->setWireSlewDegradationTable(table_model, rf_); } else libWarn(1254, group, "unsupported model axis."); } endTableModel(); } //////////////////////////////////////////////////////////////// void LibertyReader::beginTimingTableModel(LibertyGroup *group, RiseFall *rf, ScaleFactorType scale_factor_type) { if (timing_) beginTableModel(group, TableTemplateType::delay, rf, time_scale_, scale_factor_type); else libWarn(1255, group, "%s group not in timing group.", group->firstName()); } void LibertyReader::beginTableModel(LibertyGroup *group, TableTemplateType type, RiseFall *rf, float scale, ScaleFactorType scale_factor_type) { beginTable(group, type, scale); rf_ = rf; scale_factor_type_ = scale_factor_type; sigma_type_ = EarlyLateAll::all(); } void LibertyReader::endTableModel() { endTable(); scale_factor_type_ = ScaleFactorType::unknown; sigma_type_ = nullptr; index_ = 0; } void LibertyReader::beginTable(LibertyGroup *group, TableTemplateType type, float scale) { const char *template_name = group->firstName(); if (library_ && template_name) { tbl_template_ = library_->findTableTemplate(template_name, type); if (tbl_template_) { axis_[0] = tbl_template_->axis1ptr(); axis_[1] = tbl_template_->axis2ptr(); axis_[2] = tbl_template_->axis3ptr(); } else { libWarn(1256, group, "table template %s not found.", template_name); axis_[0] = nullptr; axis_[1] = nullptr; axis_[2] = nullptr; } clearAxisValues(); table_ = nullptr; table_model_scale_ = scale; } } void LibertyReader::endTable() { table_ = nullptr; tbl_template_ = nullptr; axis_[0] = nullptr; axis_[1] = nullptr; axis_[2] = nullptr; } void LibertyReader::visitValue(LibertyAttr *attr) { if (leakage_power_) { float value; bool valid; getAttrFloat(attr, value, valid); if (valid) leakage_power_->setPower(value * power_scale_); } } void LibertyReader::visitValues(LibertyAttr *attr) { if (tbl_template_ // Ignore values in ecsm_waveform groups. && !stringEq(libertyGroup()->type(), "ecsm_waveform")) makeTable(attr, table_model_scale_); } void LibertyReader::makeTable(LibertyAttr *attr, float scale) { if (attr->isComplex()) { makeTableAxis(0); makeTableAxis(1); makeTableAxis(2); if (axis_[0] && axis_[1] && axis_[2]) { // 3D table // Column index1*size(index2) + index2 // Row index3 FloatTable *table = makeFloatTable(attr, axis_[0]->size()*axis_[1]->size(), axis_[2]->size(), scale); table_ = make_shared(table, axis_[0], axis_[1], axis_[2]); } else if (axis_[0] && axis_[1]) { // 2D table // Row variable1/axis[0] // Column variable2/axis[1] FloatTable *table = makeFloatTable(attr, axis_[0]->size(), axis_[1]->size(), scale); table_ = make_shared(table, axis_[0], axis_[1]); } else if (axis_[0]) { // 1D table FloatTable *table = makeFloatTable(attr, 1, axis_[0]->size(), scale); FloatSeq *values = (*table)[0]; delete table; table_ = make_shared(values, axis_[0]); } else { // scalar FloatTable *table = makeFloatTable(attr, 1, 1, scale); float value = (*(*table)[0])[0]; delete (*table)[0]; delete table; table_ = make_shared(value); } } else libWarn(1257, attr, "%s is missing values.", attr->name()); } FloatTable * LibertyReader::makeFloatTable(LibertyAttr *attr, size_t rows, size_t cols, float scale) { FloatTable *table = new FloatTable; table->reserve(rows); for (LibertyAttrValue *value : *attr->values()) { FloatSeq *row = new FloatSeq; row->reserve(cols); table->push_back(row); if (value->isString()) { const char *values_list = value->stringValue(); parseStringFloatList(values_list, scale, row, attr); } else if (value->isFloat()) // Scalar value. row->push_back(value->floatValue() * scale); else libWarn(1258, attr, "%s is not a list of floats.", attr->name()); if (row->size() != cols) { libWarn(1259, attr, "table row has %u columns but axis has %d.", // size_t is long on 64 bit ports. static_cast(row->size()), static_cast(cols)); // Fill out row columns with zeros. for (size_t c = row->size(); c < cols; c++) row->push_back(0.0); } } if (table->size() != rows) { libWarn(1260, attr, "table has %u rows but axis has %d.", // size_t is long on 64 bit ports. static_cast(table->size()), static_cast(rows)); // Fill with zero'd rows. for (size_t r = table->size(); r < rows; r++) { FloatSeq *row = new FloatSeq; table->push_back(row); // Fill out row with zeros. for (size_t c = row->size(); c < cols; c++) row->push_back(0.0); } } return table; } void LibertyReader::makeTableAxis(int index) { if (axis_values_[index]) { TableAxisVariable var = axis_[index]->variable(); FloatSeq *values = axis_values_[index]; const Units *units = library_->units(); float scale = tableVariableUnit(var, units)->scale(); scaleFloats(values, scale); axis_[index] = make_shared(var, values); } } //////////////////////////////////////////////////////////////// // Define lut output variables as internal ports. // I can't find any documentation for this group. void LibertyReader::beginLut(LibertyGroup *group) { if (cell_) { for (LibertyAttrValue *param : *group->params()) { if (param->isString()) { const char *names = param->stringValue(); // Parse space separated list of related port names. TokenParser parser(names, " "); while (parser.hasNext()) { char *name = parser.next(); if (name[0] != '\0') { LibertyPort *port = builder_.makePort(cell_, name); port->setDirection(PortDirection::internal()); } } } else libWarn(1261, group, "lut output is not a string."); } } } void LibertyReader::endLut(LibertyGroup *) { } //////////////////////////////////////////////////////////////// void LibertyReader::beginTestCell(LibertyGroup *group) { if (cell_ && cell_->testCell()) libWarn(1262, group, "cell %s test_cell redefinition.", cell_->name()); else { string name = cell_->name(); name += "/test_cell"; test_cell_ = new TestCell(cell_->libertyLibrary(), name.c_str(), cell_->filename()); cell_->setTestCell(test_cell_); // Do a recursive parse of cell into the test_cell because it has // pins, buses, bundles, and sequentials just like a cell. save_cell_ = cell_; save_cell_port_groups_ = std::move(cell_port_groups_); save_statetable_ = statetable_; statetable_ = nullptr; save_cell_sequentials_ = std::move(cell_sequentials_); save_cell_funcs_ = std::move(cell_funcs_); cell_ = test_cell_; } } void LibertyReader::endTestCell(LibertyGroup *) { makeCellSequentials(); makeStatetable(); parseCellFuncs(); finishPortGroups(); // Restore reader state to enclosing cell. cell_port_groups_ = std::move(save_cell_port_groups_); statetable_ = save_statetable_; cell_sequentials_ = std::move(save_cell_sequentials_); cell_funcs_= std::move(save_cell_funcs_); cell_ = save_cell_; test_cell_ = nullptr; save_statetable_ = nullptr; } //////////////////////////////////////////////////////////////// void LibertyReader::beginModeDef(LibertyGroup *group) { const char *name = group->firstName(); if (name) mode_def_ = cell_->makeModeDef(name); else libWarn(1263, group, "mode definition missing name."); } void LibertyReader::endModeDef(LibertyGroup *) { mode_def_ = nullptr; } void LibertyReader::beginModeValue(LibertyGroup *group) { if (mode_def_) { const char *name = group->firstName(); if (name) mode_value_ = mode_def_->defineValue(name, nullptr, nullptr); else libWarn(1264, group, "mode value missing name."); } } void LibertyReader::endModeValue(LibertyGroup *) { mode_value_ = nullptr; } void LibertyReader::visitWhen(LibertyAttr *attr) { if (tbl_template_) libWarn(1265, attr, "when attribute inside table model."); if (mode_value_) { const char *func = getAttrString(attr); if (func) makeLibertyFunc(func, mode_value_->condRef(), false, "when", attr); } if (timing_) { const char *func = getAttrString(attr); if (func) makeLibertyFunc(func, timing_->attrs()->condRef(), false, "when", attr); } if (internal_power_) { const char *func = getAttrString(attr); if (func) makeLibertyFunc(func, internal_power_->whenRef(), false, "when", attr); } if (leakage_power_) { const char *func = getAttrString(attr); if (func) makeLibertyFunc(func, leakage_power_->whenRef(), false, "when", attr); } } void LibertyReader::visitSdfCond(LibertyAttr *attr) { if (mode_value_) { const char *cond = getAttrString(attr); if (cond) mode_value_->setSdfCond(cond); } else if (timing_) { const char *cond = getAttrString(attr); if (cond) timing_->attrs()->setSdfCond(cond); } // sdf_cond can also appear inside minimum_period groups. } //////////////////////////////////////////////////////////////// const char * LibertyReader::getAttrString(LibertyAttr *attr) { if (attr->isSimple()) { LibertyAttrValue *value = attr->firstValue(); if (value->isString()) return value->stringValue(); else libWarn(1266, attr, "%s attribute is not a string.", attr->name()); } else libWarn(1267, attr, "%s is not a simple attribute.", attr->name()); return nullptr; } void LibertyReader::getAttrInt(LibertyAttr *attr, // Return values. int &value, bool &exists) { value = 0; exists = false; if (attr->isSimple()) { LibertyAttrValue *attr_value = attr->firstValue(); 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()); } else libWarn(1269, attr, "%s is not a simple attribute.", attr->name()); } void LibertyReader::getAttrFloat(LibertyAttr *attr, // Return values. float &value, bool &valid) { valid = false; if (attr->isSimple()) getAttrFloat(attr, attr->firstValue(), value, valid); else libWarn(1270, attr, "%s is not a simple attribute.", attr->name()); } void LibertyReader::getAttrFloat(LibertyAttr *attr, 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 char *string = attr_value->stringValue(); // See if attribute string is a variable. variableValue(string, value, valid); if (!valid) { // For some reason area attributes for pads are quoted floats. // Check that the string is a valid double. char *end; value = strtof(string, &end); if ((*end && !isspace(*end)) // strtof support INF as a valid float. || stringEqual(string, "inf")) libWarn(1271, attr, "%s value %s is not a float.", attr->name(), string); valid = true; } } } // Get two floats in a complex attribute. // attr(float1, float2); void LibertyReader::getAttrFloat2(LibertyAttr *attr, // Return values. float &value1, float &value2, bool &exists) { exists = false; if (attr->isComplex()) { LibertyAttrValueIterator value_iter(attr->values()); if (value_iter.hasNext()) { LibertyAttrValue *value = value_iter.next(); getAttrFloat(attr, value, value1, exists); if (exists) { if (value_iter.hasNext()) { value = value_iter.next(); getAttrFloat(attr, value, value2, exists); } else libWarn(1272, attr, "%s missing values.", attr->name()); } } else libWarn(1273, attr, "%s missing values.", attr->name()); } else libWarn(1274, attr, "%s is not a complex attribute.", attr->name()); } // Parse string of comma separated floats. // Note that some brain damaged vendors (that used to "Think") are not // consistent about including the delimiters. void LibertyReader::parseStringFloatList(const char *float_list, float scale, FloatSeq *values, LibertyAttr *attr) { const char *delimiters = ", "; TokenParser parser(float_list, delimiters); while (parser.hasNext()) { char *token = parser.next(); // Some (brain dead) libraries enclose floats in brackets. if (*token == '{') token++; char *end; float value = strtof(token, &end) * scale; if (end == token || (end && !(*end == '\0' || isspace(*end) || strchr(delimiters, *end) != nullptr || *end == '}'))) libWarn(1275, attr, "%s is not a float.", token); values->push_back(value); } } FloatSeq * LibertyReader::readFloatSeq(LibertyAttr *attr, float scale) { FloatSeq *values = nullptr; if (attr->isComplex()) { LibertyAttrValueIterator value_iter(attr->values()); if (value_iter.hasNext()) { LibertyAttrValue *value = value_iter.next(); if (value->isString()) { values = new FloatSeq; parseStringFloatList(value->stringValue(), scale, values, attr); } else if (value->isFloat()) { values = new FloatSeq; values->push_back(value->floatValue()); } else libWarn(1276, attr, "%s is missing values.", attr->name()); } if (value_iter.hasNext()) libWarn(1277, attr, "%s has more than one string.", attr->name()); } else { LibertyAttrValue *value = attr->firstValue(); if (value->isString()) { values = new FloatSeq; parseStringFloatList(value->stringValue(), scale, values, attr); } else libWarn(1278, attr, "%s is missing values.", attr->name()); } return values; } void LibertyReader::getAttrBool(LibertyAttr *attr, // Return values. bool &value, bool &exists) { exists = false; if (attr->isSimple()) { LibertyAttrValue *val = attr->firstValue(); if (val->isString()) { const char *str = val->stringValue(); if (stringEqual(str, "true")) { value = true; exists = true; } else if (stringEqual(str, "false")) { value = false; exists = true; } else libWarn(1279, attr, "%s attribute is not boolean.", attr->name()); } else libWarn(1280, attr, "%s attribute is not boolean.", attr->name()); } else libWarn(1281, attr, "%s is not a simple attribute.", attr->name()); } // Read L/H/X string attribute values as bool. LogicValue LibertyReader::getAttrLogicValue(LibertyAttr *attr) { const char *str = getAttrString(attr); if (str) { if (stringEq(str, "L")) return LogicValue::zero; else if (stringEq(str, "H")) return LogicValue::one; else if (stringEq(str, "X")) return LogicValue::unknown; else libWarn(1282, attr, "attribute %s value %s not recognized.", attr->name(), str); // fall thru } return LogicValue::unknown; } FuncExpr * LibertyReader::parseFunc(const char *func, const char *attr_name, int line) { string error_msg; stringPrint(error_msg, "%s, line %d %s", filename_, line, attr_name); return parseFuncExpr(func, cell_, error_msg.c_str(), report_); } EarlyLateAll * LibertyReader::getAttrEarlyLate(LibertyAttr *attr) { const char *value = getAttrString(attr); if (stringEq(value, "early")) return EarlyLateAll::early(); else if (stringEq(value, "late")) return EarlyLateAll::late(); else if (stringEq(value, "early_and_late")) return EarlyLateAll::all(); else { libWarn(1283, attr, "unknown early/late value."); return EarlyLateAll::all(); } } //////////////////////////////////////////////////////////////// void LibertyReader::visitVariable(LibertyVariable *var) { if (var_map_ == nullptr) var_map_ = new LibertyVariableMap; const char *var_name = var->variable(); const char *key; float value; bool exists; var_map_->findKey(var_name, key, value, exists); if (exists) { // Duplicate variable name. (*var_map_)[key] = var->value(); } else (*var_map_)[stringCopy(var_name)] = var->value(); } void LibertyReader::variableValue(const char *var, float &value, bool &exists) { if (var_map_) var_map_->findKey(var, value, exists); else exists = false; } //////////////////////////////////////////////////////////////// void LibertyReader::libWarn(int id, LibertyStmt *stmt, const char *fmt, ...) { va_list args; va_start(args, fmt); report_->vfileWarn(id, filename_, stmt->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, LibertyStmt *stmt, const char *fmt, ...) { va_list args; va_start(args, fmt); report_->vfileError(id, filename_, stmt->line(), fmt, args); va_end(args); } //////////////////////////////////////////////////////////////// void LibertyReader::beginTableTemplatePower(LibertyGroup *group) { beginTableTemplate(group, TableTemplateType::power); } void LibertyReader::beginLeakagePower(LibertyGroup *group) { if (cell_) { leakage_power_ = new LeakagePowerGroup(group->line()); leakage_powers_.push_back(leakage_power_); } } void LibertyReader::endLeakagePower(LibertyGroup *) { leakage_power_ = nullptr; } void LibertyReader::beginInternalPower(LibertyGroup *group) { if (port_group_) { internal_power_ = makeInternalPowerGroup(group->line()); port_group_->addInternalPowerGroup(internal_power_); } } InternalPowerGroup * LibertyReader::makeInternalPowerGroup(int line) { return new InternalPowerGroup(line); } void LibertyReader::endInternalPower(LibertyGroup *) { internal_power_ = nullptr; } void LibertyReader::beginFallPower(LibertyGroup *group) { if (internal_power_) beginTableModel(group, TableTemplateType::power, RiseFall::fall(), energy_scale_, ScaleFactorType::internal_power); } void LibertyReader::beginRisePower(LibertyGroup *group) { if (internal_power_) beginTableModel(group, TableTemplateType::power, RiseFall::rise(), energy_scale_, ScaleFactorType::internal_power); } void LibertyReader::endRiseFallPower(LibertyGroup *) { if (table_) { TableModel *table_model = new TableModel(table_, tbl_template_, scale_factor_type_, rf_); internal_power_->setModel(rf_, new InternalPowerModel(table_model)); } endTableModel(); } void LibertyReader::endPower(LibertyGroup *) { if (table_) { TableModel *table_model = new TableModel(table_, tbl_template_, scale_factor_type_, rf_); // Share the model for rise/fall. InternalPowerModel *power_model = new InternalPowerModel(table_model); internal_power_->setModel(RiseFall::rise(), power_model); internal_power_->setModel(RiseFall::fall(), power_model); } endTableModel(); } void LibertyReader::visitRelatedGroundPin(LibertyAttr *attr) { if (ports_) { const char *related_ground_pin = getAttrString(attr); for (LibertyPort *port : *ports_) port->setRelatedGroundPin(related_ground_pin); } } void LibertyReader::visitRelatedPowerPin(LibertyAttr *attr) { if (ports_) { const char *related_power_pin = getAttrString(attr); for (LibertyPort *port : *ports_) port->setRelatedPowerPin(related_power_pin); } } void LibertyReader::visitRelatedPgPin(LibertyAttr *attr) { if (internal_power_) internal_power_->setRelatedPgPin(getAttrString(attr)); } //////////////////////////////////////////////////////////////// void LibertyReader::beginTableTemplateOcv(LibertyGroup *group) { beginTableTemplate(group, TableTemplateType::ocv); } void LibertyReader::visitOcvArcDepth(LibertyAttr *attr) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) { if (timing_) timing_->attrs()->setOcvArcDepth(value); else if (cell_) cell_->setOcvArcDepth(value); else library_->setOcvArcDepth(value); } } void LibertyReader::visitDefaultOcvDerateGroup(LibertyAttr *attr) { const char *derate_name = getAttrString(attr); OcvDerate *derate = library_->findOcvDerate(derate_name); if (derate) library_->setDefaultOcvDerate(derate); else libWarn(1284, attr, "OCV derate group named %s not found.", derate_name); } void LibertyReader::visitOcvDerateGroup(LibertyAttr *attr) { ocv_derate_name_ = stringCopy(getAttrString(attr)); } void LibertyReader::beginOcvDerate(LibertyGroup *group) { const char *name = group->firstName(); if (name) ocv_derate_ = new OcvDerate(stringCopy(name)); else libWarn(1285, group, "ocv_derate missing name."); } void LibertyReader::endOcvDerate(LibertyGroup *) { if (cell_) library_->addOcvDerate(ocv_derate_); else if (library_) library_->addOcvDerate(ocv_derate_); ocv_derate_ = nullptr; } void LibertyReader::beginOcvDerateFactors(LibertyGroup *group) { if (ocv_derate_) { rf_type_ = RiseFallBoth::riseFall(); derate_type_ = EarlyLateAll::all(); path_type_ = PathType::clk_and_data; beginTable(group, TableTemplateType::ocv, 1.0); } } void LibertyReader::endOcvDerateFactors(LibertyGroup *) { if (ocv_derate_) { for (auto early_late : derate_type_->range()) { for (auto 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_); } } } endTable(); } void LibertyReader::visitRfType(LibertyAttr *attr) { const char *rf_name = getAttrString(attr); if (stringEq(rf_name, "rise")) rf_type_ = RiseFallBoth::rise(); else if (stringEq(rf_name, "fall")) rf_type_ = RiseFallBoth::fall(); else if (stringEq(rf_name, "rise_and_fall")) rf_type_ = RiseFallBoth::riseFall(); else libError(1286, attr, "unknown rise/fall."); } void LibertyReader::visitDerateType(LibertyAttr *attr) { derate_type_ = getAttrEarlyLate(attr); } void LibertyReader::visitPathType(LibertyAttr *attr) { const char *path_type = getAttrString(attr); if (stringEq(path_type, "clock")) path_type_ = PathType::clk; else if (stringEq(path_type, "data")) path_type_ = PathType::data; else if (stringEq(path_type, "clock_and_data")) path_type_ = PathType::clk_and_data; else libWarn(1287, attr, "unknown derate type."); } //////////////////////////////////////////////////////////////// void LibertyReader::beginOcvSigmaCellRise(LibertyGroup *group) { beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::unknown); } void LibertyReader::beginOcvSigmaCellFall(LibertyGroup *group) { beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::unknown); } void LibertyReader::endOcvSigmaCell(LibertyGroup *group) { if (table_) { if (GateTableModel::checkAxes(table_)) { TableModel *table_model = new TableModel(table_, tbl_template_, scale_factor_type_, rf_); if (sigma_type_ == EarlyLateAll::all()) { timing_->setDelaySigma(rf_, EarlyLate::min(), table_model); timing_->setDelaySigma(rf_, EarlyLate::max(), table_model); } else timing_->setDelaySigma(rf_, sigma_type_->asMinMax(), table_model); } else libWarn(1288, group, "unsupported model axis."); } endTableModel(); } void LibertyReader::beginOcvSigmaRiseTransition(LibertyGroup *group) { beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::unknown); } void LibertyReader::beginOcvSigmaFallTransition(LibertyGroup *group) { beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::unknown); } void LibertyReader::endOcvSigmaTransition(LibertyGroup *group) { if (table_) { if (GateTableModel::checkAxes(table_)) { TableModel *table_model = new TableModel(table_, tbl_template_, scale_factor_type_, rf_); if (sigma_type_ == EarlyLateAll::all()) { timing_->setSlewSigma(rf_, EarlyLate::min(), table_model); timing_->setSlewSigma(rf_, EarlyLate::max(), table_model); } else timing_->setSlewSigma(rf_, sigma_type_->asMinMax(), table_model); } else libWarn(1289, group, "unsupported model axis."); } endTableModel(); } void LibertyReader::beginOcvSigmaRiseConstraint(LibertyGroup *group) { beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::unknown); } void LibertyReader::beginOcvSigmaFallConstraint(LibertyGroup *group) { beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::unknown); } void LibertyReader::endOcvSigmaConstraint(LibertyGroup *group) { if (table_) { if (CheckTableModel::checkAxes(table_)) { TableModel *table_model = new TableModel(table_, tbl_template_, scale_factor_type_, rf_); if (sigma_type_ == EarlyLateAll::all()) { timing_->setConstraintSigma(rf_, EarlyLate::min(), table_model); timing_->setConstraintSigma(rf_, EarlyLate::max(), table_model); } else timing_->setConstraintSigma(rf_, sigma_type_->asMinMax(), table_model); } else libWarn(1290, group, "unsupported model axis."); } endTableModel(); } void LibertyReader::visitSigmaType(LibertyAttr *attr) { sigma_type_ = getAttrEarlyLate(attr); } void LibertyReader::visitCellLeakagePower(LibertyAttr *attr) { if (cell_) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) cell_->setLeakagePower(value * power_scale_); } } void LibertyReader::beginPgPin(LibertyGroup *group) { if (cell_) { const char *name = group->firstName(); pg_port_ = new LibertyPgPort(name, cell_); cell_->addPgPort(pg_port_); } } void LibertyReader::endPgPin(LibertyGroup *) { pg_port_ = nullptr; } void LibertyReader::visitPgType(LibertyAttr *attr) { if (pg_port_) { const char *type_name = getAttrString(attr); LibertyPgPort::PgType type = LibertyPgPort::PgType::unknown; if (stringEqual(type_name, "primary_ground")) type = LibertyPgPort::PgType::primary_ground; else if (stringEqual(type_name, "primary_power")) type = LibertyPgPort::PgType::primary_power; else if (stringEqual(type_name, "backup_ground")) type = LibertyPgPort::PgType::backup_ground; else if (stringEqual(type_name, "backup_power")) type = LibertyPgPort::PgType::backup_power; else if (stringEqual(type_name, "internal_ground")) type = LibertyPgPort::PgType::internal_ground; else if (stringEqual(type_name, "internal_power")) type = LibertyPgPort::PgType::internal_power; else if (stringEqual(type_name, "nwell")) type = LibertyPgPort::PgType::nwell; else if (stringEqual(type_name, "pwell")) type = LibertyPgPort::PgType::pwell; else if (stringEqual(type_name, "deepnwell")) type = LibertyPgPort::PgType::deepnwell; else if (stringEqual(type_name, "deeppwell")) type = LibertyPgPort::PgType::deeppwell; else libError(1291, attr, "unknown pg_type."); pg_port_->setPgType(type); } } void LibertyReader::visitVoltageName(LibertyAttr *attr) { if (pg_port_) { const char *voltage_name = getAttrString(attr); pg_port_->setVoltageName(voltage_name); } } //////////////////////////////////////////////////////////////// LibertyFunc::LibertyFunc(const char *expr, FuncExpr *&func_ref, bool invert, const char *attr_name, int line) : expr_(stringCopy(expr)), func_ref_(func_ref), invert_(invert), attr_name_(stringCopy(attr_name)), line_(line) { } LibertyFunc::~LibertyFunc() { stringDelete(expr_); stringDelete(attr_name_); } //////////////////////////////////////////////////////////////// PortGroup::PortGroup(LibertyPortSeq *ports, int line) : ports_(ports), line_(line) { } PortGroup::~PortGroup() { timings_.deleteContents(); delete ports_; } void PortGroup::addTimingGroup(TimingGroup *timing) { timings_.push_back(timing); } void PortGroup::addInternalPowerGroup(InternalPowerGroup *internal_power) { internal_power_groups_.push_back(internal_power); } //////////////////////////////////////////////////////////////// SequentialGroup::SequentialGroup(bool is_register, bool is_bank, LibertyPort *out_port, LibertyPort *out_inv_port, int size, int line) : is_register_(is_register), is_bank_(is_bank), out_port_(out_port), out_inv_port_(out_inv_port), size_(size), clk_(nullptr), data_(nullptr), preset_(nullptr), clear_(nullptr), clr_preset_var1_(LogicValue::unknown), clr_preset_var2_(LogicValue::unknown), line_(line) { } SequentialGroup::~SequentialGroup() { if (clk_) stringDelete(clk_); if (data_) stringDelete(data_); if (preset_) stringDelete(preset_); if (clear_) stringDelete(clear_); } void SequentialGroup::setClock(const char *clk) { clk_ = clk; } void SequentialGroup::setData(const char *data) { data_ = data; } void SequentialGroup::setClear(const char *clr) { clear_ = clr; } void SequentialGroup::setPreset(const char *preset) { preset_ = preset; } void SequentialGroup::setClrPresetVar1(LogicValue var) { clr_preset_var1_ = var; } void SequentialGroup::setClrPresetVar2(LogicValue var) { clr_preset_var2_ = var; } //////////////////////////////////////////////////////////////// StatetableGroup::StatetableGroup(StdStringSeq &input_ports, StdStringSeq &internal_ports, int line) : input_ports_(input_ports), internal_ports_(internal_ports), line_(line) { } void StatetableGroup::addRow(StateInputValues &input_values, StateInternalValues ¤t_values, StateInternalValues &next_values) { table_.emplace_back(input_values, current_values, next_values); } //////////////////////////////////////////////////////////////// RelatedPortGroup::RelatedPortGroup(int line) : related_port_names_(nullptr), line_(line) { } RelatedPortGroup::~RelatedPortGroup() { if (related_port_names_) { deleteContents(related_port_names_); delete related_port_names_; } } void RelatedPortGroup::setRelatedPortNames(StringSeq *names) { related_port_names_ = names; } void RelatedPortGroup::setIsOneToOne(bool one) { is_one_to_one_ = one; } //////////////////////////////////////////////////////////////// TimingGroup::TimingGroup(int line) : RelatedPortGroup(line), attrs_(make_shared()), related_output_port_name_(nullptr), receiver_model_(nullptr) { for (auto rf_index : RiseFall::rangeIndex()) { cell_[rf_index] = nullptr; constraint_[rf_index] = nullptr; transition_[rf_index] = nullptr; intrinsic_[rf_index] = 0.0F; intrinsic_exists_[rf_index] = false; resistance_[rf_index] = 0.0F; resistance_exists_[rf_index] = false; output_waveforms_[rf_index] = nullptr; for (auto el_index : EarlyLate::rangeIndex()) { delay_sigma_[rf_index][el_index] = nullptr; slew_sigma_[rf_index][el_index] = nullptr; constraint_sigma_[rf_index][el_index] = nullptr; } } } TimingGroup::~TimingGroup() { if (related_output_port_name_) stringDelete(related_output_port_name_); } void TimingGroup::setRelatedOutputPortName(const char *name) { related_output_port_name_ = stringCopy(name); } void TimingGroup::setIntrinsic(RiseFall *rf, float value) { int rf_index = rf->index(); intrinsic_[rf_index] = value; intrinsic_exists_[rf_index] = true; } void TimingGroup::intrinsic(RiseFall *rf, // Return values. float &value, bool &exists) { int rf_index = rf->index(); value = intrinsic_[rf_index]; exists = intrinsic_exists_[rf_index]; } void TimingGroup::setResistance(RiseFall *rf, float value) { int rf_index = rf->index(); resistance_[rf_index] = value; resistance_exists_[rf_index] = true; } void TimingGroup::resistance(RiseFall *rf, // Return values. float &value, bool &exists) { int rf_index = rf->index(); value = resistance_[rf_index]; exists = resistance_exists_[rf_index]; } TableModel * TimingGroup::cell(RiseFall *rf) { return cell_[rf->index()]; } void TimingGroup::setCell(RiseFall *rf, TableModel *model) { cell_[rf->index()] = model; } TableModel * TimingGroup::constraint(RiseFall *rf) { return constraint_[rf->index()]; } void TimingGroup::setConstraint(RiseFall *rf, TableModel *model) { constraint_[rf->index()] = model; } TableModel * TimingGroup::transition(RiseFall *rf) { return transition_[rf->index()]; } void TimingGroup::setTransition(RiseFall *rf, TableModel *model) { transition_[rf->index()] = model; } void TimingGroup::setDelaySigma(RiseFall *rf, EarlyLate *early_late, TableModel *model) { delay_sigma_[rf->index()][early_late->index()] = model; } void TimingGroup::setSlewSigma(RiseFall *rf, EarlyLate *early_late, TableModel *model) { slew_sigma_[rf->index()][early_late->index()] = model; } void TimingGroup::setConstraintSigma(RiseFall *rf, EarlyLate *early_late, TableModel *model) { constraint_sigma_[rf->index()][early_late->index()] = model; } void TimingGroup::setReceiverModel(ReceiverModelPtr receiver_model) { receiver_model_ = receiver_model; } OutputWaveforms * TimingGroup::outputWaveforms(RiseFall *rf) { return output_waveforms_[rf->index()]; } void TimingGroup::setOutputWaveforms(RiseFall *rf, OutputWaveforms *output_waveforms) { output_waveforms_[rf->index()] = output_waveforms; } //////////////////////////////////////////////////////////////// InternalPowerGroup::InternalPowerGroup(int line) : InternalPowerAttrs(), RelatedPortGroup(line) { } InternalPowerGroup::~InternalPowerGroup() { } //////////////////////////////////////////////////////////////// LeakagePowerGroup::LeakagePowerGroup(int line) : LeakagePowerAttrs(), line_(line) { } LeakagePowerGroup::~LeakagePowerGroup() { } //////////////////////////////////////////////////////////////// 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(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; 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(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(); string bus_bit_name; stringPrint(bus_bit_name, "%s%c%d%c", range_bus_name_.c_str(), library->busBrktLeft(), range_bit_, library->busBrktRight()); range_name_next_ = visitor_->findPort(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, Table1 *currents, float reference_time) : slew_(slew), cap_(cap), currents_(currents), reference_time_(reference_time) { } OutputWaveform::~OutputWaveform() { delete currents_; } Table1 * OutputWaveform::stealCurrents() { Table1 *currents = currents_; currents_ = nullptr; return currents; } } // namespace