2751 lines
63 KiB
C++
2751 lines
63 KiB
C++
// OpenSTA, Static Timing Analyzer
|
|
// Copyright (c) 2018, 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 <https://www.gnu.org/licenses/>.
|
|
|
|
#include "Machine.hh"
|
|
#include "DisallowCopyAssign.hh"
|
|
#include "Report.hh"
|
|
#include "Debug.hh"
|
|
#include "Error.hh"
|
|
#include "StringUtil.hh"
|
|
#include "StringSet.hh"
|
|
#include "PatternMatch.hh"
|
|
#include "Units.hh"
|
|
#include "PortDirection.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 "Liberty.hh"
|
|
|
|
namespace sta {
|
|
|
|
typedef Set<TimingModel*> TimingModelSet;
|
|
typedef Set<FuncExpr*> FuncExprSet;
|
|
typedef Set<LatchEnable*> LatchEnableSet;
|
|
|
|
bool LibertyLibrary::found_rise_fall_caps_ = false;
|
|
const float LibertyLibrary::input_threshold_default_ = .5F;
|
|
const float LibertyLibrary::output_threshold_default_ = .5F;
|
|
const float LibertyLibrary::slew_lower_threshold_default_ = .2F;
|
|
const float LibertyLibrary::slew_upper_threshold_default_ = .8F;
|
|
|
|
void
|
|
makeScaleFactorTypeMap();
|
|
void
|
|
deleteScaleFactorTypeMap();
|
|
|
|
void
|
|
initLiberty()
|
|
{
|
|
makeTimingTypeMap();
|
|
makeScaleFactorTypeMap();
|
|
TimingArcSet::init();
|
|
}
|
|
|
|
void
|
|
deleteLiberty()
|
|
{
|
|
deleteTimingTypeMap();
|
|
deleteScaleFactorTypeMap();
|
|
TimingArcSet::destroy();
|
|
}
|
|
|
|
LibertyLibrary::LibertyLibrary(const char *name,
|
|
const char *filename) :
|
|
ConcreteLibrary(name, filename),
|
|
units_(new Units()),
|
|
delay_model_type_(delay_model_cmos_linear), // default
|
|
nominal_process_(0.0),
|
|
nominal_voltage_(0.0),
|
|
nominal_temperature_(0.0),
|
|
scale_factors_(NULL),
|
|
default_input_pin_cap_(0.0),
|
|
default_output_pin_cap_(0.0),
|
|
default_bidirect_pin_cap_(0.0),
|
|
default_fanout_load_(0.0),
|
|
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_(NULL),
|
|
default_wire_load_mode_(wire_load_mode_unknown),
|
|
default_wire_load_selection_(NULL),
|
|
default_operating_conditions_(NULL),
|
|
equiv_cell_map_(NULL),
|
|
ocv_arc_depth_(0.0),
|
|
default_ocv_derate_(NULL)
|
|
{
|
|
// Scalar templates are builtin.
|
|
TableTemplate *scalar_template = new TableTemplate("scalar", NULL, NULL, NULL);
|
|
addTableTemplate(scalar_template);
|
|
|
|
TransRiseFallIterator tr_iter;
|
|
while (tr_iter.hasNext()) {
|
|
TransRiseFall *tr = tr_iter.next();
|
|
int tr_index = tr->index();
|
|
wire_slew_degradation_tbls_[tr_index] = NULL;
|
|
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();
|
|
templates_.deleteContents();
|
|
scale_factors_map_.deleteContents();
|
|
delete scale_factors_;
|
|
|
|
TransRiseFallIterator tr_iter;
|
|
while (tr_iter.hasNext()) {
|
|
TransRiseFall *tr = tr_iter.next();
|
|
int tr_index = tr->index();
|
|
TableModel *model = wire_slew_degradation_tbls_[tr_index];
|
|
delete model;
|
|
}
|
|
operating_conditions_.deleteContents();
|
|
wireloads_.deleteContents();
|
|
wire_load_selections_.deleteContents();
|
|
if (equiv_cell_map_)
|
|
deleteEquivCellMap(equiv_cell_map_);
|
|
delete units_;
|
|
ocv_derate_map_.deleteContents();
|
|
}
|
|
|
|
LibertyCell *
|
|
LibertyLibrary::findLibertyCell(const char *name) const
|
|
{
|
|
return dynamic_cast<LibertyCell*>(findCell(name));
|
|
}
|
|
|
|
void
|
|
LibertyLibrary::findLibertyCellsMatching(PatternMatch *pattern,
|
|
LibertyCellSeq *cells)
|
|
{
|
|
LibertyLibraryCellIterator cell_iter(this);
|
|
while (cell_iter.hasNext()) {
|
|
LibertyCell *cell = cell_iter.next();
|
|
if (pattern->match(cell->name()))
|
|
cells->push_back(cell);
|
|
}
|
|
}
|
|
|
|
LibertyLibraryCellIterator *
|
|
LibertyLibrary::libertyCellIterator() const
|
|
{
|
|
return new LibertyLibraryCellIterator(this);
|
|
}
|
|
|
|
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)
|
|
{
|
|
templates_[tbl_template->name()] = tbl_template;
|
|
}
|
|
|
|
void
|
|
LibertyLibrary::deleteTableTemplate(TableTemplate *tbl_template)
|
|
{
|
|
templates_.eraseKey(tbl_template->name());
|
|
}
|
|
|
|
TableTemplate *
|
|
LibertyLibrary::findTableTemplate(const char *name)
|
|
{
|
|
return templates_[name];
|
|
}
|
|
|
|
TableTemplateIterator *
|
|
LibertyLibrary::tableTemplateIterator()
|
|
{
|
|
return new TableTemplateIterator(templates_);
|
|
}
|
|
|
|
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, NULL, 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 == NULL)
|
|
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 = NULL;
|
|
// Cell level scale factors have precidence over library scale factors.
|
|
if (cell)
|
|
scale_factors = cell->scaleFactors();
|
|
if (scale_factors == NULL)
|
|
scale_factors = scale_factors_;
|
|
if (scale_factors) {
|
|
float process_scale = 1.0F + (pvt->process() - nominal_process_)
|
|
* scale_factors->scale(type, scale_factor_process, tr_index);
|
|
float temp_scale = 1.0F + (pvt->temperature() - nominal_temperature_)
|
|
* scale_factors->scale(type, scale_factor_temp, tr_index);
|
|
float volt_scale = 1.0F + (pvt->voltage() - nominal_voltage_)
|
|
* scale_factors->scale(type, scale_factor_volt, tr_index);
|
|
float scale = process_scale * temp_scale * volt_scale;
|
|
return scale;
|
|
}
|
|
}
|
|
return 1.0F;
|
|
}
|
|
|
|
void
|
|
LibertyLibrary::setWireSlewDegradationTable(TableModel *model,
|
|
TransRiseFall *tr)
|
|
{
|
|
int tr_index = tr->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 TransRiseFall *tr) const
|
|
{
|
|
return wire_slew_degradation_tbls_[tr->index()];
|
|
}
|
|
|
|
float
|
|
LibertyLibrary::degradeWireSlew(const LibertyCell *cell,
|
|
const TransRiseFall *tr,
|
|
const Pvt *pvt,
|
|
float in_slew,
|
|
float wire_delay) const
|
|
{
|
|
const TableModel *model = wireSlewDegradationTable(tr);
|
|
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 == table_axis_output_pin_transition)
|
|
return model->findValue(this, cell, pvt, in_slew, 0.0, 0.0);
|
|
else if (var1 == table_axis_connect_delay)
|
|
return model->findValue(this, cell, pvt, wire_delay, 0.0, 0.0);
|
|
else {
|
|
internalError("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 == table_axis_output_pin_transition
|
|
&& var2 == table_axis_connect_delay)
|
|
return model->findValue(this, cell, pvt, in_slew, wire_delay, 0.0);
|
|
else if (var1 == table_axis_connect_delay
|
|
&& var2 == table_axis_output_pin_transition)
|
|
return model->findValue(this, cell, pvt, wire_delay, in_slew, 0.0);
|
|
else {
|
|
internalError("unsupported slew degradation table axes");
|
|
return 0.0;
|
|
}
|
|
}
|
|
default:
|
|
internalError("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 == table_axis_output_pin_transition
|
|
|| var1 == table_axis_connect_delay;
|
|
}
|
|
case 2: {
|
|
TableAxis *axis1 = table->axis1();
|
|
TableAxis *axis2 = table->axis2();
|
|
TableAxisVariable var1 = axis1->variable();
|
|
TableAxisVariable var2 = axis2->variable();
|
|
return (var1 == table_axis_output_pin_transition
|
|
&& var2 == table_axis_connect_delay)
|
|
|| (var1 == table_axis_connect_delay
|
|
&& var2 == table_axis_output_pin_transition);
|
|
}
|
|
default:
|
|
internalError("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::setDefaultFanoutLoad(float load)
|
|
{
|
|
default_fanout_load_ = load;
|
|
}
|
|
|
|
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 TransRiseFall *tr,
|
|
// Return values.
|
|
float &intrinsic,
|
|
bool &exists) const
|
|
{
|
|
default_intrinsic_.value(tr, intrinsic, exists);
|
|
}
|
|
|
|
void
|
|
LibertyLibrary::setDefaultIntrinsic(const TransRiseFall *tr,
|
|
float value)
|
|
{
|
|
default_intrinsic_.setValue(tr, value);
|
|
}
|
|
|
|
void
|
|
LibertyLibrary::defaultPinResistance(const TransRiseFall *tr,
|
|
const PortDirection *dir,
|
|
// Return values.
|
|
float &res,
|
|
bool &exists) const
|
|
{
|
|
if (dir->isAnyTristate())
|
|
defaultBidirectPinRes(tr, res, exists);
|
|
else
|
|
defaultOutputPinRes(tr, res, exists);
|
|
}
|
|
|
|
void
|
|
LibertyLibrary::defaultBidirectPinRes(const TransRiseFall *tr,
|
|
// Return values.
|
|
float &res,
|
|
bool &exists) const
|
|
{
|
|
return default_inout_pin_res_.value(tr, res, exists);
|
|
}
|
|
|
|
void
|
|
LibertyLibrary::setDefaultBidirectPinRes(const TransRiseFall *tr,
|
|
float value)
|
|
{
|
|
default_inout_pin_res_.setValue(tr, value);
|
|
}
|
|
|
|
void
|
|
LibertyLibrary::defaultOutputPinRes(const TransRiseFall *tr,
|
|
// Return values.
|
|
float &res,
|
|
bool &exists) const
|
|
{
|
|
default_output_pin_res_.value(tr, res, exists);
|
|
}
|
|
|
|
void
|
|
LibertyLibrary::setDefaultOutputPinRes(const TransRiseFall *tr,
|
|
float value)
|
|
{
|
|
default_output_pin_res_.setValue(tr, value);
|
|
}
|
|
|
|
LibertyCellSeq *
|
|
LibertyLibrary::findEquivCells(LibertyCell *cell)
|
|
{
|
|
if (equiv_cell_map_ == NULL)
|
|
equiv_cell_map_ = makeEquivCellMap(this);
|
|
LibertyCellSeq *equivs = equiv_cell_map_->findKey(cell);
|
|
return equivs;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
OperatingConditionsIterator *
|
|
LibertyLibrary::operatingConditionsIterator()
|
|
{
|
|
return new OperatingConditionsIterator(operating_conditions_);
|
|
}
|
|
|
|
OperatingConditions *
|
|
LibertyLibrary::defaultOperatingConditions() const
|
|
{
|
|
return default_operating_conditions_;
|
|
}
|
|
|
|
void
|
|
LibertyLibrary::setDefaultOperatingConditions(OperatingConditions *op_cond)
|
|
{
|
|
default_operating_conditions_ = op_cond;
|
|
}
|
|
|
|
float
|
|
LibertyLibrary::inputThreshold(const TransRiseFall *tr) const
|
|
{
|
|
return input_threshold_[tr->index()];
|
|
}
|
|
|
|
void
|
|
LibertyLibrary::setInputThreshold(const TransRiseFall *tr,
|
|
float th)
|
|
{
|
|
input_threshold_[tr->index()] = th;
|
|
}
|
|
|
|
float
|
|
LibertyLibrary::outputThreshold(const TransRiseFall *tr) const
|
|
{
|
|
return output_threshold_[tr->index()];
|
|
}
|
|
|
|
void
|
|
LibertyLibrary::setOutputThreshold(const TransRiseFall *tr,
|
|
float th)
|
|
{
|
|
output_threshold_[tr->index()] = th;
|
|
}
|
|
|
|
float
|
|
LibertyLibrary::slewLowerThreshold(const TransRiseFall *tr) const
|
|
{
|
|
return slew_lower_threshold_[tr->index()];
|
|
}
|
|
|
|
void
|
|
LibertyLibrary::setSlewLowerThreshold(const TransRiseFall *tr,
|
|
float th)
|
|
{
|
|
slew_lower_threshold_[tr->index()] = th;
|
|
}
|
|
|
|
float
|
|
LibertyLibrary::slewUpperThreshold(const TransRiseFall *tr) const
|
|
{
|
|
return slew_upper_threshold_[tr->index()];
|
|
}
|
|
|
|
void
|
|
LibertyLibrary::setSlewUpperThreshold(const TransRiseFall *tr,
|
|
float th)
|
|
{
|
|
slew_upper_threshold_[tr->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)
|
|
{
|
|
LibertyLibraryCellIterator *cell_iter = lib->libertyCellIterator();
|
|
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);
|
|
}
|
|
delete cell_iter;
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
link_cell->setCornerCell(corner_cell, ap_index);
|
|
|
|
LibertyCellPortBitIterator *port_iter = link_cell->libertyPortBitIterator();
|
|
while (port_iter->hasNext()) {
|
|
LibertyPort *link_port = port_iter->next();
|
|
const char *port_name = link_port->name();
|
|
LibertyPort *corner_port = corner_cell->findLibertyPort(port_name);
|
|
if (corner_port)
|
|
link_port->setCornerPort(corner_port, ap_index);
|
|
}
|
|
delete port_iter;
|
|
|
|
CellTimingArcSetIterator *set_iter = link_cell->timingArcSetIterator();
|
|
while (set_iter->hasNext()) {
|
|
TimingArcSet *link_arc_set = set_iter->next();
|
|
TimingArcSet *corner_arc_set = corner_cell->findTimingArcSet(link_arc_set);
|
|
if (corner_arc_set) {
|
|
TimingArcSetArcIterator *arc_iter = link_arc_set->timingArcIterator();
|
|
TimingArcSetArcIterator *corner_arc_iter =
|
|
corner_arc_set->timingArcIterator();
|
|
while (arc_iter->hasNext() && corner_arc_iter->hasNext()) {
|
|
TimingArc *link_arc = arc_iter->next();
|
|
TimingArc *corner_arc = corner_arc_iter->next();
|
|
if (TimingArc::equiv(link_arc, corner_arc))
|
|
link_arc->setCornerArc(corner_arc, ap_index);
|
|
}
|
|
delete arc_iter;
|
|
delete corner_arc_iter;
|
|
}
|
|
}
|
|
delete set_iter;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
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;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
LibertyLibraryCellIterator::LibertyLibraryCellIterator(const LibertyLibrary *
|
|
library):
|
|
iter_(library->cell_map_)
|
|
{
|
|
}
|
|
|
|
bool
|
|
LibertyLibraryCellIterator::hasNext()
|
|
{
|
|
return iter_.hasNext();
|
|
}
|
|
|
|
LibertyCell *
|
|
LibertyLibraryCellIterator::next()
|
|
{
|
|
return dynamic_cast<LibertyCell*>(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),
|
|
has_internal_ports_(false),
|
|
interface_timing_(false),
|
|
clock_gate_type_(clock_gate_none),
|
|
timing_arc_sets_(NULL),
|
|
port_timing_arc_set_map_(NULL),
|
|
timing_arc_set_from_map_(NULL),
|
|
timing_arc_set_to_map_(NULL),
|
|
has_infered_reg_timing_arcs_(false),
|
|
internal_powers_(NULL),
|
|
leakage_powers_(NULL),
|
|
sequentials_(NULL),
|
|
port_to_seq_map_(NULL),
|
|
mode_defs_(NULL),
|
|
scale_factors_(NULL),
|
|
scaled_cells_(NULL),
|
|
test_cell_(NULL),
|
|
ocv_arc_depth_(0.0),
|
|
ocv_derate_(NULL),
|
|
is_disabled_constraint_(false)
|
|
{
|
|
}
|
|
|
|
LibertyCell::~LibertyCell()
|
|
{
|
|
if (mode_defs_) {
|
|
mode_defs_->deleteContents();
|
|
delete mode_defs_;
|
|
}
|
|
|
|
latch_d_to_q_map_.deleteContents();
|
|
|
|
if (timing_arc_sets_) {
|
|
deleteTimingModels();
|
|
timing_arc_sets_->deleteContents();
|
|
delete timing_arc_sets_;
|
|
delete timing_arc_set_map_;
|
|
|
|
LibertyPortTimingArcSetMap::Iterator port_map_iter(port_timing_arc_set_map_);
|
|
while (port_map_iter.hasNext()) {
|
|
LibertyPortPair *port_pair;
|
|
TimingArcSetSeq *sets;
|
|
port_map_iter.next(port_pair, sets);
|
|
delete port_pair;
|
|
delete sets;
|
|
}
|
|
delete port_timing_arc_set_map_;
|
|
|
|
timing_arc_set_from_map_->deleteContents();
|
|
delete timing_arc_set_from_map_;
|
|
|
|
timing_arc_set_to_map_->deleteContents();
|
|
delete timing_arc_set_to_map_;
|
|
}
|
|
|
|
if (internal_powers_) {
|
|
internal_powers_->deleteContents();
|
|
delete internal_powers_;
|
|
}
|
|
|
|
if (leakage_powers_) {
|
|
leakage_powers_->deleteContents();
|
|
delete leakage_powers_;
|
|
}
|
|
|
|
if (sequentials_) {
|
|
sequentials_->deleteContents();
|
|
delete sequentials_;
|
|
delete port_to_seq_map_;
|
|
}
|
|
|
|
bus_dcls_.deleteContents();
|
|
|
|
if (scaled_cells_) {
|
|
scaled_cells_->deleteContents();
|
|
delete scaled_cells_;
|
|
}
|
|
|
|
delete test_cell_;
|
|
ocv_derate_map_.deleteContents();
|
|
}
|
|
|
|
// Multiple timing arc sets (buses bits or a related_ports list)
|
|
// can share the same model, cond, and sdf_conds, so collect them
|
|
// into a set so they are only deleted once.
|
|
void
|
|
LibertyCell::deleteTimingModels()
|
|
{
|
|
TimingModelSet models;
|
|
FuncExprSet exprs;
|
|
StringSetEq strings;
|
|
|
|
TimingArcSetSeq::Iterator set_iter(timing_arc_sets_);
|
|
while (set_iter.hasNext()) {
|
|
TimingArcSet *set = set_iter.next();
|
|
TimingArcSetArcIterator *arc_iter = set->timingArcIterator();
|
|
while (arc_iter->hasNext()) {
|
|
TimingArc *arc = arc_iter->next();
|
|
TimingModel *model = arc->model();
|
|
if (model)
|
|
models.insert(model);
|
|
}
|
|
delete arc_iter;
|
|
|
|
FuncExpr *cond = set->cond();
|
|
if (cond)
|
|
exprs.insert(cond);
|
|
|
|
const char *str = set->sdfCondStart();
|
|
if (str)
|
|
strings.insert(str);
|
|
|
|
str = set->sdfCondEnd();
|
|
if (str)
|
|
strings.insert(str);
|
|
|
|
str = set->modeName();
|
|
if (str)
|
|
strings.insert(str);
|
|
|
|
str = set->modeValue();
|
|
if (str)
|
|
strings.insert(str);
|
|
|
|
}
|
|
models.deleteContents();
|
|
|
|
FuncExprSet::Iterator expr_iter(exprs);
|
|
while (expr_iter.hasNext()) {
|
|
FuncExpr *expr = expr_iter.next();
|
|
expr->deleteSubexprs();
|
|
}
|
|
deleteContents(&strings);
|
|
}
|
|
|
|
LibertyCellPortIterator *
|
|
LibertyCell::libertyPortIterator() const
|
|
{
|
|
return new LibertyCellPortIterator(this);
|
|
}
|
|
|
|
LibertyCellPortBitIterator *
|
|
LibertyCell::libertyPortBitIterator() const
|
|
{
|
|
return new LibertyCellPortBitIterator(this);
|
|
}
|
|
|
|
LibertyPort *
|
|
LibertyCell::findLibertyPort(const char *name) const
|
|
{
|
|
return dynamic_cast<LibertyPort*>(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;
|
|
}
|
|
|
|
ModeDef *
|
|
LibertyCell::makeModeDef(const char *name)
|
|
{
|
|
ModeDef *mode = new ModeDef(name);
|
|
if (mode_defs_ == NULL)
|
|
mode_defs_ = new ModeDefMap;
|
|
(*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::setInterfaceTiming(bool value)
|
|
{
|
|
interface_timing_ = value;
|
|
}
|
|
|
|
bool
|
|
LibertyCell::isClockGateLatchPosedge() const
|
|
{
|
|
return clock_gate_type_ == clock_gate_latch_posedge;
|
|
}
|
|
|
|
bool
|
|
LibertyCell::isClockGateLatchNegedge() const
|
|
{
|
|
return clock_gate_type_ == clock_gate_latch_posedge;
|
|
}
|
|
|
|
bool
|
|
LibertyCell::isClockGateOther() const
|
|
{
|
|
return clock_gate_type_ == clock_gate_other;
|
|
}
|
|
|
|
bool
|
|
LibertyCell::isClockGate() const
|
|
{
|
|
return clock_gate_type_ != clock_gate_none;
|
|
}
|
|
|
|
void
|
|
LibertyCell::setClockGateType(ClockGateType clock_gate_type)
|
|
{
|
|
clock_gate_type_ = clock_gate_type;
|
|
}
|
|
|
|
unsigned
|
|
LibertyCell::addTimingArcSet(TimingArcSet *arc_set)
|
|
{
|
|
if (timing_arc_sets_ == NULL) {
|
|
timing_arc_sets_ = new TimingArcSetSeq;
|
|
timing_arc_set_map_ = new TimingArcSetMap;
|
|
port_timing_arc_set_map_ = new LibertyPortTimingArcSetMap;
|
|
timing_arc_set_from_map_ = new LibertyPortTimingArcSetSeqMap;
|
|
timing_arc_set_to_map_ = new LibertyPortTimingArcSetSeqMap;
|
|
}
|
|
int set_index = timing_arc_sets_->size();
|
|
if (set_index > timing_arc_set_index_max)
|
|
internalError("timing arc set max index exceeded");
|
|
timing_arc_sets_->push_back(arc_set);
|
|
timing_arc_set_map_->insert(arc_set);
|
|
|
|
LibertyPort *from = arc_set->from();
|
|
LibertyPort *to = arc_set->to();
|
|
TimingRole *role = arc_set->role();
|
|
if (role == TimingRole::regClkToQ()
|
|
|| role == TimingRole::latchEnToQ())
|
|
from->setIsRegClk(true);
|
|
if (role->isTimingCheck())
|
|
from->setIsCheckClk(true);
|
|
|
|
LibertyPortPair port_pair(from, to);
|
|
TimingArcSetSeq *sets = port_timing_arc_set_map_->findKey(&port_pair);
|
|
if (sets == NULL) {
|
|
// First arc set for from/to ports.
|
|
sets = new TimingArcSetSeq;
|
|
LibertyPortPair *port_pair1 = new LibertyPortPair(from, to);
|
|
(*port_timing_arc_set_map_)[port_pair1] = sets;
|
|
}
|
|
sets->push_back(arc_set);
|
|
|
|
sets = timing_arc_set_from_map_->findKey(from);
|
|
if (sets == NULL) {
|
|
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 == NULL) {
|
|
sets = new TimingArcSetSeq;
|
|
(*timing_arc_set_to_map_)[to] = sets;
|
|
}
|
|
sets->push_back(arc_set);
|
|
return set_index;
|
|
}
|
|
|
|
void
|
|
LibertyCell::addInternalPower(InternalPower *power)
|
|
{
|
|
if (internal_powers_ == NULL)
|
|
internal_powers_ = new InternalPowerSeq;
|
|
internal_powers_->push_back(power);
|
|
}
|
|
|
|
LibertyCellInternalPowerIterator *
|
|
LibertyCell::internalPowerIterator()
|
|
{
|
|
return new LibertyCellInternalPowerIterator(internal_powers_);
|
|
}
|
|
|
|
void
|
|
LibertyCell::addLeakagePower(LeakagePower *power)
|
|
{
|
|
if (leakage_powers_ == NULL)
|
|
leakage_powers_ = new LeakagePowerSeq;
|
|
leakage_powers_->push_back(power);
|
|
}
|
|
|
|
LibertyCellLeakagePowerIterator *
|
|
LibertyCell::leakagePowerIterator()
|
|
{
|
|
return new LibertyCellLeakagePowerIterator(leakage_powers_);
|
|
}
|
|
|
|
void
|
|
LibertyCell::finish(bool infer_latches,
|
|
Report *report,
|
|
Debug *debug)
|
|
{
|
|
findDefaultCondArcs();
|
|
translatePresetClrCheckRoles();
|
|
makeLatchEnables(report, debug);
|
|
if (infer_latches
|
|
&& !interface_timing_)
|
|
inferLatchRoles(debug);
|
|
}
|
|
|
|
void
|
|
LibertyCell::findDefaultCondArcs()
|
|
{
|
|
LibertyPortTimingArcSetMap::Iterator set_iter1(port_timing_arc_set_map_);
|
|
while (set_iter1.hasNext()) {
|
|
LibertyPortPair *port_pair;
|
|
TimingArcSetSeq *sets;
|
|
set_iter1.next(port_pair, sets);
|
|
bool has_cond_arcs = false;
|
|
TimingArcSetSeq::Iterator set_iter2(sets);
|
|
while (set_iter2.hasNext()) {
|
|
TimingArcSet *set = set_iter2.next();
|
|
if (set->cond()) {
|
|
has_cond_arcs = true;
|
|
break;
|
|
}
|
|
}
|
|
if (has_cond_arcs) {
|
|
TimingArcSetSeq::Iterator set_iter3(sets);
|
|
while (set_iter3.hasNext()) {
|
|
TimingArcSet *set = set_iter3.next();
|
|
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;
|
|
CellTimingArcSetIterator *set_iter = timingArcSetIterator();
|
|
while (set_iter->hasNext()) {
|
|
TimingArcSet *set = set_iter->next();
|
|
if (set->role() == TimingRole::regSetClr())
|
|
pre_clr_ports.insert(set->from());
|
|
}
|
|
delete set_iter;
|
|
|
|
if (!pre_clr_ports.empty()) {
|
|
set_iter = timingArcSetIterator();
|
|
while (set_iter->hasNext()) {
|
|
TimingArcSet *set = set_iter->next();
|
|
if (pre_clr_ports.findKey(set->to())) {
|
|
if (set->role() == TimingRole::setup())
|
|
set->setRole(TimingRole::recovery());
|
|
else if (set->role() == TimingRole::hold())
|
|
set->setRole(TimingRole::removal());
|
|
}
|
|
}
|
|
delete set_iter;
|
|
}
|
|
}
|
|
|
|
TimingArcSet *
|
|
LibertyCell::findTimingArcSet(TimingArcSet *key) const
|
|
{
|
|
if (timing_arc_sets_)
|
|
return timing_arc_set_map_->findKey(key);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
TimingArcSet *
|
|
LibertyCell::findTimingArcSet(unsigned arc_set_index) const
|
|
{
|
|
return (*timing_arc_sets_)[arc_set_index];
|
|
}
|
|
|
|
size_t
|
|
LibertyCell::timingArcSetCount() const
|
|
{
|
|
if (timing_arc_sets_)
|
|
return timing_arc_sets_->size();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
CellTimingArcSetIterator *
|
|
LibertyCell::timingArcSetIterator() const
|
|
{
|
|
return new CellTimingArcSetIterator(timing_arc_sets_);
|
|
}
|
|
|
|
CellTimingArcSetIterator *
|
|
LibertyCell::timingArcSetIterator(LibertyPort *from,
|
|
LibertyPort *to) const
|
|
{
|
|
if (timing_arc_sets_) {
|
|
LibertyPortPair port_pair(from, to);
|
|
TimingArcSetSeq *sets = port_timing_arc_set_map_->findKey(&port_pair);
|
|
return new CellTimingArcSetIterator(sets);
|
|
}
|
|
else
|
|
return new CellTimingArcSetIterator(NULL);
|
|
}
|
|
|
|
CellTimingArcSetIterator *
|
|
LibertyCell::timingArcSetFromIterator(LibertyPort *from) const
|
|
{
|
|
if (timing_arc_sets_) {
|
|
TimingArcSetSeq *sets = timing_arc_set_from_map_->findKey(from);
|
|
return new CellTimingArcSetIterator(sets);
|
|
}
|
|
else
|
|
return new CellTimingArcSetIterator(NULL);
|
|
}
|
|
|
|
bool
|
|
LibertyCell::hasTimingArcs(LibertyPort *port) const
|
|
{
|
|
return timing_arc_sets_
|
|
&& (timing_arc_set_from_map_->findKey(port)
|
|
|| timing_arc_set_to_map_->findKey(port));
|
|
}
|
|
|
|
CellTimingArcSetIterator *
|
|
LibertyCell::timingArcSetToIterator(LibertyPort *to) const
|
|
{
|
|
if (timing_arc_sets_) {
|
|
TimingArcSetSeq *sets = timing_arc_set_to_map_->findKey(to);
|
|
return new CellTimingArcSetIterator(sets);
|
|
}
|
|
else
|
|
return new CellTimingArcSetIterator(NULL);
|
|
}
|
|
|
|
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 = NULL;
|
|
if (clk)
|
|
clk_bit = clk->bitSubExpr(bit);
|
|
FuncExpr *data_bit = NULL;
|
|
if (data)
|
|
data_bit = data->bitSubExpr(bit);
|
|
FuncExpr *clear_bit = NULL;
|
|
if (clear)
|
|
clear_bit = clear->bitSubExpr(bit);
|
|
FuncExpr *preset_bit = NULL;
|
|
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);
|
|
if (sequentials_ == NULL) {
|
|
sequentials_ = new SequentialSeq;
|
|
port_to_seq_map_ = new PortToSequentialMap;
|
|
}
|
|
sequentials_->push_back(seq);
|
|
(*port_to_seq_map_)[seq->output()] = seq;
|
|
(*port_to_seq_map_)[seq->outputInv()] = seq;
|
|
}
|
|
}
|
|
|
|
Sequential *
|
|
LibertyCell::outputPortSequential(LibertyPort *port)
|
|
{
|
|
if (port_to_seq_map_)
|
|
return port_to_seq_map_->findKey(port);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
bool
|
|
LibertyCell::hasSequentials() const
|
|
{
|
|
return sequentials_ && !sequentials_->empty();
|
|
}
|
|
|
|
CellSequentialIterator *
|
|
LibertyCell::sequentialIterator() const
|
|
{
|
|
return new CellSequentialIterator(sequentials_);
|
|
}
|
|
|
|
void
|
|
LibertyCell::addScaledCell(OperatingConditions *op_cond,
|
|
LibertyCell *scaled_cell)
|
|
{
|
|
if (scaled_cells_ == NULL)
|
|
scaled_cells_ = new ScaledCellMap;
|
|
(*scaled_cells_)[op_cond] = scaled_cell;
|
|
|
|
LibertyCellPortBitIterator *port_iter1 = libertyPortBitIterator();
|
|
LibertyCellPortBitIterator *port_iter2=scaled_cell->libertyPortBitIterator();
|
|
while (port_iter1->hasNext() && port_iter2->hasNext()) {
|
|
LibertyPort *port = port_iter1->next();
|
|
LibertyPort *scaled_port = port_iter2->next();
|
|
port->addScaledPort(op_cond, scaled_port);
|
|
}
|
|
delete port_iter1;
|
|
delete port_iter2;
|
|
|
|
CellTimingArcSetIterator *set_iter1 = timingArcSetIterator();
|
|
CellTimingArcSetIterator *set_iter2 = scaled_cell->timingArcSetIterator();
|
|
while (set_iter1->hasNext() && set_iter2->hasNext()) {
|
|
TimingArcSet *arc_set1 = set_iter1->next();
|
|
TimingArcSet *arc_set2 = set_iter2->next();
|
|
TimingArcSetArcIterator *arc_iter1 = arc_set1->timingArcIterator();
|
|
TimingArcSetArcIterator *arc_iter2 = arc_set2->timingArcIterator();
|
|
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);
|
|
}
|
|
}
|
|
delete arc_iter1;
|
|
delete arc_iter2;
|
|
}
|
|
delete set_iter1;
|
|
delete set_iter2;
|
|
}
|
|
|
|
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<int>(corner_cells_.size()))
|
|
return corner_cells_[ap_index];
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
LibertyCell::setCornerCell(LibertyCell *corner_cell,
|
|
int ap_index)
|
|
{
|
|
if (ap_index >= static_cast<int>(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;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Latch enable port/function for a latch D->Q timing arc set.
|
|
class LatchEnable
|
|
{
|
|
public:
|
|
LatchEnable(LibertyPort *data,
|
|
LibertyPort *enable,
|
|
TransRiseFall *enable_tr,
|
|
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_; }
|
|
TransRiseFall *enableTransition() const { return enable_tr_; }
|
|
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_;
|
|
TransRiseFall *enable_tr_;
|
|
FuncExpr *enable_func_;
|
|
LibertyPort *output_;
|
|
TimingArcSet *d_to_q_;
|
|
TimingArcSet *en_to_q_;
|
|
TimingArcSet *setup_check_;
|
|
};
|
|
|
|
LatchEnable::LatchEnable(LibertyPort *data,
|
|
LibertyPort *enable,
|
|
TransRiseFall *enable_tr,
|
|
FuncExpr *enable_func,
|
|
LibertyPort *output,
|
|
TimingArcSet *d_to_q,
|
|
TimingArcSet *en_to_q,
|
|
TimingArcSet *setup_check) :
|
|
data_(data),
|
|
enable_(enable),
|
|
enable_tr_(enable_tr),
|
|
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()) {
|
|
CellTimingArcSetIterator *set_iter = timingArcSetIterator();
|
|
while (set_iter->hasNext()) {
|
|
TimingArcSet *en_to_q = set_iter->next();
|
|
if (en_to_q->role() == TimingRole::latchEnToQ()) {
|
|
LibertyPort *en = en_to_q->from();
|
|
LibertyPort *q = en_to_q->to();
|
|
CellTimingArcSetIterator *to_iter = timingArcSetToIterator(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();
|
|
CellTimingArcSetIterator *check_iter = timingArcSetIterator(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->timingArcIterator();
|
|
if (check_arc_iter->hasNext()) {
|
|
TimingArc *check_arc = check_arc_iter->next();
|
|
delete check_arc_iter;
|
|
TransRiseFall *en_tr = latch_enable->enableTransition();
|
|
TransRiseFall *check_tr = check_arc->fromTrans()->asRiseFall();
|
|
if (check_tr == en_tr) {
|
|
report->warn("%s, cell %s latch enable %s -> %s %s_edge timing arc is inconsistent with %s -> %s setup_%s check.\n",
|
|
filename_,
|
|
name_,
|
|
en->name(),
|
|
q->name(),
|
|
en_tr == TransRiseFall::rise()?"rising":"falling",
|
|
en->name(),
|
|
d->name(),
|
|
check_tr==TransRiseFall::rise()?"rising":"falling");
|
|
}
|
|
FuncExpr *en_func = latch_enable->enableFunc();
|
|
if (en_func) {
|
|
TimingSense en_sense = en_func->portTimingSense(en);
|
|
if (en_sense == timing_sense_positive_unate
|
|
&& en_tr != TransRiseFall::rise())
|
|
report->warn("%s, cell %s latch enable %s -> %s %s_edge is inconsistent with latch group enable function positive sense.\n",
|
|
filename_,
|
|
name_,
|
|
en->name(),
|
|
q->name(),
|
|
en_tr == TransRiseFall::rise()?"rising":"falling");
|
|
else if (en_sense == timing_sense_negative_unate
|
|
&& en_tr != TransRiseFall::fall())
|
|
report->warn("%s, cell %s latch enable %s -> %s %s_edge is inconsistent with latch group enable function negative sense.\n",
|
|
filename_,
|
|
name_,
|
|
en->name(),
|
|
q->name(),
|
|
en_tr == TransRiseFall::rise()?"rising":"falling");
|
|
}
|
|
break;
|
|
}
|
|
delete check_arc_iter;
|
|
}
|
|
}
|
|
delete check_iter;
|
|
}
|
|
}
|
|
delete to_iter;
|
|
}
|
|
}
|
|
delete set_iter;
|
|
}
|
|
}
|
|
|
|
FuncExpr *
|
|
LibertyCell::findLatchEnableFunc(LibertyPort *data,
|
|
LibertyPort *enable) const
|
|
{
|
|
CellSequentialIterator *iter = sequentialIterator();
|
|
while (iter->hasNext()) {
|
|
Sequential *seq = iter->next();
|
|
if (seq->isLatch()
|
|
&& seq->data()
|
|
&& seq->data()->hasPort(data)
|
|
&& seq->clock()
|
|
&& seq->clock()->hasPort(enable)) {
|
|
delete iter;
|
|
return seq->clock();
|
|
}
|
|
}
|
|
delete iter;
|
|
return NULL;
|
|
}
|
|
|
|
LatchEnable *
|
|
LibertyCell::makeLatchEnable(LibertyPort *d,
|
|
LibertyPort *en,
|
|
LibertyPort *q,
|
|
TimingArcSet *d_to_q,
|
|
TimingArcSet *en_to_q,
|
|
TimingArcSet *setup_check,
|
|
Debug *debug)
|
|
{
|
|
TransRiseFall *en_tr = en_to_q->isRisingFallingEdge();
|
|
FuncExpr *en_func = findLatchEnableFunc(d, en);
|
|
LatchEnable *latch_enable = new LatchEnable(d, en, en_tr, 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);
|
|
debugPrint3(debug, "liberty", 2, "latch d=%s en=%s q=%s\n",
|
|
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;
|
|
CellTimingArcSetIterator *set_iter = timingArcSetIterator();
|
|
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();
|
|
CellTimingArcSetIterator *to_iter = timingArcSetToIterator(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() == timing_sense_positive_unate
|
|
|| d_to_q->sense() == timing_sense_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.
|
|
CellTimingArcSetIterator *check_iter = timingArcSetIterator(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());
|
|
}
|
|
}
|
|
delete check_iter;
|
|
}
|
|
}
|
|
delete to_iter;
|
|
}
|
|
}
|
|
delete set_iter;
|
|
}
|
|
}
|
|
|
|
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,
|
|
TransRiseFall *&enable_tr) const
|
|
{
|
|
enable_port = NULL;
|
|
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_tr = latch_enable->enableTransition();
|
|
}
|
|
}
|
|
|
|
TransRiseFall *
|
|
LibertyCell::latchCheckEnableTrans(TimingArcSet *check_set)
|
|
{
|
|
LatchEnable *latch_enable = latch_check_map_.findKey(check_set);
|
|
if (latch_enable)
|
|
return latch_enable->enableTransition();
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
LibertyCellPortIterator::LibertyCellPortIterator(const LibertyCell *cell) :
|
|
iter_(cell->ports_)
|
|
{
|
|
}
|
|
|
|
bool
|
|
LibertyCellPortIterator::hasNext()
|
|
{
|
|
return iter_.hasNext();
|
|
}
|
|
|
|
LibertyPort *
|
|
LibertyCellPortIterator::next()
|
|
{
|
|
return dynamic_cast<LibertyPort*>(iter_.next());
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
LibertyCellPortBitIterator::LibertyCellPortBitIterator(const LibertyCell *cell):
|
|
iter_(cell->portBitIterator())
|
|
{
|
|
}
|
|
|
|
LibertyCellPortBitIterator::~LibertyCellPortBitIterator()
|
|
{
|
|
delete iter_;
|
|
}
|
|
|
|
bool
|
|
LibertyCellPortBitIterator::hasNext()
|
|
{
|
|
return iter_->hasNext();
|
|
}
|
|
|
|
LibertyPort *
|
|
LibertyCellPortBitIterator::next()
|
|
{
|
|
return dynamic_cast<LibertyPort*>(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_(NULL),
|
|
tristate_enable_(NULL),
|
|
scaled_ports_(NULL),
|
|
min_period_(0.0),
|
|
pulse_clk_trigger_(NULL),
|
|
pulse_clk_sense_(NULL),
|
|
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)
|
|
{
|
|
min_pulse_width_[TransRiseFall::riseIndex()] = 0.0;
|
|
min_pulse_width_[TransRiseFall::fallIndex()] = 0.0;
|
|
}
|
|
|
|
LibertyPort::~LibertyPort()
|
|
{
|
|
if (function_)
|
|
function_->deleteSubexprs();
|
|
if (tristate_enable_)
|
|
tristate_enable_->deleteSubexprs();
|
|
delete scaled_ports_;
|
|
}
|
|
|
|
void
|
|
LibertyPort::setDirection(PortDirection *dir)
|
|
{
|
|
ConcretePort::setDirection(dir);
|
|
if (dir->isInternal())
|
|
liberty_cell_->setHasInternalPorts(true);
|
|
}
|
|
|
|
LibertyPortMemberIterator *
|
|
LibertyPort::libertyMemberIterator() const
|
|
{
|
|
return new LibertyPortMemberIterator(this);
|
|
}
|
|
|
|
LibertyPort *
|
|
LibertyPort::findLibertyMember(int index) const
|
|
{
|
|
return dynamic_cast<LibertyPort*>(findMember(index));
|
|
}
|
|
|
|
LibertyPort *
|
|
LibertyPort::findLibertyBusBit(int index) const
|
|
{
|
|
return dynamic_cast<LibertyPort*>(findBusBit(index));
|
|
}
|
|
|
|
void
|
|
LibertyPort::setCapacitance(float cap)
|
|
{
|
|
setCapacitance(TransRiseFall::rise(), MinMax::min(), cap);
|
|
setCapacitance(TransRiseFall::fall(), MinMax::min(), cap);
|
|
setCapacitance(TransRiseFall::rise(), MinMax::max(), cap);
|
|
setCapacitance(TransRiseFall::fall(), MinMax::max(), cap);
|
|
}
|
|
|
|
void
|
|
LibertyPort::setCapacitance(const TransRiseFall *tr,
|
|
const MinMax *min_max,
|
|
float cap)
|
|
{
|
|
capacitance_.setValue(tr, min_max, cap);
|
|
if (hasMembers()) {
|
|
LibertyPortMemberIterator *member_iter = libertyMemberIterator();
|
|
while (member_iter->hasNext()) {
|
|
LibertyPort *port_bit = member_iter->next();
|
|
port_bit->setCapacitance(tr, min_max, cap);
|
|
}
|
|
delete member_iter;
|
|
}
|
|
}
|
|
|
|
float
|
|
LibertyPort::capacitance(const TransRiseFall *tr,
|
|
const MinMax *min_max) const
|
|
{
|
|
float cap;
|
|
bool exists;
|
|
capacitance_.value(tr, min_max, cap, exists);
|
|
if (exists)
|
|
return cap;
|
|
else
|
|
return 0.0;
|
|
}
|
|
|
|
void
|
|
LibertyPort::capacitance(const TransRiseFall *tr,
|
|
const MinMax *min_max,
|
|
// Return values.
|
|
float &cap,
|
|
bool &exists) const
|
|
{
|
|
capacitance_.value(tr, min_max, cap, exists);
|
|
}
|
|
|
|
float
|
|
LibertyPort::capacitance(const TransRiseFall *tr,
|
|
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(tr, min_max);
|
|
}
|
|
LibertyLibrary *lib = liberty_cell_->libertyLibrary();
|
|
float cap = capacitance(tr, min_max);
|
|
return cap * lib->scaleFactor(scale_factor_pin_cap, liberty_cell_, pvt);
|
|
}
|
|
|
|
void
|
|
LibertyPort::setFunction(FuncExpr *func)
|
|
{
|
|
function_ = func;
|
|
if (is_bus_ || is_bundle_) {
|
|
LibertyPortMemberIterator *member_iter = libertyMemberIterator();
|
|
int bit_offset = 0;
|
|
while (member_iter->hasNext()) {
|
|
LibertyPort *port_bit = member_iter->next();
|
|
FuncExpr *sub_expr = (func) ? func->bitSubExpr(bit_offset) : NULL;
|
|
port_bit->setFunction(sub_expr);
|
|
bit_offset++;
|
|
}
|
|
delete member_iter;
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyPort::setTristateEnable(FuncExpr *enable)
|
|
{
|
|
tristate_enable_ = enable;
|
|
if (hasMembers()) {
|
|
LibertyPortMemberIterator *member_iter = libertyMemberIterator();
|
|
while (member_iter->hasNext()) {
|
|
LibertyPort *port_bit = member_iter->next();
|
|
FuncExpr *sub_expr =
|
|
(enable) ? enable->bitSubExpr(port_bit->busBitIndex()) : NULL;
|
|
port_bit->setTristateEnable(sub_expr);
|
|
}
|
|
delete member_iter;
|
|
}
|
|
}
|
|
|
|
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::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(scale_factor_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 TransRiseFall *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(scale_factor_min_pulse_width, hi_low_index,
|
|
liberty_cell_, pvt);
|
|
exists = min_pulse_width_exists_ & (1 << hi_low_index);
|
|
}
|
|
|
|
void
|
|
LibertyPort::minPulseWidth(const TransRiseFall *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(TransRiseFall *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 == NULL && port2 == NULL)
|
|
|| (port1 != NULL && port2 != NULL
|
|
&& 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_ == NULL)
|
|
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(TransRiseFall *trigger,
|
|
TransRiseFall *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<int>(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<int>(corner_ports_.size()))
|
|
corner_ports_.resize(ap_index + 1);
|
|
corner_ports_[ap_index] = corner_port;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
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);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
LibertyPortMemberIterator::LibertyPortMemberIterator(const LibertyPort *port) :
|
|
iter_(port->memberIterator())
|
|
{
|
|
}
|
|
|
|
LibertyPortMemberIterator::~LibertyPortMemberIterator()
|
|
{
|
|
delete iter_;
|
|
}
|
|
|
|
bool
|
|
LibertyPortMemberIterator::hasNext()
|
|
{
|
|
return iter_->hasNext();
|
|
}
|
|
|
|
LibertyPort *
|
|
LibertyPortMemberIterator::next()
|
|
{
|
|
return dynamic_cast<LibertyPort*>(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_(NULL),
|
|
axis2_(NULL),
|
|
axis3_(NULL)
|
|
{
|
|
}
|
|
|
|
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_(wire_load_balanced_tree)
|
|
{
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
typedef Map<const char*,ScaleFactorType,CharPtrLess> ScaleFactorTypeMap;
|
|
|
|
static const char *scale_factor_type_names[] = {"pin_cap",
|
|
"wire_cap",
|
|
"wire_res",
|
|
"min_period",
|
|
"cell",
|
|
"hold",
|
|
"setup",
|
|
"recovery",
|
|
"removal",
|
|
"nochange",
|
|
"skew",
|
|
"leakage_power",
|
|
"internal_power",
|
|
"transition",
|
|
"min_pulse_width",
|
|
"count",
|
|
"unknown"};
|
|
|
|
static ScaleFactorTypeMap *scale_factor_type_name_map = NULL;
|
|
|
|
void
|
|
makeScaleFactorTypeMap()
|
|
{
|
|
scale_factor_type_name_map = new ScaleFactorTypeMap;
|
|
for (int i = 0; i < scale_factor_count; i++) {
|
|
ScaleFactorType type = (ScaleFactorType) i;
|
|
(*scale_factor_type_name_map)[scale_factor_type_names[type]] = type;
|
|
}
|
|
}
|
|
|
|
void
|
|
deleteScaleFactorTypeMap()
|
|
{
|
|
delete scale_factor_type_name_map;
|
|
scale_factor_type_name_map = NULL;
|
|
}
|
|
|
|
const char *
|
|
scaleFactorTypeName(ScaleFactorType type)
|
|
{
|
|
return scale_factor_type_names[type];
|
|
}
|
|
|
|
ScaleFactorType
|
|
findScaleFactorType(const char *name)
|
|
{
|
|
ScaleFactorType type;
|
|
bool exists;
|
|
scale_factor_type_name_map->findKey(name, type, exists);
|
|
if (exists)
|
|
return type;
|
|
else
|
|
return scale_factor_unknown;
|
|
}
|
|
|
|
bool
|
|
scaleFactorTypeRiseFallSuffix(ScaleFactorType type)
|
|
{
|
|
return type == scale_factor_cell
|
|
|| type == scale_factor_hold
|
|
|| type == scale_factor_setup
|
|
|| type == scale_factor_recovery
|
|
|| type == scale_factor_removal
|
|
|| type == scale_factor_nochange
|
|
|| type == scale_factor_skew;
|
|
}
|
|
|
|
bool
|
|
scaleFactorTypeRiseFallPrefix(ScaleFactorType type)
|
|
{
|
|
return type == scale_factor_transition;
|
|
}
|
|
|
|
bool
|
|
scaleFactorTypeLowHighSuffix(ScaleFactorType type)
|
|
{
|
|
return type == scale_factor_min_pulse_width;
|
|
}
|
|
|
|
static const char *scale_factor_pvt_names[] = {"process",
|
|
"volt",
|
|
"temp"};
|
|
|
|
ScaleFactorPvt
|
|
findScaleFactorPvt(const char *name)
|
|
{
|
|
if (stringEq(name, scale_factor_pvt_names[scale_factor_process]))
|
|
return scale_factor_process;
|
|
else if (stringEq(name, scale_factor_pvt_names[scale_factor_temp]))
|
|
return scale_factor_temp;
|
|
else if (stringEq(name, scale_factor_pvt_names[scale_factor_volt]))
|
|
return scale_factor_volt;
|
|
else
|
|
return scale_factor_pvt_unknown;
|
|
}
|
|
|
|
const char *
|
|
scaleFactorPvtName(ScaleFactorPvt pvt)
|
|
{
|
|
return scale_factor_pvt_names[pvt];
|
|
}
|
|
|
|
ScaleFactors::ScaleFactors(const char *name) :
|
|
name_(stringCopy(name))
|
|
{
|
|
for (int type = 0; type < scale_factor_count; type++) {
|
|
for (int pvt = 0; pvt < scale_factor_pvt_count; pvt++) {
|
|
TransRiseFallIterator tr_iter;
|
|
while (tr_iter.hasNext()) {
|
|
TransRiseFall *tr = tr_iter.next();
|
|
int tr_index = tr->index();
|
|
scales_[type][pvt][tr_index] = 0.0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ScaleFactors::~ScaleFactors()
|
|
{
|
|
stringDelete(name_);
|
|
}
|
|
|
|
void
|
|
ScaleFactors::setScale(ScaleFactorType type,
|
|
ScaleFactorPvt pvt,
|
|
TransRiseFall *tr,
|
|
float scale)
|
|
{
|
|
scales_[type][pvt][tr->index()] = scale;
|
|
}
|
|
|
|
void
|
|
ScaleFactors::setScale(ScaleFactorType type,
|
|
ScaleFactorPvt pvt,
|
|
float scale)
|
|
{
|
|
scales_[type][pvt][0] = scale;
|
|
}
|
|
|
|
float
|
|
ScaleFactors::scale(ScaleFactorType type,
|
|
ScaleFactorPvt pvt,
|
|
TransRiseFall *tr)
|
|
{
|
|
return scales_[type][pvt][tr->index()];
|
|
}
|
|
|
|
float
|
|
ScaleFactors::scale(ScaleFactorType type,
|
|
ScaleFactorPvt pvt,
|
|
int tr_index)
|
|
{
|
|
return scales_[type][pvt][tr_index];
|
|
}
|
|
|
|
float
|
|
ScaleFactors::scale(ScaleFactorType type,
|
|
ScaleFactorPvt pvt)
|
|
{
|
|
return scales_[type][pvt][0];
|
|
}
|
|
|
|
void
|
|
ScaleFactors::print()
|
|
{
|
|
printf("%10s", " ");
|
|
for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) {
|
|
ScaleFactorPvt pvt = (ScaleFactorPvt) pvt_index;
|
|
printf("%10s", scaleFactorPvtName(pvt));
|
|
}
|
|
printf("\n");
|
|
for (int type_index = 0; type_index < scale_factor_count; type_index++) {
|
|
ScaleFactorType type = (ScaleFactorType) type_index;
|
|
printf("%10s ", scaleFactorTypeName(type));
|
|
for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) {
|
|
TransRiseFallIterator tr_iter;
|
|
if (scaleFactorTypeRiseFallSuffix(type)
|
|
|| scaleFactorTypeRiseFallPrefix(type)
|
|
|| scaleFactorTypeLowHighSuffix(type)) {
|
|
printf(" %.3f,%.3f",
|
|
scales_[type_index][pvt_index][TransRiseFall::riseIndex()],
|
|
scales_[type_index][pvt_index][TransRiseFall::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_(NULL),
|
|
scan_in_(NULL),
|
|
scan_enable_(NULL),
|
|
scan_out_(NULL),
|
|
scan_out_inv_(NULL)
|
|
{
|
|
}
|
|
|
|
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)
|
|
{
|
|
MinMaxIterator el_iter;
|
|
while (el_iter.hasNext()) {
|
|
EarlyLate *early_late = el_iter.next();
|
|
int el_index = early_late->index();
|
|
TransRiseFallIterator tr_iter;
|
|
while (tr_iter.hasNext()) {
|
|
TransRiseFall *tr = tr_iter.next();
|
|
int tr_index = tr->index();
|
|
derate_[tr_index][el_index][path_type_clk] = NULL;
|
|
derate_[tr_index][el_index][path_type_data] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
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<Table*> models;
|
|
MinMaxIterator el_iter;
|
|
while (el_iter.hasNext()) {
|
|
EarlyLate *early_late = el_iter.next();
|
|
int el_index = early_late->index();
|
|
TransRiseFallIterator tr_iter;
|
|
while (tr_iter.hasNext()) {
|
|
TransRiseFall *tr = tr_iter.next();
|
|
int tr_index = tr->index();
|
|
Table *derate;
|
|
derate = derate_[tr_index][el_index][path_type_clk];
|
|
if (derate)
|
|
models.insert(derate);
|
|
derate = derate_[tr_index][el_index][path_type_data];
|
|
if (derate)
|
|
models.insert(derate);
|
|
}
|
|
}
|
|
Set<Table*>::Iterator model_iter(models);
|
|
while (model_iter.hasNext()) {
|
|
Table *model = model_iter.next();
|
|
delete model;
|
|
}
|
|
}
|
|
|
|
Table *
|
|
OcvDerate::derateTable(const TransRiseFall *tr,
|
|
const EarlyLate *early_late,
|
|
PathType path_type)
|
|
{
|
|
return derate_[tr->index()][early_late->index()][path_type];
|
|
}
|
|
|
|
void
|
|
OcvDerate::setDerateTable(const TransRiseFall *tr,
|
|
const EarlyLate *early_late,
|
|
const PathType path_type,
|
|
Table *derate)
|
|
{
|
|
derate_[tr->index()][early_late->index()][path_type] = derate;
|
|
}
|
|
|
|
} // namespace
|