OpenSTA/sdf/SdfWriter.cc

825 lines
22 KiB
C++
Raw Normal View History

2018-09-28 17:54:21 +02:00
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2022, 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
2018-09-28 17:54:21 +02:00
// 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/>.
2018-09-28 17:54:21 +02:00
2020-04-05 20:35:51 +02:00
#include "sdf/SdfWriter.hh"
2018-09-28 17:54:21 +02:00
#include <stdio.h>
#include <time.h>
2020-04-05 20:35:51 +02:00
2020-04-05 23:53:44 +02:00
#include "Zlib.hh"
#include "StaConfig.hh" // STA_VERSION
#include "Fuzzy.hh"
#include "StringUtil.hh"
#include "Units.hh"
#include "TimingRole.hh"
#include "TimingArc.hh"
#include "Liberty.hh"
#include "Sdc.hh"
#include "MinMaxValues.hh"
#include "Network.hh"
#include "Graph.hh"
#include "DcalcAnalysisPt.hh"
#include "GraphDelayCalc.hh"
#include "StaState.hh"
#include "Corner.hh"
#include "PathAnalysisPt.hh"
2018-09-28 17:54:21 +02:00
namespace sta {
class SdfWriter : public StaState
{
public:
SdfWriter(StaState *sta);
~SdfWriter();
void write(const char *filename,
Corner *corner,
char sdf_divider,
bool include_typ,
int digits,
2018-09-28 17:54:21 +02:00
bool gzip,
bool no_timestamp,
bool no_version);
protected:
void writeHeader(LibertyLibrary *default_lib,
bool no_timestamp,
bool no_version);
void writeTrailer();
void writeInterconnects();
void writeInstInterconnects(Instance *inst);
void writeInterconnectFromPin(Pin *drvr_pin);
void writeInstances();
void writeInstHeader(const Instance *inst);
void writeInstTrailer();
void writeIopaths(const Instance *inst,
bool &inst_header);
void writeIopathHeader();
void writeIopathTrailer();
void writeTimingChecks(const Instance *inst,
bool &inst_header);
void ensureTimingCheckheaders(bool &check_header,
const Instance *inst,
bool &inst_header);
void writeCheck(Edge *edge,
const char *sdf_check);
void writeCheck(Edge *edge,
TimingArc *arc,
const char *sdf_check,
bool use_data_edge,
bool use_clk_edge);
void writeEdgeCheck(Edge *edge,
const char *sdf_check,
2019-11-11 23:30:19 +01:00
int clk_rf_index,
TimingArc *arcs[RiseFall::index_count][RiseFall::index_count]);
2018-09-28 17:54:21 +02:00
void writeTimingCheckHeader();
void writeTimingCheckTrailer();
void writeWidthCheck(const Pin *pin,
2019-11-11 23:30:19 +01:00
const RiseFall *hi_low,
2018-09-28 17:54:21 +02:00
float min_width,
float max_width);
void writePeriodCheck(const Pin *pin,
float min_period);
const char *sdfEdge(const Transition *tr);
void writeArcDelays(Edge *edge);
void writeSdfTriple(RiseFallMinMax &delays,
RiseFall *rf);
void writeSdfTriple(float min,
float max);
2018-09-28 17:54:21 +02:00
void writeSdfDelay(double delay);
char *sdfPortName(const Pin *pin);
char *sdfPathName(const Pin *pin);
char *sdfPathName(const Instance *inst);
char *sdfName(const Instance *inst);
private:
DISALLOW_COPY_AND_ASSIGN(SdfWriter);
char sdf_divider_;
bool include_typ_;
2018-09-28 17:54:21 +02:00
float timescale_;
char sdf_escape_;
char network_escape_;
char *delay_format_;
gzFile stream_;
const Corner *corner_;
int arc_delay_min_index_;
int arc_delay_max_index_;
};
void
writeSdf(const char *filename,
Corner *corner,
char sdf_divider,
bool include_typ,
2018-09-28 17:54:21 +02:00
int digits,
bool gzip,
bool no_timestamp,
bool no_version,
StaState *sta)
{
SdfWriter writer(sta);
writer.write(filename, corner, sdf_divider, include_typ, digits, gzip,
2018-09-28 17:54:21 +02:00
no_timestamp, no_version);
}
SdfWriter::SdfWriter(StaState *sta) :
StaState(sta),
sdf_escape_('\\'),
network_escape_(network_->pathEscape()),
2019-03-13 01:25:53 +01:00
delay_format_(nullptr)
2018-09-28 17:54:21 +02:00
{
}
SdfWriter::~SdfWriter()
{
stringDelete(delay_format_);
}
void
SdfWriter::write(const char *filename,
Corner *corner,
char sdf_divider,
bool include_typ,
2018-09-28 17:54:21 +02:00
int digits,
bool gzip,
bool no_timestamp,
bool no_version)
{
sdf_divider_ = sdf_divider;
include_typ_ = include_typ;
2019-03-13 01:25:53 +01:00
if (delay_format_ == nullptr)
2018-09-28 17:54:21 +02:00
delay_format_ = new char[10];
sprintf(delay_format_, "%%.%df", digits);
LibertyLibrary *default_lib = network_->defaultLibertyLibrary();
timescale_ = default_lib->units()->timeUnit()->scale();
corner_ = corner;
MinMax *min_max;
const DcalcAnalysisPt *dcalc_ap;
min_max = MinMax::min();
dcalc_ap = corner_->findDcalcAnalysisPt(min_max);
arc_delay_min_index_ = dcalc_ap->index();
min_max = MinMax::max();
dcalc_ap = corner_->findDcalcAnalysisPt(min_max);
arc_delay_max_index_ = dcalc_ap->index();
stream_ = gzopen(filename, gzip ? "wb" : "wT");
if (stream_ == nullptr)
throw FileNotWritable(filename);
2018-09-28 17:54:21 +02:00
writeHeader(default_lib, no_timestamp, no_version);
writeInterconnects();
writeInstances();
writeTrailer();
gzclose(stream_);
2019-03-13 01:25:53 +01:00
stream_ = nullptr;
2018-09-28 17:54:21 +02:00
}
void
SdfWriter::writeHeader(LibertyLibrary *default_lib,
bool no_timestamp,
bool no_version)
{
gzprintf(stream_, "(DELAYFILE\n");
gzprintf(stream_, " (SDFVERSION \"3.0\")\n");
gzprintf(stream_, " (DESIGN \"%s\")\n",
network_->cellName(network_->topInstance()));
if (!no_timestamp) {
time_t now;
time(&now);
char *time_str = ctime(&now);
// Remove trailing \n.
time_str[strlen(time_str) - 1] = '\0';
gzprintf(stream_, " (DATE \"%s\")\n", time_str);
}
gzprintf(stream_, " (VENDOR \"Parallax\")\n");
gzprintf(stream_, " (PROGRAM \"STA\")\n");
if (!no_version)
2019-02-16 21:07:59 +01:00
gzprintf(stream_, " (VERSION \"%s\")\n", STA_VERSION);
2018-09-28 17:54:21 +02:00
gzprintf(stream_, " (DIVIDER %c)\n", sdf_divider_);
OperatingConditions *cond_min =
sdc_->operatingConditions(MinMax::min());
2019-03-13 01:25:53 +01:00
if (cond_min == nullptr)
2018-09-28 17:54:21 +02:00
cond_min = default_lib->defaultOperatingConditions();
OperatingConditions *cond_max =
sdc_->operatingConditions(MinMax::max());
2019-03-13 01:25:53 +01:00
if (cond_max == nullptr)
2018-09-28 17:54:21 +02:00
cond_max = default_lib->defaultOperatingConditions();
if (cond_min && cond_max) {
gzprintf(stream_, " (VOLTAGE %.3f::%.3f)\n",
cond_min->voltage(),
cond_max->voltage());
gzprintf(stream_, " (PROCESS \"%.3f::%.3f\")\n",
cond_min->process(),
cond_max->process());
gzprintf(stream_, " (TEMPERATURE %.3f::%.3f)\n",
cond_min->temperature(),
cond_max->temperature());
}
2019-03-13 01:25:53 +01:00
const char *sdf_timescale = nullptr;
2018-09-28 17:54:21 +02:00
if (fuzzyEqual(timescale_, 1e-6))
sdf_timescale = "1us";
else if (fuzzyEqual(timescale_, 10e-6))
sdf_timescale = "10us";
else if (fuzzyEqual(timescale_, 100e-6))
sdf_timescale = "100us";
else if (fuzzyEqual(timescale_, 1e-9))
sdf_timescale = "1ns";
else if (fuzzyEqual(timescale_, 10e-9))
sdf_timescale = "10ns";
else if (fuzzyEqual(timescale_, 100e-9))
sdf_timescale = "100ns";
else if (fuzzyEqual(timescale_, 1e-12))
sdf_timescale = "1ps";
else if (fuzzyEqual(timescale_, 10e-12))
sdf_timescale = "10ps";
else if (fuzzyEqual(timescale_, 100e-12))
sdf_timescale = "100ps";
if (sdf_timescale)
gzprintf(stream_, " (TIMESCALE %s)\n", sdf_timescale);
}
void
SdfWriter::writeTrailer()
{
gzprintf(stream_, ")\n");
}
void
SdfWriter::writeInterconnects()
{
gzprintf(stream_, " (CELL\n");
gzprintf(stream_, " (CELLTYPE \"%s\")\n",
network_->cellName(network_->topInstance()));
gzprintf(stream_, " (INSTANCE)\n");
gzprintf(stream_, " (DELAY\n");
gzprintf(stream_, " (ABSOLUTE\n");
writeInstInterconnects(network_->topInstance());
LeafInstanceIterator *inst_iter = network_->leafInstanceIterator();
while (inst_iter->hasNext()) {
Instance *inst = inst_iter->next();
writeInstInterconnects(inst);
}
delete inst_iter;
gzprintf(stream_, " )\n");
gzprintf(stream_, " )\n");
gzprintf(stream_, " )\n");
}
void
SdfWriter::writeInstInterconnects(Instance *inst)
{
InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) {
Pin *pin = pin_iter->next();
if (network_->isDriver(pin))
writeInterconnectFromPin(pin);
}
delete pin_iter;
}
void
SdfWriter::writeInterconnectFromPin(Pin *drvr_pin)
{
Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin);
VertexOutEdgeIterator edge_iter(drvr_vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
if (edge->isWire()) {
Pin *load_pin = edge->to(graph_)->pin();
gzprintf(stream_, " (INTERCONNECT %s %s ",
sdfPathName(drvr_pin),
sdfPathName(load_pin));
writeArcDelays(edge);
gzprintf(stream_, ")\n");
}
}
}
void
SdfWriter::writeInstances()
{
LeafInstanceIterator *leaf_iter = network_->leafInstanceIterator();
while (leaf_iter->hasNext()) {
const Instance *inst = leaf_iter->next();
bool inst_header = false;
writeIopaths(inst, inst_header);
writeTimingChecks(inst, inst_header);
if (inst_header)
writeInstTrailer();
}
delete leaf_iter;
}
void
SdfWriter::writeInstHeader(const Instance *inst)
{
gzprintf(stream_, " (CELL\n");
gzprintf(stream_, " (CELLTYPE \"%s\")\n", network_->cellName(inst));
gzprintf(stream_, " (INSTANCE %s)\n", sdfPathName(inst));
}
void
SdfWriter::writeInstTrailer()
{
gzprintf(stream_, " )\n");
}
void
SdfWriter::writeIopaths(const Instance *inst,
bool &inst_header)
{
bool iopath_header = false;
InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) {
Pin *from_pin = pin_iter->next();
if (network_->isLoad(from_pin)) {
Vertex *from_vertex = graph_->pinLoadVertex(from_pin);
VertexOutEdgeIterator edge_iter(from_vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
TimingRole *role = edge->role();
if (role == TimingRole::combinational()
|| role == TimingRole::tristateEnable()
|| role == TimingRole::regClkToQ()
|| role == TimingRole::regSetClr()
|| role == TimingRole::latchEnToQ()
|| role == TimingRole::latchDtoQ()) {
Vertex *to_vertex = edge->to(graph_);
Pin *to_pin = to_vertex->pin();
if (!inst_header) {
writeInstHeader(inst);
inst_header = true;
}
if (!iopath_header) {
writeIopathHeader();
iopath_header = true;
}
const char *sdf_cond = edge->timingArcSet()->sdfCond();
if (sdf_cond) {
gzprintf(stream_, " (COND %s\n", sdf_cond);
gzprintf(stream_, " ");
}
gzprintf(stream_, " (IOPATH %s %s ",
sdfPortName(from_pin),
sdfPortName(to_pin));
writeArcDelays(edge);
if (sdf_cond)
gzprintf(stream_, ")");
gzprintf(stream_, ")\n");
}
}
}
}
delete pin_iter;
if (iopath_header)
writeIopathTrailer();
}
void
SdfWriter::writeIopathHeader()
{
gzprintf(stream_, " (DELAY\n");
gzprintf(stream_, " (ABSOLUTE\n");
}
void
SdfWriter::writeIopathTrailer()
{
gzprintf(stream_, " )\n");
gzprintf(stream_, " )\n");
}
void
SdfWriter::writeArcDelays(Edge *edge)
{
RiseFallMinMax delays;
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();
2019-11-11 23:30:19 +01:00
RiseFall *rf = arc->toTrans()->asRiseFall();
2018-09-28 17:54:21 +02:00
ArcDelay min_delay = graph_->arcDelay(edge, arc, arc_delay_min_index_);
2019-11-11 23:30:19 +01:00
delays.setValue(rf, MinMax::min(), delayAsFloat(min_delay));
2018-09-28 17:54:21 +02:00
ArcDelay max_delay = graph_->arcDelay(edge, arc, arc_delay_max_index_);
2019-11-11 23:30:19 +01:00
delays.setValue(rf, MinMax::max(), delayAsFloat(max_delay));
2018-09-28 17:54:21 +02:00
}
2019-11-11 23:30:19 +01:00
if (delays.hasValue(RiseFall::rise(), MinMax::min())
&& delays.hasValue(RiseFall::fall(), MinMax::min())) {
2018-09-28 17:54:21 +02:00
// Rise and fall.
writeSdfTriple(delays, RiseFall::rise());
2018-09-28 17:54:21 +02:00
// Merge rise/fall values if they are the same.
2019-11-11 23:30:19 +01:00
if (!(fuzzyEqual(delays.value(RiseFall::rise(), MinMax::min()),
delays.value(RiseFall::fall(), MinMax::min()))
&& fuzzyEqual(delays.value(RiseFall::rise(), MinMax::max()),
delays.value(RiseFall::fall(),MinMax::max())))) {
2018-09-28 17:54:21 +02:00
gzprintf(stream_, " ");
writeSdfTriple(delays, RiseFall::fall());
2018-09-28 17:54:21 +02:00
}
}
2019-11-11 23:30:19 +01:00
else if (delays.hasValue(RiseFall::rise(), MinMax::min()))
2018-09-28 17:54:21 +02:00
// Rise only.
writeSdfTriple(delays, RiseFall::rise());
2019-11-11 23:30:19 +01:00
else if (delays.hasValue(RiseFall::fall(), MinMax::min())) {
2018-09-28 17:54:21 +02:00
// Fall only.
gzprintf(stream_, "() ");
writeSdfTriple(delays, RiseFall::fall());
2018-09-28 17:54:21 +02:00
}
}
void
SdfWriter::writeSdfTriple(RiseFallMinMax &delays,
RiseFall *rf)
2018-09-28 17:54:21 +02:00
{
float min = delays.value(rf, MinMax::min());
float max = delays.value(rf, MinMax::max());
writeSdfTriple(min, max);
2018-09-28 17:54:21 +02:00
}
void
SdfWriter::writeSdfTriple(float min,
float max)
2018-09-28 17:54:21 +02:00
{
gzprintf(stream_, "(");
writeSdfDelay(min);
if (include_typ_) {
gzprintf(stream_, ":");
writeSdfDelay((min + max) / 2.0);
gzprintf(stream_, ":");
}
else
gzprintf(stream_, "::");
writeSdfDelay(max);
2018-09-28 17:54:21 +02:00
gzprintf(stream_, ")");
}
void
SdfWriter::writeSdfDelay(double delay)
{
gzprintf(stream_, delay_format_, delay / timescale_);
}
void
SdfWriter::writeTimingChecks(const Instance *inst,
bool &inst_header)
{
bool check_header = false;
InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) {
Pin *pin = pin_iter->next();
Vertex *vertex = graph_->pinLoadVertex(pin);
VertexOutEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
TimingRole *role = edge->role();
2019-03-13 01:25:53 +01:00
const char *sdf_check = nullptr;
2018-09-28 17:54:21 +02:00
if (role == TimingRole::setup())
sdf_check = "SETUP";
else if (role == TimingRole::hold())
sdf_check = "HOLD";
else if (role == TimingRole::recovery())
sdf_check = "RECOVERY";
else if (role == TimingRole::removal())
sdf_check = "REMOVAL";
if (sdf_check) {
ensureTimingCheckheaders(check_header, inst, inst_header);
writeCheck(edge, sdf_check);
}
}
2019-11-11 23:30:19 +01:00
for (auto hi_low : RiseFall::range()) {
2018-09-28 17:54:21 +02:00
float min_width, max_width;
bool exists;
graph_delay_calc_->minPulseWidth(pin, hi_low, arc_delay_min_index_,
MinMax::min(),
min_width, exists);
graph_delay_calc_->minPulseWidth(pin, hi_low, arc_delay_max_index_,
MinMax::max(),
max_width, exists);
if (exists) {
ensureTimingCheckheaders(check_header, inst, inst_header);
writeWidthCheck(pin, hi_low, min_width, max_width);
}
}
float min_period;
bool exists;
graph_delay_calc_->minPeriod(pin, min_period, exists);
if (exists) {
ensureTimingCheckheaders(check_header, inst, inst_header);
writePeriodCheck(pin, min_period);
}
}
delete pin_iter;
if (check_header)
writeTimingCheckTrailer();
}
void
SdfWriter::ensureTimingCheckheaders(bool &check_header,
const Instance *inst,
bool &inst_header)
{
if (!inst_header) {
writeInstHeader(inst);
inst_header = true;
}
if (!check_header) {
writeTimingCheckHeader();
check_header = true;
}
}
void
SdfWriter::writeTimingCheckHeader()
{
gzprintf(stream_, " (TIMINGCHECK\n");
}
void
SdfWriter::writeTimingCheckTrailer()
{
gzprintf(stream_, " )\n");
}
void
SdfWriter::writeCheck(Edge *edge,
const char *sdf_check)
{
TimingArcSet *arc_set = edge->timingArcSet();
// Examine the arcs to see if the check requires clk or data edge specifiers.
2019-11-11 23:30:19 +01:00
TimingArc *arcs[RiseFall::index_count][RiseFall::index_count] =
2019-03-13 01:25:53 +01:00
{{nullptr, nullptr}, {nullptr, nullptr}};
2018-12-05 23:18:41 +01:00
TimingArcSetArcIterator arc_iter(arc_set);
while (arc_iter.hasNext()) {
TimingArc *arc = arc_iter.next();
2019-11-11 23:30:19 +01:00
RiseFall *clk_rf = arc->fromTrans()->asRiseFall();
RiseFall *data_rf = arc->toTrans()->asRiseFall();;
arcs[clk_rf->index()][data_rf->index()] = arc;
2018-09-28 17:54:21 +02:00
}
2019-11-11 23:30:19 +01:00
if (arcs[RiseFall::fallIndex()][RiseFall::riseIndex()] == nullptr
&& arcs[RiseFall::fallIndex()][RiseFall::fallIndex()] == nullptr)
writeEdgeCheck(edge, sdf_check, RiseFall::riseIndex(), arcs);
else if (arcs[RiseFall::riseIndex()][RiseFall::riseIndex()] == nullptr
&& arcs[RiseFall::riseIndex()][RiseFall::fallIndex()] == nullptr)
writeEdgeCheck(edge, sdf_check, RiseFall::fallIndex(), arcs);
2018-09-28 17:54:21 +02:00
else {
// No special case; write all the checks with data and clock edge specifiers.
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
writeCheck(edge, arc, sdf_check, true, true);
}
}
}
void
SdfWriter::writeEdgeCheck(Edge *edge,
const char *sdf_check,
2019-11-11 23:30:19 +01:00
int clk_rf_index,
TimingArc *arcs[RiseFall::index_count][RiseFall::index_count])
2018-09-28 17:54:21 +02:00
{
// SDF requires edge specifiers on the data port to define separate
// rise/fall check values.
// Check the rise/fall margins to see if they are the same to avoid adding
// data port edge specifiers if they aren't necessary.
2019-11-11 23:30:19 +01:00
if (arcs[clk_rf_index][RiseFall::riseIndex()]
&& arcs[clk_rf_index][RiseFall::fallIndex()]
&& arcs[clk_rf_index][RiseFall::riseIndex()]
&& arcs[clk_rf_index][RiseFall::fallIndex()]
2020-07-12 01:24:48 +02:00
&& delayEqual(graph_->arcDelay(edge,
arcs[clk_rf_index][RiseFall::riseIndex()],
arc_delay_min_index_),
graph_->arcDelay(edge,
arcs[clk_rf_index][RiseFall::fallIndex()],
arc_delay_min_index_))
&& delayEqual(graph_->arcDelay(edge,
arcs[clk_rf_index][RiseFall::riseIndex()],
arc_delay_max_index_),
graph_->arcDelay(edge,
arcs[clk_rf_index][RiseFall::fallIndex()],
arc_delay_max_index_)))
2018-09-28 17:54:21 +02:00
// Rise/fall margins are the same, so no data edge specifier is required.
2019-11-11 23:30:19 +01:00
writeCheck(edge, arcs[clk_rf_index][RiseFall::riseIndex()],
2018-09-28 17:54:21 +02:00
sdf_check, false, true);
else {
2019-11-11 23:30:19 +01:00
if (arcs[clk_rf_index][RiseFall::riseIndex()])
writeCheck(edge, arcs[clk_rf_index][RiseFall::riseIndex()],
2018-09-28 17:54:21 +02:00
sdf_check, true, true);
2019-11-11 23:30:19 +01:00
if (arcs[clk_rf_index][RiseFall::fallIndex()])
writeCheck(edge, arcs[clk_rf_index][RiseFall::fallIndex()],
2018-09-28 17:54:21 +02:00
sdf_check, true, true);
}
}
void
SdfWriter::writeCheck(Edge *edge,
TimingArc *arc,
const char *sdf_check,
bool use_data_edge,
bool use_clk_edge)
{
TimingArcSet *arc_set = edge->timingArcSet();
Pin *from_pin = edge->from(graph_)->pin();
Pin *to_pin = edge->to(graph_)->pin();
const char *sdf_cond_start = arc_set->sdfCondStart();
const char *sdf_cond_end = arc_set->sdfCondEnd();
gzprintf(stream_, " (%s ", sdf_check);
if (sdf_cond_start)
gzprintf(stream_, "(COND %s ", sdf_cond_start);
if (use_data_edge)
gzprintf(stream_, "(%s %s)",
sdfEdge(arc->toTrans()),
sdfPortName(to_pin));
else
gzprintf(stream_, "%s", sdfPortName(to_pin));
if (sdf_cond_start)
gzprintf(stream_, ")");
gzprintf(stream_, " ");
if (sdf_cond_end)
gzprintf(stream_, "(COND %s ", sdf_cond_end);
if (use_clk_edge)
gzprintf(stream_, "(%s %s)",
sdfEdge(arc->fromTrans()),
sdfPortName(from_pin));
else
gzprintf(stream_, "%s", sdfPortName(from_pin));
if (sdf_cond_end)
gzprintf(stream_, ")");
gzprintf(stream_, " ");
ArcDelay min_delay = graph_->arcDelay(edge, arc, arc_delay_min_index_);
ArcDelay max_delay = graph_->arcDelay(edge, arc, arc_delay_max_index_);
writeSdfTriple(delayAsFloat(min_delay), delayAsFloat(max_delay));
2018-09-28 17:54:21 +02:00
gzprintf(stream_, ")\n");
}
void
SdfWriter::writeWidthCheck(const Pin *pin,
2019-11-11 23:30:19 +01:00
const RiseFall *hi_low,
2018-09-28 17:54:21 +02:00
float min_width,
float max_width)
{
gzprintf(stream_, " (WIDTH (%s %s) ",
sdfEdge(hi_low->asTransition()),
sdfPortName(pin));
writeSdfTriple(min_width, max_width);
2018-09-28 17:54:21 +02:00
gzprintf(stream_, ")\n");
}
void
SdfWriter::writePeriodCheck(const Pin *pin,
float min_period)
{
gzprintf(stream_, " (PERIOD %s ",
sdfPortName(pin));
writeSdfTriple(min_period, min_period);
2018-09-28 17:54:21 +02:00
gzprintf(stream_, ")\n");
}
const char *
SdfWriter::sdfEdge(const Transition *tr)
{
if (tr == Transition::rise())
return "posedge";
else if (tr == Transition::fall())
return "negedge";
2019-03-13 01:25:53 +01:00
return nullptr;
2018-09-28 17:54:21 +02:00
}
////////////////////////////////////////////////////////////////
char *
SdfWriter::sdfPathName(const Pin *pin)
{
Instance *inst = network_->instance(pin);
if (network_->isTopInstance(inst))
return sdfPortName(pin);
else {
char *inst_path = sdfPathName(inst);
const char *port_name = sdfPortName(pin);
char *sdf_name = makeTmpString(strlen(inst_path)+1+strlen(port_name)+1);
sprintf(sdf_name, "%s%c%s", inst_path, sdf_divider_, port_name);
return sdf_name;
}
}
// Based on Network::pathName.
char *
SdfWriter::sdfPathName(const Instance *instance)
{
ConstInstanceSeq inst_path;
network_->path(instance, inst_path);
size_t name_length = 0;
ConstInstanceSeq::Iterator path_iter1(inst_path);
while (path_iter1.hasNext()) {
const Instance *inst = path_iter1.next();
name_length += strlen(sdfName(inst)) + 1;
}
char *path_name = makeTmpString(name_length);
char *path_ptr = path_name;
// Top instance has null string name, so terminate the string here.
*path_name = '\0';
while (inst_path.size()) {
const Instance *inst = inst_path.back();
const char *inst_name = sdfName(inst);
strcpy(path_ptr, inst_name);
path_ptr += strlen(inst_name);
inst_path.pop_back();
if (inst_path.size())
*path_ptr++ = sdf_divider_;
*path_ptr = '\0';
}
return path_name;
}
// Escape for non-alpha numeric characters.
char *
SdfWriter::sdfName(const Instance *inst)
{
const char *name = network_->name(inst);
char *sdf_name = makeTmpString(strlen(name) * 2 + 1);
const char *p = name;
char *s = sdf_name;
while (*p) {
char ch = *p;
// Ignore sta escapes.
if (ch != network_escape_) {
if (!(isalnum(ch) || ch == '_'))
// Insert escape.
*s++ = sdf_escape_;
*s++ = ch;
}
p++;
}
*s = '\0';
return sdf_name;
}
char *
SdfWriter::sdfPortName(const Pin *pin)
{
const char *name = network_->portName(pin);
char *sdf_name = makeTmpString(strlen(name) * 2 + 1);
const char *p = name;
char *s = sdf_name;
while (*p) {
char ch = *p;
if (ch == network_escape_) {
// Copy escape and escaped char.
*s++ = sdf_escape_;
*s++ = *++p;
}
else {
if (!(isalnum(ch) || ch == '_' || ch == '[' || ch == ']'))
// Insert escape.
*s++ = sdf_escape_;
*s++ = ch;
}
p++;
}
*s = '\0';
return sdf_name;
}
2018-09-28 17:54:21 +02:00
} // namespace