2018-09-28 17:54:21 +02:00
|
|
|
// OpenSTA, Static Timing Analyzer
|
2025-01-22 02:54:33 +01:00
|
|
|
// Copyright (c) 2025, 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
|
2022-01-04 18:17:08 +01:00
|
|
|
// 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
|
2022-01-04 18:17:08 +01:00
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2025-01-22 02:54:33 +01:00
|
|
|
//
|
|
|
|
|
// The origin of this software must not be misrepresented; you must not
|
|
|
|
|
// claim that you wrote the original software.
|
|
|
|
|
//
|
|
|
|
|
// Altered source versions must be plainly marked as such, and must not be
|
|
|
|
|
// misrepresented as being the original software.
|
|
|
|
|
//
|
|
|
|
|
// This notice may not be removed or altered from any source distribution.
|
2018-09-28 17:54:21 +02:00
|
|
|
|
2020-04-05 20:35:51 +02:00
|
|
|
#include "sdf/SdfWriter.hh"
|
|
|
|
|
|
2023-06-15 17:59:56 +02:00
|
|
|
#include <cstdio>
|
|
|
|
|
#include <ctime>
|
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,
|
2024-06-27 22:57:58 +02:00
|
|
|
const Corner *corner,
|
2018-09-28 17:54:21 +02:00
|
|
|
char sdf_divider,
|
2021-11-09 00:49:43 +01:00
|
|
|
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);
|
2021-11-09 00:49:43 +01:00
|
|
|
void writeSdfTriple(RiseFallMinMax &delays,
|
2025-03-31 00:27:53 +02:00
|
|
|
const RiseFall *rf);
|
2021-11-09 00:49:43 +01:00
|
|
|
void writeSdfTriple(float min,
|
|
|
|
|
float max);
|
2018-09-28 17:54:21 +02:00
|
|
|
void writeSdfDelay(double delay);
|
2023-03-26 15:34:36 +02:00
|
|
|
string sdfPortName(const Pin *pin);
|
|
|
|
|
string sdfPathName(const Pin *pin);
|
|
|
|
|
string sdfPathName(const Instance *inst);
|
|
|
|
|
string sdfName(const Instance *inst);
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
char sdf_divider_;
|
2021-11-09 00:49:43 +01:00
|
|
|
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,
|
2024-06-27 22:57:58 +02:00
|
|
|
const Corner *corner,
|
2018-09-28 17:54:21 +02:00
|
|
|
char sdf_divider,
|
2021-11-09 00:49:43 +01:00
|
|
|
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);
|
2021-11-09 00:49:43 +01:00
|
|
|
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,
|
2024-06-27 22:57:58 +02:00
|
|
|
const Corner *corner,
|
2018-09-28 17:54:21 +02:00
|
|
|
char sdf_divider,
|
2021-11-09 00:49:43 +01:00
|
|
|
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;
|
2021-11-09 00:49:43 +01:00
|
|
|
include_typ_ = include_typ;
|
2019-03-13 01:25:53 +01:00
|
|
|
if (delay_format_ == nullptr)
|
2022-01-13 19:59:36 +01:00
|
|
|
delay_format_ = stringPrint("%%.%df", digits);
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
LibertyLibrary *default_lib = network_->defaultLibertyLibrary();
|
|
|
|
|
timescale_ = default_lib->units()->timeUnit()->scale();
|
|
|
|
|
|
|
|
|
|
corner_ = corner;
|
2025-03-31 00:27:53 +02:00
|
|
|
const MinMax *min_max;
|
2018-09-28 17:54:21 +02:00
|
|
|
const DcalcAnalysisPt *dcalc_ap;
|
2025-03-31 00:27:53 +02:00
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
min_max = MinMax::min();
|
|
|
|
|
dcalc_ap = corner_->findDcalcAnalysisPt(min_max);
|
|
|
|
|
arc_delay_min_index_ = dcalc_ap->index();
|
2025-03-31 00:27:53 +02:00
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
min_max = MinMax::max();
|
|
|
|
|
dcalc_ap = corner_->findDcalcAnalysisPt(min_max);
|
|
|
|
|
arc_delay_max_index_ = dcalc_ap->index();
|
|
|
|
|
|
|
|
|
|
stream_ = gzopen(filename, gzip ? "wb" : "wT");
|
2021-12-08 18:23:44 +01:00
|
|
|
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_);
|
|
|
|
|
|
2023-09-19 01:02:30 +02:00
|
|
|
LibertyLibrary *lib_min = default_lib;
|
|
|
|
|
const LibertySeq &libs_min = corner_->libertyLibraries(MinMax::min());
|
|
|
|
|
if (!libs_min.empty())
|
|
|
|
|
lib_min = libs_min[0];
|
|
|
|
|
LibertyLibrary *lib_max = default_lib;
|
|
|
|
|
const LibertySeq &libs_max = corner_->libertyLibraries(MinMax::max());
|
|
|
|
|
if (!libs_max.empty())
|
|
|
|
|
lib_max = libs_max[0];
|
|
|
|
|
|
|
|
|
|
OperatingConditions *cond_min = lib_min->defaultOperatingConditions();
|
|
|
|
|
OperatingConditions *cond_max = lib_max->defaultOperatingConditions();
|
2018-09-28 17:54:21 +02:00
|
|
|
if (cond_min && cond_max) {
|
|
|
|
|
gzprintf(stream_, " (VOLTAGE %.3f::%.3f)\n",
|
2023-09-19 01:02:30 +02:00
|
|
|
cond_min->voltage(),
|
|
|
|
|
cond_max->voltage());
|
2018-09-28 17:54:21 +02:00
|
|
|
gzprintf(stream_, " (PROCESS \"%.3f::%.3f\")\n",
|
2023-09-19 01:02:30 +02:00
|
|
|
cond_min->process(),
|
|
|
|
|
cond_max->process());
|
2018-09-28 17:54:21 +02:00
|
|
|
gzprintf(stream_, " (TEMPERATURE %.3f::%.3f)\n",
|
2023-09-19 01:02:30 +02:00
|
|
|
cond_min->temperature(),
|
|
|
|
|
cond_max->temperature());
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
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);
|
2022-01-15 20:51:05 +01:00
|
|
|
if (drvr_vertex) {
|
|
|
|
|
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();
|
2023-03-26 15:34:36 +02:00
|
|
|
string drvr_pin_name = sdfPathName(drvr_pin);
|
|
|
|
|
string load_pin_name = sdfPathName(load_pin);
|
2022-01-15 20:51:05 +01:00
|
|
|
gzprintf(stream_, " (INTERCONNECT %s %s ",
|
2023-03-26 15:34:36 +02:00
|
|
|
drvr_pin_name.c_str(),
|
|
|
|
|
load_pin_name.c_str());
|
2022-01-15 20:51:05 +01:00
|
|
|
writeArcDelays(edge);
|
|
|
|
|
gzprintf(stream_, ")\n");
|
|
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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));
|
2023-03-26 15:34:36 +02:00
|
|
|
string inst_name = sdfPathName(inst);
|
|
|
|
|
gzprintf(stream_, " (INSTANCE %s)\n", inst_name.c_str());
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
2025-03-31 00:27:53 +02:00
|
|
|
const TimingRole *role = edge->role();
|
2018-09-28 17:54:21 +02:00
|
|
|
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_, " ");
|
|
|
|
|
}
|
2023-03-26 15:34:36 +02:00
|
|
|
string from_pin_name = sdfPortName(from_pin);
|
|
|
|
|
string to_pin_name = sdfPortName(to_pin);
|
|
|
|
|
gzprintf(stream_, " (IOPATH %s %s ",
|
|
|
|
|
from_pin_name.c_str(),
|
|
|
|
|
to_pin_name.c_str());
|
2018-09-28 17:54:21 +02:00
|
|
|
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();
|
2022-06-12 20:47:26 +02:00
|
|
|
for (TimingArc *arc : arc_set->arcs()) {
|
2025-03-31 00:27:53 +02:00
|
|
|
const RiseFall *rf = arc->toEdge()->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.
|
2021-11-09 00:49:43 +01:00
|
|
|
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_, " ");
|
2021-11-09 00:49:43 +01:00
|
|
|
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.
|
2021-11-09 00:49:43 +01:00
|
|
|
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_, "() ");
|
2021-11-09 00:49:43 +01:00
|
|
|
writeSdfTriple(delays, RiseFall::fall());
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2021-11-09 00:49:43 +01:00
|
|
|
SdfWriter::writeSdfTriple(RiseFallMinMax &delays,
|
2025-03-31 00:27:53 +02:00
|
|
|
const RiseFall *rf)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2021-11-09 00:49:43 +01: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
|
2021-11-09 00:49:43 +01:00
|
|
|
SdfWriter::writeSdfTriple(float min,
|
|
|
|
|
float max)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
gzprintf(stream_, "(");
|
2021-11-09 00:49:43 +01:00
|
|
|
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();
|
2025-03-31 00:27:53 +02:00
|
|
|
const 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;
|
2024-04-17 20:49:19 +02:00
|
|
|
Edge *edge;
|
|
|
|
|
TimingArc *arc;
|
|
|
|
|
graph_->minPulseWidthArc(vertex, hi_low, edge, arc);
|
|
|
|
|
if (edge) {
|
|
|
|
|
min_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_min_index_));
|
|
|
|
|
max_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_max_index_));
|
2018-09-28 17:54:21 +02:00
|
|
|
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}};
|
2022-06-12 20:47:26 +02:00
|
|
|
for (TimingArc *arc : arc_set->arcs()) {
|
2025-03-31 00:27:53 +02:00
|
|
|
const RiseFall *clk_rf = arc->fromEdge()->asRiseFall();
|
|
|
|
|
const RiseFall *data_rf = arc->toEdge()->asRiseFall();;
|
2019-11-11 23:30:19 +01:00
|
|
|
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.
|
2022-06-12 20:47:26 +02:00
|
|
|
for (TimingArc *arc : arc_set->arcs())
|
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);
|
|
|
|
|
|
2023-03-26 15:34:36 +02:00
|
|
|
string to_pin_name = sdfPortName(to_pin);
|
|
|
|
|
if (use_data_edge) {
|
2018-09-28 17:54:21 +02:00
|
|
|
gzprintf(stream_, "(%s %s)",
|
2022-02-14 01:46:45 +01:00
|
|
|
sdfEdge(arc->toEdge()),
|
2023-03-26 15:34:36 +02:00
|
|
|
to_pin_name.c_str());
|
|
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
2023-03-26 15:34:36 +02:00
|
|
|
gzprintf(stream_, "%s", to_pin_name.c_str());
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
if (sdf_cond_start)
|
|
|
|
|
gzprintf(stream_, ")");
|
|
|
|
|
|
|
|
|
|
gzprintf(stream_, " ");
|
|
|
|
|
|
|
|
|
|
if (sdf_cond_end)
|
|
|
|
|
gzprintf(stream_, "(COND %s ", sdf_cond_end);
|
|
|
|
|
|
2023-03-26 15:34:36 +02:00
|
|
|
string from_pin_name = sdfPortName(from_pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (use_clk_edge)
|
|
|
|
|
gzprintf(stream_, "(%s %s)",
|
2022-02-14 01:46:45 +01:00
|
|
|
sdfEdge(arc->fromEdge()),
|
2023-03-26 15:34:36 +02:00
|
|
|
from_pin_name.c_str());
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
2023-03-26 15:34:36 +02:00
|
|
|
gzprintf(stream_, "%s", from_pin_name.c_str());
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
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_);
|
2021-11-09 00:49:43 +01:00
|
|
|
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)
|
|
|
|
|
{
|
2023-03-26 15:34:36 +02:00
|
|
|
string pin_name = sdfPortName(pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
gzprintf(stream_, " (WIDTH (%s %s) ",
|
|
|
|
|
sdfEdge(hi_low->asTransition()),
|
2023-03-26 15:34:36 +02:00
|
|
|
pin_name.c_str());
|
2021-11-09 00:49:43 +01:00
|
|
|
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)
|
|
|
|
|
{
|
2023-03-26 15:34:36 +02:00
|
|
|
string pin_name = sdfPortName(pin);
|
|
|
|
|
gzprintf(stream_, " (PERIOD %s ", pin_name.c_str());
|
2021-11-09 00:49:43 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2023-03-26 15:34:36 +02:00
|
|
|
string
|
2018-09-28 17:54:21 +02:00
|
|
|
SdfWriter::sdfPathName(const Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
Instance *inst = network_->instance(pin);
|
|
|
|
|
if (network_->isTopInstance(inst))
|
|
|
|
|
return sdfPortName(pin);
|
|
|
|
|
else {
|
2023-03-26 15:34:36 +02:00
|
|
|
string inst_path = sdfPathName(inst);
|
|
|
|
|
string port_name = sdfPortName(pin);
|
|
|
|
|
string sdf_name = inst_path;
|
|
|
|
|
sdf_name += sdf_divider_;
|
|
|
|
|
sdf_name += port_name;
|
2018-09-28 17:54:21 +02:00
|
|
|
return sdf_name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Based on Network::pathName.
|
2023-03-26 15:34:36 +02:00
|
|
|
string
|
2018-09-28 17:54:21 +02:00
|
|
|
SdfWriter::sdfPathName(const Instance *instance)
|
|
|
|
|
{
|
2023-01-19 19:23:45 +01:00
|
|
|
InstanceSeq inst_path;
|
2018-09-28 17:54:21 +02:00
|
|
|
network_->path(instance, inst_path);
|
2023-01-19 19:23:45 +01:00
|
|
|
InstanceSeq::Iterator path_iter1(inst_path);
|
2023-03-26 15:34:36 +02:00
|
|
|
string path_name;
|
|
|
|
|
while (!inst_path.empty()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
const Instance *inst = inst_path.back();
|
2023-03-26 15:34:36 +02:00
|
|
|
string inst_name = sdfName(inst);
|
|
|
|
|
path_name += inst_name;
|
2018-09-28 17:54:21 +02:00
|
|
|
inst_path.pop_back();
|
2023-03-26 15:34:36 +02:00
|
|
|
if (!inst_path.empty())
|
|
|
|
|
path_name += sdf_divider_;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
return path_name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Escape for non-alpha numeric characters.
|
2023-03-26 15:34:36 +02:00
|
|
|
string
|
2018-09-28 17:54:21 +02:00
|
|
|
SdfWriter::sdfName(const Instance *inst)
|
|
|
|
|
{
|
|
|
|
|
const char *name = network_->name(inst);
|
2023-03-26 15:34:36 +02:00
|
|
|
string sdf_name;
|
2018-09-28 17:54:21 +02:00
|
|
|
const char *p = name;
|
|
|
|
|
while (*p) {
|
|
|
|
|
char ch = *p;
|
|
|
|
|
// Ignore sta escapes.
|
|
|
|
|
if (ch != network_escape_) {
|
|
|
|
|
if (!(isalnum(ch) || ch == '_'))
|
|
|
|
|
// Insert escape.
|
2023-03-26 15:34:36 +02:00
|
|
|
sdf_name += sdf_escape_;
|
|
|
|
|
sdf_name += ch;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
p++;
|
|
|
|
|
}
|
|
|
|
|
return sdf_name;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-26 15:34:36 +02:00
|
|
|
string
|
2021-05-28 18:52:32 +02:00
|
|
|
SdfWriter::sdfPortName(const Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
const char *name = network_->portName(pin);
|
2023-02-19 01:20:40 +01:00
|
|
|
size_t name_length = strlen(name);
|
2023-03-26 15:34:36 +02:00
|
|
|
string sdf_name;
|
2023-02-19 01:20:40 +01:00
|
|
|
|
|
|
|
|
constexpr char bus_brkt_left = '[';
|
|
|
|
|
constexpr char bus_brkt_right = ']';
|
|
|
|
|
size_t bus_index = name_length;
|
|
|
|
|
if (name_length >= 4
|
|
|
|
|
&& name[name_length - 1] == bus_brkt_right) {
|
|
|
|
|
const char *left = strrchr(name, bus_brkt_left);
|
|
|
|
|
if (left)
|
|
|
|
|
bus_index = left - name;
|
|
|
|
|
}
|
|
|
|
|
for (size_t i = 0; i < name_length; i++) {
|
|
|
|
|
char ch = name[i];
|
2021-05-28 18:52:32 +02:00
|
|
|
if (ch == network_escape_) {
|
|
|
|
|
// Copy escape and escaped char.
|
2023-03-26 15:34:36 +02:00
|
|
|
sdf_name += sdf_escape_;
|
|
|
|
|
sdf_name += name[++i];
|
2021-05-28 18:52:32 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2023-02-19 01:20:40 +01:00
|
|
|
if (!(isalnum(ch) || ch == '_')
|
|
|
|
|
&& !(i >= bus_index && (ch == bus_brkt_right || ch == bus_brkt_left)))
|
2021-05-28 18:52:32 +02:00
|
|
|
// Insert escape.
|
2023-03-26 15:34:36 +02:00
|
|
|
sdf_name += sdf_escape_;
|
|
|
|
|
sdf_name += ch;
|
2021-05-28 18:52:32 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return sdf_name;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
} // namespace
|