Files
OpenSTA/sdf/SdfWriter.cc
T

827 lines
25 KiB
C++
Raw Normal View History

2018-09-28 08:54:21 -07:00
// OpenSTA, Static Timing Analyzer
2026-03-10 13:21:17 -07:00
// Copyright (c) 2026, Parallax Software, Inc.
2018-09-28 08:54:21 -07: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 10:17:08 -07:00
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2018-09-28 08:54:21 -07:00
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
2022-01-04 10:17:08 -07:00
// along with this program. If not, see <https://www.gnu.org/licenses/>.
2025-01-21 18:54:33 -07: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 08:54:21 -07:00
2020-04-05 11:35:51 -07:00
#include "sdf/SdfWriter.hh"
2023-06-15 08:59:56 -07:00
#include <cstdio>
#include <ctime>
2026-03-28 19:13:35 -07:00
#include <string>
#include <string_view>
2020-04-05 11:35:51 -07:00
2026-03-15 14:35:24 -07:00
#include "Format.hh"
2020-04-05 14:53:44 -07:00
#include "Fuzzy.hh"
2026-04-15 09:38:10 -07:00
#include "Graph.hh"
#include "GraphDelayCalc.hh"
2020-04-05 14:53:44 -07:00
#include "Liberty.hh"
#include "MinMaxValues.hh"
#include "Network.hh"
2026-01-03 16:59:35 -08:00
#include "Scene.hh"
2026-04-15 09:38:10 -07:00
#include "Sdc.hh"
#include "StaConfig.hh" // STA_VERSION
#include "StaState.hh"
#include "StringUtil.hh"
#include "TimingArc.hh"
#include "TimingRole.hh"
#include "Units.hh"
#include "Zlib.hh"
2018-09-28 08:54:21 -07:00
namespace sta {
class SdfWriter : public StaState
{
public:
SdfWriter(StaState *sta);
2026-03-28 19:13:35 -07:00
void write(std::string_view filename,
2026-01-03 16:59:35 -08:00
const Scene *scene,
char sdf_divider,
bool include_typ,
2021-11-08 16:49:43 -07:00
int digits,
2026-01-03 16:59:35 -08:00
bool gzip,
bool no_timestamp,
bool no_version);
2018-09-28 08:54:21 -07:00
protected:
void writeHeader(LibertyLibrary *default_lib,
2026-01-03 16:59:35 -08:00
bool no_timestamp,
bool no_version);
2018-09-28 08:54:21 -07:00
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,
2026-01-03 16:59:35 -08:00
bool &inst_header);
2018-09-28 08:54:21 -07:00
void writeIopathHeader();
void writeIopathTrailer();
void writeTimingChecks(const Instance *inst,
2026-01-03 16:59:35 -08:00
bool &inst_header);
2018-09-28 08:54:21 -07:00
void ensureTimingCheckheaders(bool &check_header,
2026-01-03 16:59:35 -08:00
const Instance *inst,
bool &inst_header);
2018-09-28 08:54:21 -07:00
void writeCheck(Edge *edge,
2026-03-28 19:13:35 -07:00
std::string_view sdf_check);
2018-09-28 08:54:21 -07:00
void writeCheck(Edge *edge,
2026-01-03 16:59:35 -08:00
TimingArc *arc,
2026-03-28 19:13:35 -07:00
std::string_view sdf_check,
2026-01-03 16:59:35 -08:00
bool use_data_edge,
bool use_clk_edge);
2018-09-28 08:54:21 -07:00
void writeEdgeCheck(Edge *edge,
2026-03-28 19:13:35 -07:00
std::string_view sdf_check,
2026-01-03 16:59:35 -08:00
int clk_rf_index,
TimingArc *arcs[RiseFall::index_count][RiseFall::index_count]);
2018-09-28 08:54:21 -07:00
void writeTimingCheckHeader();
void writeTimingCheckTrailer();
void writeWidthCheck(const Pin *pin,
2026-01-03 16:59:35 -08:00
const RiseFall *hi_low,
float min_width,
float max_width);
2018-09-28 08:54:21 -07:00
void writePeriodCheck(const Pin *pin,
2026-01-03 16:59:35 -08:00
float min_period);
2026-03-28 19:13:35 -07:00
std::string_view sdfEdge(const Transition *tr);
2018-09-28 08:54:21 -07:00
void writeArcDelays(Edge *edge);
2021-11-08 16:49:43 -07:00
void writeSdfTriple(RiseFallMinMax &delays,
2025-03-30 15:27:53 -07:00
const RiseFall *rf);
2021-11-08 16:49:43 -07:00
void writeSdfTriple(float min,
float max);
2018-09-28 08:54:21 -07:00
void writeSdfDelay(double delay);
2026-03-02 12:13:13 -08:00
std::string sdfPortName(const Pin *pin);
std::string sdfPathName(const Pin *pin);
std::string sdfPathName(const Instance *inst);
std::string sdfName(const Instance *inst);
2018-09-28 08:54:21 -07:00
private:
char sdf_divider_;
2021-11-08 16:49:43 -07:00
bool include_typ_;
2018-09-28 08:54:21 -07:00
float timescale_;
2026-04-15 09:38:10 -07:00
char sdf_escape_{'\\'};
2018-09-28 08:54:21 -07:00
char network_escape_;
2026-03-15 14:35:24 -07:00
int digits_;
2018-09-28 08:54:21 -07:00
gzFile stream_;
2026-01-03 16:59:35 -08:00
const Scene *scene_;
2018-09-28 08:54:21 -07:00
int arc_delay_min_index_;
int arc_delay_max_index_;
};
void
2026-03-28 19:13:35 -07:00
writeSdf(std::string_view filename,
2026-01-03 16:59:35 -08:00
const Scene *scene,
char sdf_divider,
2021-11-08 16:49:43 -07:00
bool include_typ,
2026-01-03 16:59:35 -08:00
int digits,
bool gzip,
bool no_timestamp,
bool no_version,
StaState *sta)
2018-09-28 08:54:21 -07:00
{
SdfWriter writer(sta);
2026-01-03 16:59:35 -08:00
writer.write(filename, scene, sdf_divider, include_typ, digits, gzip,
no_timestamp, no_version);
2018-09-28 08:54:21 -07:00
}
SdfWriter::SdfWriter(StaState *sta) :
StaState(sta),
2026-03-15 14:35:24 -07:00
network_escape_(network_->pathEscape())
2018-09-28 08:54:21 -07:00
{
}
void
2026-03-28 19:13:35 -07:00
SdfWriter::write(std::string_view filename,
2026-01-03 16:59:35 -08:00
const Scene *scene,
char sdf_divider,
2021-11-08 16:49:43 -07:00
bool include_typ,
2026-01-03 16:59:35 -08:00
int digits,
bool gzip,
bool no_timestamp,
bool no_version)
2018-09-28 08:54:21 -07:00
{
sdf_divider_ = sdf_divider;
2021-11-08 16:49:43 -07:00
include_typ_ = include_typ;
2026-03-15 14:35:24 -07:00
digits_ = digits;
2018-09-28 08:54:21 -07:00
LibertyLibrary *default_lib = network_->defaultLibertyLibrary();
timescale_ = default_lib->units()->timeUnit()->scale();
2026-01-03 16:59:35 -08:00
scene_ = scene;
arc_delay_min_index_ = scene->dcalcAnalysisPtIndex(MinMax::min());
arc_delay_max_index_ = scene->dcalcAnalysisPtIndex(MinMax::max());
2018-09-28 08:54:21 -07:00
2026-03-28 19:13:35 -07:00
stream_ = gzopen(std::string(filename).c_str(), gzip ? "wb" : "wT");
2021-12-08 10:23:44 -07:00
if (stream_ == nullptr)
throw FileNotWritable(filename);
2018-09-28 08:54:21 -07:00
writeHeader(default_lib, no_timestamp, no_version);
writeInterconnects();
writeInstances();
writeTrailer();
gzclose(stream_);
2019-03-12 17:25:53 -07:00
stream_ = nullptr;
2018-09-28 08:54:21 -07:00
}
void
SdfWriter::writeHeader(LibertyLibrary *default_lib,
2026-01-03 16:59:35 -08:00
bool no_timestamp,
bool no_version)
2018-09-28 08:54:21 -07:00
{
2026-03-15 14:35:24 -07:00
sta::print(stream_, "(DELAYFILE\n");
sta::print(stream_, " (SDFVERSION \"3.0\")\n");
sta::print(stream_, " (DESIGN \"{}\")\n",
network_->cellName(network_->topInstance()));
2018-09-28 08:54:21 -07:00
if (!no_timestamp) {
time_t now;
time(&now);
char *time_str = ctime(&now);
// Remove trailing \n.
time_str[strlen(time_str) - 1] = '\0';
2026-03-15 14:35:24 -07:00
sta::print(stream_, " (DATE \"{}\")\n", time_str);
2018-09-28 08:54:21 -07:00
}
2026-03-15 14:35:24 -07:00
sta::print(stream_, " (VENDOR \"Parallax\")\n");
sta::print(stream_, " (PROGRAM \"STA\")\n");
2018-09-28 08:54:21 -07:00
if (!no_version)
2026-03-15 14:35:24 -07:00
sta::print(stream_, " (VERSION \"{}\")\n", STA_VERSION);
sta::print(stream_, " (DIVIDER {:c})\n", sdf_divider_);
2018-09-28 08:54:21 -07:00
2023-09-18 16:02:30 -07:00
LibertyLibrary *lib_min = default_lib;
2026-01-03 16:59:35 -08:00
const LibertySeq &libs_min = scene_->libertyLibraries(MinMax::min());
2023-09-18 16:02:30 -07:00
if (!libs_min.empty())
lib_min = libs_min[0];
LibertyLibrary *lib_max = default_lib;
2026-01-03 16:59:35 -08:00
const LibertySeq &libs_max = scene_->libertyLibraries(MinMax::max());
2023-09-18 16:02:30 -07:00
if (!libs_max.empty())
lib_max = libs_max[0];
OperatingConditions *cond_min = lib_min->defaultOperatingConditions();
OperatingConditions *cond_max = lib_max->defaultOperatingConditions();
2018-09-28 08:54:21 -07:00
if (cond_min && cond_max) {
2026-03-15 14:35:24 -07:00
sta::print(stream_, " (VOLTAGE {:.3f}::{:.3f})\n",
cond_min->voltage(),
cond_max->voltage());
sta::print(stream_, " (PROCESS \"{:.3f}::{:.3f}\")\n",
cond_min->process(),
cond_max->process());
sta::print(stream_, " (TEMPERATURE {:.3f}::{:.3f})\n",
cond_min->temperature(),
cond_max->temperature());
2018-09-28 08:54:21 -07:00
}
2019-03-12 17:25:53 -07:00
const char *sdf_timescale = nullptr;
2018-09-28 08:54:21 -07: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)
2026-03-15 14:35:24 -07:00
sta::print(stream_, " (TIMESCALE {})\n", sdf_timescale);
2018-09-28 08:54:21 -07:00
}
void
SdfWriter::writeTrailer()
{
2026-03-15 14:35:24 -07:00
sta::print(stream_, ")\n");
2018-09-28 08:54:21 -07:00
}
void
SdfWriter::writeInterconnects()
{
2026-03-15 14:35:24 -07:00
sta::print(stream_, " (CELL\n");
sta::print(stream_, " (CELLTYPE \"{}\")\n",
network_->cellName(network_->topInstance()));
sta::print(stream_, " (INSTANCE)\n");
sta::print(stream_, " (DELAY\n");
sta::print(stream_, " (ABSOLUTE\n");
2018-09-28 08:54:21 -07:00
writeInstInterconnects(network_->topInstance());
LeafInstanceIterator *inst_iter = network_->leafInstanceIterator();
while (inst_iter->hasNext()) {
Instance *inst = inst_iter->next();
writeInstInterconnects(inst);
}
delete inst_iter;
2026-03-15 14:35:24 -07:00
sta::print(stream_, " )\n");
sta::print(stream_, " )\n");
sta::print(stream_, " )\n");
2018-09-28 08:54:21 -07:00
}
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 12:51:05 -07: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();
2026-03-02 12:13:13 -08:00
std::string drvr_pin_name = sdfPathName(drvr_pin);
std::string load_pin_name = sdfPathName(load_pin);
2026-03-15 14:35:24 -07:00
sta::print(stream_, " (INTERCONNECT {} {} ",
drvr_pin_name,
load_pin_name);
2022-01-15 12:51:05 -07:00
writeArcDelays(edge);
2026-03-15 14:35:24 -07:00
sta::print(stream_, ")\n");
2022-01-15 12:51:05 -07:00
}
2018-09-28 08:54:21 -07: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)
{
2026-03-15 14:35:24 -07:00
sta::print(stream_, " (CELL\n");
sta::print(stream_, " (CELLTYPE \"{}\")\n", network_->cellName(inst));
2026-03-02 12:13:13 -08:00
std::string inst_name = sdfPathName(inst);
2026-03-15 14:35:24 -07:00
sta::print(stream_, " (INSTANCE {})\n", inst_name);
2018-09-28 08:54:21 -07:00
}
void
SdfWriter::writeInstTrailer()
{
2026-03-15 14:35:24 -07:00
sta::print(stream_, " )\n");
2018-09-28 08:54:21 -07:00
}
void
SdfWriter::writeIopaths(const Instance *inst,
2026-01-03 16:59:35 -08:00
bool &inst_header)
2018-09-28 08:54:21 -07:00
{
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()) {
2026-01-03 16:59:35 -08:00
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;
}
2026-02-04 18:33:04 -07:00
const std::string &sdf_cond = edge->timingArcSet()->sdfCond();
if (!sdf_cond.empty()) {
2026-03-15 14:35:24 -07:00
sta::print(stream_, " (COND {}\n", sdf_cond);
sta::print(stream_, " ");
2026-01-03 16:59:35 -08:00
}
2026-03-02 12:13:13 -08:00
std::string from_pin_name = sdfPortName(from_pin);
std::string to_pin_name = sdfPortName(to_pin);
2026-03-15 14:35:24 -07:00
sta::print(stream_, " (IOPATH {} {} ",
from_pin_name,
to_pin_name);
2026-01-03 16:59:35 -08:00
writeArcDelays(edge);
2026-02-04 18:33:04 -07:00
if (!sdf_cond.empty())
2026-03-15 14:35:24 -07:00
sta::print(stream_, ")");
sta::print(stream_, ")\n");
2026-01-03 16:59:35 -08:00
}
2018-09-28 08:54:21 -07:00
}
}
}
delete pin_iter;
if (iopath_header)
writeIopathTrailer();
}
void
SdfWriter::writeIopathHeader()
{
2026-03-15 14:35:24 -07:00
sta::print(stream_, " (DELAY\n");
sta::print(stream_, " (ABSOLUTE\n");
2018-09-28 08:54:21 -07:00
}
void
SdfWriter::writeIopathTrailer()
{
2026-03-15 14:35:24 -07:00
sta::print(stream_, " )\n");
sta::print(stream_, " )\n");
2018-09-28 08:54:21 -07:00
}
void
SdfWriter::writeArcDelays(Edge *edge)
{
RiseFallMinMax delays;
TimingArcSet *arc_set = edge->timingArcSet();
2022-06-12 11:47:26 -07:00
for (TimingArc *arc : arc_set->arcs()) {
2025-03-30 15:27:53 -07:00
const RiseFall *rf = arc->toEdge()->asRiseFall();
2026-03-13 14:06:35 -07:00
const ArcDelay &min_delay = graph_->arcDelay(edge, arc, arc_delay_min_index_);
delays.setValue(rf, MinMax::min(), delayAsFloat(min_delay, MinMax::min(), this));
2018-09-28 08:54:21 -07:00
2026-03-13 14:06:35 -07:00
const ArcDelay &max_delay = graph_->arcDelay(edge, arc, arc_delay_max_index_);
delays.setValue(rf, MinMax::max(), delayAsFloat(max_delay, MinMax::max(), this));
2018-09-28 08:54:21 -07:00
}
2019-11-11 15:30:19 -07:00
if (delays.hasValue(RiseFall::rise(), MinMax::min())
&& delays.hasValue(RiseFall::fall(), MinMax::min())) {
2018-09-28 08:54:21 -07:00
// Rise and fall.
2021-11-08 16:49:43 -07:00
writeSdfTriple(delays, RiseFall::rise());
2018-09-28 08:54:21 -07:00
// Merge rise/fall values if they are the same.
2019-11-11 15:30:19 -07:00
if (!(fuzzyEqual(delays.value(RiseFall::rise(), MinMax::min()),
2026-01-03 16:59:35 -08:00
delays.value(RiseFall::fall(), MinMax::min()))
&& fuzzyEqual(delays.value(RiseFall::rise(), MinMax::max()),
delays.value(RiseFall::fall(),MinMax::max())))) {
2026-03-15 14:35:24 -07:00
sta::print(stream_, " ");
2021-11-08 16:49:43 -07:00
writeSdfTriple(delays, RiseFall::fall());
2018-09-28 08:54:21 -07:00
}
}
2019-11-11 15:30:19 -07:00
else if (delays.hasValue(RiseFall::rise(), MinMax::min()))
2018-09-28 08:54:21 -07:00
// Rise only.
2021-11-08 16:49:43 -07:00
writeSdfTriple(delays, RiseFall::rise());
2019-11-11 15:30:19 -07:00
else if (delays.hasValue(RiseFall::fall(), MinMax::min())) {
2018-09-28 08:54:21 -07:00
// Fall only.
2026-03-15 14:35:24 -07:00
sta::print(stream_, "() ");
2021-11-08 16:49:43 -07:00
writeSdfTriple(delays, RiseFall::fall());
2018-09-28 08:54:21 -07:00
}
}
void
2021-11-08 16:49:43 -07:00
SdfWriter::writeSdfTriple(RiseFallMinMax &delays,
2025-03-30 15:27:53 -07:00
const RiseFall *rf)
2018-09-28 08:54:21 -07:00
{
2021-11-08 16:49:43 -07:00
float min = delays.value(rf, MinMax::min());
float max = delays.value(rf, MinMax::max());
writeSdfTriple(min, max);
2018-09-28 08:54:21 -07:00
}
void
2021-11-08 16:49:43 -07:00
SdfWriter::writeSdfTriple(float min,
float max)
2018-09-28 08:54:21 -07:00
{
2026-03-15 14:35:24 -07:00
sta::print(stream_, "(");
2021-11-08 16:49:43 -07:00
writeSdfDelay(min);
if (include_typ_) {
2026-03-15 14:35:24 -07:00
sta::print(stream_, ":");
2021-11-08 16:49:43 -07:00
writeSdfDelay((min + max) / 2.0);
2026-03-15 14:35:24 -07:00
sta::print(stream_, ":");
2021-11-08 16:49:43 -07:00
}
else
2026-03-15 14:35:24 -07:00
sta::print(stream_, "::");
2021-11-08 16:49:43 -07:00
writeSdfDelay(max);
2026-03-15 14:35:24 -07:00
sta::print(stream_, ")");
2018-09-28 08:54:21 -07:00
}
void
SdfWriter::writeSdfDelay(double delay)
{
2026-03-15 14:35:24 -07:00
std::string str = sta::formatRuntime("{:.{}f}", delay / timescale_, digits_);
sta::print(stream_, "{}", str);
2018-09-28 08:54:21 -07:00
}
void
SdfWriter::writeTimingChecks(const Instance *inst,
2026-01-03 16:59:35 -08:00
bool &inst_header)
2018-09-28 08:54:21 -07:00
{
bool check_header = false;
InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) {
Pin *pin = pin_iter->next();
Vertex *vertex = graph_->pinLoadVertex(pin);
if (vertex) {
VertexOutEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) {
2026-01-03 16:59:35 -08:00
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);
}
2018-09-28 08:54:21 -07:00
}
for (auto hi_low : RiseFall::range()) {
2026-01-03 16:59:35 -08:00
float min_width, max_width;
Edge *edge;
TimingArc *arc;
graph_->minPulseWidthArc(vertex, hi_low, edge, arc);
if (edge) {
2026-03-13 14:06:35 -07:00
min_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_min_index_),
MinMax::min(), this);
max_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_max_index_),
MinMax::max(), this);
2026-01-03 16:59:35 -08:00
ensureTimingCheckheaders(check_header, inst, inst_header);
writeWidthCheck(pin, hi_low, min_width, max_width);
}
}
float min_period;
bool exists;
2026-01-03 16:59:35 -08:00
graph_delay_calc_->minPeriod(pin, scene_, min_period, exists);
if (exists) {
2026-01-03 16:59:35 -08:00
ensureTimingCheckheaders(check_header, inst, inst_header);
writePeriodCheck(pin, min_period);
2018-09-28 08:54:21 -07:00
}
}
}
delete pin_iter;
if (check_header)
writeTimingCheckTrailer();
}
void
SdfWriter::ensureTimingCheckheaders(bool &check_header,
2026-01-03 16:59:35 -08:00
const Instance *inst,
bool &inst_header)
2018-09-28 08:54:21 -07:00
{
if (!inst_header) {
writeInstHeader(inst);
inst_header = true;
}
if (!check_header) {
writeTimingCheckHeader();
check_header = true;
}
}
void
SdfWriter::writeTimingCheckHeader()
{
2026-03-15 14:35:24 -07:00
sta::print(stream_, " (TIMINGCHECK\n");
2018-09-28 08:54:21 -07:00
}
void
SdfWriter::writeTimingCheckTrailer()
{
2026-03-15 14:35:24 -07:00
sta::print(stream_, " )\n");
2018-09-28 08:54:21 -07:00
}
void
SdfWriter::writeCheck(Edge *edge,
2026-03-28 19:13:35 -07:00
std::string_view sdf_check)
2018-09-28 08:54:21 -07:00
{
TimingArcSet *arc_set = edge->timingArcSet();
// Examine the arcs to see if the check requires clk or data edge specifiers.
2019-11-11 15:30:19 -07:00
TimingArc *arcs[RiseFall::index_count][RiseFall::index_count] =
2019-03-12 17:25:53 -07:00
{{nullptr, nullptr}, {nullptr, nullptr}};
2022-06-12 11:47:26 -07:00
for (TimingArc *arc : arc_set->arcs()) {
2025-03-30 15:27:53 -07:00
const RiseFall *clk_rf = arc->fromEdge()->asRiseFall();
const RiseFall *data_rf = arc->toEdge()->asRiseFall();;
2019-11-11 15:30:19 -07:00
arcs[clk_rf->index()][data_rf->index()] = arc;
2018-09-28 08:54:21 -07:00
}
2019-11-11 15:30:19 -07: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
2026-01-03 16:59:35 -08:00
&& arcs[RiseFall::riseIndex()][RiseFall::fallIndex()] == nullptr)
2019-11-11 15:30:19 -07:00
writeEdgeCheck(edge, sdf_check, RiseFall::fallIndex(), arcs);
2018-09-28 08:54:21 -07:00
else {
// No special case; write all the checks with data and clock edge specifiers.
2022-06-12 11:47:26 -07:00
for (TimingArc *arc : arc_set->arcs())
2018-09-28 08:54:21 -07:00
writeCheck(edge, arc, sdf_check, true, true);
}
}
void
SdfWriter::writeEdgeCheck(Edge *edge,
2026-03-28 19:13:35 -07:00
std::string_view sdf_check,
2026-01-03 16:59:35 -08:00
int clk_rf_index,
TimingArc *arcs[RiseFall::index_count][RiseFall::index_count])
2018-09-28 08:54:21 -07: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 15:30:19 -07:00
if (arcs[clk_rf_index][RiseFall::riseIndex()]
&& arcs[clk_rf_index][RiseFall::fallIndex()]
&& arcs[clk_rf_index][RiseFall::riseIndex()]
2026-03-13 14:06:35 -07:00
&& arcs[clk_rf_index][RiseFall::fallIndex()]) {
float rise_min=delayAsFloat(graph_->arcDelay(edge,
arcs[clk_rf_index][RiseFall::riseIndex()],
arc_delay_min_index_),
MinMax::min(), this);
float fall_min=delayAsFloat(graph_->arcDelay(edge,
arcs[clk_rf_index][RiseFall::fallIndex()],
arc_delay_min_index_),
MinMax::min(), this);
float rise_max=delayAsFloat(graph_->arcDelay(edge,
arcs[clk_rf_index][RiseFall::riseIndex()],
arc_delay_max_index_),
MinMax::max(), this);
float fall_max=delayAsFloat(graph_->arcDelay(edge,
arcs[clk_rf_index][RiseFall::fallIndex()],
arc_delay_max_index_),
MinMax::max(), this);
if (fuzzyEqual(rise_min, fall_min)
&& fuzzyEqual(rise_max, fall_max)) {
// 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);
return;
}
2018-09-28 08:54:21 -07:00
}
2026-03-13 14:06:35 -07:00
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);
2018-09-28 08:54:21 -07:00
}
void
SdfWriter::writeCheck(Edge *edge,
2026-01-03 16:59:35 -08:00
TimingArc *arc,
2026-03-28 19:13:35 -07:00
std::string_view sdf_check,
2026-01-03 16:59:35 -08:00
bool use_data_edge,
bool use_clk_edge)
2018-09-28 08:54:21 -07:00
{
TimingArcSet *arc_set = edge->timingArcSet();
Pin *from_pin = edge->from(graph_)->pin();
Pin *to_pin = edge->to(graph_)->pin();
2026-02-04 18:33:04 -07:00
const std::string &sdf_cond_start = arc_set->sdfCondStart();
const std::string &sdf_cond_end = arc_set->sdfCondEnd();
2018-09-28 08:54:21 -07:00
2026-03-15 14:35:24 -07:00
sta::print(stream_, " ({} ", sdf_check);
2018-09-28 08:54:21 -07:00
2026-02-04 18:33:04 -07:00
if (!sdf_cond_start.empty())
2026-03-15 14:35:24 -07:00
sta::print(stream_, "(COND {} ", sdf_cond_start);
2018-09-28 08:54:21 -07:00
2026-03-02 12:13:13 -08:00
std::string to_pin_name = sdfPortName(to_pin);
2023-03-26 06:34:36 -07:00
if (use_data_edge) {
2026-03-15 14:35:24 -07:00
sta::print(stream_, "({} {})",
sdfEdge(arc->toEdge()),
to_pin_name);
2023-03-26 06:34:36 -07:00
}
2018-09-28 08:54:21 -07:00
else
2026-03-15 14:35:24 -07:00
sta::print(stream_, "{}", to_pin_name);
2018-09-28 08:54:21 -07:00
2026-02-04 18:33:04 -07:00
if (!sdf_cond_start.empty())
2026-03-15 14:35:24 -07:00
sta::print(stream_, ")");
2018-09-28 08:54:21 -07:00
2026-03-15 14:35:24 -07:00
sta::print(stream_, " ");
2018-09-28 08:54:21 -07:00
2026-02-04 18:33:04 -07:00
if (!sdf_cond_end.empty())
2026-03-15 14:35:24 -07:00
sta::print(stream_, "(COND {} ", sdf_cond_end);
2018-09-28 08:54:21 -07:00
2026-03-02 12:13:13 -08:00
std::string from_pin_name = sdfPortName(from_pin);
2018-09-28 08:54:21 -07:00
if (use_clk_edge)
2026-03-15 14:35:24 -07:00
sta::print(stream_, "({} {})",
sdfEdge(arc->fromEdge()),
from_pin_name);
2018-09-28 08:54:21 -07:00
else
2026-03-15 14:35:24 -07:00
sta::print(stream_, "{}", from_pin_name);
2018-09-28 08:54:21 -07:00
2026-02-04 18:33:04 -07:00
if (!sdf_cond_end.empty())
2026-03-15 14:35:24 -07:00
sta::print(stream_, ")");
2018-09-28 08:54:21 -07:00
2026-03-15 14:35:24 -07:00
sta::print(stream_, " ");
2018-09-28 08:54:21 -07:00
2026-03-13 14:06:35 -07:00
float min_delay = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_min_index_),
MinMax::min(), this);
float max_delay = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_max_index_),
MinMax::max(), this);
writeSdfTriple(min_delay, max_delay);
2018-09-28 08:54:21 -07:00
2026-03-15 14:35:24 -07:00
sta::print(stream_, ")\n");
2018-09-28 08:54:21 -07:00
}
void
SdfWriter::writeWidthCheck(const Pin *pin,
2026-01-03 16:59:35 -08:00
const RiseFall *hi_low,
float min_width,
float max_width)
2018-09-28 08:54:21 -07:00
{
2026-03-02 12:13:13 -08:00
std::string pin_name = sdfPortName(pin);
2026-03-15 14:35:24 -07:00
sta::print(stream_, " (WIDTH ({} {}) ",
sdfEdge(hi_low->asTransition()),
pin_name);
2021-11-08 16:49:43 -07:00
writeSdfTriple(min_width, max_width);
2026-03-15 14:35:24 -07:00
sta::print(stream_, ")\n");
2018-09-28 08:54:21 -07:00
}
void
SdfWriter::writePeriodCheck(const Pin *pin,
2026-01-03 16:59:35 -08:00
float min_period)
2018-09-28 08:54:21 -07:00
{
2026-03-02 12:13:13 -08:00
std::string pin_name = sdfPortName(pin);
2026-03-15 14:35:24 -07:00
sta::print(stream_, " (PERIOD {} ", pin_name);
2021-11-08 16:49:43 -07:00
writeSdfTriple(min_period, min_period);
2026-03-15 14:35:24 -07:00
sta::print(stream_, ")\n");
2018-09-28 08:54:21 -07:00
}
2026-03-28 19:13:35 -07:00
std::string_view
2018-09-28 08:54:21 -07:00
SdfWriter::sdfEdge(const Transition *tr)
{
if (tr == Transition::rise())
return "posedge";
else if (tr == Transition::fall())
return "negedge";
2026-03-28 19:13:35 -07:00
return {};
2018-09-28 08:54:21 -07:00
}
////////////////////////////////////////////////////////////////
2026-03-02 12:13:13 -08:00
std::string
2018-09-28 08:54:21 -07:00
SdfWriter::sdfPathName(const Pin *pin)
{
Instance *inst = network_->instance(pin);
if (network_->isTopInstance(inst))
return sdfPortName(pin);
else {
2026-03-02 12:13:13 -08:00
std::string inst_path = sdfPathName(inst);
std::string port_name = sdfPortName(pin);
std::string sdf_name = inst_path;
2023-03-26 06:34:36 -07:00
sdf_name += sdf_divider_;
sdf_name += port_name;
2018-09-28 08:54:21 -07:00
return sdf_name;
}
}
// Based on Network::pathName.
2026-03-02 12:13:13 -08:00
std::string
2018-09-28 08:54:21 -07:00
SdfWriter::sdfPathName(const Instance *instance)
{
2023-01-19 11:23:45 -07:00
InstanceSeq inst_path;
2018-09-28 08:54:21 -07:00
network_->path(instance, inst_path);
2026-03-02 12:13:13 -08:00
std::string path_name;
2023-03-26 06:34:36 -07:00
while (!inst_path.empty()) {
2018-09-28 08:54:21 -07:00
const Instance *inst = inst_path.back();
2026-03-28 19:13:35 -07:00
path_name += sdfName(inst);
2018-09-28 08:54:21 -07:00
inst_path.pop_back();
2023-03-26 06:34:36 -07:00
if (!inst_path.empty())
path_name += sdf_divider_;
2018-09-28 08:54:21 -07:00
}
return path_name;
}
// Escape for non-alpha numeric characters.
2026-03-02 12:13:13 -08:00
std::string
2018-09-28 08:54:21 -07:00
SdfWriter::sdfName(const Instance *inst)
{
2026-03-28 19:13:35 -07:00
const std::string &name = network_->name(inst);
2026-03-02 12:13:13 -08:00
std::string sdf_name;
2026-03-28 19:13:35 -07:00
for (char ch : name) {
2018-09-28 08:54:21 -07:00
// Ignore sta escapes.
if (ch != network_escape_) {
if (!(isalnum(ch) || ch == '_'))
2026-01-03 16:59:35 -08:00
// Insert escape.
sdf_name += sdf_escape_;
2023-03-26 06:34:36 -07:00
sdf_name += ch;
2018-09-28 08:54:21 -07:00
}
}
return sdf_name;
}
2026-03-02 12:13:13 -08:00
std::string
SdfWriter::sdfPortName(const Pin *pin)
{
2026-03-28 19:13:35 -07:00
const std::string &name = network_->portName(pin);
size_t name_length = name.size();
2026-03-02 12:13:13 -08:00
std::string sdf_name;
2023-02-18 17:20:40 -07: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) {
2026-03-28 19:13:35 -07:00
size_t left = name.rfind(bus_brkt_left);
if (left != std::string_view::npos)
bus_index = left;
2023-02-18 17:20:40 -07:00
}
for (size_t i = 0; i < name_length; i++) {
char ch = name[i];
if (ch == network_escape_) {
// Copy escape and escaped char.
2023-03-26 06:34:36 -07:00
sdf_name += sdf_escape_;
sdf_name += name[++i];
}
else {
2023-02-18 17:20:40 -07:00
if (!(isalnum(ch) || ch == '_')
&& !(i >= bus_index && (ch == bus_brkt_right || ch == bus_brkt_left)))
// Insert escape.
2023-03-26 06:34:36 -07:00
sdf_name += sdf_escape_;
sdf_name += ch;
}
}
return sdf_name;
}
2026-04-13 14:58:16 -07:00
} // namespace sta