From f2c6b49d07ccce4b428d32a53e04c768fcb0ae02 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 8 Jun 2022 08:29:53 -0700 Subject: [PATCH] write_timing_model Signed-off-by: James Cherry --- CMakeLists.txt | 3 +- include/sta/Liberty.hh | 3 +- include/sta/LibertyWriter.hh | 2 - include/sta/Sta.hh | 4 + liberty/LibertyWriter.cc | 4 +- messages.txt | 6 +- search/MakeTimingModel.cc | 276 +++++++++++++++++++++++++++++++++++ search/MakeTimingModel.hh | 64 ++++++++ search/Sta.cc | 12 ++ tcl/Search.tcl | 14 ++ tcl/StaTcl.i | 10 ++ 11 files changed, 390 insertions(+), 8 deletions(-) create mode 100644 search/MakeTimingModel.cc create mode 100644 search/MakeTimingModel.hh 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) {