diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2d855785..961a6ab9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -164,6 +164,7 @@ set(STA_SOURCE
search/Genclks.cc
search/Latches.cc
search/Levelize.cc
+ search/MakeTimingModel.cc
search/Path.cc
search/PathAnalysisPt.cc
search/PathEnd.cc
@@ -189,7 +190,7 @@ set(STA_SOURCE
search/VisitPathGroupVertices.cc
search/WorstSlack.cc
search/WritePathSpice.cc
-
+
util/Debug.cc
util/DispatchQueue.cc
util/Error.cc
diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh
index ae5b33fb..cc242634 100644
--- a/include/sta/Liberty.hh
+++ b/include/sta/Liberty.hh
@@ -27,6 +27,7 @@
namespace sta {
+class WriteTimingModel;
class LibertyCellIterator;
class LibertyCellPortIterator;
class LibertyCellPortBitIterator;
@@ -644,6 +645,7 @@ public:
LibertyLibrary *libertyLibrary() const { return liberty_cell_->libertyLibrary(); }
LibertyPort *findLibertyMember(int index) const;
LibertyPort *findLibertyBusBit(int index) const;
+ void setDirection(PortDirection *dir);
void fanoutLoad(// Return values.
float &fanout_load,
bool &exists) const;
@@ -769,7 +771,6 @@ protected:
bool is_bundle,
ConcretePortSeq *members);
virtual ~LibertyPort();
- void setDirection(PortDirection *dir);
void setMinPort(LibertyPort *min);
void addScaledPort(OperatingConditions *op_cond,
LibertyPort *scaled_port);
diff --git a/include/sta/LibertyWriter.hh b/include/sta/LibertyWriter.hh
index 9241b4a1..de558a8d 100644
--- a/include/sta/LibertyWriter.hh
+++ b/include/sta/LibertyWriter.hh
@@ -28,5 +28,3 @@ writeLiberty(LibertyLibrary *lib,
StaState *sta);
} // namespace
-
-
diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh
index 8ff4a228..bb0a5c9e 100644
--- a/include/sta/Sta.hh
+++ b/include/sta/Sta.hh
@@ -1267,6 +1267,10 @@ public:
// Return values.
PowerResult &result);
+ void writeTimingModel(const char *cell_name,
+ const char *filename,
+ const Corner *corner);
+
// Find equivalent cells in equiv_libs.
// Optionally add mappings for cells in map_libs.
void makeEquivCells(LibertyLibrarySeq *equiv_libs,
diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc
index 39aaee07..00ecc0ae 100644
--- a/liberty/LibertyWriter.cc
+++ b/liberty/LibertyWriter.cc
@@ -250,7 +250,9 @@ void
LibertyWriter::writeCell(const LibertyCell *cell)
{
fprintf(stream_, " cell (\"%s\") {\n", cell->name());
- fprintf(stream_, " area : %.3f \n", cell->area());
+ float area = cell->area();
+ if (area > 0.0)
+ fprintf(stream_, " area : %.3f \n", area);
if (cell->isMacro())
fprintf(stream_, " is_macro : true;\n");
diff --git a/messages.txt b/messages.txt
index dc62f33c..a7a15ae5 100644
--- a/messages.txt
+++ b/messages.txt
@@ -476,6 +476,6 @@
0622 PathVertex.cc:279 missing requireds.
0623 PathVertexRep.cc:153 missing arrivals.
0624 PathVertexRep.cc:150 missing arrivals
-0701 LibertyWriter.cc:354 %s/%s/%s timing model not supported.
-0702 LibertyWriter.cc:373 3 axis table models not supported.
-0703 LibertyWriter.cc:484 %s/%s/%s timing arc type %s not supported.
+0701 LibertyWriter.cc:360 %s/%s/%s timing model not supported.
+0702 LibertyWriter.cc:379 3 axis table models not supported.
+0703 LibertyWriter.cc:504 %s/%s/%s timing arc type %s not supported.
diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc
new file mode 100644
index 00000000..b9c1563e
--- /dev/null
+++ b/search/MakeTimingModel.cc
@@ -0,0 +1,276 @@
+// OpenSTA, Static Timing Analyzer
+// Copyright (c) 2022, 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 .
+
+#include "MakeTimingModel.hh"
+
+#include "Debug.hh"
+#include "Units.hh"
+#include "Transition.hh"
+#include "Liberty.hh"
+#include "liberty/LibertyBuilder.hh"
+#include "LibertyWriter.hh"
+#include "Network.hh"
+#include "PortDirection.hh"
+#include "Corner.hh"
+#include "DcalcAnalysisPt.hh"
+#include "dcalc/GraphDelayCalc1.hh"
+#include "Sdc.hh"
+#include "StaState.hh"
+#include "Sta.hh"
+
+namespace sta {
+
+void
+writeTimingModel(const char *cell_name,
+ const char *filename,
+ const Corner *corner,
+ Sta *sta)
+{
+ MakeTimingModel writer(corner, sta);
+ writer.writeTimingModel(cell_name, filename);
+}
+
+MakeTimingModel::MakeTimingModel(const Corner *corner,
+ Sta *sta) :
+ StaState(sta),
+ sta_(sta),
+ corner_(corner),
+ min_max_(MinMax::max()),
+ lib_builder_(new LibertyBuilder)
+{
+}
+
+MakeTimingModel::~MakeTimingModel()
+{
+ delete lib_builder_;
+}
+
+void
+MakeTimingModel::writeTimingModel(const char *cell_name,
+ const char *filename)
+{
+ makeTimingModel(cell_name, filename);
+ writeLibertyFile(filename);
+}
+
+void
+MakeTimingModel::writeLibertyFile(const char *filename)
+{
+ writeLiberty(library_, filename, this);
+}
+
+void
+MakeTimingModel::makeTimingModel(const char *cell_name,
+ const char *filename)
+{
+ makeLibrary(cell_name, filename);
+ makeCell(cell_name, filename);
+ makePorts();
+
+ for (Clock *clk : *sdc_->clocks())
+ sta_->setPropagatedClock(clk);
+
+#if 0
+ findInputToOutputPaths();
+ findInputSetupHolds();
+ findClkedOutputPaths();
+#endif
+ findInputSetupHolds();
+}
+
+void
+MakeTimingModel::makeLibrary(const char *cell_name,
+ const char *filename)
+{
+ library_ = network_->makeLibertyLibrary(cell_name, filename);
+ LibertyLibrary *default_lib = network_->defaultLibertyLibrary();
+ *library_->units()->timeUnit() = *default_lib->units()->timeUnit();
+ *library_->units()->capacitanceUnit() = *default_lib->units()->capacitanceUnit();
+ *library_->units()->voltageUnit() = *default_lib->units()->voltageUnit();
+ *library_->units()->resistanceUnit() = *default_lib->units()->resistanceUnit();
+ *library_->units()->pullingResistanceUnit() = *default_lib->units()->pullingResistanceUnit();
+ *library_->units()->powerUnit() = *default_lib->units()->powerUnit();
+ *library_->units()->distanceUnit() = *default_lib->units()->distanceUnit();
+
+ for (RiseFall *rf : RiseFall::range()) {
+ library_->setInputThreshold(rf, default_lib->inputThreshold(rf));
+ library_->setOutputThreshold(rf, default_lib->outputThreshold(rf));
+ library_->setSlewLowerThreshold(rf, default_lib->slewLowerThreshold(rf));
+ library_->setSlewUpperThreshold(rf, default_lib->slewUpperThreshold(rf));
+ }
+
+ library_->setDelayModelType(default_lib->delayModelType());
+ library_->setNominalProcess(default_lib->nominalProcess());
+ library_->setNominalVoltage(default_lib->nominalVoltage());
+ library_->setNominalTemperature(default_lib->nominalTemperature());
+}
+
+void
+MakeTimingModel::makeCell(const char *cell_name,
+ const char *filename)
+{
+ cell_ = lib_builder_->makeCell(library_, cell_name, filename);
+}
+
+void
+MakeTimingModel::makePorts()
+{
+ const DcalcAnalysisPt *dcalc_ap = corner_->findDcalcAnalysisPt(min_max_);
+ InstancePinIterator *pin_iter = network_->pinIterator(network_->topInstance());
+ while (pin_iter->hasNext()) {
+ Pin *pin = pin_iter->next();
+ Port *port = network_->port(pin);
+ LibertyPort *lib_port = lib_builder_->makePort(cell_, network_->name(port));
+ lib_port->setDirection(network_->direction(port));
+ float load_cap = graph_delay_calc_->loadCap(pin, dcalc_ap);
+ lib_port->setCapacitance(load_cap);
+ }
+ delete pin_iter;
+}
+
+// input -> output combinational paths
+void
+MakeTimingModel::findInputToOutputPaths()
+{
+ InstancePinIterator *input_iter = network_->pinIterator(network_->topInstance());
+ while (input_iter->hasNext()) {
+ Pin *input_pin = input_iter->next();
+ if (network_->direction(input_pin)->isInput()
+ && !sta_->isClockSrc(input_pin)) {
+ InstancePinIterator *output_iter = network_->pinIterator(network_->topInstance());
+ while (output_iter->hasNext()) {
+ Pin *output_pin = output_iter->next();
+ if (network_->direction(output_pin)->isOutput()) {
+ PinSet *from_pins = new PinSet;
+ from_pins->insert(input_pin);
+ ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
+ RiseFallBoth::riseFall());
+ PinSet *to_pins = new PinSet;
+ to_pins->insert(output_pin);
+ ExceptionTo *to = sta_->makeExceptionTo(to_pins, nullptr, nullptr,
+ RiseFallBoth::riseFall(),
+ RiseFallBoth::riseFall());
+ PathEndSeq *ends = sta_->findPathEnds(from, nullptr, to, true, corner_,
+ min_max_->asMinMaxAll(),
+ 1, 1, false,
+ -INF, INF, false, nullptr,
+ false, false, false, false, false, false);
+ if (!ends->empty()) {
+ debugPrint(debug_, "timing_model", 1, "input %s -> output %s",
+ network_->pathName(input_pin),
+ network_->pathName(output_pin));
+ PathEnd *end = (*ends)[0];
+ sta_->reportPathEnd(end);
+ }
+ }
+ }
+ }
+ }
+}
+
+// input -> register setup/hold
+void
+MakeTimingModel::findInputSetupHolds()
+{
+ InstancePinIterator *input_iter = network_->pinIterator(network_->topInstance());
+ while (input_iter->hasNext()) {
+ Pin *input_pin = input_iter->next();
+ if (network_->direction(input_pin)->isInput()
+ && !sta_->isClockSrc(input_pin)) {
+ for (Clock *clk : *sdc_->clocks()) {
+ for (RiseFall *clk_rf : RiseFall::range()) {
+ for (MinMax *min_max : MinMax::range()) {
+ MinMaxAll *min_max2 = min_max->asMinMaxAll();
+ bool setup = min_max == MinMax::max();
+ bool hold = !setup;
+ for (RiseFall *input_rf : RiseFall::range()) {
+ sdc_->setInputDelay(input_pin, RiseFallBoth::riseFall(), clk, clk_rf,
+ nullptr, false, false, min_max2, false, 0.0);
+
+ PinSet *from_pins = new PinSet;
+ from_pins->insert(input_pin);
+ ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
+ input_rf->asRiseFallBoth());
+
+ ClockSet *to_clks = new ClockSet;
+ to_clks->insert(clk);
+ ExceptionTo *to = sta_->makeExceptionTo(nullptr, to_clks, nullptr,
+ clk_rf->asRiseFallBoth(),
+ RiseFallBoth::riseFall());
+ PathEndSeq *ends = sta_->findPathEnds(from, nullptr, to, false, corner_,
+ min_max2,
+ 1, 1, false,
+ -INF, INF, false, nullptr,
+ setup, hold, setup, hold, setup, hold);
+ if (!ends->empty()) {
+ debugPrint(debug_, "timing_model", 1, "%s %s %s -> clock %s %s",
+ setup ? "setup" : "hold",
+ network_->pathName(input_pin),
+ input_rf->asString(),
+ clk->name(),
+ clk_rf->asString());
+ PathEnd *end = (*ends)[0];
+ sta_->reportPathEnd(end);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+MakeTimingModel::findClkedOutputPaths()
+{
+ InstancePinIterator *output_iter = network_->pinIterator(network_->topInstance());
+ while (output_iter->hasNext()) {
+ Pin *output_pin = output_iter->next();
+ if (network_->direction(output_pin)->isOutput()) {
+ for (Clock *clk : *sdc_->clocks()) {
+ for (RiseFall *clk_rf : RiseFall::range()) {
+ sdc_->setOutputDelay(output_pin, RiseFallBoth::riseFall(), clk, clk_rf,
+ nullptr, false, false, MinMaxAll::max(), false, 0.0);
+
+ ClockSet *from_clks = new ClockSet;
+ from_clks->insert(clk);
+ ExceptionFrom *from = sta_->makeExceptionFrom(nullptr, from_clks, nullptr,
+ clk_rf->asRiseFallBoth());
+ PinSet *to_pins = new PinSet;
+ to_pins->insert(output_pin);
+ ExceptionTo *to = sta_->makeExceptionTo(to_pins, nullptr, nullptr,
+ RiseFallBoth::riseFall(),
+ RiseFallBoth::riseFall());
+
+ PathEndSeq *ends = sta_->findPathEnds(from, nullptr, to, false, corner_,
+ MinMaxAll::max(),
+ 1, 1, false,
+ -INF, INF, false, nullptr,
+ true, false, false, false, false, false);
+ if (!ends->empty()) {
+ debugPrint(debug_, "timing_model", 1, "clock %s -> output %s",
+ clk->name(),
+ network_->pathName(output_pin));
+ PathEnd *end = (*ends)[0];
+ sta_->reportPathEnd(end);
+ }
+ }
+ }
+ }
+ }
+}
+
+} // namespace
diff --git a/search/MakeTimingModel.hh b/search/MakeTimingModel.hh
new file mode 100644
index 00000000..3d8f7ded
--- /dev/null
+++ b/search/MakeTimingModel.hh
@@ -0,0 +1,64 @@
+// OpenSTA, Static Timing Analyzer
+// Copyright (c) 2022, 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 .
+
+#pragma once
+
+#include "LibertyClass.hh"
+#include "SearchClass.hh"
+#include "StaState.hh"
+
+namespace sta {
+
+class Sta;
+class LibertyBuilder;
+
+class MakeTimingModel : public StaState
+{
+public:
+ MakeTimingModel(const Corner *corner,
+ Sta *sta);
+ ~MakeTimingModel();
+ void writeTimingModel(const char *cell_name,
+ const char *filename);
+ void makeTimingModel(const char *cell_name,
+ const char *filename);
+ void writeLibertyFile(const char *filename);
+
+private:
+ void makeLibrary(const char *cell_name,
+ const char *filename);
+ void makeCell(const char *cell_name,
+ const char *filename);
+ void makePorts();
+ void findInputToOutputPaths();
+ void findInputSetupHolds();
+ void findClkedOutputPaths();
+
+ Sta *sta_;
+ LibertyLibrary *library_;
+ LibertyCell *cell_;
+ const Corner *corner_;
+ MinMax *min_max_;
+ LibertyBuilder *lib_builder_;
+};
+
+void
+writeTimingModel(const char *cell_name,
+ const char *filename,
+ const Corner *corner,
+ Sta *sta);
+
+} // namespace
diff --git a/search/Sta.cc b/search/Sta.cc
index 5d816b59..d56036e7 100644
--- a/search/Sta.cc
+++ b/search/Sta.cc
@@ -69,6 +69,7 @@
#include "Power.hh"
#include "VisitPathEnds.hh"
#include "PathExpanded.hh"
+#include "MakeTimingModel.hh"
namespace sta {
@@ -5565,6 +5566,17 @@ Sta::equivCells(LibertyCell *cell)
}
////////////////////////////////////////////////////////////////
+
+void
+Sta::writeTimingModel(const char *cell_name,
+ const char *filename,
+ const Corner *corner)
+{
+ sta::writeTimingModel(cell_name, filename, corner, this);
+}
+
+////////////////////////////////////////////////////////////////
+
void
Sta::powerPreamble()
{
diff --git a/tcl/Search.tcl b/tcl/Search.tcl
index 532dd301..6f4443f3 100644
--- a/tcl/Search.tcl
+++ b/tcl/Search.tcl
@@ -1030,6 +1030,20 @@ proc worst_clock_skew { args } {
return [time_sta_ui [worst_clk_skew_cmd $setup_hold]]
}
+################################################################
+
+define_cmd_args "write_timing_model" {[-corner corner] cell_name filename}
+
+proc write_timing_model { args } {
+ parse_key_args "write_timing_model" args keys {corner} flags {}
+ check_argc_eq2 "write_timing_model" $args
+
+ set cell_name [lindex $args 0]
+ set filename [lindex $args 1]
+ set corner [parse_corner keys]
+ write_timing_model_cmd $cell_name [file nativename $filename] $corner
+}
+
################################################################
#
# Helper functions
diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i
index 5afc2a40..550216ef 100644
--- a/tcl/StaTcl.i
+++ b/tcl/StaTcl.i
@@ -5043,6 +5043,16 @@ write_path_spice_cmd(PathRef *path,
power_name, gnd_name, sta);
}
+void
+write_timing_model_cmd(const char *cell_name,
+ const char *filename,
+ const Corner *corner)
+{
+ Sta::sta()->writeTimingModel(cell_name, filename, corner);
+}
+
+////////////////////////////////////////////////////////////////
+
bool
liberty_supply_exists(const char *supply_name)
{