2018-09-28 17:54:21 +02:00
|
|
|
// OpenSTA, Static Timing Analyzer
|
2020-03-07 03:50:37 +01:00
|
|
|
// Copyright (c) 2020, Parallax Software, Inc.
|
2018-09-28 17:54:21 +02:00
|
|
|
//
|
|
|
|
|
// 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/>.
|
|
|
|
|
|
2020-04-05 23:53:44 +02:00
|
|
|
#include "TimingModel.hh"
|
2020-04-05 20:35:51 +02:00
|
|
|
|
2020-04-05 23:53:44 +02:00
|
|
|
#include "EnumNameMap.hh"
|
|
|
|
|
#include "FuncExpr.hh"
|
|
|
|
|
#include "TimingRole.hh"
|
|
|
|
|
#include "Liberty.hh"
|
|
|
|
|
#include "TimingArc.hh"
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
namespace sta {
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
timingArcsEquiv(const TimingArcSet *set1,
|
|
|
|
|
const TimingArcSet *set2);
|
|
|
|
|
static bool
|
|
|
|
|
timingArcsLess(const TimingArcSet *set1,
|
|
|
|
|
const TimingArcSet *set2);
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
TimingArcAttrs::TimingArcAttrs() :
|
2019-03-13 01:25:53 +01:00
|
|
|
timing_type_(TimingType::combinational),
|
|
|
|
|
timing_sense_(TimingSense::unknown),
|
|
|
|
|
cond_(nullptr),
|
|
|
|
|
sdf_cond_(nullptr),
|
|
|
|
|
sdf_cond_start_(nullptr),
|
|
|
|
|
sdf_cond_end_(nullptr),
|
|
|
|
|
mode_name_(nullptr),
|
|
|
|
|
mode_value_(nullptr),
|
2018-11-26 18:15:52 +01:00
|
|
|
ocv_arc_depth_(0.0),
|
2019-03-13 01:25:53 +01:00
|
|
|
models_{nullptr, nullptr}
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-05 23:18:41 +01:00
|
|
|
// Destructor does NOT delete contents because it is a component
|
|
|
|
|
// of TimingGroup (that is deleted after building the LibertyCell)
|
|
|
|
|
// and (potentially) multiple TimingArcSets.
|
2018-09-28 17:54:21 +02:00
|
|
|
TimingArcAttrs::~TimingArcAttrs()
|
|
|
|
|
{
|
2018-12-05 23:18:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArcAttrs::deleteContents()
|
|
|
|
|
{
|
|
|
|
|
if (cond_)
|
|
|
|
|
cond_->deleteSubexprs();
|
2018-11-26 18:15:52 +01:00
|
|
|
stringDelete(sdf_cond_);
|
|
|
|
|
stringDelete(sdf_cond_start_);
|
|
|
|
|
stringDelete(sdf_cond_end_);
|
|
|
|
|
stringDelete(mode_name_);
|
|
|
|
|
stringDelete(mode_value_);
|
2019-11-11 23:30:19 +01:00
|
|
|
delete models_[RiseFall::riseIndex()];
|
|
|
|
|
delete models_[RiseFall::fallIndex()];
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArcAttrs::setTimingType(TimingType type)
|
|
|
|
|
{
|
|
|
|
|
timing_type_ = type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArcAttrs::setTimingSense(TimingSense sense)
|
|
|
|
|
{
|
|
|
|
|
timing_sense_ = sense;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArcAttrs::setSdfCond(const char *cond)
|
|
|
|
|
{
|
|
|
|
|
stringDelete(sdf_cond_);
|
|
|
|
|
sdf_cond_ = stringCopy(cond);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArcAttrs::setSdfCondStart(const char *cond)
|
|
|
|
|
{
|
|
|
|
|
stringDelete(sdf_cond_start_);
|
|
|
|
|
sdf_cond_start_ = stringCopy(cond);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArcAttrs::setSdfCondEnd(const char *cond)
|
|
|
|
|
{
|
|
|
|
|
stringDelete(sdf_cond_end_);
|
|
|
|
|
sdf_cond_end_ = stringCopy(cond);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArcAttrs::setModeName(const char *name)
|
|
|
|
|
{
|
|
|
|
|
stringDelete(mode_name_);
|
|
|
|
|
mode_name_ = stringCopy(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArcAttrs::setModeValue(const char *value)
|
|
|
|
|
{
|
|
|
|
|
stringDelete(mode_value_);
|
|
|
|
|
mode_value_ = stringCopy(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TimingModel *
|
2019-11-11 23:30:19 +01:00
|
|
|
TimingArcAttrs::model(RiseFall *rf) const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
return models_[rf->index()];
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2019-11-11 23:30:19 +01:00
|
|
|
TimingArcAttrs::setModel(RiseFall *rf,
|
2018-09-28 17:54:21 +02:00
|
|
|
TimingModel *model)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
models_[rf->index()] = model;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArcAttrs::setOcvArcDepth(float depth)
|
|
|
|
|
{
|
|
|
|
|
ocv_arc_depth_ = depth;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-20 17:16:14 +01:00
|
|
|
float
|
|
|
|
|
TimingArc::driveResistance() const
|
|
|
|
|
{
|
|
|
|
|
GateTimingModel *model = dynamic_cast<GateTimingModel*>(model_);
|
|
|
|
|
if (model) {
|
|
|
|
|
LibertyCell *cell = set_->libertyCell();
|
|
|
|
|
return model->driveResistance(cell, nullptr);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-20 16:31:33 +01:00
|
|
|
ArcDelay
|
2020-11-20 17:16:14 +01:00
|
|
|
TimingArc::intrinsicDelay() const
|
|
|
|
|
{
|
|
|
|
|
GateTimingModel *model = dynamic_cast<GateTimingModel*>(model_);
|
|
|
|
|
if (model) {
|
|
|
|
|
LibertyCell *cell = set_->libertyCell();
|
|
|
|
|
ArcDelay arc_delay;
|
|
|
|
|
Slew slew;
|
|
|
|
|
model->gateDelay(cell, nullptr, 0.0, 0.0, 0.0, false,
|
|
|
|
|
arc_delay, slew);
|
|
|
|
|
return arc_delay;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2019-03-13 01:25:53 +01:00
|
|
|
TimingArcSet *TimingArcSet::wire_timing_arc_set_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
TimingArcSet::TimingArcSet(LibertyCell *cell,
|
|
|
|
|
LibertyPort *from,
|
|
|
|
|
LibertyPort *to,
|
|
|
|
|
LibertyPort *related_out,
|
|
|
|
|
TimingRole *role,
|
|
|
|
|
TimingArcAttrs *attrs) :
|
|
|
|
|
from_(from),
|
|
|
|
|
to_(to),
|
|
|
|
|
related_out_(related_out),
|
|
|
|
|
role_(role),
|
|
|
|
|
cond_(attrs->cond()),
|
|
|
|
|
is_cond_default_(false),
|
2018-12-05 23:18:41 +01:00
|
|
|
sdf_cond_start_(attrs->sdfCondStart()),
|
|
|
|
|
sdf_cond_end_(attrs->sdfCondEnd()),
|
|
|
|
|
mode_name_(attrs->modeName()),
|
|
|
|
|
mode_value_(attrs->modeValue()),
|
2018-11-26 18:15:52 +01:00
|
|
|
ocv_arc_depth_(attrs->ocvArcDepth()),
|
2018-09-28 17:54:21 +02:00
|
|
|
index_(0),
|
|
|
|
|
is_disabled_constraint_(false)
|
|
|
|
|
{
|
|
|
|
|
const char *sdf_cond = attrs->sdfCond();
|
|
|
|
|
if (sdf_cond)
|
2018-12-05 23:18:41 +01:00
|
|
|
sdf_cond_start_ = sdf_cond_end_ = sdf_cond;
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
init(cell);
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-05 23:18:41 +01:00
|
|
|
TimingArcSet::TimingArcSet(TimingRole *role) :
|
2019-03-13 01:25:53 +01:00
|
|
|
from_(nullptr),
|
|
|
|
|
to_(nullptr),
|
|
|
|
|
related_out_(nullptr),
|
2018-09-28 17:54:21 +02:00
|
|
|
role_(role),
|
2019-03-13 01:25:53 +01:00
|
|
|
cond_(nullptr),
|
2018-09-28 17:54:21 +02:00
|
|
|
is_cond_default_(false),
|
2019-03-13 01:25:53 +01:00
|
|
|
sdf_cond_start_(nullptr),
|
|
|
|
|
sdf_cond_end_(nullptr),
|
|
|
|
|
mode_name_(nullptr),
|
|
|
|
|
mode_value_(nullptr),
|
2018-09-28 17:54:21 +02:00
|
|
|
index_(0),
|
|
|
|
|
is_disabled_constraint_(false)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
init(nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArcSet::init(LibertyCell *cell)
|
|
|
|
|
{
|
|
|
|
|
if (cell)
|
|
|
|
|
index_ = cell->addTimingArcSet(this);
|
|
|
|
|
|
2019-11-11 23:30:19 +01:00
|
|
|
for (auto tr_index : RiseFall::rangeIndex()) {
|
2019-03-13 01:25:53 +01:00
|
|
|
from_arc1_[tr_index] = nullptr;
|
|
|
|
|
from_arc2_[tr_index] = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TimingArcSet::~TimingArcSet()
|
|
|
|
|
{
|
2019-03-27 00:07:32 +01:00
|
|
|
arcs_.deleteContents();
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LibertyCell *
|
|
|
|
|
TimingArcSet::libertyCell() const
|
|
|
|
|
{
|
|
|
|
|
if (from_)
|
|
|
|
|
return from_->libertyCell();
|
|
|
|
|
else
|
|
|
|
|
// Wire timing arc set.
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-26 17:26:12 +01:00
|
|
|
TimingArcSetArcIterator *
|
|
|
|
|
TimingArcSet::timingArcIterator()
|
|
|
|
|
{
|
|
|
|
|
return new TimingArcSetArcIterator(this);
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
TimingArcIndex
|
|
|
|
|
TimingArcSet::addTimingArc(TimingArc *arc)
|
|
|
|
|
{
|
|
|
|
|
TimingArcIndex arc_index = arcs_.size();
|
|
|
|
|
if (arc_index > timing_arc_index_max)
|
2020-12-14 02:21:35 +01:00
|
|
|
criticalError(243, "timing arc max index exceeded\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
arcs_.push_back(arc);
|
|
|
|
|
|
2019-11-11 23:30:19 +01:00
|
|
|
int from_rf_index = arc->fromTrans()->asRiseFall()->index();
|
|
|
|
|
if (from_arc1_[from_rf_index] == nullptr)
|
|
|
|
|
from_arc1_[from_rf_index] = arc;
|
|
|
|
|
else if (from_arc2_[from_rf_index] == nullptr)
|
|
|
|
|
from_arc2_[from_rf_index] = arc;
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
return arc_index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArcSet::deleteTimingArc(TimingArc *arc)
|
|
|
|
|
{
|
|
|
|
|
TimingArc *last_arc = arcs_.back();
|
|
|
|
|
if (arc == last_arc)
|
|
|
|
|
arcs_.pop_back();
|
|
|
|
|
else {
|
|
|
|
|
last_arc->setIndex(arc->index());
|
|
|
|
|
arcs_[arc->index()] = last_arc;
|
|
|
|
|
arcs_.pop_back();
|
|
|
|
|
}
|
2019-11-11 23:30:19 +01:00
|
|
|
int from_rf_index = arc->fromTrans()->asRiseFall()->index();
|
|
|
|
|
if (from_arc1_[from_rf_index] == arc) {
|
|
|
|
|
from_arc1_[from_rf_index] = from_arc2_[from_rf_index];
|
|
|
|
|
from_arc2_[from_rf_index] = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
2019-11-11 23:30:19 +01:00
|
|
|
else if (from_arc2_[from_rf_index] == arc)
|
|
|
|
|
from_arc2_[from_rf_index] = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
delete arc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TimingArc *
|
|
|
|
|
TimingArcSet::findTimingArc(unsigned arc_index)
|
|
|
|
|
{
|
|
|
|
|
return arcs_[arc_index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArcSet::setRole(TimingRole *role)
|
|
|
|
|
{
|
|
|
|
|
role_ = role;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArcSet::setIsCondDefault(bool is_default)
|
|
|
|
|
{
|
|
|
|
|
is_cond_default_ = is_default;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2019-11-11 23:30:19 +01:00
|
|
|
TimingArcSet::arcsFrom(const RiseFall *from_rf,
|
2018-09-28 17:54:21 +02:00
|
|
|
// Return values.
|
|
|
|
|
TimingArc *&arc1,
|
|
|
|
|
TimingArc *&arc2)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
int tr_index = from_rf->index();
|
2018-09-28 17:54:21 +02:00
|
|
|
arc1 = from_arc1_[tr_index];
|
|
|
|
|
arc2 = from_arc2_[tr_index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TimingSense
|
|
|
|
|
TimingArcSet::sense() const
|
|
|
|
|
{
|
|
|
|
|
if (arcs_.size() == 1)
|
|
|
|
|
return arcs_[0]->sense();
|
|
|
|
|
else if (arcs_.size() == 2 && arcs_[0]->sense() == arcs_[1]->sense())
|
|
|
|
|
return arcs_[0]->sense();
|
|
|
|
|
else
|
2019-03-13 01:25:53 +01:00
|
|
|
return TimingSense::non_unate;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFall *
|
2018-09-28 17:54:21 +02:00
|
|
|
TimingArcSet::isRisingFallingEdge() const
|
|
|
|
|
{
|
|
|
|
|
int arc_count = arcs_.size();
|
|
|
|
|
if (arc_count == 2) {
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFall *from_rf1 = arcs_[0]->fromTrans()->asRiseFall();
|
|
|
|
|
RiseFall *from_rf2 = arcs_[1]->fromTrans()->asRiseFall();
|
|
|
|
|
if (from_rf1 == from_rf2)
|
|
|
|
|
return from_rf1;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
if (arcs_.size() == 1)
|
|
|
|
|
return arcs_[0]->fromTrans()->asRiseFall();
|
|
|
|
|
else
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArcSet::setIsDisabledConstraint(bool is_disabled)
|
|
|
|
|
{
|
|
|
|
|
is_disabled_constraint_ = is_disabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
TimingArcSet::ocvArcDepth() const
|
|
|
|
|
{
|
|
|
|
|
if (from_) {
|
|
|
|
|
if (ocv_arc_depth_ != 0.0)
|
|
|
|
|
return ocv_arc_depth_;
|
|
|
|
|
else {
|
|
|
|
|
LibertyCell *cell = from_->libertyCell();
|
|
|
|
|
float depth = cell->ocvArcDepth();
|
|
|
|
|
if (depth != 0.0)
|
|
|
|
|
return depth;
|
|
|
|
|
else {
|
|
|
|
|
float depth = cell->libertyLibrary()->ocvArcDepth();
|
|
|
|
|
if (depth != 0.0)
|
|
|
|
|
return depth;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Wire timing arc set.
|
|
|
|
|
return 1.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
TimingArcSet::equiv(const TimingArcSet *set1,
|
|
|
|
|
const TimingArcSet *set2)
|
|
|
|
|
{
|
|
|
|
|
return LibertyPort::equiv(set1->from(), set2->from())
|
|
|
|
|
&& LibertyPort::equiv(set1->to(), set2->to())
|
|
|
|
|
&& set1->role() == set2->role()
|
|
|
|
|
&& FuncExpr::equiv(set1->cond(), set2->cond())
|
|
|
|
|
&& stringEqIf(set1->sdfCond(), set2->sdfCond())
|
|
|
|
|
&& stringEqIf(set1->sdfCondStart(), set2->sdfCondStart())
|
|
|
|
|
&& stringEqIf(set1->sdfCondEnd(), set2->sdfCondEnd())
|
|
|
|
|
&& timingArcsEquiv(set1, set2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
timingArcsEquiv(const TimingArcSet *set1,
|
|
|
|
|
const TimingArcSet *set2)
|
|
|
|
|
{
|
2018-12-05 23:18:41 +01:00
|
|
|
TimingArcSetArcIterator arc_iter1(set1);
|
|
|
|
|
TimingArcSetArcIterator arc_iter2(set2);
|
|
|
|
|
while (arc_iter1.hasNext() && arc_iter2.hasNext()) {
|
|
|
|
|
TimingArc *arc1 = arc_iter1.next();
|
|
|
|
|
TimingArc *arc2 = arc_iter2.next();
|
|
|
|
|
if (!TimingArc::equiv(arc1, arc2))
|
2018-09-28 17:54:21 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2018-12-05 23:18:41 +01:00
|
|
|
return !arc_iter1.hasNext() && !arc_iter2.hasNext();
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
TimingArcSet::less(const TimingArcSet *set1,
|
|
|
|
|
const TimingArcSet *set2)
|
|
|
|
|
{
|
|
|
|
|
return timingArcSetLess(set1, set2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
timingArcSetLess(const TimingArcSet *set1,
|
|
|
|
|
const TimingArcSet *set2)
|
|
|
|
|
{
|
|
|
|
|
LibertyPort *from1 = set1->from();
|
|
|
|
|
LibertyPort *from2 = set2->from();
|
|
|
|
|
if (LibertyPort::equiv(from1, from2)) {
|
|
|
|
|
LibertyPort *to1 = set1->to();
|
|
|
|
|
LibertyPort *to2 = set2->to();
|
|
|
|
|
if (LibertyPort::equiv(to1, to2)) {
|
|
|
|
|
TimingRole *role1 = set1->role();
|
|
|
|
|
TimingRole *role2 = set2->role();
|
|
|
|
|
if (role1 == role2) {
|
|
|
|
|
const FuncExpr *cond1 = set1->cond();
|
|
|
|
|
const FuncExpr *cond2 = set2->cond();
|
|
|
|
|
if (FuncExpr::equiv(cond1, cond2)) {
|
|
|
|
|
const char *sdf_cond1 = set1->sdfCond();
|
|
|
|
|
const char *sdf_cond2 = set2->sdfCond();
|
|
|
|
|
if (stringEqIf(sdf_cond1, sdf_cond2)) {
|
|
|
|
|
const char *sdf_cond_start1 = set1->sdfCondStart();
|
|
|
|
|
const char *sdf_cond_start2 = set2->sdfCondStart();
|
|
|
|
|
if (stringEqIf(sdf_cond_start1, sdf_cond_start2)) {
|
|
|
|
|
const char *sdf_cond_end1 = set1->sdfCondEnd();
|
|
|
|
|
const char *sdf_cond_end2 = set2->sdfCondEnd();
|
|
|
|
|
if (stringEqIf(sdf_cond_end1, sdf_cond_end2)) {
|
|
|
|
|
const char *mode_name1 = set1->modeName();
|
|
|
|
|
const char *mode_name2 = set2->modeName();
|
|
|
|
|
if (stringEqIf(mode_name1, mode_name2)) {
|
|
|
|
|
const char *mode_value1 = set1->modeValue();
|
|
|
|
|
const char *mode_value2 = set2->modeValue();
|
|
|
|
|
if (stringEqIf(mode_value1, mode_value2))
|
|
|
|
|
return timingArcsLess(set1, set2);
|
|
|
|
|
else
|
|
|
|
|
return stringLessIf(mode_value1, mode_value2);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return stringLessIf(mode_name1, mode_name2);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return stringLessIf(sdf_cond_end1, sdf_cond_end2);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return stringLessIf(sdf_cond_start1, sdf_cond_start2);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return stringLessIf(sdf_cond1, sdf_cond2);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return FuncExpr::less(cond1, cond2);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return TimingRole::less(role1, role2);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return LibertyPort::less(to1, to2);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return LibertyPort::less(from1, from2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
timingArcsLess(const TimingArcSet *set1,
|
|
|
|
|
const TimingArcSet *set2)
|
|
|
|
|
{
|
2018-12-05 23:18:41 +01:00
|
|
|
TimingArcSetArcIterator arc_iter1(set1);
|
|
|
|
|
TimingArcSetArcIterator arc_iter2(set2);
|
|
|
|
|
while (arc_iter1.hasNext() && arc_iter2.hasNext()) {
|
|
|
|
|
TimingArc *arc1 = arc_iter1.next();
|
|
|
|
|
TimingArc *arc2 = arc_iter2.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
int from_index1 = arc1->fromTrans()->index();
|
|
|
|
|
int from_index2 = arc2->fromTrans()->index();
|
2018-12-05 23:18:41 +01:00
|
|
|
if (from_index1 < from_index2)
|
2018-09-28 17:54:21 +02:00
|
|
|
return true;
|
2018-12-05 23:18:41 +01:00
|
|
|
if (from_index1 > from_index2)
|
2018-09-28 17:54:21 +02:00
|
|
|
return false;
|
|
|
|
|
// from_index1 == from_index2
|
|
|
|
|
int to_index1 = arc1->toTrans()->index();
|
|
|
|
|
int to_index2 = arc2->toTrans()->index();
|
2018-12-05 23:18:41 +01:00
|
|
|
if (to_index1 < to_index2)
|
2018-09-28 17:54:21 +02:00
|
|
|
return true;
|
2018-12-05 23:18:41 +01:00
|
|
|
if (to_index1 > to_index2)
|
2018-09-28 17:54:21 +02:00
|
|
|
return false;
|
|
|
|
|
// Continue if arc transitions are equal.
|
|
|
|
|
}
|
2018-12-05 23:18:41 +01:00
|
|
|
return !arc_iter1.hasNext() && arc_iter2.hasNext();
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
int
|
2019-11-11 23:30:19 +01:00
|
|
|
TimingArcSet::wireArcIndex(const RiseFall *rf)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
return rf->index();
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArcSet::init()
|
|
|
|
|
{
|
2018-12-05 23:18:41 +01:00
|
|
|
wire_timing_arc_set_ = new TimingArcSet(TimingRole::wire());
|
2018-09-28 17:54:21 +02:00
|
|
|
new TimingArc(wire_timing_arc_set_, Transition::rise(),
|
2019-03-13 01:25:53 +01:00
|
|
|
Transition::rise(), nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
new TimingArc(wire_timing_arc_set_, Transition::fall(),
|
2019-03-13 01:25:53 +01:00
|
|
|
Transition::fall(), nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArcSet::destroy()
|
|
|
|
|
{
|
|
|
|
|
delete wire_timing_arc_set_;
|
2019-03-13 01:25:53 +01:00
|
|
|
wire_timing_arc_set_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
TimingArcSetArcIterator::TimingArcSetArcIterator(const TimingArcSet *set) :
|
|
|
|
|
TimingArcSeq::ConstIterator(set->arcs())
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
TimingArc::TimingArc(TimingArcSet *set,
|
2019-11-11 23:30:19 +01:00
|
|
|
Transition *from_rf,
|
|
|
|
|
Transition *to_rf,
|
2018-09-28 17:54:21 +02:00
|
|
|
TimingModel *model) :
|
|
|
|
|
set_(set),
|
2019-11-11 23:30:19 +01:00
|
|
|
from_rf_(from_rf),
|
|
|
|
|
to_rf_(to_rf),
|
2018-09-28 17:54:21 +02:00
|
|
|
model_(model),
|
2019-03-13 01:25:53 +01:00
|
|
|
scaled_models_(nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
index_ = set->addTimingArc(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TimingArc::~TimingArc()
|
|
|
|
|
{
|
|
|
|
|
// The models referenced by scaled_models_ are owned by the scaled
|
|
|
|
|
// cells and are deleted by ~LibertyCell.
|
|
|
|
|
delete scaled_models_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TimingModel *
|
|
|
|
|
TimingArc::model(const OperatingConditions *op_cond) const
|
|
|
|
|
{
|
|
|
|
|
if (scaled_models_) {
|
|
|
|
|
TimingModel *model = scaled_models_->findKey(op_cond);
|
|
|
|
|
if (model)
|
|
|
|
|
return model;
|
|
|
|
|
else
|
|
|
|
|
return model_;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return model_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArc::addScaledModel(const OperatingConditions *op_cond,
|
|
|
|
|
TimingModel *scaled_model)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
if (scaled_models_ == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
scaled_models_ = new ScaledTimingModelMap;
|
|
|
|
|
(*scaled_models_)[op_cond] = scaled_model;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
TimingArc::equiv(const TimingArc *arc1,
|
|
|
|
|
const TimingArc *arc2)
|
|
|
|
|
{
|
|
|
|
|
return arc1->fromTrans() == arc2->fromTrans()
|
|
|
|
|
&& arc1->toTrans() == arc2->toTrans();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArc::setIndex(unsigned index)
|
|
|
|
|
{
|
|
|
|
|
index_ = index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TimingArc *
|
|
|
|
|
TimingArc::cornerArc(int ap_index)
|
|
|
|
|
{
|
|
|
|
|
if (ap_index < static_cast<int>(corner_arcs_.size())) {
|
|
|
|
|
TimingArc *corner_arc = corner_arcs_[ap_index];
|
|
|
|
|
if (corner_arc)
|
|
|
|
|
return corner_arc;
|
|
|
|
|
}
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
TimingArc::setCornerArc(TimingArc *corner_arc,
|
|
|
|
|
int ap_index)
|
|
|
|
|
{
|
|
|
|
|
if (ap_index >= static_cast<int>(corner_arcs_.size()))
|
|
|
|
|
corner_arcs_.resize(ap_index + 1);
|
|
|
|
|
corner_arcs_[ap_index] = corner_arc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
TimingSense
|
|
|
|
|
TimingArc::sense() const
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
if ((from_rf_ == Transition::rise()
|
|
|
|
|
&& to_rf_ == Transition::rise())
|
|
|
|
|
|| (from_rf_ == Transition::fall()
|
|
|
|
|
&& to_rf_ == Transition::fall()))
|
2019-03-13 01:25:53 +01:00
|
|
|
return TimingSense::positive_unate;
|
2019-11-11 23:30:19 +01:00
|
|
|
else if ((from_rf_ == Transition::rise()
|
|
|
|
|
&& to_rf_ == Transition::fall())
|
|
|
|
|
|| (from_rf_ == Transition::fall()
|
|
|
|
|
&& to_rf_ == Transition::rise()))
|
2019-03-13 01:25:53 +01:00
|
|
|
return TimingSense::negative_unate;
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
2019-03-13 01:25:53 +01:00
|
|
|
return TimingSense::non_unate;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
2019-03-13 01:25:53 +01:00
|
|
|
static EnumNameMap<TimingSense> timing_sense_name_map =
|
|
|
|
|
{{TimingSense::positive_unate, "positive_unate"},
|
|
|
|
|
{TimingSense::negative_unate, "negative_unate"},
|
|
|
|
|
{TimingSense::non_unate, "non_unate"},
|
|
|
|
|
{TimingSense::none, "none"},
|
|
|
|
|
{TimingSense::unknown, "unknown"}
|
|
|
|
|
};
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
const char *
|
|
|
|
|
timingSenseString(TimingSense sense)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
return timing_sense_name_map.find(sense);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TimingSense
|
|
|
|
|
timingSenseOpposite(TimingSense sense)
|
|
|
|
|
{
|
|
|
|
|
switch (sense) {
|
2019-03-13 01:25:53 +01:00
|
|
|
case TimingSense::positive_unate:
|
|
|
|
|
return TimingSense::negative_unate;
|
|
|
|
|
case TimingSense::negative_unate:
|
|
|
|
|
return TimingSense::positive_unate;
|
|
|
|
|
case TimingSense::non_unate:
|
|
|
|
|
return TimingSense::non_unate;
|
|
|
|
|
case TimingSense::unknown:
|
|
|
|
|
return TimingSense::unknown;
|
|
|
|
|
case TimingSense::none:
|
|
|
|
|
return TimingSense::none;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
// Prevent warnings from lame compilers.
|
2019-03-13 01:25:53 +01:00
|
|
|
return TimingSense::unknown;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
// Same order as enum TimingType.
|
2019-03-13 01:25:53 +01:00
|
|
|
EnumNameMap<TimingType> timing_type_name_map =
|
|
|
|
|
{{TimingType::clear, "clear"},
|
|
|
|
|
{TimingType::combinational, "combinational"},
|
|
|
|
|
{TimingType::combinational_fall, "combinational_fall"},
|
|
|
|
|
{TimingType::combinational_rise, "combinational_rise"},
|
|
|
|
|
{TimingType::falling_edge, "falling_edge"},
|
|
|
|
|
{TimingType::hold_falling, "hold_falling"},
|
|
|
|
|
{TimingType::hold_rising, "hold_rising"},
|
|
|
|
|
{TimingType::min_pulse_width, "min_pulse_width"},
|
|
|
|
|
{TimingType::minimum_period, "minimum_period"},
|
|
|
|
|
{TimingType::nochange_high_high, "nochange_high_high"},
|
|
|
|
|
{TimingType::nochange_high_low, "nochange_high_low"},
|
|
|
|
|
{TimingType::nochange_low_high, "nochange_low_high"},
|
|
|
|
|
{TimingType::nochange_low_low, "nochange_low_low"},
|
|
|
|
|
{TimingType::non_seq_hold_falling, "non_seq_hold_falling"},
|
|
|
|
|
{TimingType::non_seq_hold_rising, "non_seq_hold_rising"},
|
|
|
|
|
{TimingType::non_seq_setup_falling, "non_seq_setup_falling"},
|
|
|
|
|
{TimingType::non_seq_setup_rising, "non_seq_setup_rising"},
|
|
|
|
|
{TimingType::preset, "preset"},
|
|
|
|
|
{TimingType::recovery_falling, "recovery_falling"},
|
|
|
|
|
{TimingType::recovery_rising, "recovery_rising"},
|
|
|
|
|
{TimingType::removal_falling, "removal_falling"},
|
|
|
|
|
{TimingType::removal_rising, "removal_rising"},
|
|
|
|
|
{TimingType::retaining_time, "retaining_time"},
|
|
|
|
|
{TimingType::rising_edge, "rising_edge"},
|
|
|
|
|
{TimingType::setup_falling, "setup_falling"},
|
|
|
|
|
{TimingType::setup_rising, "setup_rising"},
|
|
|
|
|
{TimingType::skew_falling, "skew_falling"},
|
|
|
|
|
{TimingType::skew_rising, "skew_rising"},
|
|
|
|
|
{TimingType::three_state_disable, "three_state_disable"},
|
|
|
|
|
{TimingType::three_state_disable_fall, "three_state_disable_fall"},
|
|
|
|
|
{TimingType::three_state_disable_rise, "three_state_disable_rise"},
|
|
|
|
|
{TimingType::three_state_enable, "three_state_enable"},
|
|
|
|
|
{TimingType::three_state_enable_fall, "three_state_enable_fall"},
|
|
|
|
|
{TimingType::three_state_enable_rise, "three_state_enable_rise"},
|
|
|
|
|
{TimingType::min_clock_tree_path, "min_clock_tree_path"},
|
|
|
|
|
{TimingType::max_clock_tree_path, "max_clock_tree_path"},
|
|
|
|
|
{TimingType::unknown, "unknown"}
|
|
|
|
|
};
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
timingTypeString(TimingType type)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
return timing_type_name_map.find(type);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TimingType
|
|
|
|
|
findTimingType(const char *type_name)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
return timing_type_name_map.find(type_name, TimingType::unknown);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
timingTypeIsCheck(TimingType type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
2019-03-13 01:25:53 +01:00
|
|
|
case TimingType::hold_falling:
|
|
|
|
|
case TimingType::hold_rising:
|
|
|
|
|
case TimingType::min_pulse_width:
|
|
|
|
|
case TimingType::minimum_period:
|
|
|
|
|
case TimingType::nochange_high_high:
|
|
|
|
|
case TimingType::nochange_high_low:
|
|
|
|
|
case TimingType::nochange_low_high:
|
|
|
|
|
case TimingType::nochange_low_low:
|
|
|
|
|
case TimingType::non_seq_hold_falling:
|
|
|
|
|
case TimingType::non_seq_hold_rising:
|
|
|
|
|
case TimingType::non_seq_setup_falling:
|
|
|
|
|
case TimingType::non_seq_setup_rising:
|
|
|
|
|
case TimingType::recovery_falling:
|
|
|
|
|
case TimingType::recovery_rising:
|
|
|
|
|
case TimingType::removal_falling:
|
|
|
|
|
case TimingType::removal_rising:
|
|
|
|
|
case TimingType::retaining_time:
|
|
|
|
|
case TimingType::setup_falling:
|
|
|
|
|
case TimingType::setup_rising:
|
|
|
|
|
case TimingType::skew_falling:
|
|
|
|
|
case TimingType::skew_rising:
|
2018-09-28 17:54:21 +02:00
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ScaleFactorType
|
|
|
|
|
timingTypeScaleFactorType(TimingType type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
2019-03-13 01:25:53 +01:00
|
|
|
case TimingType::non_seq_setup_falling:
|
|
|
|
|
case TimingType::non_seq_setup_rising:
|
|
|
|
|
case TimingType::setup_falling:
|
|
|
|
|
case TimingType::setup_rising:
|
|
|
|
|
return ScaleFactorType::setup;
|
|
|
|
|
case TimingType::hold_falling:
|
|
|
|
|
case TimingType::hold_rising:
|
|
|
|
|
case TimingType::non_seq_hold_falling:
|
|
|
|
|
case TimingType::non_seq_hold_rising:
|
|
|
|
|
return ScaleFactorType::hold;
|
|
|
|
|
case TimingType::recovery_falling:
|
|
|
|
|
case TimingType::recovery_rising:
|
|
|
|
|
return ScaleFactorType::recovery;
|
|
|
|
|
case TimingType::removal_falling:
|
|
|
|
|
case TimingType::removal_rising:
|
|
|
|
|
return ScaleFactorType::removal;
|
|
|
|
|
case TimingType::skew_falling:
|
|
|
|
|
case TimingType::skew_rising:
|
|
|
|
|
return ScaleFactorType::skew;
|
|
|
|
|
case TimingType::minimum_period:
|
|
|
|
|
return ScaleFactorType::min_period;
|
|
|
|
|
case TimingType::nochange_high_high:
|
|
|
|
|
case TimingType::nochange_high_low:
|
|
|
|
|
case TimingType::nochange_low_high:
|
|
|
|
|
case TimingType::nochange_low_low:
|
|
|
|
|
return ScaleFactorType::nochange;
|
|
|
|
|
case TimingType::min_pulse_width:
|
|
|
|
|
return ScaleFactorType::min_pulse_width;
|
|
|
|
|
case TimingType::clear:
|
|
|
|
|
case TimingType::combinational:
|
|
|
|
|
case TimingType::combinational_fall:
|
|
|
|
|
case TimingType::combinational_rise:
|
|
|
|
|
case TimingType::falling_edge:
|
|
|
|
|
case TimingType::preset:
|
|
|
|
|
case TimingType::retaining_time:
|
|
|
|
|
case TimingType::rising_edge:
|
|
|
|
|
case TimingType::three_state_disable:
|
|
|
|
|
case TimingType::three_state_disable_fall:
|
|
|
|
|
case TimingType::three_state_disable_rise:
|
|
|
|
|
case TimingType::three_state_enable:
|
|
|
|
|
case TimingType::three_state_enable_fall:
|
|
|
|
|
case TimingType::three_state_enable_rise:
|
|
|
|
|
case TimingType::min_clock_tree_path:
|
|
|
|
|
case TimingType::max_clock_tree_path:
|
|
|
|
|
return ScaleFactorType::cell;
|
|
|
|
|
case TimingType::unknown:
|
|
|
|
|
return ScaleFactorType::unknown;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
return ScaleFactorType::unknown;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|