OpenSTA/sdf/SdfWriter.cc

836 lines
23 KiB
C++

// OpenSTA, Static Timing Analyzer
// Copyright (c) 2025, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// 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.
#include "sdf/SdfWriter.hh"
#include <cstdio>
#include <ctime>
#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"
namespace sta {
using std::string;
class SdfWriter : public StaState
{
public:
SdfWriter(StaState *sta);
~SdfWriter();
void write(const char *filename,
const Corner *corner,
char sdf_divider,
bool include_typ,
int digits,
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,
int clk_rf_index,
TimingArc *arcs[RiseFall::index_count][RiseFall::index_count]);
void writeTimingCheckHeader();
void writeTimingCheckTrailer();
void writeWidthCheck(const Pin *pin,
const RiseFall *hi_low,
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,
const RiseFall *rf);
void writeSdfTriple(float min,
float max);
void writeSdfDelay(double delay);
string sdfPortName(const Pin *pin);
string sdfPathName(const Pin *pin);
string sdfPathName(const Instance *inst);
string sdfName(const Instance *inst);
private:
char sdf_divider_;
bool include_typ_;
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,
const Corner *corner,
char sdf_divider,
bool include_typ,
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,
no_timestamp, no_version);
}
SdfWriter::SdfWriter(StaState *sta) :
StaState(sta),
sdf_escape_('\\'),
network_escape_(network_->pathEscape()),
delay_format_(nullptr)
{
}
SdfWriter::~SdfWriter()
{
stringDelete(delay_format_);
}
void
SdfWriter::write(const char *filename,
const Corner *corner,
char sdf_divider,
bool include_typ,
int digits,
bool gzip,
bool no_timestamp,
bool no_version)
{
sdf_divider_ = sdf_divider;
include_typ_ = include_typ;
if (delay_format_ == nullptr)
delay_format_ = stringPrint("%%.%df", digits);
LibertyLibrary *default_lib = network_->defaultLibertyLibrary();
timescale_ = default_lib->units()->timeUnit()->scale();
corner_ = corner;
const 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);
writeHeader(default_lib, no_timestamp, no_version);
writeInterconnects();
writeInstances();
writeTrailer();
gzclose(stream_);
stream_ = nullptr;
}
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)
gzprintf(stream_, " (VERSION \"%s\")\n", STA_VERSION);
gzprintf(stream_, " (DIVIDER %c)\n", sdf_divider_);
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();
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());
}
const char *sdf_timescale = nullptr;
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);
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();
string drvr_pin_name = sdfPathName(drvr_pin);
string load_pin_name = sdfPathName(load_pin);
gzprintf(stream_, " (INTERCONNECT %s %s ",
drvr_pin_name.c_str(),
load_pin_name.c_str());
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));
string inst_name = sdfPathName(inst);
gzprintf(stream_, " (INSTANCE %s)\n", inst_name.c_str());
}
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();
const 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_, " ");
}
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());
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();
for (TimingArc *arc : arc_set->arcs()) {
const RiseFall *rf = arc->toEdge()->asRiseFall();
ArcDelay min_delay = graph_->arcDelay(edge, arc, arc_delay_min_index_);
delays.setValue(rf, MinMax::min(), delayAsFloat(min_delay));
ArcDelay max_delay = graph_->arcDelay(edge, arc, arc_delay_max_index_);
delays.setValue(rf, MinMax::max(), delayAsFloat(max_delay));
}
if (delays.hasValue(RiseFall::rise(), MinMax::min())
&& delays.hasValue(RiseFall::fall(), MinMax::min())) {
// Rise and fall.
writeSdfTriple(delays, RiseFall::rise());
// Merge rise/fall values if they are the same.
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())))) {
gzprintf(stream_, " ");
writeSdfTriple(delays, RiseFall::fall());
}
}
else if (delays.hasValue(RiseFall::rise(), MinMax::min()))
// Rise only.
writeSdfTriple(delays, RiseFall::rise());
else if (delays.hasValue(RiseFall::fall(), MinMax::min())) {
// Fall only.
gzprintf(stream_, "() ");
writeSdfTriple(delays, RiseFall::fall());
}
}
void
SdfWriter::writeSdfTriple(RiseFallMinMax &delays,
const RiseFall *rf)
{
float min = delays.value(rf, MinMax::min());
float max = delays.value(rf, MinMax::max());
writeSdfTriple(min, max);
}
void
SdfWriter::writeSdfTriple(float min,
float max)
{
gzprintf(stream_, "(");
writeSdfDelay(min);
if (include_typ_) {
gzprintf(stream_, ":");
writeSdfDelay((min + max) / 2.0);
gzprintf(stream_, ":");
}
else
gzprintf(stream_, "::");
writeSdfDelay(max);
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();
const TimingRole *role = edge->role();
const char *sdf_check = nullptr;
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);
}
}
for (auto hi_low : RiseFall::range()) {
float min_width, max_width;
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_));
ensureTimingCheckheaders(check_header, inst, inst_header);
writeWidthCheck(pin, hi_low, min_width, max_width);
}
}
float min_period;
bool exists;
graph_delay_calc_->minPeriod(pin, corner_, 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.
TimingArc *arcs[RiseFall::index_count][RiseFall::index_count] =
{{nullptr, nullptr}, {nullptr, nullptr}};
for (TimingArc *arc : arc_set->arcs()) {
const RiseFall *clk_rf = arc->fromEdge()->asRiseFall();
const RiseFall *data_rf = arc->toEdge()->asRiseFall();;
arcs[clk_rf->index()][data_rf->index()] = arc;
}
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);
else {
// No special case; write all the checks with data and clock edge specifiers.
for (TimingArc *arc : arc_set->arcs())
writeCheck(edge, arc, sdf_check, true, true);
}
}
void
SdfWriter::writeEdgeCheck(Edge *edge,
const char *sdf_check,
int clk_rf_index,
TimingArc *arcs[RiseFall::index_count][RiseFall::index_count])
{
// 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.
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()]
&& 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_)))
// Rise/fall margins are the same, so no data edge specifier is required.
writeCheck(edge, arcs[clk_rf_index][RiseFall::riseIndex()],
sdf_check, false, true);
else {
if (arcs[clk_rf_index][RiseFall::riseIndex()])
writeCheck(edge, arcs[clk_rf_index][RiseFall::riseIndex()],
sdf_check, true, true);
if (arcs[clk_rf_index][RiseFall::fallIndex()])
writeCheck(edge, arcs[clk_rf_index][RiseFall::fallIndex()],
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);
string to_pin_name = sdfPortName(to_pin);
if (use_data_edge) {
gzprintf(stream_, "(%s %s)",
sdfEdge(arc->toEdge()),
to_pin_name.c_str());
}
else
gzprintf(stream_, "%s", to_pin_name.c_str());
if (sdf_cond_start)
gzprintf(stream_, ")");
gzprintf(stream_, " ");
if (sdf_cond_end)
gzprintf(stream_, "(COND %s ", sdf_cond_end);
string from_pin_name = sdfPortName(from_pin);
if (use_clk_edge)
gzprintf(stream_, "(%s %s)",
sdfEdge(arc->fromEdge()),
from_pin_name.c_str());
else
gzprintf(stream_, "%s", from_pin_name.c_str());
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));
gzprintf(stream_, ")\n");
}
void
SdfWriter::writeWidthCheck(const Pin *pin,
const RiseFall *hi_low,
float min_width,
float max_width)
{
string pin_name = sdfPortName(pin);
gzprintf(stream_, " (WIDTH (%s %s) ",
sdfEdge(hi_low->asTransition()),
pin_name.c_str());
writeSdfTriple(min_width, max_width);
gzprintf(stream_, ")\n");
}
void
SdfWriter::writePeriodCheck(const Pin *pin,
float min_period)
{
string pin_name = sdfPortName(pin);
gzprintf(stream_, " (PERIOD %s ", pin_name.c_str());
writeSdfTriple(min_period, min_period);
gzprintf(stream_, ")\n");
}
const char *
SdfWriter::sdfEdge(const Transition *tr)
{
if (tr == Transition::rise())
return "posedge";
else if (tr == Transition::fall())
return "negedge";
return nullptr;
}
////////////////////////////////////////////////////////////////
string
SdfWriter::sdfPathName(const Pin *pin)
{
Instance *inst = network_->instance(pin);
if (network_->isTopInstance(inst))
return sdfPortName(pin);
else {
string inst_path = sdfPathName(inst);
string port_name = sdfPortName(pin);
string sdf_name = inst_path;
sdf_name += sdf_divider_;
sdf_name += port_name;
return sdf_name;
}
}
// Based on Network::pathName.
string
SdfWriter::sdfPathName(const Instance *instance)
{
InstanceSeq inst_path;
network_->path(instance, inst_path);
InstanceSeq::Iterator path_iter1(inst_path);
string path_name;
while (!inst_path.empty()) {
const Instance *inst = inst_path.back();
string inst_name = sdfName(inst);
path_name += inst_name;
inst_path.pop_back();
if (!inst_path.empty())
path_name += sdf_divider_;
}
return path_name;
}
// Escape for non-alpha numeric characters.
string
SdfWriter::sdfName(const Instance *inst)
{
const char *name = network_->name(inst);
string sdf_name;
const char *p = name;
while (*p) {
char ch = *p;
// Ignore sta escapes.
if (ch != network_escape_) {
if (!(isalnum(ch) || ch == '_'))
// Insert escape.
sdf_name += sdf_escape_;
sdf_name += ch;
}
p++;
}
return sdf_name;
}
string
SdfWriter::sdfPortName(const Pin *pin)
{
const char *name = network_->portName(pin);
size_t name_length = strlen(name);
string sdf_name;
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];
if (ch == network_escape_) {
// Copy escape and escaped char.
sdf_name += sdf_escape_;
sdf_name += name[++i];
}
else {
if (!(isalnum(ch) || ch == '_')
&& !(i >= bus_index && (ch == bus_brkt_right || ch == bus_brkt_left)))
// Insert escape.
sdf_name += sdf_escape_;
sdf_name += ch;
}
}
return sdf_name;
}
} // namespace