// OpenSTA, Static Timing Analyzer // Copyright (c) 2020, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "Liberty.hh" #include "DisallowCopyAssign.hh" #include "EnumNameMap.hh" #include "Report.hh" #include "Debug.hh" #include "Error.hh" #include "StringUtil.hh" #include "StringSet.hh" #include "PatternMatch.hh" #include "Units.hh" #include "Transition.hh" #include "TimingRole.hh" #include "FuncExpr.hh" #include "TableModel.hh" #include "TimingArc.hh" #include "InternalPower.hh" #include "LeakagePower.hh" #include "Sequential.hh" #include "Wireload.hh" #include "EquivCells.hh" #include "Network.hh" #include "PortDirection.hh" namespace sta { typedef Set TimingModelSet; typedef Set FuncExprSet; typedef Set LatchEnableSet; bool LibertyLibrary::found_rise_fall_caps_ = false; void initLiberty() { TimingArcSet::init(); } void deleteLiberty() { TimingArcSet::destroy(); } LibertyLibrary::LibertyLibrary(const char *name, const char *filename) : ConcreteLibrary(name, filename, true), units_(new Units()), delay_model_type_(DelayModelType::cmos_linear), // default nominal_process_(0.0), nominal_voltage_(0.0), nominal_temperature_(0.0), scale_factors_(nullptr), default_input_pin_cap_(0.0), default_output_pin_cap_(0.0), default_bidirect_pin_cap_(0.0), default_fanout_load_(0.0), default_fanout_load_exists_(false), default_max_cap_(0.0), default_max_cap_exists_(false), default_max_fanout_(0.0), default_max_fanout_exists_(false), default_max_slew_(0.0), default_max_slew_exists_(false), slew_derate_from_library_(1.0), default_wire_load_(nullptr), default_wire_load_mode_(WireloadMode::unknown), default_wire_load_selection_(nullptr), default_operating_conditions_(nullptr), ocv_arc_depth_(0.0), default_ocv_derate_(nullptr), buffers_(nullptr) { // Scalar templates are builtin. for (int i = 0; i != int(TableTemplateType::count); i++) { TableTemplateType type = static_cast(i); TableTemplate *scalar_template = new TableTemplate("scalar", nullptr, nullptr, nullptr); addTableTemplate(scalar_template, type); } for (auto tr_index : RiseFall::rangeIndex()) { wire_slew_degradation_tbls_[tr_index] = nullptr; input_threshold_[tr_index] = input_threshold_default_; output_threshold_[tr_index] = output_threshold_default_; slew_lower_threshold_[tr_index] = slew_lower_threshold_default_; slew_upper_threshold_[tr_index] = slew_upper_threshold_default_; } } LibertyLibrary::~LibertyLibrary() { bus_dcls_.deleteContents(); for (int i = 0; i < int(TableTemplateType::count); i++) template_maps_[i].deleteContents(); scale_factors_map_.deleteContents(); delete scale_factors_; for (auto tr_index : RiseFall::rangeIndex()) { TableModel *model = wire_slew_degradation_tbls_[tr_index]; delete model; } operating_conditions_.deleteContents(); wireloads_.deleteContents(); wire_load_selections_.deleteContents(); delete units_; ocv_derate_map_.deleteContents(); for (auto name_volt : supply_voltage_map_) { const char *supply_name = name_volt.first; stringDelete(supply_name); } delete buffers_; } LibertyCell * LibertyLibrary::findLibertyCell(const char *name) const { return static_cast(findCell(name)); } void LibertyLibrary::findLibertyCellsMatching(PatternMatch *pattern, LibertyCellSeq *cells) { LibertyCellIterator cell_iter(this); while (cell_iter.hasNext()) { LibertyCell *cell = cell_iter.next(); if (pattern->match(cell->name())) cells->push_back(cell); } } LibertyCellSeq * LibertyLibrary::buffers() { if (buffers_ == nullptr) { buffers_ = new LibertyCellSeq; LibertyCellIterator cell_iter(this); while (cell_iter.hasNext()) { LibertyCell *cell = cell_iter.next(); if (!cell->dontUse() && cell->isBuffer()) buffers_->push_back(cell); } } return buffers_; } void LibertyLibrary::setDelayModelType(DelayModelType type) { delay_model_type_ = type; } void LibertyLibrary::addBusDcl(BusDcl *bus_dcl) { bus_dcls_[bus_dcl->name()] = bus_dcl; } BusDcl * LibertyLibrary::findBusDcl(const char *name) const { return bus_dcls_.findKey(name); } void LibertyLibrary::addTableTemplate(TableTemplate *tbl_template, TableTemplateType type) { template_maps_[int(type)][tbl_template->name()] = tbl_template; } TableTemplate * LibertyLibrary::findTableTemplate(const char *name, TableTemplateType type) { return template_maps_[int(type)][name]; } void LibertyLibrary::setNominalProcess(float process) { nominal_process_ = process; } void LibertyLibrary::setNominalVoltage(float voltage) { nominal_voltage_ = voltage; } void LibertyLibrary::setNominalTemperature(float temperature) { nominal_temperature_ = temperature; } void LibertyLibrary::setScaleFactors(ScaleFactors *scales) { scale_factors_ = scales; } void LibertyLibrary::addScaleFactors(ScaleFactors *scales) { scale_factors_map_[scales->name()] = scales; } ScaleFactors * LibertyLibrary::findScaleFactors(const char *name) { return scale_factors_map_[name]; } float LibertyLibrary::scaleFactor(ScaleFactorType type, const Pvt *pvt) const { return scaleFactor(type, 0, nullptr, pvt); } float LibertyLibrary::scaleFactor(ScaleFactorType type, const LibertyCell *cell, const Pvt *pvt) const { return scaleFactor(type, 0, cell, pvt); } float LibertyLibrary::scaleFactor(ScaleFactorType type, int tr_index, const LibertyCell *cell, const Pvt *pvt) const { if (pvt == nullptr) pvt = default_operating_conditions_; // If there is no operating condition, nominal pvt values are used. // All scale factors are unity for nominal pvt. if (pvt) { ScaleFactors *scale_factors = nullptr; // Cell level scale factors have precidence over library scale factors. if (cell) scale_factors = cell->scaleFactors(); if (scale_factors == nullptr) scale_factors = scale_factors_; if (scale_factors) { float process_scale = 1.0F + (pvt->process() - nominal_process_) * scale_factors->scale(type, ScaleFactorPvt::process, tr_index); float temp_scale = 1.0F + (pvt->temperature() - nominal_temperature_) * scale_factors->scale(type, ScaleFactorPvt::temp, tr_index); float volt_scale = 1.0F + (pvt->voltage() - nominal_voltage_) * scale_factors->scale(type, ScaleFactorPvt::volt, tr_index); float scale = process_scale * temp_scale * volt_scale; return scale; } } return 1.0F; } void LibertyLibrary::setWireSlewDegradationTable(TableModel *model, RiseFall *rf) { int tr_index = rf->index(); if (wire_slew_degradation_tbls_[tr_index]) delete wire_slew_degradation_tbls_[tr_index]; wire_slew_degradation_tbls_[tr_index] = model; } TableModel * LibertyLibrary::wireSlewDegradationTable(const RiseFall *rf) const { return wire_slew_degradation_tbls_[rf->index()]; } float LibertyLibrary::degradeWireSlew(const LibertyCell *cell, const RiseFall *rf, const Pvt *pvt, float in_slew, float wire_delay) const { const TableModel *model = wireSlewDegradationTable(rf); if (model) return degradeWireSlew(cell, pvt, model, in_slew, wire_delay); else return in_slew; } float LibertyLibrary::degradeWireSlew(const LibertyCell *cell, const Pvt *pvt, const TableModel *model, float in_slew, float wire_delay) const { switch (model->order()) { case 0: return model->findValue(this, cell, pvt, 0.0, 0.0, 0.0); case 1: { TableAxis *axis1 = model->axis1(); TableAxisVariable var1 = axis1->variable(); if (var1 == TableAxisVariable::output_pin_transition) return model->findValue(this, cell, pvt, in_slew, 0.0, 0.0); else if (var1 == TableAxisVariable::connect_delay) return model->findValue(this, cell, pvt, wire_delay, 0.0, 0.0); else { criticalError(231, "unsupported slew degradation table axes"); return 0.0; } } case 2: { TableAxis *axis1 = model->axis1(); TableAxis *axis2 = model->axis2(); TableAxisVariable var1 = axis1->variable(); TableAxisVariable var2 = axis2->variable(); if (var1 == TableAxisVariable::output_pin_transition && var2 == TableAxisVariable::connect_delay) return model->findValue(this, cell, pvt, in_slew, wire_delay, 0.0); else if (var1 == TableAxisVariable::connect_delay && var2 == TableAxisVariable::output_pin_transition) return model->findValue(this, cell, pvt, wire_delay, in_slew, 0.0); else { criticalError(232, "unsupported slew degradation table axes"); return 0.0; } } default: criticalError(233, "unsupported slew degradation table order"); return 0.0; } } // Check for supported axis variables. // Return true if axes are supported. bool LibertyLibrary::checkSlewDegradationAxes(Table *table) { switch (table->order()) { case 0: return true; case 1: { TableAxis *axis1 = table->axis1(); TableAxisVariable var1 = axis1->variable(); return var1 == TableAxisVariable::output_pin_transition || var1 == TableAxisVariable::connect_delay; } case 2: { TableAxis *axis1 = table->axis1(); TableAxis *axis2 = table->axis2(); TableAxisVariable var1 = axis1->variable(); TableAxisVariable var2 = axis2->variable(); return (var1 == TableAxisVariable::output_pin_transition && var2 == TableAxisVariable::connect_delay) || (var1 == TableAxisVariable::connect_delay && var2 == TableAxisVariable::output_pin_transition); } default: criticalError(234, "unsupported slew degradation table axes"); return 0.0; } } void LibertyLibrary::defaultMaxFanout(float &fanout, bool &exists) const { fanout = default_max_fanout_; exists = default_max_fanout_exists_; } void LibertyLibrary::setDefaultMaxFanout(float fanout) { default_max_fanout_ = fanout; default_max_fanout_exists_ = true; } void LibertyLibrary::defaultMaxSlew(float &slew, bool &exists) const { slew = default_max_slew_; exists = default_max_slew_exists_; } void LibertyLibrary::setDefaultMaxSlew(float slew) { default_max_slew_ = slew; default_max_slew_exists_ = true; } void LibertyLibrary::defaultMaxCapacitance(float &cap, bool &exists) const { cap = default_max_cap_; exists = default_max_cap_exists_; } void LibertyLibrary::setDefaultMaxCapacitance(float cap) { default_max_cap_ = cap; default_max_cap_exists_ = true; } void LibertyLibrary::defaultFanoutLoad(// Return values. float &fanout, bool &exists) const { fanout = default_fanout_load_; exists = default_fanout_load_exists_; } void LibertyLibrary::setDefaultFanoutLoad(float load) { default_fanout_load_ = load; default_fanout_load_exists_ = true; } void LibertyLibrary::setDefaultBidirectPinCap(float cap) { default_bidirect_pin_cap_ = cap; } void LibertyLibrary::setDefaultInputPinCap(float cap) { default_input_pin_cap_ = cap; } void LibertyLibrary::setDefaultOutputPinCap(float cap) { default_output_pin_cap_ = cap; } void LibertyLibrary::defaultIntrinsic(const RiseFall *rf, // Return values. float &intrinsic, bool &exists) const { default_intrinsic_.value(rf, intrinsic, exists); } void LibertyLibrary::setDefaultIntrinsic(const RiseFall *rf, float value) { default_intrinsic_.setValue(rf, value); } void LibertyLibrary::defaultPinResistance(const RiseFall *rf, const PortDirection *dir, // Return values. float &res, bool &exists) const { if (dir->isAnyTristate()) defaultBidirectPinRes(rf, res, exists); else defaultOutputPinRes(rf, res, exists); } void LibertyLibrary::defaultBidirectPinRes(const RiseFall *rf, // Return values. float &res, bool &exists) const { return default_inout_pin_res_.value(rf, res, exists); } void LibertyLibrary::setDefaultBidirectPinRes(const RiseFall *rf, float value) { default_inout_pin_res_.setValue(rf, value); } void LibertyLibrary::defaultOutputPinRes(const RiseFall *rf, // Return values. float &res, bool &exists) const { default_output_pin_res_.value(rf, res, exists); } void LibertyLibrary::setDefaultOutputPinRes(const RiseFall *rf, float value) { default_output_pin_res_.setValue(rf, value); } void LibertyLibrary::addWireload(Wireload *wireload) { wireloads_[wireload->name()] = wireload; } Wireload * LibertyLibrary::findWireload(const char *name) const { return wireloads_.findKey(name); } void LibertyLibrary::setDefaultWireload(Wireload *wireload) { default_wire_load_ = wireload; } Wireload * LibertyLibrary::defaultWireload() const { return default_wire_load_; } void LibertyLibrary::addWireloadSelection(WireloadSelection *selection) { wire_load_selections_[selection->name()] = selection; } WireloadSelection * LibertyLibrary::findWireloadSelection(const char *name) const { return wire_load_selections_.findKey(name); } WireloadSelection * LibertyLibrary::defaultWireloadSelection() const { return default_wire_load_selection_; } void LibertyLibrary::setDefaultWireloadSelection(WireloadSelection *selection) { default_wire_load_selection_ = selection; } WireloadMode LibertyLibrary::defaultWireloadMode() const { return default_wire_load_mode_; } void LibertyLibrary::setDefaultWireloadMode(WireloadMode mode) { default_wire_load_mode_ = mode; } void LibertyLibrary::addOperatingConditions(OperatingConditions *op_cond) { operating_conditions_[op_cond->name()] = op_cond; } OperatingConditions * LibertyLibrary::findOperatingConditions(const char *name) { return operating_conditions_.findKey(name); } OperatingConditions * LibertyLibrary::defaultOperatingConditions() const { return default_operating_conditions_; } void LibertyLibrary::setDefaultOperatingConditions(OperatingConditions *op_cond) { default_operating_conditions_ = op_cond; } float LibertyLibrary::inputThreshold(const RiseFall *rf) const { return input_threshold_[rf->index()]; } void LibertyLibrary::setInputThreshold(const RiseFall *rf, float th) { input_threshold_[rf->index()] = th; } float LibertyLibrary::outputThreshold(const RiseFall *rf) const { return output_threshold_[rf->index()]; } void LibertyLibrary::setOutputThreshold(const RiseFall *rf, float th) { output_threshold_[rf->index()] = th; } float LibertyLibrary::slewLowerThreshold(const RiseFall *rf) const { return slew_lower_threshold_[rf->index()]; } void LibertyLibrary::setSlewLowerThreshold(const RiseFall *rf, float th) { slew_lower_threshold_[rf->index()] = th; } float LibertyLibrary::slewUpperThreshold(const RiseFall *rf) const { return slew_upper_threshold_[rf->index()]; } void LibertyLibrary::setSlewUpperThreshold(const RiseFall *rf, float th) { slew_upper_threshold_[rf->index()] = th; } float LibertyLibrary::slewDerateFromLibrary() const { return slew_derate_from_library_; } void LibertyLibrary::setSlewDerateFromLibrary(float derate) { slew_derate_from_library_ = derate; } LibertyCell * LibertyLibrary::makeScaledCell(const char *name, const char *filename) { LibertyCell *cell = new LibertyCell(this, name, filename); return cell; } //////////////////////////////////////////////////////////////// void LibertyLibrary::makeCornerMap(LibertyLibrary *lib, int ap_index, Network *network, Report *report) { LibertyCellIterator cell_iter(lib); while (cell_iter.hasNext()) { LibertyCell *cell = cell_iter.next(); const char *name = cell->name(); LibertyCell *link_cell = network->findLibertyCell(name); if (link_cell) makeCornerMap(link_cell, cell, ap_index, report); } } // Map a cell linked in the network to the corresponding liberty cell // to use for delay calculation at a corner. void LibertyLibrary::makeCornerMap(LibertyCell *link_cell, LibertyCell *corner_cell, int ap_index, Report *report) { link_cell->setCornerCell(corner_cell, ap_index); makeCornerMap(link_cell, corner_cell, true, ap_index, report); // Check for brain damage in the other direction. makeCornerMap(corner_cell, link_cell, false, ap_index, report); } void LibertyLibrary::makeCornerMap(LibertyCell *cell1, LibertyCell *cell2, bool link, int ap_index, Report *report) { LibertyCellPortBitIterator port_iter1(cell1); while (port_iter1.hasNext()) { LibertyPort *port1 = port_iter1.next(); const char *port_name = port1->name(); LibertyPort *port2 = cell2->findLibertyPort(port_name); if (port2) { if (link) port1->setCornerPort(port2, ap_index); } else report->warn(2, "cell %s/%s port %s not found in cell %s/%s.", cell1->library()->name(), cell1->name(), port_name, cell2->library()->name(), cell2->name()); } for (auto arc_set1 : cell1->timing_arc_sets_) { auto arc_set2 = cell2->findTimingArcSet(arc_set1); if (arc_set2) { if (link) { TimingArcSetArcIterator arc_iter1(arc_set1); TimingArcSetArcIterator arc_iter2(arc_set2); while (arc_iter1.hasNext() && arc_iter2.hasNext()) { TimingArc *arc1 = arc_iter1.next(); TimingArc *arc2 = arc_iter2.next(); if (TimingArc::equiv(arc1, arc2)) arc1->setCornerArc(arc2, ap_index); } } } else report->warn(3, "cell %s/%s %s -> %s timing group %s not found in cell %s/%s.", cell1->library()->name(), cell1->name(), arc_set1->from()->name(), arc_set1->to()->name(), arc_set1->role()->asString(), cell2->library()->name(), cell2->name()); } } //////////////////////////////////////////////////////////////// float LibertyLibrary::ocvArcDepth() const { return ocv_arc_depth_; } void LibertyLibrary::setOcvArcDepth(float depth) { ocv_arc_depth_ = depth; } OcvDerate * LibertyLibrary::defaultOcvDerate() const { return default_ocv_derate_; } void LibertyLibrary::setDefaultOcvDerate(OcvDerate *derate) { default_ocv_derate_ = derate; } OcvDerate * LibertyLibrary::findOcvDerate(const char *derate_name) { return ocv_derate_map_.findKey(derate_name); } void LibertyLibrary::addOcvDerate(OcvDerate *derate) { ocv_derate_map_[derate->name()] = derate; } void LibertyLibrary::addSupplyVoltage(const char *supply_name, float voltage) { supply_voltage_map_[stringCopy(supply_name)] = voltage; } void LibertyLibrary::supplyVoltage(const char *supply_name, // Return value. float &voltage, bool &exists) const { supply_voltage_map_.findKey(supply_name, voltage, exists); } bool LibertyLibrary::supplyExists(const char *supply_name) const { return supply_voltage_map_.hasKey(supply_name); } //////////////////////////////////////////////////////////////// LibertyCellIterator::LibertyCellIterator(const LibertyLibrary * library): iter_(library->cell_map_) { } bool LibertyCellIterator::hasNext() { return iter_.hasNext(); } LibertyCell * LibertyCellIterator::next() { return static_cast(iter_.next()); } //////////////////////////////////////////////////////////////// LibertyCell::LibertyCell(LibertyLibrary *library, const char *name, const char *filename) : ConcreteCell(library, name, true, filename), liberty_library_(library), area_(0.0), dont_use_(false), is_macro_(false), is_memory_(false), is_pad_(false), is_level_shifter_(false), has_internal_ports_(false), interface_timing_(false), clock_gate_type_(ClockGateType::none), has_infered_reg_timing_arcs_(false), scale_factors_(nullptr), test_cell_(nullptr), ocv_arc_depth_(0.0), ocv_derate_(nullptr), is_disabled_constraint_(false), leakage_power_(0.0), leakage_power_exists_(false) { liberty_cell_ = this; } LibertyCell::~LibertyCell() { mode_defs_.deleteContents(); latch_d_to_q_map_.deleteContents(); deleteTimingArcAttrs(); timing_arc_sets_.deleteContents(); port_timing_arc_set_map_.deleteContents(); timing_arc_set_from_map_.deleteContents(); timing_arc_set_to_map_.deleteContents(); deleteInternalPowerAttrs(); internal_powers_.deleteContents(); leakage_powers_.deleteContents(); sequentials_.deleteContents(); bus_dcls_.deleteContents(); scaled_cells_.deleteContents(); delete test_cell_; ocv_derate_map_.deleteContents(); pg_port_map_.deleteContents(); } void LibertyCell::deleteTimingArcAttrs() { for (auto attrs : timing_arc_attrs_) { attrs->deleteContents(); delete attrs; } } LibertyPort * LibertyCell::findLibertyPort(const char *name) const { return static_cast(findPort(name)); } void LibertyCell::findLibertyPortsMatching(PatternMatch *pattern, LibertyPortSeq *ports) const { LibertyCellPortIterator port_iter(this); while (port_iter.hasNext()) { LibertyPort *port = port_iter.next(); if (pattern->match(port->name())) ports->push_back(port); } } void LibertyCell::addPort(ConcretePort *port) { ConcreteCell::addPort(port); if (port->direction()->isInternal()) has_internal_ports_ = true; } void LibertyCell::setHasInternalPorts(bool has_internal) { has_internal_ports_ = has_internal; } void LibertyCell::addPgPort(LibertyPgPort *pg_port) { pg_port_map_[pg_port->name()] = pg_port; } LibertyPgPort * LibertyCell::findPgPort(const char *name) const { return pg_port_map_.findKey(name); } ModeDef * LibertyCell::makeModeDef(const char *name) { ModeDef *mode = new ModeDef(name); mode_defs_[mode->name()] = mode; return mode; } ModeDef * LibertyCell::findModeDef(const char *name) { return mode_defs_.findKey(name); } void LibertyCell::setScaleFactors(ScaleFactors *scale_factors) { scale_factors_ = scale_factors; } void LibertyCell::addBusDcl(BusDcl *bus_dcl) { bus_dcls_[bus_dcl->name()] = bus_dcl; } BusDcl * LibertyCell::findBusDcl(const char *name) const { return bus_dcls_.findKey(name); } void LibertyCell::setArea(float area) { area_ = area; } void LibertyCell::setDontUse(bool dont_use) { dont_use_ = dont_use; } void LibertyCell::setIsMacro(bool is_macro) { is_macro_ = is_macro; } void LibertyCell::setIsMemory(bool is_memory) { is_memory_ = is_memory; } void LibertyCell::LibertyCell::setIsPad(bool is_pad) { is_pad_ = is_pad; } void LibertyCell::LibertyCell::setIsLevelShifter(bool is_level_shifter) { is_level_shifter_ = is_level_shifter; } void LibertyCell::setInterfaceTiming(bool value) { interface_timing_ = value; } bool LibertyCell::isClockGateLatchPosedge() const { return clock_gate_type_ == ClockGateType::latch_posedge; } bool LibertyCell::isClockGateLatchNegedge() const { return clock_gate_type_ == ClockGateType::latch_posedge; } bool LibertyCell::isClockGateOther() const { return clock_gate_type_ == ClockGateType::other; } bool LibertyCell::isClockGate() const { return clock_gate_type_ != ClockGateType::none; } void LibertyCell::setClockGateType(ClockGateType type) { clock_gate_type_ = type; } bool LibertyCell::isBuffer() const { LibertyPort *input; LibertyPort *output; bufferPorts(input, output); return input && output && hasBufferFunc(input, output) && !is_level_shifter_; } bool LibertyCell::hasBufferFunc(const LibertyPort *input, const LibertyPort *output) const { FuncExpr *func = output->function(); return func && func->op() == FuncExpr::op_port && func->port() == input; } bool LibertyCell::isInverter() const { LibertyPort *input; LibertyPort *output; bufferPorts(input, output); return input && output && hasInverterFunc(input, output); } bool LibertyCell::hasInverterFunc(const LibertyPort *input, const LibertyPort *output) const { FuncExpr *func = output->function(); return func && func->op() == FuncExpr::op_not && func->left()->op() == FuncExpr::op_port && func->left()->port() == input; } void LibertyCell::bufferPorts(// Return values. LibertyPort *&input, LibertyPort *&output) const { input = nullptr; output = nullptr; for (ConcretePort *cport : ports_) { LibertyPort *port = static_cast(cport); PortDirection *dir = port->direction(); if (dir->isInput()) { if (input) { // More than one input. input = nullptr; output = nullptr; break; } input = port; } else if (dir->isOutput()) { if (output) { // More than one output. input = nullptr; output = nullptr; break; } output = port; } else if (!dir->isPowerGround()) { input = nullptr; output = nullptr; break; } } } unsigned LibertyCell::addTimingArcSet(TimingArcSet *arc_set) { int set_index = timing_arc_sets_.size(); if (set_index > timing_arc_set_index_max) criticalError(235, "timing arc set max index exceeded"); timing_arc_sets_.push_back(arc_set); LibertyPort *from = arc_set->from(); TimingRole *role = arc_set->role(); if (role == TimingRole::regClkToQ() || role == TimingRole::latchEnToQ()) from->setIsRegClk(true); if (role->isTimingCheck()) from->setIsCheckClk(true); return set_index; } void LibertyCell::addTimingArcAttrs(TimingArcAttrs *attrs) { timing_arc_attrs_.push_back(attrs); } void LibertyCell::addInternalPower(InternalPower *power) { internal_powers_.push_back(power); port_internal_powers_[power->port()].push_back(power); } InternalPowerSeq * LibertyCell::internalPowers() { return &internal_powers_; } InternalPowerSeq * LibertyCell::internalPowers(const LibertyPort *port) { return &port_internal_powers_[port]; } void LibertyCell::addInternalPowerAttrs(InternalPowerAttrs *attrs) { internal_power_attrs_.push_back(attrs); } void LibertyCell::deleteInternalPowerAttrs() { for (auto attrs : internal_power_attrs_) { attrs->deleteContents(); delete attrs; } } void LibertyCell::addLeakagePower(LeakagePower *power) { leakage_powers_.push_back(power); } void LibertyCell::setLeakagePower(float leakage) { leakage_power_ = leakage; leakage_power_exists_ = true; } void LibertyCell::leakagePower(// Return values. float &leakage, bool &exists) const { leakage = leakage_power_; exists = leakage_power_exists_; } void LibertyCell::finish(bool infer_latches, Report *report, Debug *debug) { translatePresetClrCheckRoles(); makeTimingArcMap(report); makeTimingArcPortMaps(); findDefaultCondArcs(); makeLatchEnables(report, debug); if (infer_latches && !interface_timing_) inferLatchRoles(debug); } void LibertyCell::findDefaultCondArcs() { for (auto port_pair_set : port_timing_arc_set_map_) { TimingArcSetSeq *sets = port_pair_set.second; bool has_cond_arcs = false; for (auto set : *sets) { if (set->cond()) { has_cond_arcs = true; break; } } if (has_cond_arcs) { for (auto set : *sets) { if (!set->cond()) set->setIsCondDefault(true); } } } } // Timing checks for set/clear pins use setup/hold times. This // changes their roles to recovery/removal by finding the set/clear // pins and then translating the timing check roles. void LibertyCell::translatePresetClrCheckRoles() { LibertyPortSet pre_clr_ports; for (auto arc_set : timing_arc_sets_) { if (arc_set->role() == TimingRole::regSetClr()) pre_clr_ports.insert(arc_set->from()); } if (!pre_clr_ports.empty()) { for (auto arc_set : timing_arc_sets_) { if (pre_clr_ports.findKey(arc_set->to())) { if (arc_set->role() == TimingRole::setup()) arc_set->setRole(TimingRole::recovery()); else if (arc_set->role() == TimingRole::hold()) arc_set->setRole(TimingRole::removal()); } } } } void LibertyCell::makeTimingArcMap(Report *) { // Filter duplicate timing arcs, keeping the later definition. for (auto arc_set : timing_arc_sets_) // The last definition will be left in the set. timing_arc_set_map_.insert(arc_set); // Prune the arc sets not in the map. int j = 0; for (size_t i = 0; i < timing_arc_sets_.size(); i++) { TimingArcSet *arc_set = timing_arc_sets_[i]; TimingArcSet *match = timing_arc_set_map_.findKey(arc_set); if (match != arc_set) { // Unfortunately these errors are common in some brain damaged // libraries. // report->warn("cell %s/%s has duplicate %s -> %s %s timing groups.", // library_->name(), // name_, // match->from()->name(), // match->to()->name(), // match->role()->asString()); delete arc_set; } else // Shift arc sets down to fill holes left by removed duplicates. timing_arc_sets_[j++] = arc_set; } timing_arc_sets_.resize(j); if (timing_arc_set_map_.size() != timing_arc_sets_.size()) criticalError(205, "timing arc count mismatch"); } void LibertyCell::makeTimingArcPortMaps() { for (auto arc_set : timing_arc_sets_) { LibertyPort *from = arc_set->from(); LibertyPort *to = arc_set->to(); LibertyPortPair port_pair(from, to); TimingArcSetSeq *sets = port_timing_arc_set_map_.findKey(port_pair); if (sets == nullptr) { // First arc set for from/to ports. sets = new TimingArcSetSeq; port_timing_arc_set_map_[port_pair] = sets; } sets->push_back(arc_set); sets = timing_arc_set_from_map_.findKey(from); if (sets == nullptr) { sets = new TimingArcSetSeq; timing_arc_set_from_map_[from] = sets; } sets->push_back(arc_set); sets = timing_arc_set_to_map_.findKey(to); if (sets == nullptr) { sets = new TimingArcSetSeq; timing_arc_set_to_map_[to] = sets; } sets->push_back(arc_set); } } TimingArcSetSeq * LibertyCell::timingArcSets(const LibertyPort *from, const LibertyPort *to) const { if (from && to) { LibertyPortPair port_pair(from, to); return port_timing_arc_set_map_.findKey(port_pair); } else if (from) return timing_arc_set_from_map_.findKey(from); else if (to) return timing_arc_set_to_map_.findKey(to); else return nullptr; } TimingArcSet * LibertyCell::findTimingArcSet(TimingArcSet *key) const { return timing_arc_set_map_.findKey(key); } TimingArcSet * LibertyCell::findTimingArcSet(unsigned arc_set_index) const { return timing_arc_sets_[arc_set_index]; } size_t LibertyCell::timingArcSetCount() const { return timing_arc_sets_.size(); } bool LibertyCell::hasTimingArcs(LibertyPort *port) const { return timing_arc_set_from_map_.findKey(port) || timing_arc_set_to_map_.findKey(port); } void LibertyCell::makeSequential(int size, bool is_register, FuncExpr *clk, FuncExpr *data, FuncExpr *clear, FuncExpr *preset, LogicValue clr_preset_out, LogicValue clr_preset_out_inv, LibertyPort *output, LibertyPort *output_inv) { for (int bit = 0; bit < size; bit++) { FuncExpr *clk_bit = nullptr; if (clk) clk_bit = clk->bitSubExpr(bit); FuncExpr *data_bit = nullptr; if (data) data_bit = data->bitSubExpr(bit); FuncExpr *clear_bit = nullptr; if (clear) clear_bit = clear->bitSubExpr(bit); FuncExpr *preset_bit = nullptr; if (preset) preset_bit = preset->bitSubExpr(bit); LibertyPort *out_bit = output; if (output && output->hasMembers()) out_bit = output->findLibertyMember(bit); LibertyPort *out_inv_bit = output_inv; if (output_inv && output_inv->hasMembers()) out_inv_bit = output_inv->findLibertyMember(bit); Sequential *seq = new Sequential(is_register, clk_bit, data_bit, clear_bit,preset_bit, clr_preset_out, clr_preset_out_inv, out_bit, out_inv_bit); sequentials_.push_back(seq); port_to_seq_map_[seq->output()] = seq; port_to_seq_map_[seq->outputInv()] = seq; } } Sequential * LibertyCell::outputPortSequential(LibertyPort *port) { return port_to_seq_map_.findKey(port); } bool LibertyCell::hasSequentials() const { return !sequentials_.empty(); } void LibertyCell::addScaledCell(OperatingConditions *op_cond, LibertyCell *scaled_cell) { scaled_cells_[op_cond] = scaled_cell; LibertyCellPortBitIterator port_iter1(this); LibertyCellPortBitIterator port_iter2(scaled_cell); while (port_iter1.hasNext() && port_iter2.hasNext()) { LibertyPort *port = port_iter1.next(); LibertyPort *scaled_port = port_iter2.next(); port->addScaledPort(op_cond, scaled_port); } LibertyCellTimingArcSetIterator set_iter1(this); LibertyCellTimingArcSetIterator set_iter2(scaled_cell); while (set_iter1.hasNext() && set_iter2.hasNext()) { TimingArcSet *arc_set1 = set_iter1.next(); TimingArcSet *arc_set2 = set_iter2.next(); TimingArcSetArcIterator arc_iter1(arc_set1); TimingArcSetArcIterator arc_iter2(arc_set2); while (arc_iter1.hasNext() && arc_iter2.hasNext()) { TimingArc *arc = arc_iter1.next(); TimingArc *scaled_arc = arc_iter2.next(); if (TimingArc::equiv(arc, scaled_arc)) { TimingModel *model = scaled_arc->model(); model->setIsScaled(true); arc->addScaledModel(op_cond, model); } } } } void LibertyCell::setLibertyLibrary(LibertyLibrary *library) { liberty_library_ = library; library_ = library; } void LibertyCell::setHasInferedRegTimingArcs(bool infered) { has_infered_reg_timing_arcs_ = infered; } void LibertyCell::setTestCell(TestCell *test) { test_cell_ = test; } void LibertyCell::setIsDisabledConstraint(bool is_disabled) { is_disabled_constraint_ = is_disabled; } LibertyCell * LibertyCell::cornerCell(int ap_index) { if (ap_index < static_cast(corner_cells_.size())) return corner_cells_[ap_index]; else return nullptr; } void LibertyCell::setCornerCell(LibertyCell *corner_cell, int ap_index) { if (ap_index >= static_cast(corner_cells_.size())) corner_cells_.resize(ap_index + 1); corner_cells_[ap_index] = corner_cell; } //////////////////////////////////////////////////////////////// float LibertyCell::ocvArcDepth() const { return ocv_arc_depth_; } void LibertyCell::setOcvArcDepth(float depth) { ocv_arc_depth_ = depth; } OcvDerate * LibertyCell::ocvDerate() const { if (ocv_derate_) return ocv_derate_; else return liberty_library_->defaultOcvDerate(); } void LibertyCell::setOcvDerate(OcvDerate *derate) { ocv_derate_ = derate; } OcvDerate * LibertyCell::findOcvDerate(const char *derate_name) { return ocv_derate_map_.findKey(derate_name); } void LibertyCell::addOcvDerate(OcvDerate *derate) { ocv_derate_map_[derate->name()] = derate; } //////////////////////////////////////////////////////////////// LibertyCellTimingArcSetIterator::LibertyCellTimingArcSetIterator(const LibertyCell *cell) : TimingArcSetSeq::ConstIterator(&cell->timing_arc_sets_) { } LibertyCellTimingArcSetIterator::LibertyCellTimingArcSetIterator(const LibertyCell *cell, const LibertyPort *from, const LibertyPort *to): TimingArcSetSeq::ConstIterator(cell->timingArcSets(from, to)) { } //////////////////////////////////////////////////////////////// // Latch enable port/function for a latch D->Q timing arc set. class LatchEnable { public: LatchEnable(LibertyPort *data, LibertyPort *enable, RiseFall *enable_rf, FuncExpr *enable_func, LibertyPort *output, TimingArcSet *d_to_q, TimingArcSet *en_to_q, TimingArcSet *setup_check); LibertyPort *data() const { return data_; } LibertyPort *output() const { return output_; } LibertyPort *enable() const { return enable_; } FuncExpr *enableFunc() const { return enable_func_; } RiseFall *enableTransition() const { return enable_rf_; } TimingArcSet *dToQ() const { return d_to_q_; } TimingArcSet *enToQ() const { return en_to_q_; } TimingArcSet *setupCheck() const { return setup_check_; } private: DISALLOW_COPY_AND_ASSIGN(LatchEnable); LibertyPort *data_; LibertyPort *enable_; RiseFall *enable_rf_; FuncExpr *enable_func_; LibertyPort *output_; TimingArcSet *d_to_q_; TimingArcSet *en_to_q_; TimingArcSet *setup_check_; }; LatchEnable::LatchEnable(LibertyPort *data, LibertyPort *enable, RiseFall *enable_rf, FuncExpr *enable_func, LibertyPort *output, TimingArcSet *d_to_q, TimingArcSet *en_to_q, TimingArcSet *setup_check) : data_(data), enable_(enable), enable_rf_(enable_rf), enable_func_(enable_func), output_(output), d_to_q_(d_to_q), en_to_q_(en_to_q), setup_check_(setup_check) { } // Latch enable port/function for a latch D->Q timing arc set. // This augments cell timing data by linking enables to D->Q arcs. // Use timing arcs rather than sequentials (because they are optional). void LibertyCell::makeLatchEnables(Report *report, Debug *debug) { if (hasSequentials() || hasInferedRegTimingArcs()) { for (auto en_to_q : timing_arc_sets_) { if (en_to_q->role() == TimingRole::latchEnToQ()) { LibertyPort *en = en_to_q->from(); LibertyPort *q = en_to_q->to(); LibertyCellTimingArcSetIterator to_iter(this, nullptr, q); while (to_iter.hasNext()) { TimingArcSet *d_to_q = to_iter.next(); if (d_to_q->role() == TimingRole::latchDtoQ()) { LibertyPort *d = d_to_q->from(); LibertyCellTimingArcSetIterator check_iter(this, en, d); while (check_iter.hasNext()) { TimingArcSet *setup_check = check_iter.next(); if (setup_check->role() == TimingRole::setup()) { LatchEnable *latch_enable = makeLatchEnable(d, en, q, d_to_q, en_to_q, setup_check, debug); TimingArcSetArcIterator check_arc_iter(setup_check); if (check_arc_iter.hasNext()) { TimingArc *check_arc = check_arc_iter.next(); RiseFall *en_rf = latch_enable->enableTransition(); RiseFall *check_rf = check_arc->fromTrans()->asRiseFall(); if (check_rf == en_rf) { report->warn(4, "cell %s/%s %s -> %s latch enable %s_edge timing arc is inconsistent with %s -> %s setup_%s check.", library_->name(), name_, en->name(), q->name(), en_rf == RiseFall::rise()?"rising":"falling", en->name(), d->name(), check_rf==RiseFall::rise()?"rising":"falling"); } FuncExpr *en_func = latch_enable->enableFunc(); if (en_func) { TimingSense en_sense = en_func->portTimingSense(en); if (en_sense == TimingSense::positive_unate && en_rf != RiseFall::rise()) report->warn(5, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.", library_->name(), name_, en->name(), q->name(), en_rf == RiseFall::rise()?"rising":"falling"); else if (en_sense == TimingSense::negative_unate && en_rf != RiseFall::fall()) report->warn(6, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense.", library_->name(), name_, en->name(), q->name(), en_rf == RiseFall::rise()?"rising":"falling"); } break; } } } } } } } } } FuncExpr * LibertyCell::findLatchEnableFunc(LibertyPort *data, LibertyPort *enable) const { for (auto seq : sequentials_) { if (seq->isLatch() && seq->data() && seq->data()->hasPort(data) && seq->clock() && seq->clock()->hasPort(enable)) return seq->clock(); } return nullptr; } LatchEnable * LibertyCell::makeLatchEnable(LibertyPort *d, LibertyPort *en, LibertyPort *q, TimingArcSet *d_to_q, TimingArcSet *en_to_q, TimingArcSet *setup_check, Debug *debug) { RiseFall *en_rf = en_to_q->isRisingFallingEdge(); FuncExpr *en_func = findLatchEnableFunc(d, en); LatchEnable *latch_enable = new LatchEnable(d, en, en_rf, en_func, q, d_to_q, en_to_q, setup_check); // Multiple enables for D->Q pairs are not supported. if (latch_d_to_q_map_[d_to_q]) delete latch_d_to_q_map_[d_to_q]; latch_d_to_q_map_[d_to_q] = latch_enable; latch_check_map_[setup_check] = latch_enable; latch_data_ports_.insert(d); debugPrint(debug, "liberty", 2, "latch d=%s en=%s q=%s", d->name(), en->name(), q->name()); return latch_enable; } void LibertyCell::inferLatchRoles(Debug *debug) { if (hasInferedRegTimingArcs()) { // Hunt down potential latch D/EN/Q triples. LatchEnableSet latch_enables; LibertyCellTimingArcSetIterator set_iter(this); while (set_iter.hasNext()) { TimingArcSet *en_to_q = set_iter.next(); // Locate potential d->q arcs from reg clk->q arcs. if (en_to_q->role() == TimingRole::regClkToQ()) { LibertyPort *en = en_to_q->from(); LibertyPort *q = en_to_q->to(); LibertyCellTimingArcSetIterator to_iter(this, nullptr, q); while (to_iter.hasNext()) { TimingArcSet *d_to_q = to_iter.next(); // Look for combinational d->q arcs. TimingRole *d_to_q_role = d_to_q->role(); if ((d_to_q_role == TimingRole::combinational() && ((d_to_q->arcCount() == 2 && (d_to_q->sense() == TimingSense::positive_unate || d_to_q->sense() == TimingSense::negative_unate)) || (d_to_q->arcCount() == 4))) // Previously identified as D->Q arc. || d_to_q_role == TimingRole::latchDtoQ()) { LibertyPort *d = d_to_q->from(); // Check for setup check from en -> d. LibertyCellTimingArcSetIterator check_iter(this, en, d); while (check_iter.hasNext()) { TimingArcSet *setup_check = check_iter.next(); if (setup_check->role() == TimingRole::setup()) { makeLatchEnable(d, en, q, d_to_q, en_to_q, setup_check, debug); d_to_q->setRole(TimingRole::latchDtoQ()); en_to_q->setRole(TimingRole::latchEnToQ()); } } } } } } } } bool LibertyCell::isLatchData(LibertyPort *port) { return latch_data_ports_.hasKey(port); } void LibertyCell::latchEnable(TimingArcSet *d_to_q_set, // Return values. LibertyPort *&enable_port, FuncExpr *&enable_func, RiseFall *&enable_rf) const { enable_port = nullptr; LatchEnable *latch_enable = latch_d_to_q_map_.findKey(d_to_q_set); if (latch_enable) { enable_port = latch_enable->enable(); enable_func = latch_enable->enableFunc(); enable_rf = latch_enable->enableTransition(); } } RiseFall * LibertyCell::latchCheckEnableTrans(TimingArcSet *check_set) { LatchEnable *latch_enable = latch_check_map_.findKey(check_set); if (latch_enable) return latch_enable->enableTransition(); else return nullptr; } //////////////////////////////////////////////////////////////// LibertyCellPortIterator::LibertyCellPortIterator(const LibertyCell *cell) : iter_(cell->ports_) { } bool LibertyCellPortIterator::hasNext() { return iter_.hasNext(); } LibertyPort * LibertyCellPortIterator::next() { return static_cast(iter_.next()); } //////////////////////////////////////////////////////////////// LibertyCellPortBitIterator::LibertyCellPortBitIterator(const LibertyCell *cell): iter_(cell->portBitIterator()) { } LibertyCellPortBitIterator::~LibertyCellPortBitIterator() { delete iter_; } bool LibertyCellPortBitIterator::hasNext() { return iter_->hasNext(); } LibertyPort * LibertyCellPortBitIterator::next() { return static_cast(iter_->next()); } //////////////////////////////////////////////////////////////// LibertyPort::LibertyPort(LibertyCell *cell, const char *name, bool is_bus, int from_index, int to_index, bool is_bundle, ConcretePortSeq *members) : ConcretePort(cell, name, is_bus, from_index, to_index, is_bundle, members), liberty_cell_(cell), function_(nullptr), tristate_enable_(nullptr), scaled_ports_(nullptr), fanout_load_(0.0), fanout_load_exists_(false), min_period_(0.0), pulse_clk_trigger_(nullptr), pulse_clk_sense_(nullptr), related_ground_pin_(nullptr), related_power_pin_(nullptr), min_pulse_width_exists_(false), min_period_exists_(false), is_clk_(false), is_reg_clk_(false), is_check_clk_(false), is_clk_gate_clk_pin_(false), is_clk_gate_enable_pin_(false), is_clk_gate_out_pin_(false), is_pll_feedback_pin_(false), is_disabled_constraint_(false) { liberty_port_ = this; min_pulse_width_[RiseFall::riseIndex()] = 0.0; min_pulse_width_[RiseFall::fallIndex()] = 0.0; } LibertyPort::~LibertyPort() { if (function_) function_->deleteSubexprs(); if (tristate_enable_) tristate_enable_->deleteSubexprs(); delete scaled_ports_; stringDelete(related_ground_pin_); stringDelete(related_power_pin_); } void LibertyPort::setDirection(PortDirection *dir) { ConcretePort::setDirection(dir); if (dir->isInternal()) liberty_cell_->setHasInternalPorts(true); } LibertyPort * LibertyPort::findLibertyMember(int index) const { return static_cast(findMember(index)); } LibertyPort * LibertyPort::findLibertyBusBit(int index) const { return static_cast(findBusBit(index)); } void LibertyPort::setCapacitance(float cap) { setCapacitance(RiseFall::rise(), MinMax::min(), cap); setCapacitance(RiseFall::fall(), MinMax::min(), cap); setCapacitance(RiseFall::rise(), MinMax::max(), cap); setCapacitance(RiseFall::fall(), MinMax::max(), cap); } void LibertyPort::setCapacitance(const RiseFall *rf, const MinMax *min_max, float cap) { capacitance_.setValue(rf, min_max, cap); if (hasMembers()) { LibertyPortMemberIterator member_iter(this); while (member_iter.hasNext()) { LibertyPort *port_bit = member_iter.next(); port_bit->setCapacitance(rf, min_max, cap); } } } float LibertyPort::capacitance() const { float cap; bool exists; capacitance_.maxValue(cap, exists); if (exists) return cap; else return 0.0; } float LibertyPort::capacitance(const RiseFall *rf, const MinMax *min_max) const { float cap; bool exists; capacitance_.value(rf, min_max, cap, exists); if (exists) return cap; else return 0.0; } void LibertyPort::capacitance(const RiseFall *rf, const MinMax *min_max, // Return values. float &cap, bool &exists) const { capacitance_.value(rf, min_max, cap, exists); } float LibertyPort::capacitance(const RiseFall *rf, const MinMax *min_max, const OperatingConditions *op_cond, const Pvt *pvt) const { if (scaled_ports_) { LibertyPort *scaled_port = (*scaled_ports_)[op_cond]; // Scaled capacitance is not derated because scale factors are wrt // nominal pvt. if (scaled_port) return scaled_port->capacitance(rf, min_max); } LibertyLibrary *lib = liberty_cell_->libertyLibrary(); float cap = capacitance(rf, min_max); return cap * lib->scaleFactor(ScaleFactorType::pin_cap, liberty_cell_, pvt); } bool LibertyPort::capacitanceIsOneValue() const { return capacitance_.isOneValue(); } //////////////////////////////////////////////////////////////// float LibertyPort::driveResistance() const { return driveResistance(nullptr, MinMax::max()); } // Min/max "drive" for all cell timing arcs. float LibertyPort::driveResistance(const RiseFall *rf, const MinMax *min_max) const { float max_drive = min_max->initValue(); bool found_drive = false; LibertyCellTimingArcSetIterator set_iter(liberty_cell_, nullptr, this); while (set_iter.hasNext()) { TimingArcSet *set = set_iter.next(); if (!set->role()->isTimingCheck()) { TimingArcSetArcIterator arc_iter(set); while (arc_iter.hasNext()) { TimingArc *arc = arc_iter.next(); if (rf == nullptr || arc->toTrans()->asRiseFall() == rf) { float drive = arc->driveResistance(); if (drive > 0.0) { if (min_max->compare(drive, max_drive)) max_drive = drive; found_drive = true; } } } } } if (found_drive) return max_drive; else return 0.0; } ArcDelay LibertyPort::intrinsicDelay(const StaState *sta) const { return intrinsicDelay(nullptr, MinMax::max(), sta); } ArcDelay LibertyPort::intrinsicDelay(const RiseFall *rf, const MinMax *min_max, const StaState *sta) const { ArcDelay max_delay = min_max->initValue(); bool found_delay = false; LibertyCellTimingArcSetIterator set_iter(liberty_cell_, nullptr, this); while (set_iter.hasNext()) { TimingArcSet *set = set_iter.next(); if (!set->role()->isTimingCheck()) { TimingArcSetArcIterator arc_iter(set); while (arc_iter.hasNext()) { TimingArc *arc = arc_iter.next(); if (rf == nullptr || arc->toTrans()->asRiseFall() == rf) { ArcDelay delay = arc->intrinsicDelay(); if (delayGreater(delay, 0.0, sta)) { if (delayGreater(delay, max_delay, min_max, sta)) max_delay = delay; found_delay = true; } } } } } if (found_delay) return max_delay; else return 0.0; } //////////////////////////////////////////////////////////////// void LibertyPort::setFunction(FuncExpr *func) { function_ = func; if (is_bus_ || is_bundle_) { LibertyPortMemberIterator member_iter(this); int bit_offset = 0; while (member_iter.hasNext()) { LibertyPort *port_bit = member_iter.next(); FuncExpr *sub_expr = (func) ? func->bitSubExpr(bit_offset) : nullptr; port_bit->setFunction(sub_expr); bit_offset++; } } } void LibertyPort::setTristateEnable(FuncExpr *enable) { tristate_enable_ = enable; if (hasMembers()) { LibertyPortMemberIterator member_iter(this); while (member_iter.hasNext()) { LibertyPort *port_bit = member_iter.next(); FuncExpr *sub_expr = (enable) ? enable->bitSubExpr(port_bit->busBitIndex()) : nullptr; port_bit->setTristateEnable(sub_expr); } } } void LibertyPort::slewLimit(const MinMax *min_max, // Return values. float &limit, bool &exists) const { slew_limit_.value(min_max, limit, exists); } void LibertyPort::setSlewLimit(float slew, const MinMax *min_max) { slew_limit_.setValue(min_max, slew); } void LibertyPort::capacitanceLimit(const MinMax *min_max, // Return values. float &limit, bool &exists) const { return cap_limit_.value(min_max, limit, exists); } void LibertyPort::setCapacitanceLimit(float cap, const MinMax *min_max) { cap_limit_.setValue(min_max, cap); } void LibertyPort::fanoutLoad(// Return values. float &fanout_load, bool &exists) const { fanout_load = fanout_load_; exists = fanout_load_exists_; } void LibertyPort::setFanoutLoad(float fanout_load) { fanout_load_ = fanout_load; fanout_load_exists_ = true; } void LibertyPort::fanoutLimit(const MinMax *min_max, // Return values. float &limit, bool &exists) const { return fanout_limit_.value(min_max, limit, exists); } void LibertyPort::setFanoutLimit(float fanout, const MinMax *min_max) { fanout_limit_.setValue(min_max, fanout); } void LibertyPort::minPeriod(const OperatingConditions *op_cond, const Pvt *pvt, float &min_period, bool &exists) const { if (scaled_ports_) { LibertyPort *scaled_port = (*scaled_ports_)[op_cond]; if (scaled_port) { scaled_port->minPeriod(min_period, exists); return; } } LibertyLibrary *lib = liberty_cell_->libertyLibrary(); min_period = min_period_ * lib->scaleFactor(ScaleFactorType::min_period, liberty_cell_, pvt); exists = min_period_exists_; } void LibertyPort::minPeriod(float &min_period, bool &exists) const { min_period = min_period_; exists = min_period_exists_; } void LibertyPort::setMinPeriod(float min_period) { min_period_ = min_period; min_period_exists_ = true; } void LibertyPort::minPulseWidth(const RiseFall *hi_low, const OperatingConditions *op_cond, const Pvt *pvt, float &min_width, bool &exists) const { if (scaled_ports_) { LibertyPort *scaled_port = (*scaled_ports_)[op_cond]; if (scaled_port) { scaled_port->minPulseWidth(hi_low, min_width, exists); return; } } int hi_low_index = hi_low->index(); LibertyLibrary *lib = liberty_cell_->libertyLibrary(); min_width = min_pulse_width_[hi_low_index] * lib->scaleFactor(ScaleFactorType::min_pulse_width, hi_low_index, liberty_cell_, pvt); exists = min_pulse_width_exists_ & (1 << hi_low_index); } void LibertyPort::minPulseWidth(const RiseFall *hi_low, float &min_width, bool &exists) const { int hi_low_index = hi_low->index(); min_width = min_pulse_width_[hi_low_index]; exists = min_pulse_width_exists_ & (1 << hi_low_index); } void LibertyPort::setMinPulseWidth(RiseFall *hi_low, float min_width) { int hi_low_index = hi_low->index(); min_pulse_width_[hi_low_index] = min_width; min_pulse_width_exists_ |= (1 << hi_low_index); } bool LibertyPort::equiv(const LibertyPort *port1, const LibertyPort *port2) { return (port1 == nullptr && port2 == nullptr) || (port1 != nullptr && port2 != nullptr && stringEq(port1->name(), port2->name()) && port1->direction() == port2->direction()); } bool LibertyPort::less(const LibertyPort *port1, const LibertyPort *port2) { const char *name1 = port1->name(); const char *name2 = port2->name(); if (stringEq(name1, name2)) { PortDirection *dir1 = port1->direction(); PortDirection *dir2 = port2->direction(); if (dir1 == dir2) { } else return dir1->index() < dir2->index(); } return stringLess(name1, name2); } void LibertyPort::addScaledPort(OperatingConditions *op_cond, LibertyPort *scaled_port) { if (scaled_ports_ == nullptr) scaled_ports_ = new ScaledPortMap; (*scaled_ports_)[op_cond] = scaled_port; } bool LibertyPort::isClock() const { return is_clk_; } void LibertyPort::setIsClock(bool is_clk) { is_clk_ = is_clk; } void LibertyPort::setIsRegClk(bool is_clk) { is_reg_clk_ = is_clk; } void LibertyPort::setIsCheckClk(bool is_clk) { is_check_clk_ = is_clk; } void LibertyPort::setIsClockGateClockPin(bool is_clk_gate_clk) { is_clk_gate_clk_pin_ = is_clk_gate_clk; } void LibertyPort::setIsClockGateEnablePin(bool is_clk_gate_enable) { is_clk_gate_enable_pin_ = is_clk_gate_enable; } void LibertyPort::setIsClockGateOutPin(bool is_clk_gate_out) { is_clk_gate_out_pin_ = is_clk_gate_out; } void LibertyPort::setIsPllFeedbackPin(bool is_pll_feedback_pin) { is_pll_feedback_pin_ = is_pll_feedback_pin; } void LibertyPort::setPulseClk(RiseFall *trigger, RiseFall *sense) { pulse_clk_trigger_ = trigger; pulse_clk_sense_ = sense; } void LibertyPort::setIsDisabledConstraint(bool is_disabled) { is_disabled_constraint_ = is_disabled; } LibertyPort * LibertyPort::cornerPort(int ap_index) { if (ap_index < static_cast(corner_ports_.size())) { LibertyPort *corner_port = corner_ports_[ap_index]; if (corner_port) return corner_port; } return this; } const LibertyPort * LibertyPort::cornerPort(int ap_index) const { if (ap_index < static_cast(corner_ports_.size())) { LibertyPort *corner_port = corner_ports_[ap_index]; if (corner_port) return corner_port; } return this; } void LibertyPort::setCornerPort(LibertyPort *corner_port, int ap_index) { if (ap_index >= static_cast(corner_ports_.size())) corner_ports_.resize(ap_index + 1); corner_ports_[ap_index] = corner_port; } void LibertyPort::setRelatedGroundPin(const char *related_ground_pin) { related_ground_pin_ = stringCopy(related_ground_pin); } void LibertyPort::setRelatedPowerPin(const char *related_power_pin) { related_power_pin_ = stringCopy(related_power_pin); } //////////////////////////////////////////////////////////////// void sortLibertyPortSet(LibertyPortSet *set, LibertyPortSeq &ports) { LibertyPortSet::Iterator port_iter(set); while (port_iter.hasNext()) ports.push_back(port_iter.next()); sort(ports, LibertyPortNameLess()); } bool LibertyPortNameLess::operator()(const LibertyPort *port1, const LibertyPort *port2) const { return stringLess(port1->name(), port2->name()); } bool LibertyPortPairLess::operator()(const LibertyPortPair *pair1, const LibertyPortPair *pair2) const { return pair1->first < pair2->first || (pair1->first == pair2->first && pair1->second < pair2->second); } bool LibertyPortPairLess::operator()(const LibertyPortPair &pair1, const LibertyPortPair &pair2) const { return pair1.first < pair2.first || (pair1.first == pair2.first && pair1.second < pair2.second); } //////////////////////////////////////////////////////////////// LibertyPortMemberIterator::LibertyPortMemberIterator(const LibertyPort *port) : iter_(port->memberIterator()) { } LibertyPortMemberIterator::~LibertyPortMemberIterator() { delete iter_; } bool LibertyPortMemberIterator::hasNext() { return iter_->hasNext(); } LibertyPort * LibertyPortMemberIterator::next() { return static_cast(iter_->next()); } //////////////////////////////////////////////////////////////// BusDcl::BusDcl(const char *name, int from, int to) : name_(stringCopy(name)), from_(from), to_(to) { } BusDcl::~BusDcl() { stringDelete(name_); } //////////////////////////////////////////////////////////////// ModeDef::ModeDef(const char *name) : name_(stringCopy(name)) { } ModeDef::~ModeDef() { values_.deleteContents(); stringDelete(name_); } ModeValueDef * ModeDef::defineValue(const char *value, FuncExpr *cond, const char *sdf_cond) { ModeValueDef *val_def = new ModeValueDef(value, cond, sdf_cond); values_[val_def->value()] = val_def; return val_def; } ModeValueDef * ModeDef::findValueDef(const char *value) { return values_[value]; } //////////////////////////////////////////////////////////////// ModeValueDef::ModeValueDef(const char *value, FuncExpr *cond, const char *sdf_cond) : value_(stringCopy(value)), cond_(cond), sdf_cond_(stringCopy(sdf_cond)) { } ModeValueDef::~ModeValueDef() { stringDelete(value_); if (cond_) cond_->deleteSubexprs(); if (sdf_cond_) stringDelete(sdf_cond_); } void ModeValueDef::setSdfCond(const char *sdf_cond) { sdf_cond_ = stringCopy(sdf_cond); } //////////////////////////////////////////////////////////////// TableTemplate::TableTemplate(const char *name) : name_(stringCopy(name)), axis1_(nullptr), axis2_(nullptr), axis3_(nullptr) { } TableTemplate::TableTemplate(const char *name, TableAxis *axis1, TableAxis *axis2, TableAxis *axis3) : name_(stringCopy(name)), axis1_(axis1), axis2_(axis2), axis3_(axis3) { } TableTemplate::~TableTemplate() { stringDelete(name_); delete axis1_; delete axis2_; delete axis3_; } void TableTemplate::setName(const char *name) { stringDelete(name_); name_ = stringCopy(name); } void TableTemplate::setAxis1(TableAxis *axis) { axis1_ = axis; } void TableTemplate::setAxis2(TableAxis *axis) { axis2_ = axis; } void TableTemplate::setAxis3(TableAxis *axis) { axis3_ = axis; } //////////////////////////////////////////////////////////////// Pvt::Pvt(float process, float voltage, float temperature) : process_(process), voltage_(voltage), temperature_(temperature) { } void Pvt::setProcess(float process) { process_ = process; } void Pvt::setVoltage(float voltage) { voltage_ = voltage; } void Pvt::setTemperature(float temp) { temperature_ = temp; } OperatingConditions::OperatingConditions(const char *name) : Pvt(0.0, 0.0, 0.0), name_(stringCopy(name)), // Default wireload tree. wire_load_tree_(WireloadTree::balanced) { } OperatingConditions::OperatingConditions(const char *name, float process, float voltage, float temperature, WireloadTree wire_load_tree) : Pvt(process, voltage, temperature), name_(stringCopy(name)), wire_load_tree_(wire_load_tree) { } OperatingConditions::~OperatingConditions() { stringDelete(name_); } void OperatingConditions::setWireloadTree(WireloadTree tree) { wire_load_tree_ = tree; } //////////////////////////////////////////////////////////////// static EnumNameMap scale_factor_type_map = {{ScaleFactorType::pin_cap, "pin_cap"}, {ScaleFactorType::wire_cap, "wire_res"}, {ScaleFactorType::min_period, "min_period"}, {ScaleFactorType::cell, "cell"}, {ScaleFactorType::hold, "hold"}, {ScaleFactorType::setup, "setup"}, {ScaleFactorType::recovery, "recovery"}, {ScaleFactorType::removal, "removal"}, {ScaleFactorType::nochange, "nochange"}, {ScaleFactorType::skew, "skew"}, {ScaleFactorType::leakage_power, "leakage_power"}, {ScaleFactorType::internal_power, "internal_power"}, {ScaleFactorType::transition, "transition"}, {ScaleFactorType::min_pulse_width, "min_pulse_width"}, {ScaleFactorType::unknown, "unknown"} }; const char * scaleFactorTypeName(ScaleFactorType type) { return scale_factor_type_map.find(type); } ScaleFactorType findScaleFactorType(const char *name) { return scale_factor_type_map.find(name, ScaleFactorType::unknown); } bool scaleFactorTypeRiseFallSuffix(ScaleFactorType type) { return type == ScaleFactorType::cell || type == ScaleFactorType::hold || type == ScaleFactorType::setup || type == ScaleFactorType::recovery || type == ScaleFactorType::removal || type == ScaleFactorType::nochange || type == ScaleFactorType::skew; } bool scaleFactorTypeRiseFallPrefix(ScaleFactorType type) { return type == ScaleFactorType::transition; } bool scaleFactorTypeLowHighSuffix(ScaleFactorType type) { return type == ScaleFactorType::min_pulse_width; } //////////////////////////////////////////////////////////////// EnumNameMap scale_factor_pvt_names = {{ScaleFactorPvt::process, "process"}, {ScaleFactorPvt::volt, "volt"}, {ScaleFactorPvt::temp, "temp"} }; ScaleFactorPvt findScaleFactorPvt(const char *name) { return scale_factor_pvt_names.find(name, ScaleFactorPvt::unknown); } const char * scaleFactorPvtName(ScaleFactorPvt pvt) { return scale_factor_pvt_names.find(pvt); } //////////////////////////////////////////////////////////////// ScaleFactors::ScaleFactors(const char *name) : name_(stringCopy(name)) { for (int type = 0; type < scale_factor_type_count; type++) { for (int pvt = 0; pvt < int(ScaleFactorPvt::count); pvt++) { for (auto tr_index : RiseFall::rangeIndex()) { scales_[type][pvt][tr_index] = 0.0; } } } } ScaleFactors::~ScaleFactors() { stringDelete(name_); } void ScaleFactors::setScale(ScaleFactorType type, ScaleFactorPvt pvt, RiseFall *rf, float scale) { scales_[int(type)][int(pvt)][rf->index()] = scale; } void ScaleFactors::setScale(ScaleFactorType type, ScaleFactorPvt pvt, float scale) { scales_[int(type)][int(pvt)][0] = scale; } float ScaleFactors::scale(ScaleFactorType type, ScaleFactorPvt pvt, RiseFall *rf) { return scales_[int(type)][int(pvt)][rf->index()]; } float ScaleFactors::scale(ScaleFactorType type, ScaleFactorPvt pvt, int tr_index) { return scales_[int(type)][int(pvt)][tr_index]; } float ScaleFactors::scale(ScaleFactorType type, ScaleFactorPvt pvt) { return scales_[int(type)][int(pvt)][0]; } void ScaleFactors::print() { printf("%10s", " "); for (int pvt_index = 0; pvt_index < int(ScaleFactorPvt::count); pvt_index++) { ScaleFactorPvt pvt = (ScaleFactorPvt) pvt_index; printf("%10s", scaleFactorPvtName(pvt)); } printf("\n"); for (int type_index = 0; type_index < scale_factor_type_count; type_index++) { ScaleFactorType type = (ScaleFactorType) type_index; printf("%10s ", scaleFactorTypeName(type)); for (int pvt_index = 0; pvt_index < int(ScaleFactorPvt::count); pvt_index++) { if (scaleFactorTypeRiseFallSuffix(type) || scaleFactorTypeRiseFallPrefix(type) || scaleFactorTypeLowHighSuffix(type)) { printf(" %.3f,%.3f", scales_[type_index][pvt_index][RiseFall::riseIndex()], scales_[type_index][pvt_index][RiseFall::fallIndex()]); } else { printf(" %.3f", scales_[type_index][pvt_index][0]); } } printf("\n"); } } TestCell::TestCell(LibertyPort *data_in, LibertyPort *scan_in, LibertyPort *scan_enable, LibertyPort *scan_out, LibertyPort *scan_out_inv) : data_in_(data_in), scan_in_(scan_in), scan_enable_(scan_enable), scan_out_(scan_out), scan_out_inv_(scan_out_inv) { } TestCell::TestCell() : data_in_(nullptr), scan_in_(nullptr), scan_enable_(nullptr), scan_out_(nullptr), scan_out_inv_(nullptr) { } void TestCell::setDataIn(LibertyPort *port) { data_in_ = port; } void TestCell::setScanIn(LibertyPort *port) { scan_in_ = port; } void TestCell::setScanEnable(LibertyPort *port) { scan_enable_ = port; } void TestCell::setScanOut(LibertyPort *port) { scan_out_ = port; } void TestCell::setScanOutInv(LibertyPort *port) { scan_out_inv_ = port; } //////////////////////////////////////////////////////////////// OcvDerate::OcvDerate(const char *name) : name_(name) { for (auto el_index : EarlyLate::rangeIndex()) { for (auto tr_index : RiseFall::rangeIndex()) { derate_[tr_index][el_index][int(PathType::clk)] = nullptr; derate_[tr_index][el_index][int(PathType::data)] = nullptr; } } } OcvDerate::~OcvDerate() { stringDelete(name_); // Derating table models can be shared in multiple places in derate_; // Collect them in a set to avoid duplicate deletes. Set models; for (auto el_index : EarlyLate::rangeIndex()) { for (auto tr_index : RiseFall::rangeIndex()) { Table *derate; derate = derate_[tr_index][el_index][int(PathType::clk)]; if (derate) models.insert(derate); derate = derate_[tr_index][el_index][int(PathType::data)]; if (derate) models.insert(derate); } } Set::Iterator model_iter(models); while (model_iter.hasNext()) { Table *model = model_iter.next(); delete model; } } Table * OcvDerate::derateTable(const RiseFall *rf, const EarlyLate *early_late, PathType path_type) { return derate_[rf->index()][early_late->index()][int(path_type)]; } void OcvDerate::setDerateTable(const RiseFall *rf, const EarlyLate *early_late, const PathType path_type, Table *derate) { derate_[rf->index()][early_late->index()][int(path_type)] = derate; } //////////////////////////////////////////////////////////////// LibertyPgPort::LibertyPgPort(const char *name, LibertyCell *cell) : name_(stringCopy(name)), pg_type_(unknown), voltage_name_(nullptr), cell_(cell) { } LibertyPgPort::~LibertyPgPort() { stringDelete(name_); stringDelete(voltage_name_); } void LibertyPgPort::setPgType(PgType type) { pg_type_ = type; } void LibertyPgPort::setVoltageName(const char *voltage_name) { voltage_name_ = stringCopy(voltage_name); } bool LibertyPgPort::equiv(const LibertyPgPort *port1, const LibertyPgPort *port2) { return stringEq(port1->name_, port2->name_) && port1->pg_type_ == port2->pg_type_; } //////////////////////////////////////////////////////////////// LibertyCellPgPortIterator::LibertyCellPgPortIterator(const LibertyCell *cell) : iter_(const_cast(cell)->pg_port_map_) { } bool LibertyCellPgPortIterator::hasNext() { return iter_.hasNext(); } LibertyPgPort * LibertyCellPgPortIterator::next() { const char *name; LibertyPgPort *port; iter_.next(name, port); return port; } } // namespace