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 20:35:51 +02:00
|
|
|
#include "sdf/SdfReader.hh"
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <ctype.h>
|
2020-04-05 20:35:51 +02:00
|
|
|
|
2020-04-05 23:53:44 +02:00
|
|
|
#include "DisallowCopyAssign.hh"
|
|
|
|
|
#include "Error.hh"
|
|
|
|
|
#include "Report.hh"
|
|
|
|
|
#include "MinMax.hh"
|
|
|
|
|
#include "TimingArc.hh"
|
|
|
|
|
#include "Network.hh"
|
|
|
|
|
#include "SdcNetwork.hh"
|
|
|
|
|
#include "Graph.hh"
|
|
|
|
|
#include "Corner.hh"
|
|
|
|
|
#include "DcalcAnalysisPt.hh"
|
2020-04-05 20:35:51 +02:00
|
|
|
#include "sdf/Sdf.hh"
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
extern int
|
|
|
|
|
SdfParse_parse();
|
|
|
|
|
|
|
|
|
|
namespace sta {
|
|
|
|
|
|
|
|
|
|
class SdfTriple
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
SdfTriple(float *min,
|
|
|
|
|
float *typ,
|
|
|
|
|
float *max);
|
|
|
|
|
~SdfTriple();
|
|
|
|
|
float **values() { return values_; }
|
|
|
|
|
bool hasValue() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(SdfTriple);
|
|
|
|
|
|
|
|
|
|
float *values_[3];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class SdfPortSpec
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
SdfPortSpec(Transition *tr,
|
|
|
|
|
const char *port,
|
2019-03-13 01:25:53 +01:00
|
|
|
const char *cond = nullptr) :
|
2018-09-28 17:54:21 +02:00
|
|
|
tr_(tr), port_(port), cond_(cond) {}
|
|
|
|
|
~SdfPortSpec()
|
|
|
|
|
{
|
|
|
|
|
stringDelete(port_);
|
|
|
|
|
stringDelete(cond_);
|
|
|
|
|
}
|
|
|
|
|
const char *port() const { return port_; }
|
|
|
|
|
Transition *transition() const { return tr_; }
|
|
|
|
|
const char *cond() const { return cond_; }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(SdfPortSpec);
|
|
|
|
|
|
|
|
|
|
Transition *tr_;
|
|
|
|
|
const char *port_;
|
|
|
|
|
const char *cond_; // timing checks only
|
|
|
|
|
};
|
|
|
|
|
|
2019-03-13 01:25:53 +01:00
|
|
|
SdfReader *sdf_reader = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
readSdfSingle(const char *filename,
|
|
|
|
|
const char *path,
|
|
|
|
|
Corner *corner,
|
|
|
|
|
int sdf_index,
|
|
|
|
|
AnalysisType analysis_type,
|
|
|
|
|
bool unescaped_dividers,
|
|
|
|
|
bool incremental_only,
|
|
|
|
|
MinMaxAll *cond_use,
|
|
|
|
|
StaState *sta)
|
|
|
|
|
{
|
|
|
|
|
int arc_index = corner->findDcalcAnalysisPt(MinMax::max())->index();
|
|
|
|
|
SdfReader reader(filename, path, arc_index, sdf_index,
|
|
|
|
|
SdfReader::nullIndex(), SdfReader::nullIndex(),
|
|
|
|
|
analysis_type, unescaped_dividers, incremental_only,
|
|
|
|
|
cond_use, sta);
|
|
|
|
|
sdf_reader = &reader;
|
|
|
|
|
return reader.read();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
readSdfMinMax(const char *filename,
|
|
|
|
|
const char *path,
|
|
|
|
|
Corner *corner,
|
|
|
|
|
int sdf_min_index,
|
|
|
|
|
int sdf_max_index,
|
|
|
|
|
AnalysisType analysis_type,
|
|
|
|
|
bool unescaped_dividers,
|
|
|
|
|
bool incremental_only,
|
|
|
|
|
MinMaxAll *cond_use,
|
|
|
|
|
StaState *sta)
|
|
|
|
|
{
|
|
|
|
|
int arc_min_index = corner->findDcalcAnalysisPt(MinMax::min())->index();
|
|
|
|
|
int arc_max_index = corner->findDcalcAnalysisPt(MinMax::max())->index();
|
|
|
|
|
SdfReader reader(filename, path,
|
|
|
|
|
arc_min_index, sdf_min_index,
|
|
|
|
|
arc_max_index, sdf_max_index,
|
|
|
|
|
analysis_type, unescaped_dividers, incremental_only,
|
|
|
|
|
cond_use, sta);
|
|
|
|
|
sdf_reader = &reader;
|
|
|
|
|
return reader.read();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SdfReader::SdfReader(const char *filename,
|
|
|
|
|
const char *path,
|
|
|
|
|
int arc_min_index,
|
|
|
|
|
int triple_min_index,
|
|
|
|
|
int arc_max_index,
|
|
|
|
|
int triple_max_index,
|
|
|
|
|
AnalysisType analysis_type,
|
|
|
|
|
bool unescaped_dividers,
|
|
|
|
|
bool is_incremental_only,
|
|
|
|
|
MinMaxAll *cond_use,
|
|
|
|
|
StaState *sta) :
|
|
|
|
|
StaState(sta),
|
|
|
|
|
filename_(filename),
|
|
|
|
|
path_(path),
|
|
|
|
|
triple_min_index_(triple_min_index),
|
|
|
|
|
triple_max_index_(triple_max_index),
|
|
|
|
|
arc_delay_min_index_(arc_min_index),
|
|
|
|
|
arc_delay_max_index_(arc_max_index),
|
|
|
|
|
analysis_type_(analysis_type),
|
|
|
|
|
unescaped_dividers_(unescaped_dividers),
|
|
|
|
|
is_incremental_only_(is_incremental_only),
|
|
|
|
|
cond_use_(cond_use),
|
|
|
|
|
line_(1),
|
|
|
|
|
escape_('\\'),
|
2019-03-13 01:25:53 +01:00
|
|
|
instance_(nullptr),
|
|
|
|
|
cell_name_(nullptr),
|
2018-09-28 17:54:21 +02:00
|
|
|
in_timing_check_(false),
|
|
|
|
|
in_incremental_(false),
|
|
|
|
|
timescale_(1.0E-9F) // default units of ns
|
|
|
|
|
{
|
|
|
|
|
if (unescaped_dividers)
|
|
|
|
|
network_ = makeSdcNetwork(network_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SdfReader::~SdfReader()
|
|
|
|
|
{
|
|
|
|
|
if (unescaped_dividers_)
|
|
|
|
|
delete network_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
SdfReader::read()
|
|
|
|
|
{
|
|
|
|
|
// Use zlib to uncompress gzip'd files automagically.
|
|
|
|
|
stream_ = gzopen(filename_, "rb");
|
|
|
|
|
if (stream_) {
|
|
|
|
|
// yyparse returns 0 on success.
|
|
|
|
|
bool success = (::SdfParse_parse() == 0);
|
|
|
|
|
gzclose(stream_);
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
throw FileNotReadable(filename_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::setDivider(char divider)
|
|
|
|
|
{
|
|
|
|
|
divider_ = divider;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::setTimescale(float multiplier,
|
|
|
|
|
const char *units)
|
|
|
|
|
{
|
|
|
|
|
if (multiplier == 1.0
|
|
|
|
|
|| multiplier == 10.0
|
|
|
|
|
|| multiplier == 100.0) {
|
|
|
|
|
if (stringEq(units, "us"))
|
|
|
|
|
timescale_ = multiplier * 1E-6F;
|
|
|
|
|
else if (stringEq(units, "ns"))
|
|
|
|
|
timescale_ = multiplier * 1E-9F;
|
|
|
|
|
else if (stringEq(units, "ps"))
|
|
|
|
|
timescale_ = multiplier * 1E-12F;
|
|
|
|
|
else
|
2020-12-14 02:21:35 +01:00
|
|
|
sdfError(180, "TIMESCALE units not us, ns, or ps.");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else
|
2020-12-14 02:21:35 +01:00
|
|
|
sdfError(181, "TIMESCALE multiplier not 1, 10, or 100.");
|
2018-09-28 17:54:21 +02:00
|
|
|
stringDelete(units);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::interconnect(const char *from_pin_name,
|
|
|
|
|
const char *to_pin_name,
|
|
|
|
|
SdfTripleSeq *triples)
|
|
|
|
|
{
|
|
|
|
|
// Ignore non-incremental annotations in incremental only mode.
|
|
|
|
|
if (!(is_incremental_only_ && !in_incremental_)) {
|
|
|
|
|
Pin *from_pin = findPin(from_pin_name);
|
|
|
|
|
Pin *to_pin = findPin(to_pin_name);
|
|
|
|
|
if (from_pin && to_pin) {
|
|
|
|
|
// Assume the pins are non-hierarchical and on the same net.
|
|
|
|
|
Edge *edge = findWireEdge(from_pin, to_pin);
|
|
|
|
|
if (edge)
|
|
|
|
|
setEdgeDelays(edge, triples, "INTERCONNECT");
|
|
|
|
|
else {
|
|
|
|
|
bool from_is_hier = network_->isHierarchical(from_pin);
|
|
|
|
|
bool to_is_hier = network_->isHierarchical(to_pin);
|
|
|
|
|
if (from_is_hier || to_is_hier) {
|
|
|
|
|
if (from_is_hier)
|
2020-12-14 02:21:35 +01:00
|
|
|
sdfError(182, "pin %s is a hierarchical pin.", from_pin_name);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (to_is_hier)
|
2020-12-14 02:21:35 +01:00
|
|
|
sdfError(183, "pin %s is a hierarchical pin.", to_pin_name);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else
|
2020-12-14 02:21:35 +01:00
|
|
|
sdfError(184, "INTERCONNECT from %s to %s not found.",
|
2018-09-28 17:54:21 +02:00
|
|
|
from_pin_name, to_pin_name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2019-03-13 01:25:53 +01:00
|
|
|
if (from_pin == nullptr)
|
2020-12-14 02:21:35 +01:00
|
|
|
sdfError(185, "pin %s not found.", from_pin_name);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (to_pin == nullptr)
|
2020-12-14 02:21:35 +01:00
|
|
|
sdfError(186, "pin %s not found.", to_pin_name);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
stringDelete(from_pin_name);
|
|
|
|
|
stringDelete(to_pin_name);
|
|
|
|
|
deleteTripleSeq(triples);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::port(const char *to_pin_name,
|
|
|
|
|
SdfTripleSeq *triples)
|
|
|
|
|
{
|
|
|
|
|
// Ignore non-incremental annotations in incremental only mode.
|
|
|
|
|
if (!(is_incremental_only_ && !in_incremental_)) {
|
|
|
|
|
Pin *to_pin = (instance_)
|
|
|
|
|
? network_->findPinRelative(instance_, to_pin_name)
|
|
|
|
|
: network_->findPin(to_pin_name);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (to_pin == nullptr)
|
2020-12-14 02:21:35 +01:00
|
|
|
sdfError(187, "pin %s not found.", to_pin_name);
|
2018-09-28 17:54:21 +02:00
|
|
|
else {
|
|
|
|
|
Vertex *vertex = graph_->pinLoadVertex(to_pin);
|
|
|
|
|
VertexInEdgeIterator edge_iter(vertex, graph_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
if (edge->role()->sdfRole()->isWire())
|
|
|
|
|
setEdgeDelays(edge, triples, "PORT");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
stringDelete(to_pin_name);
|
|
|
|
|
deleteTripleSeq(triples);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Edge *
|
|
|
|
|
SdfReader::findWireEdge(Pin *from_pin,
|
|
|
|
|
Pin *to_pin)
|
|
|
|
|
{
|
|
|
|
|
Vertex *to_vertex, *to_vertex_bidirect_drvr;
|
|
|
|
|
graph_->pinVertices(to_pin, to_vertex, to_vertex_bidirect_drvr);
|
|
|
|
|
// Fanin < fanout, so search for driver from load.
|
|
|
|
|
VertexInEdgeIterator edge_iter(to_vertex, graph_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
const TimingRole *edge_role = edge->role();
|
|
|
|
|
if (edge->from(graph_)->pin() == from_pin
|
|
|
|
|
&& edge_role->sdfRole()->isWire())
|
|
|
|
|
return edge;
|
|
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::setEdgeDelays(Edge *edge,
|
|
|
|
|
SdfTripleSeq *triples,
|
|
|
|
|
const char *sdf_cmd)
|
|
|
|
|
{
|
|
|
|
|
// Rise/fall triples.
|
|
|
|
|
size_t triple_count = triples->size();
|
|
|
|
|
if (triple_count == 1
|
|
|
|
|
|| triple_count == 2) {
|
|
|
|
|
TimingArcSet *arc_set = edge->timingArcSet();
|
2018-12-05 23:18:41 +01:00
|
|
|
TimingArcSetArcIterator arc_iter(arc_set);
|
|
|
|
|
while (arc_iter.hasNext()) {
|
|
|
|
|
TimingArc *arc = arc_iter.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
size_t triple_index;
|
|
|
|
|
if (triple_count == 1)
|
|
|
|
|
triple_index = 0;
|
|
|
|
|
else
|
|
|
|
|
triple_index = arc->toTrans()->sdfTripleIndex();
|
|
|
|
|
SdfTriple *triple = (*triples)[triple_index];
|
|
|
|
|
setEdgeArcDelays(edge, arc, triple);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (triple_count == 0)
|
2020-12-14 02:21:35 +01:00
|
|
|
sdfError(188, "%s with no triples.", sdf_cmd);
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
2020-12-14 02:21:35 +01:00
|
|
|
sdfError(189, "%s with more than 2 triples.", sdf_cmd);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::setCell(const char *cell_name)
|
|
|
|
|
{
|
|
|
|
|
cell_name_ = cell_name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::setInstance(const char *instance_name)
|
|
|
|
|
{
|
|
|
|
|
if (instance_name) {
|
|
|
|
|
if (stringEq(instance_name, "*")) {
|
|
|
|
|
notSupported("INSTANCE wildcards");
|
2019-03-13 01:25:53 +01:00
|
|
|
instance_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
instance_ = findInstance(instance_name);
|
|
|
|
|
if (instance_) {
|
|
|
|
|
Cell *inst_cell = network_->cell(instance_);
|
|
|
|
|
const char *inst_cell_name = network_->name(inst_cell);
|
|
|
|
|
if (cell_name_ && !stringEqual(inst_cell_name, cell_name_))
|
2020-12-14 02:21:35 +01:00
|
|
|
sdfError(190, "instance %s cell %s does not match enclosing cell %s.",
|
2018-09-28 17:54:21 +02:00
|
|
|
instance_name,
|
|
|
|
|
inst_cell_name,
|
|
|
|
|
cell_name_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
stringDelete(instance_name);
|
|
|
|
|
}
|
|
|
|
|
else
|
2019-03-13 01:25:53 +01:00
|
|
|
instance_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::setInstanceWildcard()
|
|
|
|
|
{
|
|
|
|
|
notSupported("INSTANCE wildcards");
|
2019-03-13 01:25:53 +01:00
|
|
|
instance_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::cellFinish()
|
|
|
|
|
{
|
|
|
|
|
stringDelete(cell_name_);
|
2019-03-13 01:25:53 +01:00
|
|
|
cell_name_ = nullptr;
|
|
|
|
|
instance_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::iopath(SdfPortSpec *from_edge,
|
|
|
|
|
const char *to_port_name,
|
|
|
|
|
SdfTripleSeq *triples,
|
|
|
|
|
const char *cond,
|
|
|
|
|
bool condelse)
|
|
|
|
|
{
|
|
|
|
|
if (instance_) {
|
|
|
|
|
const char *from_port_name = from_edge->port();
|
|
|
|
|
Cell *cell = network_->cell(instance_);
|
|
|
|
|
Port *from_port = network_->findPort(cell, from_port_name);
|
|
|
|
|
Port *to_port = network_->findPort(cell, to_port_name);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (from_port == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
portNotFound(from_port_name);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (to_port == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
portNotFound(to_port_name);
|
|
|
|
|
if (from_port && to_port) {
|
|
|
|
|
Pin *from_pin = network_->findPin(instance_, from_port_name);
|
|
|
|
|
Pin *to_pin = network_->findPin(instance_, to_port_name);
|
|
|
|
|
// Do not report an error if the pin is not found because the
|
|
|
|
|
// instance may not have the pin.
|
|
|
|
|
if (from_pin && to_pin) {
|
|
|
|
|
Vertex *to_vertex = graph_->pinDrvrVertex(to_pin);
|
|
|
|
|
size_t triple_count = triples->size();
|
|
|
|
|
bool matched = false;
|
|
|
|
|
// Fanin < fanout, so search for driver from load.
|
|
|
|
|
// Search for multiple matching edges because of
|
|
|
|
|
// tristate enable/disable.
|
|
|
|
|
VertexInEdgeIterator edge_iter(to_vertex, graph_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
TimingArcSet *arc_set = edge->timingArcSet();
|
|
|
|
|
const char *lib_cond = arc_set->sdfCond();
|
|
|
|
|
const TimingRole *edge_role = arc_set->role();
|
2019-03-13 01:25:53 +01:00
|
|
|
bool cond_use_flag = cond_use_ && cond && lib_cond == nullptr
|
2018-09-28 17:54:21 +02:00
|
|
|
&& !(!is_incremental_only_ && in_incremental_);
|
|
|
|
|
if (edge->from(graph_)->pin() == from_pin
|
|
|
|
|
&& edge_role->sdfRole() == TimingRole::sdfIopath()
|
|
|
|
|
&& (cond_use_flag
|
|
|
|
|
|| (!condelse && condMatch(cond, lib_cond))
|
|
|
|
|
// condelse matches the default (unconditional) arc.
|
2019-03-13 01:25:53 +01:00
|
|
|
|| (condelse && lib_cond == nullptr))) {
|
2018-09-28 17:54:21 +02:00
|
|
|
matched = true;
|
2018-12-05 23:18:41 +01:00
|
|
|
TimingArcSetArcIterator arc_iter(arc_set);
|
|
|
|
|
while (arc_iter.hasNext()) {
|
|
|
|
|
TimingArc *arc = arc_iter.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
if ((from_edge->transition() == Transition::riseFall())
|
|
|
|
|
|| (arc->fromTrans() == from_edge->transition())) {
|
|
|
|
|
size_t triple_index = arc->toTrans()->sdfTripleIndex();
|
2019-03-13 01:25:53 +01:00
|
|
|
SdfTriple *triple = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
if (triple_index < triple_count)
|
|
|
|
|
triple = (*triples)[triple_index];
|
|
|
|
|
if (triple_count == 1)
|
|
|
|
|
triple = (*triples)[0];
|
|
|
|
|
// Rules for matching when triple is missing not implemented.
|
|
|
|
|
// See SDF pg 3-17.
|
|
|
|
|
if (triple) {
|
|
|
|
|
if (cond_use_flag)
|
|
|
|
|
setEdgeArcDelaysCondUse(edge, arc, triple);
|
|
|
|
|
else
|
|
|
|
|
setEdgeArcDelays(edge, arc, triple);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!matched)
|
2020-12-14 02:21:35 +01:00
|
|
|
sdfError(191, "cell %s IOPATH %s -> %s not found.",
|
2018-09-28 17:54:21 +02:00
|
|
|
network_->cellName(instance_),
|
|
|
|
|
from_port_name,
|
|
|
|
|
to_port_name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
deletePortSpec(from_edge);
|
|
|
|
|
stringDelete(to_port_name);
|
|
|
|
|
deleteTripleSeq(triples);
|
|
|
|
|
if (cond)
|
|
|
|
|
stringDelete(cond);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::timingCheck(TimingRole *role, SdfPortSpec *data_edge,
|
|
|
|
|
SdfPortSpec *clk_edge, SdfTriple *triple)
|
|
|
|
|
{
|
|
|
|
|
timingCheck1(role, data_edge, clk_edge, triple, true);
|
|
|
|
|
deletePortSpec(data_edge);
|
|
|
|
|
deletePortSpec(clk_edge);
|
|
|
|
|
deleteTriple(triple);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::timingCheck1(TimingRole *role,
|
|
|
|
|
SdfPortSpec *data_edge,
|
|
|
|
|
SdfPortSpec *clk_edge,
|
|
|
|
|
SdfTriple *triple,
|
|
|
|
|
bool warn)
|
|
|
|
|
{
|
|
|
|
|
// Ignore non-incremental annotations in incremental only mode.
|
|
|
|
|
if (!(is_incremental_only_ && !in_incremental_)
|
|
|
|
|
&& instance_) {
|
|
|
|
|
const char *data_port_name = data_edge->port();
|
|
|
|
|
const char *clk_port_name = clk_edge->port();
|
|
|
|
|
Cell *cell = network_->cell(instance_);
|
|
|
|
|
Port *data_port = network_->findPort(cell, data_port_name);
|
|
|
|
|
Port *clk_port = network_->findPort(cell, clk_port_name);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (data_port == nullptr && warn)
|
2018-09-28 17:54:21 +02:00
|
|
|
portNotFound(data_port_name);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (clk_port == nullptr && warn)
|
2018-09-28 17:54:21 +02:00
|
|
|
portNotFound(clk_port_name);
|
|
|
|
|
if (data_port && clk_port) {
|
|
|
|
|
Pin *data_pin = network_->findPin(instance_, data_port_name);
|
|
|
|
|
Pin *clk_pin = network_->findPin(instance_, clk_port_name);
|
|
|
|
|
if (data_pin && clk_pin) {
|
|
|
|
|
// Hack to match PT; always use triple max value for check.
|
|
|
|
|
if (triple_min_index_ != null_index_
|
|
|
|
|
&& triple_max_index_ != null_index_) {
|
|
|
|
|
float **values = triple->values();
|
|
|
|
|
float *value_min = values[triple_min_index_];
|
|
|
|
|
float *value_max = values[triple_max_index_];
|
|
|
|
|
if (value_min && value_max) {
|
|
|
|
|
switch (analysis_type_) {
|
2019-03-13 01:25:53 +01:00
|
|
|
case AnalysisType::single:
|
2018-09-28 17:54:21 +02:00
|
|
|
break;
|
2019-03-13 01:25:53 +01:00
|
|
|
case AnalysisType::bc_wc:
|
2018-09-28 17:54:21 +02:00
|
|
|
if (role->genericRole() == TimingRole::setup())
|
|
|
|
|
*value_min = *value_max;
|
|
|
|
|
else
|
|
|
|
|
*value_max = *value_min;
|
|
|
|
|
break;
|
2019-03-13 01:25:53 +01:00
|
|
|
case AnalysisType::ocv:
|
2018-09-28 17:54:21 +02:00
|
|
|
*value_min = *value_max;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bool matched = annotateCheckEdges(data_pin, data_edge,
|
|
|
|
|
clk_pin, clk_edge, role,
|
|
|
|
|
triple, false);
|
|
|
|
|
// Liberty setup/hold checks on preset/clear pins can be translated
|
|
|
|
|
// into recovery/removal checks, so be flexible about matching.
|
|
|
|
|
if (!matched)
|
|
|
|
|
matched = annotateCheckEdges(data_pin, data_edge,
|
|
|
|
|
clk_pin, clk_edge, role,
|
|
|
|
|
triple, true);
|
|
|
|
|
if (!matched
|
|
|
|
|
// Only warn when non-null values are present.
|
|
|
|
|
&& triple->hasValue())
|
2020-12-14 02:21:35 +01:00
|
|
|
sdfError(192, "cell %s %s -> %s %s check not found.",
|
2018-09-28 17:54:21 +02:00
|
|
|
network_->cellName(instance_),
|
|
|
|
|
data_port_name,
|
|
|
|
|
clk_port_name,
|
|
|
|
|
role->asString());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return true if matched.
|
|
|
|
|
bool
|
|
|
|
|
SdfReader::annotateCheckEdges(Pin *data_pin,
|
|
|
|
|
SdfPortSpec *data_edge,
|
|
|
|
|
Pin *clk_pin,
|
|
|
|
|
SdfPortSpec *clk_edge,
|
|
|
|
|
TimingRole *sdf_role,
|
|
|
|
|
SdfTriple *triple,
|
|
|
|
|
bool match_generic)
|
|
|
|
|
{
|
|
|
|
|
bool matched = false;
|
|
|
|
|
const char *cond_start = data_edge->cond();
|
|
|
|
|
const char *cond_end = clk_edge->cond();
|
|
|
|
|
// Timing check graph edges from clk to data.
|
|
|
|
|
Vertex *to_vertex = graph_->pinLoadVertex(data_pin);
|
|
|
|
|
// Fanin < fanout, so search for driver from load.
|
|
|
|
|
VertexInEdgeIterator edge_iter(to_vertex, graph_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
if (edge->from(graph_)->pin() == clk_pin) {
|
|
|
|
|
TimingArcSet *arc_set = edge->timingArcSet();
|
|
|
|
|
const TimingRole *edge_role = arc_set->role();
|
|
|
|
|
const char *lib_cond_start = arc_set->sdfCondStart();
|
|
|
|
|
const char *lib_cond_end = arc_set->sdfCondEnd();
|
|
|
|
|
bool cond_matches = condMatch(cond_start, lib_cond_start)
|
|
|
|
|
&& condMatch(cond_end, lib_cond_end);
|
|
|
|
|
if (((!match_generic && edge_role->sdfRole() == sdf_role)
|
|
|
|
|
|| (match_generic
|
|
|
|
|
&& edge_role->genericRole() == sdf_role->genericRole()))
|
|
|
|
|
&& cond_matches) {
|
|
|
|
|
TimingArcSet *arc_set = edge->timingArcSet();
|
2018-12-05 23:18:41 +01:00
|
|
|
TimingArcSetArcIterator arc_iter(arc_set);
|
|
|
|
|
while (arc_iter.hasNext()) {
|
|
|
|
|
TimingArc *arc = arc_iter.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
if (((data_edge->transition() == Transition::riseFall())
|
|
|
|
|
|| (arc->toTrans() == data_edge->transition()))
|
|
|
|
|
&& ((clk_edge->transition() == Transition::riseFall())
|
|
|
|
|
|| (arc->fromTrans() == clk_edge->transition()))) {
|
|
|
|
|
setEdgeArcDelays(edge, arc, triple);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
matched = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return matched;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::timingCheckWidth(SdfPortSpec *edge,
|
|
|
|
|
SdfTriple *triple)
|
|
|
|
|
{
|
|
|
|
|
// Ignore non-incremental annotations in incremental only mode.
|
|
|
|
|
if (!(is_incremental_only_ && !in_incremental_)
|
|
|
|
|
&& instance_) {
|
|
|
|
|
const char *port_name = edge->port();
|
|
|
|
|
Cell *cell = network_->cell(instance_);
|
|
|
|
|
Port *port = network_->findPort(cell, port_name);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (port == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
portNotFound(port_name);
|
|
|
|
|
else {
|
|
|
|
|
Pin *pin = network_->findPin(instance_, port_name);
|
|
|
|
|
if (pin) {
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *rf = edge->transition()->asRiseFall();
|
2018-09-28 17:54:21 +02:00
|
|
|
float **values = triple->values();
|
|
|
|
|
float *value_ptr = values[triple_min_index_];
|
|
|
|
|
if (value_ptr) {
|
|
|
|
|
float value = *value_ptr;
|
2019-11-11 23:30:19 +01:00
|
|
|
graph_->setWidthCheckAnnotation(pin, rf, arc_delay_min_index_,
|
2018-09-28 17:54:21 +02:00
|
|
|
value);
|
|
|
|
|
}
|
|
|
|
|
if (triple_max_index_ != null_index_) {
|
|
|
|
|
value_ptr = values[triple_max_index_];
|
|
|
|
|
if (value_ptr) {
|
|
|
|
|
float value = *value_ptr;
|
2019-11-11 23:30:19 +01:00
|
|
|
graph_->setWidthCheckAnnotation(pin, rf, arc_delay_max_index_,
|
2018-09-28 17:54:21 +02:00
|
|
|
value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
deletePortSpec(edge);
|
|
|
|
|
deleteTriple(triple);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::timingCheckPeriod(SdfPortSpec *edge,
|
|
|
|
|
SdfTriple *triple)
|
|
|
|
|
{
|
|
|
|
|
// Ignore non-incremental annotations in incremental only mode.
|
|
|
|
|
if (!(is_incremental_only_ && !in_incremental_)
|
|
|
|
|
&& instance_) {
|
|
|
|
|
const char *port_name = edge->port();
|
|
|
|
|
Cell *cell = network_->cell(instance_);
|
|
|
|
|
Port *port = network_->findPort(cell, port_name);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (port == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
portNotFound(port_name);
|
|
|
|
|
else {
|
|
|
|
|
// Edge specifier is ignored for period checks.
|
|
|
|
|
Pin *pin = network_->findPin(instance_, port_name);
|
|
|
|
|
if (pin) {
|
|
|
|
|
float **values = triple->values();
|
|
|
|
|
float *value_ptr = values[triple_min_index_];
|
|
|
|
|
if (value_ptr) {
|
|
|
|
|
float value = *value_ptr;
|
|
|
|
|
graph_->setPeriodCheckAnnotation(pin, arc_delay_min_index_, value);
|
|
|
|
|
}
|
|
|
|
|
if (triple_max_index_ != null_index_) {
|
|
|
|
|
value_ptr = values[triple_max_index_];
|
|
|
|
|
if (value_ptr) {
|
|
|
|
|
float value = *value_ptr;
|
|
|
|
|
graph_->setPeriodCheckAnnotation(pin, arc_delay_max_index_, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
deletePortSpec(edge);
|
|
|
|
|
deleteTriple(triple);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::timingCheckSetupHold(SdfPortSpec *data_edge,
|
|
|
|
|
SdfPortSpec *clk_edge,
|
|
|
|
|
SdfTriple *setup_triple,
|
|
|
|
|
SdfTriple *hold_triple)
|
|
|
|
|
{
|
|
|
|
|
timingCheck1(TimingRole::setup(), data_edge, clk_edge, setup_triple, true);
|
|
|
|
|
timingCheck1(TimingRole::hold(), data_edge, clk_edge, hold_triple, false);
|
|
|
|
|
deletePortSpec(data_edge);
|
|
|
|
|
deletePortSpec(clk_edge);
|
|
|
|
|
deleteTriple(setup_triple);
|
|
|
|
|
deleteTriple(hold_triple);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::timingCheckRecRem(SdfPortSpec *data_edge,
|
|
|
|
|
SdfPortSpec *clk_edge,
|
|
|
|
|
SdfTriple *rec_triple,
|
|
|
|
|
SdfTriple *rem_triple)
|
|
|
|
|
{
|
|
|
|
|
timingCheck1(TimingRole::recovery(), data_edge, clk_edge, rec_triple, true);
|
|
|
|
|
timingCheck1(TimingRole::removal(), data_edge, clk_edge, rem_triple, false);
|
|
|
|
|
deletePortSpec(data_edge);
|
|
|
|
|
deletePortSpec(clk_edge);
|
|
|
|
|
deleteTriple(rec_triple);
|
|
|
|
|
deleteTriple(rem_triple);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::timingCheckNochange(SdfPortSpec *data_edge,
|
|
|
|
|
SdfPortSpec *clk_edge,
|
|
|
|
|
SdfTriple *before_triple,
|
|
|
|
|
SdfTriple *after_triple)
|
|
|
|
|
{
|
|
|
|
|
notSupported("NOCHANGE");
|
|
|
|
|
deletePortSpec(data_edge);
|
|
|
|
|
deletePortSpec(clk_edge);
|
|
|
|
|
deleteTriple(before_triple);
|
|
|
|
|
deleteTriple(after_triple);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::device(SdfTripleSeq *triples)
|
|
|
|
|
{
|
|
|
|
|
// Ignore non-incremental annotations in incremental only mode.
|
|
|
|
|
if (!(is_incremental_only_ && !in_incremental_)
|
|
|
|
|
&& instance_) {
|
|
|
|
|
InstancePinIterator *pin_iter = network_->pinIterator(instance_);
|
|
|
|
|
while (pin_iter->hasNext()) {
|
|
|
|
|
Pin *to_pin = pin_iter->next();
|
|
|
|
|
setDevicePinDelays(to_pin, triples);
|
|
|
|
|
}
|
|
|
|
|
delete pin_iter;
|
|
|
|
|
}
|
|
|
|
|
deleteTripleSeq(triples);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::device(const char *to_port_name,
|
|
|
|
|
SdfTripleSeq *triples)
|
|
|
|
|
{
|
|
|
|
|
// Ignore non-incremental annotations in incremental only mode.
|
|
|
|
|
if (!(is_incremental_only_ && !in_incremental_)
|
|
|
|
|
&& instance_) {
|
|
|
|
|
Cell *cell = network_->cell(instance_);
|
|
|
|
|
Port *to_port = network_->findPort(cell, to_port_name);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (to_port == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
portNotFound(to_port_name);
|
|
|
|
|
else {
|
|
|
|
|
Pin *to_pin = network_->findPin(instance_, to_port_name);
|
|
|
|
|
setDevicePinDelays(to_pin, triples);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
stringDelete(to_port_name);
|
|
|
|
|
deleteTripleSeq(triples);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::setDevicePinDelays(Pin *to_pin,
|
|
|
|
|
SdfTripleSeq *triples)
|
|
|
|
|
{
|
|
|
|
|
Vertex *vertex = graph_->pinDrvrVertex(to_pin);
|
|
|
|
|
VertexInEdgeIterator edge_iter(vertex, graph_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
if (edge->role()->sdfRole() == TimingRole::sdfIopath())
|
|
|
|
|
setEdgeDelays(edge, triples, "DEVICE");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::setEdgeArcDelays(Edge *edge,
|
|
|
|
|
TimingArc *arc,
|
|
|
|
|
SdfTriple *triple)
|
|
|
|
|
{
|
|
|
|
|
setEdgeArcDelays(edge, arc, triple, triple_min_index_, arc_delay_min_index_);
|
|
|
|
|
setEdgeArcDelays(edge, arc, triple, triple_max_index_, arc_delay_max_index_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::setEdgeArcDelays(Edge *edge,
|
|
|
|
|
TimingArc *arc,
|
|
|
|
|
SdfTriple *triple,
|
|
|
|
|
int triple_index,
|
|
|
|
|
int arc_delay_index)
|
|
|
|
|
{
|
|
|
|
|
if (triple_index != null_index_) {
|
|
|
|
|
float **values = triple->values();
|
|
|
|
|
float *value_ptr = values[triple_index];
|
|
|
|
|
if (value_ptr) {
|
|
|
|
|
ArcDelay delay;
|
|
|
|
|
if (in_incremental_)
|
|
|
|
|
delay = *value_ptr + graph_->arcDelay(edge, arc, arc_delay_index);
|
|
|
|
|
else
|
|
|
|
|
delay = *value_ptr;
|
|
|
|
|
graph_->setArcDelay(edge, arc, arc_delay_index, delay);
|
2018-11-09 19:04:16 +01:00
|
|
|
graph_->setArcDelayAnnotated(edge, arc, arc_delay_index, true);
|
2018-09-28 17:54:21 +02:00
|
|
|
edge->setDelayAnnotationIsIncremental(is_incremental_only_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::setEdgeArcDelaysCondUse(Edge *edge,
|
|
|
|
|
TimingArc *arc,
|
|
|
|
|
SdfTriple *triple)
|
|
|
|
|
{
|
|
|
|
|
float **values = triple->values();
|
2019-03-13 01:25:53 +01:00
|
|
|
float *value_min = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
if (triple_min_index_ != null_index_)
|
|
|
|
|
value_min = values[triple_min_index_];
|
2019-03-13 01:25:53 +01:00
|
|
|
float *value_max = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
if (triple_max_index_ != null_index_)
|
|
|
|
|
value_max = values[triple_max_index_];
|
|
|
|
|
MinMax *min, *max;
|
|
|
|
|
if (cond_use_ == MinMaxAll::min()) {
|
|
|
|
|
min = MinMax::min();
|
|
|
|
|
max = MinMax::min();
|
|
|
|
|
}
|
|
|
|
|
else if (cond_use_ == MinMaxAll::max()) {
|
|
|
|
|
min = MinMax::max();
|
|
|
|
|
max = MinMax::max();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
min = MinMax::min();
|
|
|
|
|
max = MinMax::max();
|
|
|
|
|
}
|
|
|
|
|
setEdgeArcDelaysCondUse(edge, arc, value_min, triple_min_index_,
|
|
|
|
|
arc_delay_min_index_, min);
|
|
|
|
|
setEdgeArcDelaysCondUse(edge, arc, value_max, triple_max_index_,
|
|
|
|
|
arc_delay_max_index_, max);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::setEdgeArcDelaysCondUse(Edge *edge,
|
|
|
|
|
TimingArc *arc,
|
|
|
|
|
float *value,
|
|
|
|
|
int triple_index,
|
|
|
|
|
int arc_delay_index,
|
|
|
|
|
const MinMax *min_max)
|
|
|
|
|
{
|
|
|
|
|
if (value
|
|
|
|
|
&& triple_index != null_index_) {
|
|
|
|
|
ArcDelay delay(*value);
|
|
|
|
|
if (!is_incremental_only_ && in_incremental_)
|
|
|
|
|
delay = graph_->arcDelay(edge, arc, arc_delay_index) + *value;
|
2018-11-09 19:04:16 +01:00
|
|
|
else if (graph_->arcDelayAnnotated(edge, arc, arc_delay_index)) {
|
2018-09-28 17:54:21 +02:00
|
|
|
ArcDelay prev_value = graph_->arcDelay(edge, arc, arc_delay_index);
|
2020-07-12 02:43:30 +02:00
|
|
|
if (delayGreater(prev_value, delay, min_max, this))
|
2018-09-28 17:54:21 +02:00
|
|
|
delay = prev_value;
|
|
|
|
|
}
|
|
|
|
|
graph_->setArcDelay(edge, arc, arc_delay_index, delay);
|
2018-11-09 19:04:16 +01:00
|
|
|
graph_->setArcDelayAnnotated(edge, arc, arc_delay_index, true);
|
2018-09-28 17:54:21 +02:00
|
|
|
edge->setDelayAnnotationIsIncremental(is_incremental_only_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
SdfReader::condMatch(const char *sdf_cond,
|
|
|
|
|
const char *lib_cond)
|
|
|
|
|
{
|
|
|
|
|
// If the sdf is not conditional it matches any library condition.
|
2019-03-13 01:25:53 +01:00
|
|
|
if (sdf_cond == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
return true;
|
|
|
|
|
else if (sdf_cond && lib_cond) {
|
|
|
|
|
// Match sdf_cond and lib_cond ignoring blanks.
|
|
|
|
|
const char *c1 = sdf_cond;
|
|
|
|
|
const char *c2 = lib_cond;
|
|
|
|
|
char ch1, ch2;
|
|
|
|
|
do {
|
|
|
|
|
ch1 = *c1++;
|
|
|
|
|
ch2 = *c2++;
|
|
|
|
|
while (ch1 && isspace(ch1))
|
|
|
|
|
ch1 = *c1++;
|
|
|
|
|
while (ch2 && isspace(ch2))
|
|
|
|
|
ch2 = *c2++;
|
|
|
|
|
if (ch1 != ch2)
|
|
|
|
|
return false;
|
|
|
|
|
} while (ch1 && ch2);
|
|
|
|
|
return (ch1 == '\0' && ch2 == '\0');
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SdfPortSpec *
|
|
|
|
|
SdfReader::makePortSpec(Transition *tr,
|
|
|
|
|
const char *port,
|
|
|
|
|
const char *cond)
|
|
|
|
|
{
|
|
|
|
|
return new SdfPortSpec(tr, port, cond);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SdfPortSpec *
|
|
|
|
|
SdfReader::makeCondPortSpec(char *cond_port)
|
|
|
|
|
{
|
|
|
|
|
char *cond = cond_port;
|
|
|
|
|
// Search from end to find port name because condition may contain spaces.
|
|
|
|
|
char *p = &cond_port[strlen(cond_port) - 1];
|
|
|
|
|
// Trim trailing port spaces.
|
|
|
|
|
while (*p != '\0' && isspace(*p))
|
|
|
|
|
p--;
|
|
|
|
|
p[1] = '\0';
|
|
|
|
|
while (*p != '\0' && !isspace(*p))
|
|
|
|
|
p--;
|
|
|
|
|
char *port = &p[1];
|
|
|
|
|
// Trim trailing cond spaces.
|
|
|
|
|
while (*p != '\0' && isspace(*p))
|
|
|
|
|
p--;
|
|
|
|
|
p[1] = '\0';
|
|
|
|
|
SdfPortSpec *port_spec = makePortSpec(Transition::riseFall(),
|
|
|
|
|
stringCopy(port),
|
|
|
|
|
stringCopy(cond));
|
|
|
|
|
stringDelete(cond_port);
|
|
|
|
|
return port_spec;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::deletePortSpec(SdfPortSpec *edge)
|
|
|
|
|
{
|
|
|
|
|
delete edge;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SdfTripleSeq *
|
|
|
|
|
SdfReader::makeTripleSeq()
|
|
|
|
|
{
|
|
|
|
|
return new SdfTripleSeq;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::deleteTripleSeq(SdfTripleSeq *triples)
|
|
|
|
|
{
|
|
|
|
|
SdfTripleSeq::Iterator iter(triples);
|
|
|
|
|
while (iter.hasNext()) {
|
|
|
|
|
SdfTriple *triple = iter.next();
|
|
|
|
|
delete triple;
|
|
|
|
|
}
|
|
|
|
|
delete triples;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SdfTriple *
|
|
|
|
|
SdfReader::makeTriple()
|
|
|
|
|
{
|
|
|
|
|
return new SdfTriple(0, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SdfTriple *
|
|
|
|
|
SdfReader::makeTriple(float value)
|
|
|
|
|
{
|
|
|
|
|
value *= timescale_;
|
|
|
|
|
float *fp = new float(value);
|
|
|
|
|
return new SdfTriple(fp, fp, fp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SdfTriple *
|
|
|
|
|
SdfReader::makeTriple(float *min,
|
|
|
|
|
float *typ,
|
|
|
|
|
float *max)
|
|
|
|
|
{
|
|
|
|
|
if (min) *min *= timescale_;
|
|
|
|
|
if (typ) *typ *= timescale_;
|
|
|
|
|
if (max) *max *= timescale_;
|
|
|
|
|
return new SdfTriple(min, typ, max);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::deleteTriple(SdfTriple *triple)
|
|
|
|
|
{
|
|
|
|
|
delete triple;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::setInTimingCheck(bool in)
|
|
|
|
|
{
|
|
|
|
|
in_timing_check_ = in;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::setInIncremental(bool incr)
|
|
|
|
|
{
|
|
|
|
|
in_incremental_ = incr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
SdfReader::unescaped(const char *token)
|
|
|
|
|
{
|
|
|
|
|
char path_escape = network_->pathEscape();
|
|
|
|
|
char path_divider = network_->pathDivider();
|
|
|
|
|
char *unescaped = makeTmpString(strlen(token) + 1);
|
|
|
|
|
char *u = unescaped;
|
|
|
|
|
for (const char *s = token; *s ; s++) {
|
|
|
|
|
char ch = *s;
|
|
|
|
|
|
|
|
|
|
if (ch == escape_) {
|
|
|
|
|
char next_ch = s[1];
|
|
|
|
|
|
|
|
|
|
if (next_ch == divider_) {
|
|
|
|
|
// Escaped divider.
|
|
|
|
|
// Translate sdf escape to network escape.
|
|
|
|
|
*u++ = path_escape;
|
|
|
|
|
// Translate sdf divider to network divider.
|
|
|
|
|
*u++ = path_divider;
|
|
|
|
|
}
|
|
|
|
|
else if (next_ch == '['
|
|
|
|
|
|| next_ch == ']'
|
|
|
|
|
|| next_ch == escape_) {
|
|
|
|
|
// Escaped bus bracket or escape.
|
|
|
|
|
// Translate sdf escape to network escape.
|
|
|
|
|
*u++ = path_escape;
|
|
|
|
|
*u++ = next_ch;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
// Escaped non-divider/bracket character.
|
|
|
|
|
*u++ = next_ch;
|
|
|
|
|
s++;
|
|
|
|
|
}
|
|
|
|
|
else if (ch == divider_)
|
|
|
|
|
// Translate sdf divider to network divider.
|
|
|
|
|
*u++ = path_divider;
|
|
|
|
|
else
|
|
|
|
|
// Just the normal noises.
|
|
|
|
|
*u++ = ch;
|
|
|
|
|
}
|
|
|
|
|
*u = '\0';
|
|
|
|
|
return unescaped;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::incrLine()
|
|
|
|
|
{
|
|
|
|
|
line_++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::getChars(char *buf,
|
|
|
|
|
size_t &result,
|
|
|
|
|
size_t max_size)
|
|
|
|
|
{
|
|
|
|
|
char *status = gzgets(stream_, buf, max_size);
|
|
|
|
|
if (status == Z_NULL)
|
2019-03-13 01:25:53 +01:00
|
|
|
result = 0; // YY_nullptr
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
|
|
|
|
result = strlen(buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::getChars(char *buf,
|
|
|
|
|
int &result,
|
|
|
|
|
size_t max_size)
|
|
|
|
|
{
|
|
|
|
|
char *status = gzgets(stream_, buf, max_size);
|
|
|
|
|
if (status == Z_NULL)
|
2019-03-13 01:25:53 +01:00
|
|
|
result = 0; // YY_nullptr
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
|
|
|
|
result = strlen(buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::notSupported(const char *feature)
|
|
|
|
|
{
|
2020-12-14 02:21:35 +01:00
|
|
|
sdfError(193, "%s not supported.", feature);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SdfReader::portNotFound(const char *port_name)
|
|
|
|
|
{
|
2020-12-14 02:21:35 +01:00
|
|
|
sdfError(194, "instance %s port %s not found.",
|
2018-09-28 17:54:21 +02:00
|
|
|
network_->pathName(instance_),
|
|
|
|
|
port_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2020-12-14 02:21:35 +01:00
|
|
|
SdfReader::sdfError(int id,
|
|
|
|
|
const char *fmt, ...)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
va_list args;
|
|
|
|
|
va_start(args, fmt);
|
2020-12-14 02:21:35 +01:00
|
|
|
report_->vfileError(id, filename_, line_, fmt, args);
|
2018-09-28 17:54:21 +02:00
|
|
|
va_end(args);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Pin *
|
|
|
|
|
SdfReader::findPin(const char *name)
|
|
|
|
|
{
|
2019-01-17 00:37:31 +01:00
|
|
|
if (path_) {
|
|
|
|
|
string path_name;
|
2020-01-17 03:49:43 +01:00
|
|
|
stringPrint(path_name, "%s%c%s", path_, divider_, name);
|
2019-01-17 00:37:31 +01:00
|
|
|
Pin *pin = network_->findPin(path_name.c_str());
|
|
|
|
|
return pin;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return network_->findPin(name);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Instance *
|
|
|
|
|
SdfReader::findInstance(const char *name)
|
|
|
|
|
{
|
2019-01-17 00:37:31 +01:00
|
|
|
string inst_name = name;
|
|
|
|
|
if (path_)
|
|
|
|
|
stringPrint(inst_name, "%s%c%s", path_, divider_, name);
|
|
|
|
|
Instance *inst = network_->findInstance(inst_name.c_str());
|
2019-03-13 01:25:53 +01:00
|
|
|
if (inst == nullptr)
|
2020-12-14 02:21:35 +01:00
|
|
|
sdfError(195, "instance %s not found.", inst_name.c_str());
|
2018-09-28 17:54:21 +02:00
|
|
|
return inst;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
SdfTriple::SdfTriple(float *min,
|
|
|
|
|
float *typ,
|
|
|
|
|
float *max)
|
|
|
|
|
{
|
|
|
|
|
values_[0] = min;
|
|
|
|
|
values_[1] = typ;
|
|
|
|
|
values_[2] = max;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SdfTriple::~SdfTriple()
|
|
|
|
|
{
|
|
|
|
|
if (values_[0] == values_[1] && values_[0] == values_[2])
|
|
|
|
|
delete values_[0];
|
|
|
|
|
else {
|
|
|
|
|
if (values_[0]) delete values_[0];
|
|
|
|
|
if (values_[1]) delete values_[1];
|
|
|
|
|
if (values_[2]) delete values_[2];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
SdfTriple::hasValue() const
|
|
|
|
|
{
|
|
|
|
|
return values_[0] || values_[1] || values_[2];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
// Global namespace
|
|
|
|
|
|
|
|
|
|
void sdfFlushBuffer();
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
SdfParse_error(const char *msg)
|
|
|
|
|
{
|
2020-12-14 02:21:35 +01:00
|
|
|
sta::sdf_reader->sdfError(196, "%s.\n", msg);
|
2018-09-28 17:54:21 +02:00
|
|
|
sdfFlushBuffer();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|