2.0.2
This commit is contained in:
parent
4f381f6669
commit
a6e21377e6
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_INIT(sta, 2.0.1)
|
||||
AC_INIT(sta, 2.0.2)
|
||||
AM_INIT_AUTOMAKE
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_HEADERS(config.h)
|
||||
|
|
|
|||
|
|
@ -4,3 +4,5 @@ Release 2.0 Patches
|
|||
2018/10/23 read_verilog mod inst with no ports seg fault
|
||||
2018/11/08 corners > 2 causes internal error
|
||||
2018/11/09 Verilog ignore attributes (* blah *)
|
||||
2018/12/24 all_fanout from input port
|
||||
2018/12/25 liberty pg_types
|
||||
|
|
@ -1015,7 +1015,12 @@ private:
|
|||
class LibertyPgPort
|
||||
{
|
||||
public:
|
||||
enum PgType { unknown, power, ground };
|
||||
enum PgType { unknown,
|
||||
primary_power, primary_ground,
|
||||
backup_power, backup_ground,
|
||||
internal_power, internal_ground,
|
||||
nwell, pwell,
|
||||
deepnwell, deeppwell};
|
||||
LibertyPgPort(const char *name);
|
||||
~LibertyPgPort();
|
||||
const char *name() { return name_; }
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ typedef enum {
|
|||
table_axis_output_voltage,
|
||||
table_axis_path_depth,
|
||||
table_axis_path_distance,
|
||||
table_axis_normalized_voltage,
|
||||
table_axis_unknown
|
||||
} TableAxisVariable;
|
||||
|
||||
|
|
|
|||
|
|
@ -289,6 +289,7 @@ LibertyReader::defineVisitors()
|
|||
&LibertyReader::endScaledCell);
|
||||
defineAttrVisitor("clock_gating_integrated_cell",
|
||||
&LibertyReader::visitClockGatingIntegratedCell);
|
||||
|
||||
defineAttrVisitor("area", &LibertyReader::visitArea);
|
||||
defineAttrVisitor("dont_use", &LibertyReader::visitDontUse);
|
||||
defineAttrVisitor("is_macro", &LibertyReader::visitIsMacro);
|
||||
|
|
@ -4747,9 +4748,30 @@ LibertyReader::visitPgType(LibertyAttr *attr)
|
|||
const char *type_name = getAttrString(attr);
|
||||
LibertyPgPort::PgType type = LibertyPgPort::PgType::unknown;
|
||||
if (stringEqual(type_name, "primary_ground"))
|
||||
type = LibertyPgPort::PgType::ground;
|
||||
else if (stringEqual(type_name, "primary_power"))
|
||||
type = LibertyPgPort::PgType::power;
|
||||
type = LibertyPgPort::PgType::primary_ground;
|
||||
else if (stringEqual(type_name, "primary_power"))
|
||||
type = LibertyPgPort::PgType::primary_power;
|
||||
|
||||
else if (stringEqual(type_name, "backup_ground"))
|
||||
type = LibertyPgPort::PgType::backup_ground;
|
||||
else if (stringEqual(type_name, "backup_power"))
|
||||
type = LibertyPgPort::PgType::backup_power;
|
||||
|
||||
else if (stringEqual(type_name, "internal_ground"))
|
||||
type = LibertyPgPort::PgType::internal_ground;
|
||||
else if (stringEqual(type_name, "internal_power"))
|
||||
type = LibertyPgPort::PgType::internal_power;
|
||||
|
||||
else if (stringEqual(type_name, "nwell"))
|
||||
type = LibertyPgPort::PgType::nwell;
|
||||
else if (stringEqual(type_name, "pwell"))
|
||||
type = LibertyPgPort::PgType::pwell;
|
||||
|
||||
else if (stringEqual(type_name, "deepnwell"))
|
||||
type = LibertyPgPort::PgType::deepnwell;
|
||||
else if (stringEqual(type_name, "deeppwell"))
|
||||
type = LibertyPgPort::PgType::deeppwell;
|
||||
|
||||
else
|
||||
libError(attr, "unknown pg_type.\n");
|
||||
pg_port_->setPgType(type);
|
||||
|
|
|
|||
|
|
@ -1311,6 +1311,8 @@ stringTableAxisVariable(const char *variable)
|
|||
return table_axis_path_depth;
|
||||
else if (stringEq(variable, "path_distance"))
|
||||
return table_axis_path_distance;
|
||||
else if (stringEq(variable, "normalzied_voltage"))
|
||||
return table_axis_normalized_voltage;
|
||||
else
|
||||
return table_axis_unknown;
|
||||
}
|
||||
|
|
@ -1376,6 +1378,7 @@ tableVariableUnit(TableAxisVariable variable,
|
|||
case table_axis_path_distance:
|
||||
return units->distanceUnit();
|
||||
case table_axis_path_depth:
|
||||
case table_axis_normalized_voltage:
|
||||
case table_axis_unknown:
|
||||
return units->scalarUnit();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ include_HEADERS = \
|
|||
PathVertex.hh \
|
||||
PathVertexRep.hh \
|
||||
Power.hh \
|
||||
Property.hh \
|
||||
ReportPath.hh \
|
||||
Search.hh \
|
||||
SearchClass.hh \
|
||||
|
|
@ -84,14 +85,15 @@ libsearch_la_SOURCES = \
|
|||
PathVertex.cc \
|
||||
PathVertexRep.cc \
|
||||
Power.cc \
|
||||
Property.cc \
|
||||
ReportPath.cc \
|
||||
Search.cc \
|
||||
SearchPred.cc \
|
||||
Sim.cc \
|
||||
Tag.cc \
|
||||
TagGroup.cc \
|
||||
Sta.cc \
|
||||
StaState.cc \
|
||||
Tag.cc \
|
||||
TagGroup.cc \
|
||||
VertexVisitor.cc \
|
||||
VisitPathEnds.cc \
|
||||
VisitPathGroupVertices.cc \
|
||||
|
|
|
|||
|
|
@ -42,6 +42,12 @@ PathRef::PathRef(const PathRef &path) :
|
|||
{
|
||||
}
|
||||
|
||||
PathRef::PathRef(const PathRef *path) :
|
||||
path_vertex_(path->path_vertex_),
|
||||
path_enumed_(path->path_enumed_)
|
||||
{
|
||||
}
|
||||
|
||||
PathRef::PathRef(const PathVertex &path) :
|
||||
path_vertex_(&path),
|
||||
path_enumed_(NULL)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ public:
|
|||
PathRef();
|
||||
PathRef(const Path *path);
|
||||
PathRef(const PathRef &path);
|
||||
PathRef(const PathRef *path);
|
||||
PathRef(const PathVertex &path);
|
||||
void init();
|
||||
void init(const PathRef &path);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,572 @@
|
|||
// 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 "StringUtil.hh"
|
||||
#include "MinMax.hh"
|
||||
#include "Transition.hh"
|
||||
#include "PortDirection.hh"
|
||||
#include "Units.hh"
|
||||
#include "TimingArc.hh"
|
||||
#include "Liberty.hh"
|
||||
#include "Network.hh"
|
||||
#include "Graph.hh"
|
||||
#include "Clock.hh"
|
||||
#include "Corner.hh"
|
||||
#include "PathEnd.hh"
|
||||
#include "PathExpanded.hh"
|
||||
#include "PathRef.hh"
|
||||
#include "Property.hh"
|
||||
#include "Sta.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
static PropertyValue
|
||||
pinSlewProperty(const Pin *pin,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta);
|
||||
static PropertyValue
|
||||
pinSlackProperty(const Pin *pin,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta);
|
||||
static PropertyValue
|
||||
portSlewProperty(const Port *port,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta);
|
||||
static PropertyValue
|
||||
portSlackProperty(const Port *port,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta);
|
||||
static PropertyValue
|
||||
edgeDelayProperty(Edge *edge,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta);
|
||||
static float
|
||||
delayPropertyValue(Delay delay,
|
||||
Sta *sta);
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
PropertyValue::PropertyValue() :
|
||||
type_(type_none)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
PropertyValue::PropertyValue(const char *value) :
|
||||
type_(type_string)
|
||||
{
|
||||
init();
|
||||
string_ = stringCopy(value);
|
||||
}
|
||||
|
||||
PropertyValue::PropertyValue(float value) :
|
||||
type_(type_float)
|
||||
{
|
||||
init();
|
||||
float_ = value;
|
||||
}
|
||||
|
||||
PropertyValue::PropertyValue(Instance *value) :
|
||||
type_(type_instance)
|
||||
{
|
||||
init();
|
||||
inst_ = value;
|
||||
}
|
||||
|
||||
PropertyValue::PropertyValue(Pin *value) :
|
||||
type_(type_pin)
|
||||
{
|
||||
init();
|
||||
pin_ = value;
|
||||
}
|
||||
|
||||
PropertyValue::PropertyValue(PinSeq *value) :
|
||||
type_(type_pins)
|
||||
{
|
||||
init();
|
||||
pins_ = value;
|
||||
}
|
||||
|
||||
PropertyValue::PropertyValue(PinSet *value) :
|
||||
type_(type_pins)
|
||||
{
|
||||
init();
|
||||
pins_ = new PinSeq;
|
||||
PinSet::Iterator pin_iter(value);
|
||||
while (pin_iter.hasNext()) {
|
||||
Pin *pin = pin_iter.next();
|
||||
pins_->push_back( pin);
|
||||
}
|
||||
}
|
||||
|
||||
PropertyValue::PropertyValue(Net *value) :
|
||||
type_(type_net)
|
||||
{
|
||||
init();
|
||||
net_ = value;
|
||||
}
|
||||
|
||||
PropertyValue::PropertyValue(Clock *value) :
|
||||
type_(type_clock)
|
||||
{
|
||||
init();
|
||||
clk_ = value;
|
||||
}
|
||||
|
||||
PropertyValue::PropertyValue(ClockSeq *value) :
|
||||
type_(type_clocks)
|
||||
{
|
||||
init();
|
||||
clks_ = new ClockSeq(*value);
|
||||
}
|
||||
|
||||
PropertyValue::PropertyValue(ClockSet *value) :
|
||||
type_(type_clocks)
|
||||
{
|
||||
init();
|
||||
clks_ = new ClockSeq;
|
||||
ClockSet::Iterator clk_iter(value);
|
||||
while (clk_iter.hasNext()) {
|
||||
Clock *clk = clk_iter.next();
|
||||
clks_->push_back(clk);
|
||||
}
|
||||
}
|
||||
|
||||
PropertyValue::PropertyValue(PathRefSeq *value) :
|
||||
type_(type_path_refs)
|
||||
{
|
||||
init();
|
||||
path_refs_ = new PathRefSeq(*value);
|
||||
}
|
||||
|
||||
PropertyValue::PropertyValue(const PropertyValue &value) :
|
||||
type_(value.type_),
|
||||
string_(stringCopy(value.string_)),
|
||||
float_(value.float_),
|
||||
inst_(value.inst_),
|
||||
pin_(value.pin_),
|
||||
pins_(value.pins_ ? new PinSeq(*value.pins_) : NULL),
|
||||
net_(value.net_),
|
||||
clk_(value.clk_),
|
||||
clks_(value.clks_ ? new ClockSeq(*value.clks_) : NULL),
|
||||
path_refs_(value.path_refs_ ? new PathRefSeq(*value.path_refs_) : NULL)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
PropertyValue::init()
|
||||
{
|
||||
string_ = NULL;
|
||||
float_ = 0.0;
|
||||
inst_ = NULL;
|
||||
pin_ = NULL;
|
||||
pins_ = NULL;
|
||||
net_ = NULL;
|
||||
clk_ = NULL;
|
||||
clks_ = NULL;
|
||||
path_refs_ = NULL;
|
||||
}
|
||||
|
||||
PropertyValue::~PropertyValue()
|
||||
{
|
||||
stringDelete(string_);
|
||||
delete clks_;
|
||||
delete pins_;
|
||||
delete path_refs_;
|
||||
}
|
||||
|
||||
void
|
||||
PropertyValue::operator=(const PropertyValue &value)
|
||||
{
|
||||
type_ = value.type_;
|
||||
string_ = stringCopy(value.string_);
|
||||
float_ = value.float_;
|
||||
inst_ = value.inst_;
|
||||
pin_ = value.pin_;
|
||||
pins_ = value.pins_ ? new PinSeq(*value.pins_) : NULL;
|
||||
net_ = value.net_;
|
||||
clk_ = value.clk_;
|
||||
clks_ = value.clks_ ? new ClockSeq(*value.clks_) : NULL;
|
||||
path_refs_ = value.path_refs_ ? new PathRefSeq(*value.path_refs_) : NULL;
|
||||
}
|
||||
|
||||
PropertyValue
|
||||
getProperty(const Instance *inst,
|
||||
const char *property,
|
||||
Sta *sta)
|
||||
{
|
||||
Network *network = sta->network();
|
||||
if (stringEqual(property, "ref_name"))
|
||||
return PropertyValue(network->name(network->cell(inst)));
|
||||
else if (stringEqual(property, "full_name"))
|
||||
return PropertyValue(network->pathName(inst));
|
||||
else
|
||||
return PropertyValue();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
PropertyValue
|
||||
getProperty(const Pin *pin,
|
||||
const char *property,
|
||||
Sta *sta)
|
||||
{
|
||||
Network *network = sta->network();
|
||||
if (stringEqual(property, "direction"))
|
||||
return PropertyValue(network->direction(pin)->name());
|
||||
else if (stringEqual(property, "full_name"))
|
||||
return PropertyValue(network->pathName(pin));
|
||||
else if (stringEqual(property, "lib_pin_name"))
|
||||
return PropertyValue(network->portName(pin));
|
||||
else if (stringEqual(property, "clocks")) {
|
||||
ClockSet clks;
|
||||
sta->clocks(pin, clks);
|
||||
return PropertyValue(&clks);
|
||||
}
|
||||
|
||||
else if (stringEqual(property, "max_fall_slack"))
|
||||
return pinSlackProperty(pin, TransRiseFall::fall(), MinMax::max(), sta);
|
||||
else if (stringEqual(property, "max_rise_slack"))
|
||||
return pinSlackProperty(pin, TransRiseFall::rise(), MinMax::max(), sta);
|
||||
else if (stringEqual(property, "min_fall_slack"))
|
||||
return pinSlackProperty(pin, TransRiseFall::fall(), MinMax::min(), sta);
|
||||
else if (stringEqual(property, "min_rise_slack"))
|
||||
return pinSlackProperty(pin, TransRiseFall::rise(), MinMax::min(), sta);
|
||||
|
||||
else if (stringEqual(property, "actual_fall_transition_max"))
|
||||
return pinSlewProperty(pin, TransRiseFall::fall(), MinMax::max(), sta);
|
||||
else if (stringEqual(property, "actual_rise_transition_max"))
|
||||
return pinSlewProperty(pin, TransRiseFall::rise(), MinMax::max(), sta);
|
||||
else if (stringEqual(property, "actual_rise_transition_min"))
|
||||
return pinSlewProperty(pin, TransRiseFall::rise(), MinMax::min(), sta);
|
||||
else if (stringEqual(property, "actual_fall_transition_min"))
|
||||
return pinSlewProperty(pin, TransRiseFall::fall(), MinMax::min(), sta);
|
||||
|
||||
else
|
||||
return PropertyValue();
|
||||
}
|
||||
|
||||
static PropertyValue
|
||||
pinSlackProperty(const Pin *pin,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta)
|
||||
{
|
||||
Slack slack = sta->pinSlack(pin, tr, min_max);
|
||||
return PropertyValue(delayPropertyValue(slack, sta));
|
||||
}
|
||||
|
||||
static PropertyValue
|
||||
pinSlewProperty(const Pin *pin,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta)
|
||||
{
|
||||
auto graph = sta->graph();
|
||||
Vertex *vertex, *bidirect_drvr_vertex;
|
||||
graph->pinVertices(pin, vertex, bidirect_drvr_vertex);
|
||||
Slew slew = min_max->initValue();
|
||||
if (vertex) {
|
||||
Slew vertex_slew = sta->vertexSlew(vertex, tr, min_max);
|
||||
if (delayFuzzyGreater(vertex_slew, slew, min_max))
|
||||
slew = vertex_slew;
|
||||
}
|
||||
if (bidirect_drvr_vertex) {
|
||||
Slew vertex_slew = sta->vertexSlew(bidirect_drvr_vertex, tr, min_max);
|
||||
if (delayFuzzyGreater(vertex_slew, slew, min_max))
|
||||
slew = vertex_slew;
|
||||
}
|
||||
return PropertyValue(delayPropertyValue(slew, sta));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
PropertyValue
|
||||
getProperty(const Net *net,
|
||||
const char *property,
|
||||
Sta *sta)
|
||||
{
|
||||
Network *network = sta->network();
|
||||
if (stringEqual(property, "full_name"))
|
||||
return PropertyValue(network->pathName(net));
|
||||
else
|
||||
return PropertyValue();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
PropertyValue
|
||||
getProperty(const Port *port,
|
||||
const char *property,
|
||||
Sta *sta)
|
||||
{
|
||||
Network *network = sta->network();
|
||||
if (stringEqual(property, "direction"))
|
||||
return PropertyValue(network->direction(port)->name());
|
||||
else if (stringEqual(property, "full_name"))
|
||||
return PropertyValue(network->name(port));
|
||||
|
||||
else if (stringEqual(property, "actual_fall_transition_min"))
|
||||
return portSlewProperty(port, TransRiseFall::fall(), MinMax::min(), sta);
|
||||
else if (stringEqual(property, "actual_fall_transition_max"))
|
||||
return portSlewProperty(port, TransRiseFall::fall(), MinMax::max(), sta);
|
||||
else if (stringEqual(property, "actual_rise_transition_min"))
|
||||
return portSlewProperty(port, TransRiseFall::rise(), MinMax::min(), sta);
|
||||
else if (stringEqual(property, "actual_rise_transition_max"))
|
||||
return portSlewProperty(port, TransRiseFall::rise(), MinMax::max(), sta);
|
||||
|
||||
else if (stringEqual(property, "min_fall_slack"))
|
||||
return portSlackProperty(port, TransRiseFall::fall(), MinMax::min(), sta);
|
||||
else if (stringEqual(property, "max_fall_slack"))
|
||||
return portSlackProperty(port, TransRiseFall::fall(), MinMax::max(), sta);
|
||||
else if (stringEqual(property, "min_rise_slack"))
|
||||
return portSlackProperty(port, TransRiseFall::rise(), MinMax::min(), sta);
|
||||
else if (stringEqual(property, "max_rise_slack"))
|
||||
return portSlackProperty(port, TransRiseFall::rise(), MinMax::max(), sta);
|
||||
|
||||
else
|
||||
return PropertyValue();
|
||||
}
|
||||
|
||||
static PropertyValue
|
||||
portSlewProperty(const Port *port,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta)
|
||||
{
|
||||
Network *network = sta->network();
|
||||
Instance *top_inst = network->topInstance();
|
||||
Pin *pin = network->findPin(top_inst, port);
|
||||
return pinSlewProperty(pin, tr, min_max, sta);
|
||||
}
|
||||
|
||||
static PropertyValue
|
||||
portSlackProperty(const Port *port,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta)
|
||||
{
|
||||
Network *network = sta->network();
|
||||
Instance *top_inst = network->topInstance();
|
||||
Pin *pin = network->findPin(top_inst, port);
|
||||
return pinSlackProperty(pin, tr, min_max, sta);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
PropertyValue
|
||||
getProperty(const LibertyCell *cell,
|
||||
const char *property,
|
||||
Sta *sta)
|
||||
{
|
||||
if (stringEqual(property, "base_name"))
|
||||
return PropertyValue(cell->name());
|
||||
else if (stringEqual(property, "full_name")) {
|
||||
Network *network = sta->network();
|
||||
const LibertyLibrary *lib = cell->libertyLibrary();
|
||||
const char *lib_name = lib->name();
|
||||
const char *cell_name = cell->name();
|
||||
char *full_name = stringPrintTmp(strlen(lib_name) + strlen(cell_name) + 2,
|
||||
"%s%c%s",
|
||||
lib_name,
|
||||
network->pathDivider(),
|
||||
cell_name);
|
||||
return PropertyValue(full_name);
|
||||
}
|
||||
else
|
||||
return PropertyValue();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
PropertyValue
|
||||
getProperty(const LibertyPort *port,
|
||||
const char *property,
|
||||
Sta *)
|
||||
{
|
||||
if (stringEqual(property, "direction"))
|
||||
return PropertyValue(port->direction()->name());
|
||||
else if (stringEqual(property, "full_name"))
|
||||
return PropertyValue(port->name());
|
||||
else
|
||||
return PropertyValue();
|
||||
}
|
||||
|
||||
PropertyValue
|
||||
getProperty(const Library *lib,
|
||||
const char *property,
|
||||
Sta *sta)
|
||||
{
|
||||
Network *network = sta->network();
|
||||
if (stringEqual(property, "name"))
|
||||
return PropertyValue(network->name(lib));
|
||||
else
|
||||
return PropertyValue();
|
||||
}
|
||||
|
||||
PropertyValue
|
||||
getProperty(const LibertyLibrary *lib,
|
||||
const char *property,
|
||||
Sta *)
|
||||
{
|
||||
if (stringEqual(property, "name"))
|
||||
return PropertyValue(lib->name());
|
||||
else if (stringEqual(property, "filename"))
|
||||
return PropertyValue(lib->filename());
|
||||
else
|
||||
return PropertyValue();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
PropertyValue
|
||||
getProperty(Edge *edge,
|
||||
const char *property,
|
||||
Sta *sta)
|
||||
{
|
||||
if (stringEqual(property, "delay_min_fall"))
|
||||
return edgeDelayProperty(edge, TransRiseFall::fall(), MinMax::min(), sta);
|
||||
else if (stringEqual(property, "delay_max_fall"))
|
||||
return edgeDelayProperty(edge, TransRiseFall::fall(), MinMax::max(), sta);
|
||||
else if (stringEqual(property, "delay_min_rise"))
|
||||
return edgeDelayProperty(edge, TransRiseFall::rise(), MinMax::min(), sta);
|
||||
else if (stringEqual(property, "delay_max_rise"))
|
||||
return edgeDelayProperty(edge, TransRiseFall::rise(), MinMax::max(), sta);
|
||||
else if (stringEqual(property, "sense"))
|
||||
return PropertyValue(timingSenseString(edge->sense()));
|
||||
else if (stringEqual(property, "from_pin"))
|
||||
return PropertyValue(edge->from(sta->graph())->pin());
|
||||
else if (stringEqual(property, "to_pin"))
|
||||
return PropertyValue(edge->to(sta->graph())->pin());
|
||||
else
|
||||
return PropertyValue();
|
||||
}
|
||||
|
||||
static PropertyValue
|
||||
edgeDelayProperty(Edge *edge,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta)
|
||||
{
|
||||
ArcDelay delay = 0.0;
|
||||
bool delay_exists = false;
|
||||
TimingArcSet *arc_set = edge->timingArcSet();
|
||||
TimingArcSetArcIterator arc_iter(arc_set);
|
||||
while (arc_iter.hasNext()) {
|
||||
TimingArc *arc = arc_iter.next();
|
||||
TransRiseFall *to_tr = arc->toTrans()->asRiseFall();
|
||||
if (to_tr == tr) {
|
||||
CornerIterator corner_iter(sta);
|
||||
while (corner_iter.hasNext()) {
|
||||
Corner *corner = corner_iter.next();
|
||||
DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
|
||||
ArcDelay arc_delay = sta->arcDelay(edge, arc, dcalc_ap);
|
||||
if (!delay_exists
|
||||
|| ((min_max == MinMax::max()
|
||||
&& arc_delay > delay)
|
||||
|| (min_max == MinMax::min()
|
||||
&& arc_delay < delay)))
|
||||
delay = arc_delay;
|
||||
}
|
||||
}
|
||||
}
|
||||
return PropertyValue(delayPropertyValue(delay, sta));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
PropertyValue
|
||||
getProperty(Clock *clk,
|
||||
const char *property,
|
||||
Sta *sta)
|
||||
{
|
||||
if (stringEqual(property, "name"))
|
||||
return PropertyValue(clk->name());
|
||||
else if (stringEqual(property, "period"))
|
||||
return PropertyValue(sta->units()->timeUnit()->asString(clk->period(), 8));
|
||||
else if (stringEqual(property, "sources"))
|
||||
return PropertyValue(clk->pins());
|
||||
else if (stringEqual(property, "propagated"))
|
||||
return PropertyValue(clk->isPropagated() ? "1" : "0");
|
||||
else
|
||||
return PropertyValue();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
PropertyValue
|
||||
getProperty(PathEnd *end,
|
||||
const char *property,
|
||||
Sta *sta)
|
||||
{
|
||||
if (stringEqual(property, "startpoint")) {
|
||||
PathExpanded expanded(end->path(), sta);
|
||||
return PropertyValue(expanded.startPath()->pin(sta));
|
||||
}
|
||||
else if (stringEqual(property, "startpoint_clock"))
|
||||
return PropertyValue(end->path()->clock(sta));
|
||||
else if (stringEqual(property, "endpoint"))
|
||||
return PropertyValue(end->path()->pin(sta));
|
||||
else if (stringEqual(property, "endpoint_clock"))
|
||||
return PropertyValue(end->targetClk(sta));
|
||||
else if (stringEqual(property, "endpoint_clock_pin"))
|
||||
return PropertyValue(end->targetClkPath()->pin(sta));
|
||||
else if (stringEqual(property, "slack"))
|
||||
return PropertyValue(delayPropertyValue(end->slack(sta), sta));
|
||||
else if (stringEqual(property, "points")) {
|
||||
PathExpanded expanded(end->path(), sta);
|
||||
PathRefSeq paths;
|
||||
for (auto i = expanded.startIndex(); i < expanded.size(); i++) {
|
||||
PathRef *path = expanded.path(i);
|
||||
paths.push_back(*path);
|
||||
}
|
||||
return PropertyValue(&paths);
|
||||
}
|
||||
else
|
||||
return PropertyValue();
|
||||
}
|
||||
|
||||
PropertyValue
|
||||
getProperty(PathRef *path,
|
||||
const char *property,
|
||||
Sta *sta)
|
||||
{
|
||||
if (stringEqual(property, "pin"))
|
||||
return PropertyValue(path->pin(sta));
|
||||
else if (stringEqual(property, "arrival"))
|
||||
return PropertyValue(delayPropertyValue(path->arrival(sta), sta));
|
||||
else if (stringEqual(property, "required"))
|
||||
return PropertyValue(delayPropertyValue(path->required(sta), sta));
|
||||
else if (stringEqual(property, "slack"))
|
||||
return PropertyValue(delayPropertyValue(path->slack(sta), sta));
|
||||
else
|
||||
return PropertyValue();
|
||||
}
|
||||
|
||||
static float
|
||||
delayPropertyValue(Delay delay,
|
||||
Sta *sta)
|
||||
{
|
||||
return delayAsFloat(delay) / sta->units()->timeUnit()->scale();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
// 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/>.
|
||||
|
||||
#ifndef STA_PROPERTY_H
|
||||
#define STA_PROPERTY_H
|
||||
|
||||
#include "NetworkClass.hh"
|
||||
#include "SearchClass.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
class Sta;
|
||||
|
||||
class PropertyValue
|
||||
{
|
||||
public:
|
||||
enum Type { type_none, type_string, type_float,
|
||||
type_instance, type_pin, type_pins, type_net,
|
||||
type_clock, type_clocks, type_path_refs };
|
||||
PropertyValue();
|
||||
PropertyValue(const char *value);
|
||||
PropertyValue(float value);
|
||||
PropertyValue(Instance *value);
|
||||
PropertyValue(Pin *value);
|
||||
PropertyValue(PinSeq *value);
|
||||
PropertyValue(PinSet *value);
|
||||
PropertyValue(Net *value);
|
||||
PropertyValue(Clock *value);
|
||||
PropertyValue(ClockSeq *value);
|
||||
PropertyValue(ClockSet *value);
|
||||
PropertyValue(PathRefSeq *value);
|
||||
// Copy constructor.
|
||||
PropertyValue(const PropertyValue &props);
|
||||
~PropertyValue();
|
||||
Type type() const { return type_; }
|
||||
const char *string() const { return string_; }
|
||||
float floatValue() const { return float_; }
|
||||
Instance *instance() const { return inst_; }
|
||||
Pin *pin() const { return pin_; }
|
||||
PinSeq *pins() const { return pins_; }
|
||||
Net *net() const { return net_; }
|
||||
Clock *clock() const { return clk_; }
|
||||
ClockSeq *clocks() const { return clks_; }
|
||||
PathRefSeq *pathRefs() const { return path_refs_; }
|
||||
void operator=(const PropertyValue &);
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
Type type_;
|
||||
const char *string_;
|
||||
float float_;
|
||||
Instance *inst_;
|
||||
Pin *pin_;
|
||||
PinSeq *pins_;
|
||||
Net *net_;
|
||||
Clock *clk_;
|
||||
ClockSeq *clks_;
|
||||
PathRefSeq *path_refs_;
|
||||
};
|
||||
|
||||
PropertyValue
|
||||
getProperty(const Instance *inst,
|
||||
const char *property,
|
||||
Sta *sta);
|
||||
|
||||
PropertyValue
|
||||
getProperty(const Pin *pin,
|
||||
const char *property,
|
||||
Sta *sta);
|
||||
|
||||
PropertyValue
|
||||
getProperty(const Net *net,
|
||||
const char *property,
|
||||
Sta *sta);
|
||||
|
||||
PropertyValue
|
||||
getProperty(const Port *port,
|
||||
const char *property,
|
||||
Sta *sta);
|
||||
|
||||
PropertyValue
|
||||
getProperty(const LibertyCell *cell,
|
||||
const char *property,
|
||||
Sta *sta);
|
||||
|
||||
PropertyValue
|
||||
getProperty(const LibertyPort *port,
|
||||
const char *property,
|
||||
Sta *);
|
||||
|
||||
PropertyValue
|
||||
getProperty(const LibertyLibrary *lib,
|
||||
const char *property,
|
||||
Sta *sta);
|
||||
|
||||
PropertyValue
|
||||
getProperty(const Library *lib,
|
||||
const char *property,
|
||||
Sta *sta);
|
||||
|
||||
PropertyValue
|
||||
getProperty(Edge *edge,
|
||||
const char *property,
|
||||
Sta *sta);
|
||||
|
||||
PropertyValue
|
||||
getProperty(Clock *clk,
|
||||
const char *property,
|
||||
Sta *sta);
|
||||
|
||||
PropertyValue
|
||||
getProperty(PathEnd *end,
|
||||
const char *property,
|
||||
Sta *sta);
|
||||
|
||||
PropertyValue
|
||||
getProperty(PathRef *end,
|
||||
const char *property,
|
||||
Sta *sta);
|
||||
|
||||
} // namespace
|
||||
#endif
|
||||
70
tcl/Cmds.tcl
70
tcl/Cmds.tcl
|
|
@ -1772,31 +1772,31 @@ proc get_property_cmd { cmd type_key cmd_args } {
|
|||
set object [get_property_object $object_type $object $quiet]
|
||||
}
|
||||
set object_type [object_type $object]
|
||||
set attr [lindex $cmd_args 1]
|
||||
set prop [lindex $cmd_args 1]
|
||||
if { $object_type == "Instance" } {
|
||||
return [instance_property $object $attr]
|
||||
return [instance_property $object $prop]
|
||||
} elseif { $object_type == "Pin" } {
|
||||
return [pin_property $object $attr]
|
||||
return [pin_property $object $prop]
|
||||
} elseif { $object_type == "Net" } {
|
||||
return [net_property $object $attr]
|
||||
return [net_property $object $prop]
|
||||
} elseif { $object_type == "Clock" } {
|
||||
return [clock_property $object $attr]
|
||||
return [clock_property $object $prop]
|
||||
} elseif { $object_type == "Port" } {
|
||||
return [port_property $object $attr]
|
||||
return [port_property $object $prop]
|
||||
} elseif { $object_type == "LibertyPort" } {
|
||||
return [liberty_port_property $object $attr]
|
||||
return [liberty_port_property $object $prop]
|
||||
} elseif { $object_type == "LibertyCell" } {
|
||||
return [liberty_cell_property $object $attr]
|
||||
return [liberty_cell_property $object $prop]
|
||||
} elseif { $object_type == "Library" } {
|
||||
return [library_property $object $attr]
|
||||
return [library_property $object $prop]
|
||||
} elseif { $object_type == "LibertyLibrary" } {
|
||||
return [liberty_library_property $object $attr]
|
||||
return [liberty_library_property $object $prop]
|
||||
} elseif { $object_type == "Edge" } {
|
||||
return [edge_property $object $attr]
|
||||
return [edge_property $object $prop]
|
||||
} elseif { $object_type == "PathEnd" } {
|
||||
return [path_end_property $object $attr]
|
||||
return [path_end_property $object $prop]
|
||||
} elseif { $object_type == "PathRef" } {
|
||||
return [path_ref_property $object $attr]
|
||||
return [path_ref_property $object $prop]
|
||||
} else {
|
||||
sta_error "$cmd unsupported object type $object_type."
|
||||
}
|
||||
|
|
@ -1829,50 +1829,6 @@ proc get_property_object { object_type object_name quiet } {
|
|||
return [lindex $object 0]
|
||||
}
|
||||
|
||||
proc edge_property { edge property } {
|
||||
if { $property == "from_pin" } {
|
||||
return [$edge from_pin]
|
||||
} elseif { $property == "to_pin" } {
|
||||
return [$edge to_pin]
|
||||
} else {
|
||||
return [edge_string_property $edge $property]
|
||||
}
|
||||
}
|
||||
|
||||
proc path_end_property { path_end property } {
|
||||
if { $property == "points" } {
|
||||
return [$path_end points]
|
||||
} elseif { $property == "startpoint" } {
|
||||
return [$path_end startpoint]
|
||||
} elseif { $property == "startpoint_clock" } {
|
||||
return [$path_end startpoint_clock]
|
||||
} elseif { $property == "endpoint" } {
|
||||
return [$path_end endpoint]
|
||||
} elseif { $property == "endpoint_clock" } {
|
||||
return [$path_end endpoint_clock]
|
||||
} elseif { $property == "endpoint_clock_pin" } {
|
||||
return [$path_end endpoint_clock_pin]
|
||||
} elseif { $property == "slack" } {
|
||||
return [time_sta_ui [$path_end slack]]
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
proc path_ref_property { path property } {
|
||||
if { $property == "pin" } {
|
||||
return [$path pin]
|
||||
} elseif { $property == "arrival" } {
|
||||
return [time_sta_ui [$path arrival]]
|
||||
} elseif { $property == "required" } {
|
||||
return [time_sta_ui [$path required]]
|
||||
} elseif { $property == "slack" } {
|
||||
return [time_sta_ui [$path slack]]
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
proc get_object_type { obj } {
|
||||
set object_type [object_type $obj]
|
||||
if { $object_type == "Clock" } {
|
||||
|
|
|
|||
|
|
@ -25,4 +25,5 @@ EXTRA_DIST = \
|
|||
libs:
|
||||
|
||||
xtags:
|
||||
etags -a -o ../TAGS $(TCL_INIT_FILES) $(TCL_SRCS)
|
||||
etags -a -o ../TAGS --lang=none --regex='/proc[ \t]+\([^ \t]+\)/\1/' \
|
||||
$(TCL_INIT_FILES) $(TCL_SRCS)
|
||||
|
|
|
|||
628
tcl/StaTcl.i
628
tcl/StaTcl.i
|
|
@ -77,6 +77,7 @@
|
|||
#include "PathAnalysisPt.hh"
|
||||
#include "ReportPath.hh"
|
||||
#include "Power.hh"
|
||||
#include "Property.hh"
|
||||
#include "Sta.hh"
|
||||
|
||||
namespace sta {
|
||||
|
|
@ -105,43 +106,6 @@ typedef MinMaxAll MinMaxAllNull;
|
|||
typedef ClockSet TmpClockSet;
|
||||
typedef StringSeq TmpStringSeq;
|
||||
|
||||
static const char *
|
||||
pinSlewProperty(const Pin *pin,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta);
|
||||
static const char *
|
||||
pinSlackProperty(const Pin *pin,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta);
|
||||
static const char *
|
||||
portSlewProperty(const Port *port,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta);
|
||||
static const char *
|
||||
portSlackProperty(const Port *port,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta);
|
||||
static const char *
|
||||
pinClocksProperty(const Pin *pin,
|
||||
Sta *sta);
|
||||
const char *
|
||||
clockProperty(Clock *clk,
|
||||
const char *property,
|
||||
Network *network,
|
||||
Sta *sta);
|
||||
static const char *
|
||||
clockSourcesProperty(Clock *clk,
|
||||
Network *network);
|
||||
static const char *
|
||||
pathNamesString(PinSet *pins,
|
||||
Network *network);
|
||||
static const char *
|
||||
clkNamesString(ClockSet *clks);
|
||||
|
||||
class CmdErrorNetworkNotLinked : public StaException
|
||||
{
|
||||
public:
|
||||
|
|
@ -456,353 +420,6 @@ TclListSeqEdge(Tcl_Obj * const source, Tcl_Interp *interp)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
portProperty(const Port *port,
|
||||
const char *property,
|
||||
Sta *sta)
|
||||
{
|
||||
Network *network = sta->network();
|
||||
if (stringEqual(property, "direction"))
|
||||
return network->direction(port)->name();
|
||||
else if (stringEqual(property, "full_name"))
|
||||
return network->name(port);
|
||||
|
||||
else if (stringEqual(property, "actual_fall_transition_min"))
|
||||
return portSlewProperty(port, TransRiseFall::fall(), MinMax::min(), sta);
|
||||
else if (stringEqual(property, "actual_fall_transition_max"))
|
||||
return portSlewProperty(port, TransRiseFall::fall(), MinMax::max(), sta);
|
||||
else if (stringEqual(property, "actual_rise_transition_min"))
|
||||
return portSlewProperty(port, TransRiseFall::rise(), MinMax::min(), sta);
|
||||
else if (stringEqual(property, "actual_rise_transition_max"))
|
||||
return portSlewProperty(port, TransRiseFall::rise(), MinMax::max(), sta);
|
||||
|
||||
else if (stringEqual(property, "min_fall_slack"))
|
||||
return portSlackProperty(port, TransRiseFall::fall(), MinMax::min(), sta);
|
||||
else if (stringEqual(property, "max_fall_slack"))
|
||||
return portSlackProperty(port, TransRiseFall::fall(), MinMax::max(), sta);
|
||||
else if (stringEqual(property, "min_rise_slack"))
|
||||
return portSlackProperty(port, TransRiseFall::rise(), MinMax::min(), sta);
|
||||
else if (stringEqual(property, "max_rise_slack"))
|
||||
return portSlackProperty(port, TransRiseFall::rise(), MinMax::max(), sta);
|
||||
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *
|
||||
portSlewProperty(const Port *port,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta)
|
||||
{
|
||||
Network *network = sta->network();
|
||||
Instance *top_inst = network->topInstance();
|
||||
Pin *pin = network->findPin(top_inst, port);
|
||||
return pinSlewProperty(pin, tr, min_max, sta);
|
||||
}
|
||||
|
||||
static const char *
|
||||
portSlackProperty(const Port *port,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta)
|
||||
{
|
||||
Network *network = sta->network();
|
||||
Instance *top_inst = network->topInstance();
|
||||
Pin *pin = network->findPin(top_inst, port);
|
||||
return pinSlackProperty(pin, tr, min_max, sta);
|
||||
}
|
||||
|
||||
const char *
|
||||
libertyCellProperty(const LibertyCell *cell,
|
||||
const char *property,
|
||||
Network *network)
|
||||
{
|
||||
if (stringEqual(property, "base_name"))
|
||||
return cell->name();
|
||||
else if (stringEqual(property, "full_name")) {
|
||||
const LibertyLibrary *lib = cell->libertyLibrary();
|
||||
const char *lib_name = lib->name();
|
||||
const char *cell_name = cell->name();
|
||||
return stringPrintTmp(strlen(lib_name) + strlen(cell_name) + 2,
|
||||
"%s%c%s",
|
||||
lib_name,
|
||||
network->pathDivider(),
|
||||
cell_name);
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
libertyPortProperty(const LibertyPort *port,
|
||||
const char *property)
|
||||
{
|
||||
if (stringEqual(property, "direction"))
|
||||
return port->direction()->name();
|
||||
else if (stringEqual(property, "full_name"))
|
||||
return port->name();
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
instanceProperty(const Instance *inst,
|
||||
const char *property,
|
||||
Network *network)
|
||||
{
|
||||
if (stringEqual(property, "ref_name"))
|
||||
return network->name(network->cell(inst));
|
||||
else if (stringEqual(property, "full_name"))
|
||||
return network->pathName(inst);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
pinProperty(const Pin *pin,
|
||||
const char *property,
|
||||
Network *network,
|
||||
Sta *sta)
|
||||
{
|
||||
if (stringEqual(property, "direction"))
|
||||
return network->direction(pin)->name();
|
||||
else if (stringEqual(property, "full_name"))
|
||||
return network->pathName(pin);
|
||||
else if (stringEqual(property, "lib_pin_name"))
|
||||
return network->portName(pin);
|
||||
else if (stringEqual(property, "clocks"))
|
||||
return pinClocksProperty(pin, sta);
|
||||
|
||||
else if (stringEqual(property, "max_fall_slack"))
|
||||
return pinSlackProperty(pin, TransRiseFall::fall(), MinMax::max(), sta);
|
||||
else if (stringEqual(property, "max_rise_slack"))
|
||||
return pinSlackProperty(pin, TransRiseFall::rise(), MinMax::max(), sta);
|
||||
else if (stringEqual(property, "min_fall_slack"))
|
||||
return pinSlackProperty(pin, TransRiseFall::fall(), MinMax::min(), sta);
|
||||
else if (stringEqual(property, "min_rise_slack"))
|
||||
return pinSlackProperty(pin, TransRiseFall::rise(), MinMax::min(), sta);
|
||||
|
||||
else if (stringEqual(property, "actual_fall_transition_max"))
|
||||
return pinSlewProperty(pin, TransRiseFall::fall(), MinMax::max(), sta);
|
||||
else if (stringEqual(property, "actual_rise_transition_max"))
|
||||
return pinSlewProperty(pin, TransRiseFall::rise(), MinMax::max(), sta);
|
||||
else if (stringEqual(property, "actual_rise_transition_min"))
|
||||
return pinSlewProperty(pin, TransRiseFall::rise(), MinMax::min(), sta);
|
||||
else if (stringEqual(property, "actual_fall_transition_min"))
|
||||
return pinSlewProperty(pin, TransRiseFall::fall(), MinMax::min(), sta);
|
||||
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *
|
||||
pinClocksProperty(const Pin *pin,
|
||||
Sta *sta)
|
||||
{
|
||||
ClockSet clks;
|
||||
sta->clocks(pin, clks);
|
||||
return clkNamesString(&clks);
|
||||
}
|
||||
|
||||
static const char *
|
||||
pinSlewProperty(const Pin *pin,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta)
|
||||
{
|
||||
Graph *graph = sta->ensureGraph();
|
||||
Vertex *vertex, *bidirect_drvr_vertex;
|
||||
graph->pinVertices(pin, vertex, bidirect_drvr_vertex);
|
||||
Slew slew = min_max->initValue();
|
||||
if (vertex) {
|
||||
Slew vertex_slew = sta->vertexSlew(vertex, tr, min_max);
|
||||
if (delayFuzzyGreater(vertex_slew, slew, min_max))
|
||||
slew = vertex_slew;
|
||||
}
|
||||
if (bidirect_drvr_vertex) {
|
||||
Slew vertex_slew = sta->vertexSlew(bidirect_drvr_vertex, tr, min_max);
|
||||
if (delayFuzzyGreater(vertex_slew, slew, min_max))
|
||||
slew = vertex_slew;
|
||||
}
|
||||
return sta->units()->timeUnit()->asString(delayAsFloat(slew), 8);
|
||||
}
|
||||
|
||||
static const char *
|
||||
pinSlackProperty(const Pin *pin,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta)
|
||||
{
|
||||
Slack slack = sta->pinSlack(pin, tr, min_max);
|
||||
return sta->units()->timeUnit()->asString(delayAsFloat(slack), 8);
|
||||
}
|
||||
|
||||
const char *
|
||||
netProperty(const Net *net,
|
||||
const char *property,
|
||||
Network *network)
|
||||
{
|
||||
if (stringEqual(property, "full_name"))
|
||||
return network->pathName(net);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
libraryProperty(const Library *lib,
|
||||
const char *property,
|
||||
Network *network)
|
||||
{
|
||||
if (stringEqual(property, "name"))
|
||||
return network->name(lib);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
libertyLibraryProperty(const LibertyLibrary *lib,
|
||||
const char *property)
|
||||
{
|
||||
if (stringEqual(property, "name"))
|
||||
return lib->name();
|
||||
else if (stringEqual(property, "filename"))
|
||||
return lib->filename();
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
edgeDelayProperty(Edge *edge,
|
||||
const TransRiseFall *tr,
|
||||
const MinMax *min_max,
|
||||
Sta *sta)
|
||||
{
|
||||
ArcDelay delay = 0.0;
|
||||
bool delay_exists = false;
|
||||
TimingArcSet *arc_set = edge->timingArcSet();
|
||||
TimingArcSetArcIterator arc_iter(arc_set);
|
||||
while (arc_iter.hasNext()) {
|
||||
TimingArc *arc = arc_iter.next();
|
||||
TransRiseFall *to_tr = arc->toTrans()->asRiseFall();
|
||||
if (to_tr == tr) {
|
||||
CornerIterator corner_iter(sta);
|
||||
while (corner_iter.hasNext()) {
|
||||
Corner *corner = corner_iter.next();
|
||||
DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
|
||||
ArcDelay arc_delay = sta->arcDelay(edge, arc, dcalc_ap);
|
||||
if (!delay_exists
|
||||
|| ((min_max == MinMax::max()
|
||||
&& arc_delay > delay)
|
||||
|| (min_max == MinMax::min()
|
||||
&& arc_delay < delay)))
|
||||
delay = arc_delay;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sta->units()->timeUnit()->asString(delayAsFloat(delay), 8);
|
||||
}
|
||||
|
||||
const char *
|
||||
edgeStringProperty(Edge *edge,
|
||||
const char *property,
|
||||
Sta *sta)
|
||||
{
|
||||
if (stringEqual(property, "delay_min_fall"))
|
||||
return edgeDelayProperty(edge, TransRiseFall::fall(), MinMax::min(), sta);
|
||||
else if (stringEqual(property, "delay_max_fall"))
|
||||
return edgeDelayProperty(edge, TransRiseFall::fall(), MinMax::max(), sta);
|
||||
else if (stringEqual(property, "delay_min_rise"))
|
||||
return edgeDelayProperty(edge, TransRiseFall::rise(), MinMax::min(), sta);
|
||||
else if (stringEqual(property, "delay_max_rise"))
|
||||
return edgeDelayProperty(edge, TransRiseFall::rise(), MinMax::max(), sta);
|
||||
else if (stringEqual(property, "sense"))
|
||||
return timingSenseString(edge->sense());
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
clockProperty(Clock *clk,
|
||||
const char *property,
|
||||
Network *network,
|
||||
Sta *sta)
|
||||
{
|
||||
if (stringEqual(property, "name"))
|
||||
return clk->name();
|
||||
else if (stringEqual(property, "period"))
|
||||
return sta->units()->timeUnit()->asString(clk->period(), 8);
|
||||
else if (stringEqual(property, "sources"))
|
||||
return clockSourcesProperty(clk, network);
|
||||
else if (stringEqual(property, "propagated"))
|
||||
return clk->isPropagated() ? "1" : "0";
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *
|
||||
clockSourcesProperty(Clock *clk,
|
||||
Network *network)
|
||||
{
|
||||
return pathNamesString(clk->pins(), network);
|
||||
}
|
||||
|
||||
// Concatenate the pin names separated with a space.
|
||||
static const char *
|
||||
pathNamesString(PinSet *pins,
|
||||
Network *network)
|
||||
{
|
||||
int length = 1;
|
||||
PinSet::Iterator pin_iter1(pins);
|
||||
while (pin_iter1.hasNext()) {
|
||||
Pin *pin = pin_iter1.next();
|
||||
const char *name = network->pathName(pin);
|
||||
length += strlen(name) + 1;
|
||||
}
|
||||
char *result = makeTmpString(length);
|
||||
char *s = result;
|
||||
bool first = true;
|
||||
PinSet::Iterator pin_iter2(pins);
|
||||
while (pin_iter2.hasNext()) {
|
||||
Pin *pin = pin_iter2.next();
|
||||
const char *name = network->pathName(pin);
|
||||
if (!first)
|
||||
*s++ = ' ';
|
||||
strcpy(s, name);
|
||||
s += strlen(name);
|
||||
first = false;
|
||||
}
|
||||
*s = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
// Concatenate the clock names separated with a space.
|
||||
static const char *
|
||||
clkNamesString(ClockSet *clks)
|
||||
{
|
||||
int length = 1;
|
||||
ClockSet::Iterator clk_iter1(clks);
|
||||
while (clk_iter1.hasNext()) {
|
||||
Clock *clk = clk_iter1.next();
|
||||
length += strlen(clk->name()) + 1;
|
||||
}
|
||||
char *result = makeTmpString(length);
|
||||
char *s = result;
|
||||
bool first = true;
|
||||
ClockSet::Iterator clk_iter2(clks);
|
||||
while (clk_iter2.hasNext()) {
|
||||
Clock *clk = clk_iter2.next();
|
||||
const char *name = clk->name();
|
||||
if (!first)
|
||||
*s++ = ' ';
|
||||
strcpy(s, name);
|
||||
s += strlen(name);
|
||||
first = false;
|
||||
}
|
||||
*s = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
TmpPinSet *
|
||||
|
|
@ -1777,9 +1394,6 @@ using namespace sta;
|
|||
}
|
||||
|
||||
%typemap(out) PathEndSeq* {
|
||||
Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
|
||||
Tcl_SetObjResult(interp, obj);
|
||||
|
||||
Tcl_Obj *list = Tcl_NewListObj(0, NULL);
|
||||
const PathEndSeq *path_ends = $1;
|
||||
PathEndSeq::ConstIterator end_iter(path_ends);
|
||||
|
|
@ -1788,6 +1402,7 @@ using namespace sta;
|
|||
Tcl_Obj *obj = SWIG_NewInstanceObj(path_end, SWIGTYPE_p_PathEnd, false);
|
||||
Tcl_ListObjAppendElement(interp, list, obj);
|
||||
}
|
||||
// Delete the PathEndSeq, not the ends.
|
||||
delete path_ends;
|
||||
Tcl_SetObjResult(interp, list);
|
||||
}
|
||||
|
|
@ -1810,7 +1425,6 @@ using namespace sta;
|
|||
Tcl_Obj *obj = SWIG_NewInstanceObj(copy, SWIGTYPE_p_PathRef, false);
|
||||
Tcl_ListObjAppendElement(interp, list, obj);
|
||||
}
|
||||
delete paths;
|
||||
Tcl_SetObjResult(interp, list);
|
||||
}
|
||||
|
||||
|
|
@ -1907,6 +1521,87 @@ using namespace sta;
|
|||
Tcl_SetObjResult(interp, obj);
|
||||
}
|
||||
|
||||
%typemap(out) PropertyValue {
|
||||
PropertyValue value = $1;
|
||||
switch (value.type()) {
|
||||
case PropertyValue::Type::type_none:
|
||||
Tcl_SetResult(interp, const_cast<char*>(""), TCL_STATIC);
|
||||
break;
|
||||
case PropertyValue::Type::type_string:
|
||||
Tcl_SetResult(interp, const_cast<char*>(value.string()), TCL_VOLATILE);
|
||||
break;
|
||||
case PropertyValue::Type::type_float: {
|
||||
char *float_string = stringPrint(10, "%.5f", value.floatValue());
|
||||
Tcl_SetResult(interp, float_string, TCL_VOLATILE);
|
||||
stringDelete(float_string);
|
||||
}
|
||||
break;
|
||||
case PropertyValue::Type::type_instance: {
|
||||
Tcl_Obj *obj = SWIG_NewInstanceObj(value.instance(),
|
||||
SWIGTYPE_p_Instance, false);
|
||||
Tcl_SetObjResult(interp, obj);
|
||||
}
|
||||
break;
|
||||
case PropertyValue::Type::type_pin: {
|
||||
Tcl_Obj *obj = SWIG_NewInstanceObj(value.pin(), SWIGTYPE_p_Pin, false);
|
||||
Tcl_SetObjResult(interp, obj);
|
||||
}
|
||||
break;
|
||||
case PropertyValue::Type::type_pins: {
|
||||
Tcl_Obj *list = Tcl_NewListObj(0, NULL);
|
||||
PinSeq *pins = value.pins();
|
||||
PinSeq::Iterator pin_iter(pins);
|
||||
while (pin_iter.hasNext()) {
|
||||
Pin *pin = pin_iter.next();
|
||||
Tcl_Obj *obj = SWIG_NewInstanceObj(pin, SWIGTYPE_p_Pin, false);
|
||||
Tcl_ListObjAppendElement(interp, list, obj);
|
||||
}
|
||||
Tcl_SetObjResult(interp, list);
|
||||
}
|
||||
break;
|
||||
case PropertyValue::Type::type_net: {
|
||||
Tcl_Obj *obj = SWIG_NewInstanceObj(value.net(),
|
||||
SWIGTYPE_p_Net, false);
|
||||
Tcl_SetObjResult(interp, obj);
|
||||
}
|
||||
break;
|
||||
case PropertyValue::Type::type_clock: {
|
||||
Tcl_Obj *obj = SWIG_NewInstanceObj(value.clock(),
|
||||
SWIGTYPE_p_Clock, false);
|
||||
Tcl_SetObjResult(interp, obj);
|
||||
}
|
||||
break;
|
||||
case PropertyValue::Type::type_clocks: {
|
||||
Tcl_Obj *list = Tcl_NewListObj(0, NULL);
|
||||
ClockSeq *clks = value.clocks();
|
||||
ClockSeq::Iterator clk_iter(clks);
|
||||
while (clk_iter.hasNext()) {
|
||||
Clock *clk = clk_iter.next();
|
||||
Tcl_Obj *obj = SWIG_NewInstanceObj(clk, SWIGTYPE_p_Clock, false);
|
||||
Tcl_ListObjAppendElement(interp, list, obj);
|
||||
}
|
||||
Tcl_SetObjResult(interp, list);
|
||||
}
|
||||
break;
|
||||
case PropertyValue::Type::type_path_refs: {
|
||||
Tcl_Obj *list = Tcl_NewListObj(0, NULL);
|
||||
PathRefSeq *paths = value.pathRefs();
|
||||
PathRefSeq::Iterator path_iter(paths);
|
||||
while (path_iter.hasNext()) {
|
||||
PathRef &path = path_iter.next();
|
||||
PathRef *copy = new PathRef(path);
|
||||
Tcl_Obj *obj = SWIG_NewInstanceObj(copy, SWIGTYPE_p_PathRef, false);
|
||||
Tcl_ListObjAppendElement(interp, list, obj);
|
||||
}
|
||||
Tcl_SetObjResult(interp, list);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Tcl_SetResult(interp, const_cast<char*>(""), TCL_STATIC);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Empty class definitions to make swig happy.
|
||||
|
|
@ -2838,7 +2533,8 @@ filter_ports(const char *property,
|
|||
bool exact_match = stringEq(op, "==");
|
||||
while (port_iter.hasNext()) {
|
||||
Port *port = port_iter.next();
|
||||
const char *prop = portProperty(port, property, sta);
|
||||
PropertyValue value(getProperty(port, property, sta));
|
||||
const char *prop = value.string();
|
||||
if (prop &&
|
||||
((exact_match && stringEq(prop, pattern))
|
||||
|| (!exact_match && patternMatch(pattern, prop))))
|
||||
|
|
@ -2854,13 +2550,15 @@ filter_insts(const char *property,
|
|||
const char *pattern,
|
||||
InstanceSeq *insts)
|
||||
{
|
||||
Network *network = cmdLinkedNetwork();
|
||||
Sta *sta = Sta::sta();
|
||||
cmdLinkedNetwork();
|
||||
TmpInstanceSeq *filtered_insts = new TmpInstanceSeq;
|
||||
TmpInstanceSeq::Iterator inst_iter(insts);
|
||||
bool exact_match = stringEq(op, "==");
|
||||
while (inst_iter.hasNext()) {
|
||||
Instance *inst = inst_iter.next();
|
||||
const char *prop = instanceProperty(inst, property, network);
|
||||
PropertyValue value(getProperty(inst, property, sta));
|
||||
const char *prop = value.string();
|
||||
if (prop &&
|
||||
((exact_match && stringEq(prop, pattern))
|
||||
|| (!exact_match && patternMatch(pattern, prop))))
|
||||
|
|
@ -2876,14 +2574,14 @@ filter_pins(const char *property,
|
|||
const char *pattern,
|
||||
PinSeq *pins)
|
||||
{
|
||||
Network *network = cmdLinkedNetwork();
|
||||
Sta *sta = Sta::sta();
|
||||
PinSeq *filtered_pins = new PinSeq;
|
||||
PinSeq::Iterator pin_iter(pins);
|
||||
bool exact_match = stringEq(op, "==");
|
||||
while (pin_iter.hasNext()) {
|
||||
Pin *pin = pin_iter.next();
|
||||
const char *prop = pinProperty(pin, property, network, sta);
|
||||
PropertyValue value(getProperty(pin, property, sta));
|
||||
const char *prop = value.string();
|
||||
if (prop &&
|
||||
((exact_match && stringEq(prop, pattern))
|
||||
|| (!exact_match && patternMatch(pattern, prop))))
|
||||
|
|
@ -2893,75 +2591,100 @@ filter_pins(const char *property,
|
|||
return filtered_pins;
|
||||
}
|
||||
|
||||
const char *
|
||||
PropertyValue
|
||||
pin_property(const Pin *pin,
|
||||
const char *property)
|
||||
{
|
||||
return pinProperty(pin, property, cmdLinkedNetwork(), Sta::sta());
|
||||
cmdGraph();
|
||||
return getProperty(pin, property, Sta::sta());
|
||||
}
|
||||
|
||||
const char *
|
||||
PropertyValue
|
||||
instance_property(const Instance *inst,
|
||||
const char *property)
|
||||
{
|
||||
return instanceProperty(inst, property, cmdLinkedNetwork());
|
||||
cmdGraph();
|
||||
return getProperty(inst, property, Sta::sta());
|
||||
}
|
||||
|
||||
const char *
|
||||
PropertyValue
|
||||
net_property(const Net *net,
|
||||
const char *property)
|
||||
{
|
||||
return netProperty(net, property, cmdLinkedNetwork());
|
||||
cmdGraph();
|
||||
return getProperty(net, property, Sta::sta());
|
||||
}
|
||||
|
||||
const char *
|
||||
PropertyValue
|
||||
port_property(const Port *port,
|
||||
const char *property)
|
||||
{
|
||||
Sta *sta = Sta::sta();
|
||||
return portProperty(port, property, sta);
|
||||
cmdGraph();
|
||||
return getProperty(port, property, Sta::sta());
|
||||
}
|
||||
|
||||
const char *
|
||||
|
||||
PropertyValue
|
||||
liberty_cell_property(const LibertyCell *cell,
|
||||
const char *property)
|
||||
{
|
||||
return libertyCellProperty(cell, property, cmdLinkedNetwork());
|
||||
cmdLinkedNetwork();
|
||||
return getProperty(cell, property, Sta::sta());
|
||||
}
|
||||
|
||||
const char *
|
||||
PropertyValue
|
||||
liberty_port_property(const LibertyPort *port,
|
||||
const char *property)
|
||||
{
|
||||
return libertyPortProperty(port, property);
|
||||
cmdLinkedNetwork();
|
||||
return getProperty(port, property, Sta::sta());
|
||||
}
|
||||
|
||||
const char *
|
||||
PropertyValue
|
||||
library_property(const Library *lib,
|
||||
const char *property)
|
||||
{
|
||||
return libraryProperty(lib, property, cmdLinkedNetwork());
|
||||
cmdLinkedNetwork();
|
||||
return getProperty(lib, property, Sta::sta());
|
||||
}
|
||||
|
||||
const char *
|
||||
PropertyValue
|
||||
liberty_library_property(const LibertyLibrary *lib,
|
||||
const char *property)
|
||||
{
|
||||
return libertyLibraryProperty(lib, property);
|
||||
return getProperty(lib, property, Sta::sta());
|
||||
}
|
||||
|
||||
const char *
|
||||
edge_string_property(Edge *edge,
|
||||
const char *property)
|
||||
PropertyValue
|
||||
edge_property(Edge *edge,
|
||||
const char *property)
|
||||
{
|
||||
return edgeStringProperty(edge, property, Sta::sta());
|
||||
cmdGraph();
|
||||
return getProperty(edge, property, Sta::sta());
|
||||
}
|
||||
|
||||
const char *
|
||||
PropertyValue
|
||||
clock_property(Clock *clk,
|
||||
const char *property)
|
||||
{
|
||||
return clockProperty(clk, property, cmdLinkedNetwork(), Sta::sta());
|
||||
cmdLinkedNetwork();
|
||||
return getProperty(clk, property, Sta::sta());
|
||||
}
|
||||
|
||||
PropertyValue
|
||||
path_end_property(PathEnd *end,
|
||||
const char *property)
|
||||
{
|
||||
cmdLinkedNetwork();
|
||||
return getProperty(end, property, Sta::sta());
|
||||
}
|
||||
|
||||
PropertyValue
|
||||
path_ref_property(PathRef *path,
|
||||
const char *property)
|
||||
{
|
||||
cmdLinkedNetwork();
|
||||
return getProperty(path, property, Sta::sta());
|
||||
}
|
||||
|
||||
LeafInstanceIterator *
|
||||
|
|
@ -3034,10 +2757,11 @@ filter_timing_arcs(const char *property,
|
|||
bool exact_match = stringEq(op, "==");
|
||||
while (edge_iter.hasNext()) {
|
||||
Edge *edge = edge_iter.next();
|
||||
const char *value = edgeStringProperty(edge, property, sta);
|
||||
if (value &&
|
||||
((exact_match && stringEq(value, pattern))
|
||||
|| (!exact_match && patternMatch(pattern, value))))
|
||||
PropertyValue value(getProperty(edge, property, sta));
|
||||
const char *prop = value.string();
|
||||
if (prop &&
|
||||
((exact_match && stringEq(prop, pattern))
|
||||
|| (!exact_match && patternMatch(pattern, prop))))
|
||||
filtered_edges->push_back(edge);
|
||||
}
|
||||
delete edges;
|
||||
|
|
@ -6259,55 +5983,6 @@ Crpr common_clk_pessimism() { return self->commonClkPessimism(Sta::sta()); }
|
|||
TransRiseFall *target_clk_end_trans()
|
||||
{ return const_cast<TransRiseFall*>(self->targetClkEndTrans(Sta::sta())); }
|
||||
|
||||
Pin *
|
||||
startpoint()
|
||||
{
|
||||
Sta *sta = Sta::sta();
|
||||
PathExpanded expanded(self->path(), sta);
|
||||
return expanded.startPath()->pin(sta);
|
||||
}
|
||||
|
||||
Clock *
|
||||
startpoint_clock()
|
||||
{
|
||||
Sta *sta = Sta::sta();
|
||||
return self->path()->clock(sta);
|
||||
}
|
||||
|
||||
Pin *
|
||||
endpoint()
|
||||
{
|
||||
Sta *sta = Sta::sta();
|
||||
return self->path()->pin(sta);
|
||||
}
|
||||
|
||||
Clock *
|
||||
endpoint_clock()
|
||||
{
|
||||
Sta *sta = Sta::sta();
|
||||
return self->targetClk(sta);
|
||||
}
|
||||
|
||||
Pin *
|
||||
endpoint_clock_pin()
|
||||
{
|
||||
Sta *sta = Sta::sta();
|
||||
return self->targetClkPath()->pin(sta);
|
||||
}
|
||||
|
||||
PathRefSeq *
|
||||
points()
|
||||
{
|
||||
Sta *sta = Sta::sta();
|
||||
PathExpanded expanded(self->path(), sta);
|
||||
PathRefSeq *paths = new PathRefSeq;
|
||||
for (auto i = expanded.startIndex(); i < expanded.size(); i++) {
|
||||
PathRef *path = expanded.path(i);
|
||||
paths->push_back(*path);
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
%extend MinPulseWidthCheckSeqIterator {
|
||||
|
|
@ -6345,21 +6020,6 @@ pin()
|
|||
return self->pin(sta);
|
||||
}
|
||||
|
||||
TmpPinSeq *
|
||||
pins()
|
||||
{
|
||||
Sta *sta = Sta::sta();
|
||||
PinSeq *pins = new PinSeq;
|
||||
PathRef path1(self);
|
||||
while (!path1.isNull()) {
|
||||
pins->push_back(path1.vertex(sta)->pin());
|
||||
PathRef prev_path;
|
||||
path1.prevPath(sta, prev_path);
|
||||
path1.init(prev_path);
|
||||
}
|
||||
return pins;
|
||||
}
|
||||
|
||||
const char *
|
||||
tag()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue