rel 2.6.0

Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
James Cherry 2024-06-27 13:57:58 -07:00
parent 948266945c
commit d0287e5a47
106 changed files with 2151 additions and 1561 deletions

View File

@ -24,13 +24,13 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.14)
cmake_policy(SET CMP0086 NEW)
endif()
project(STA VERSION 2.5.0
project(STA VERSION 2.6.0
LANGUAGES CXX
)
option(USE_CUDD "Use CUDD BDD package")
option(USE_CUDD "Use CUDD BDD package" ON)
option(CUDD_DIR "CUDD BDD package directory")
option(USE_TCL_READLINE "Use TCL readliine package")
option(USE_TCL_READLINE "Use TCL readliine package" ON)
option(USE_SANITIZE "Compile with santize address enabled")
# Turn on to debug compiler args.
@ -195,8 +195,10 @@ set(STA_SOURCE
search/VisitPathEnds.cc
search/VisitPathGroupVertices.cc
search/WorstSlack.cc
search/WritePathSpice.cc
search/WriteSpice.cc
spice/WritePathSpice.cc
spice/WriteSpice.cc
spice/Xyce.cc
power/Power.cc
power/ReadVcdActivities.cc
@ -244,11 +246,11 @@ set(STA_TCL_FILES
tcl/Sta.tcl
tcl/Splash.tcl
tcl/Variables.tcl
tcl/WritePathSpice.tcl
dcalc/DelayCalc.tcl
parasitics/Parasitics.tcl
power/Power.tcl
sdf/Sdf.tcl
spice/WriteSpice.tcl
verilog/Verilog.tcl
)
@ -258,8 +260,7 @@ set(STA_TCL_FILES
#
################################################################
# Earlier versions of flex use 'register' declarations that are deprecated
# in c++11 and illegal in c++17.
# Earlier versions of flex use 'register' declarations that are illegal in c++17.
#find_package(FLEX 2.6.4)
find_package(FLEX)
find_package(BISON)
@ -431,6 +432,7 @@ set_property(SOURCE ${STA_SWIG_FILE}
-I${STA_HOME}/dcalc
-I${STA_HOME}/parasitics
-I${STA_HOME}/power
-I${STA_HOME}/spice
-I${STA_HOME}/verilog
)
@ -439,6 +441,7 @@ set(SWIG_FILES
${STA_HOME}/parasitics/Parasitics.i
${STA_HOME}/power/Power.i
${STA_HOME}/sdf/Sdf.i
${STA_HOME}/spice/WriteSpice.i
${STA_HOME}/tcl/Exception.i
${STA_HOME}/tcl/StaTcl.i
${STA_HOME}/tcl/StaTclTypes.i
@ -559,7 +562,7 @@ target_compile_options(OpenSTA
# Disable compiler specific extensions like gnu++11.
set_target_properties(OpenSTA PROPERTIES CXX_EXTENSIONS OFF)
target_compile_features(OpenSTA PUBLIC cxx_std_11)
target_compile_features(OpenSTA PUBLIC cxx_std_17)
###########################################################
# Executable

View File

@ -85,15 +85,15 @@ The build dependency versions are show below. Other versions may
work, but these are the versions used for development.
```
from Ubuntu Macos
22.04.2 14.4.1
cmake 3.10.2 3.24.2 3.29.2
clang 9.1.0 15.0.0
gcc 3.3.2 11.4.0
tcl 8.4 8.6 8.6.6
swig 1.3.28 4.1.0 4.2.1
bison 1.35 3.8.2 3.8.2
flex 2.5.4 2.6.4 2.6.4
Ubuntu Macos
22.04.2 14.5
cmake 3.24.2 3.29.2
clang 15.0.0
gcc 11.4.0
tcl 8.6 8.6.6
swig 4.1.0 4.1.1
bison 3.8.2 3.8.2
flex 2.6.4 2.6.4
```
Note that flex versions before 2.6.4 contain 'register' declarations that
@ -101,38 +101,47 @@ are illegal in c++17.
External library dependencies:
```
from Ubuntu Macos
eigen 3.4 .0 3.4.0 required
tclreadline 2.3.8 optional
libz 1.1.4 1.2.5 1.2.8 optional
cudd 2.4.1 3.0.0 optional
Ubuntu Macos
eigen 3.4.0 3.4.0 required
tclreadline 2.3.8 2.3.8 optional
libz 1.2.5 1.2.8 optional
cudd 3.0.0 3.0.0 optional
```
The [TCL readline library](https://tclreadline.sourceforge.net/tclreadline.html)
links the GNU readline library to the TCL interpreter for command line editing
On OSX, Homebrew does not support tclreadline, but the macports system does
(see https://www.macports.org). To enable TCL readline support use the following
Cmake option:
Cmake option: See (https://tclreadline.sourceforge.net/) for TCL readline
documentation. To change the overly verbose default prompt, add something this
to your ~/.sta init file:
```
cmake .. -DUSE_TCL_READLINE=ON
if { ![catch {package require tclreadline}] } {
proc tclreadline::prompt1 {} {
return "> "
}
}
```
The Zlib library is an optional. If CMake finds libz, OpenSTA can
read Verilog, SDF, SPF, and SPEF files compressed with gzip.
read Liberty, Verilog, SDF, SPF, and SPEF files compressed with gzip.
CUDD is a binary decision diageram (BDD) package that is used to
improve conditional timing arc handling. OpenSTA does not require it
to be installed. It is available
to be installed, but it improves constant propagation, power activity propagation
and spice netlist generation if it is installed.
CUDD is available
[here](https://www.davidkebo.com/source/cudd_versions/cudd-3.0.0.tar.gz)
or [here](https://sourceforge.net/projects/cudd-mirror/).
Note that the file hierarchy of the CUDD installation changed with version 3.0.
Some changes to CMakeLists.txt are required to support older versions.
Use the CUDD_DIR option to set the install directory of the CUDD
library if it is not in one of the normal system install directories.
Use the USE_CUDD option to look for the cudd library.
Use the CUDD_DIR option to set the install directory if it is not in
one of the normal install directories.
```
cmake -DCUDD_DIR=$HOME/stax/cudd-3.0.0 .."
```
When building CUDD you may use the `--prefix ` option to `configure` to
install in a location other than the default (`/usr/local/lib`).

View File

@ -19,6 +19,7 @@
#include <stdio.h>
#include <cstdlib> // exit
#include <filesystem>
#include <tcl.h>
#if TCL_READLINE
#include <tclreadline.h>
@ -39,7 +40,6 @@ using sta::evalTclInit;
using sta::sourceTclFile;
using sta::parseThreadsArg;
using sta::tcl_inits;
using sta::is_regular_file;
// Swig uses C linkage for init functions.
extern "C" {
@ -133,15 +133,15 @@ staTclAppInit(int argc,
string init_path = home;
init_path += "/";
init_path += init_filename;
if (is_regular_file(init_path.c_str()))
if (std::filesystem::is_regular_file(init_path.c_str()))
sourceTclFile(init_path.c_str(), true, true, interp);
}
}
bool exit_after_cmd_file = findCmdLineFlag(argc, argv, "-exit");
if (argc > 2 ||
(argc > 1 && argv[1][0] == '-')) {
if (argc > 2
|| (argc > 1 && argv[1][0] == '-')) {
showUsage(argv[0], init_filename);
exit(1);
}

View File

@ -23,5 +23,6 @@
%include "NetworkEdit.i"
%include "Sdf.i"
%include "DelayCalc.i"
%include "WriteSpice.i"
%include "Parasitics.i"
%include "Power.i"

View File

@ -51,10 +51,11 @@ findCmdLineFlag(int &argc,
for (int i = 1; i < argc; i++) {
char *arg = argv[i];
if (stringEq(arg, flag)) {
// remove flag from argv.
// Remove flag from argv.
for (int j = i + 1; j < argc; j++, i++)
argv[i] = argv[j];
argc--;
argv[argc] = nullptr;
return true;
}
}
@ -70,10 +71,11 @@ findCmdLineKey(int &argc,
char *arg = argv[i];
if (stringEq(arg, key) && i + 1 < argc) {
char *value = argv[i + 1];
// remove key and value from argv.
// Remove key and value from argv.
for (int j = i + 2; j < argc; j++, i++)
argv[i] = argv[j];
argc -= 2;
argv[argc] = nullptr;
return value;
}
}
@ -138,12 +140,4 @@ unencode(const char *inits[])
return unencoded;
}
// Hack until c++17 filesystem is better supported.
bool
is_regular_file(const char *filename)
{
struct stat sb;
return stat(filename, &sb) == 0 && S_ISREG(sb.st_mode);
}
} // namespace

View File

@ -14,30 +14,64 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <memory>
#include "ArcDcalcWaveforms.hh"
#include "Report.hh"
#include "Liberty.hh"
#include "Network.hh"
#include "Graph.hh"
#include "ArcDelayCalc.hh"
#include "DcalcAnalysisPt.hh"
#include "GraphDelayCalc.hh"
namespace sta {
Table1
ArcDcalcWaveforms::inputWaveform(const Pin *,
const RiseFall *,
const Corner *,
const MinMax *)
{
return Table1();
}
using std::make_shared;
Table1
ArcDcalcWaveforms::drvrRampWaveform(const Pin *,
const RiseFall *,
const Pin *,
const RiseFall *,
const Pin *,
const Corner *,
const MinMax *)
Waveform
ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg,
const DcalcAnalysisPt *dcalc_ap,
const StaState *sta)
{
return Table1();
const Network *network = sta->network();
Graph *graph = sta->graph();
Report *report = sta->report();
const Pin *in_pin = dcalc_arg.inPin();
LibertyPort *port = network->libertyPort(in_pin);
if (port) {
const RiseFall *in_rf = dcalc_arg.inEdge();
DriverWaveform *driver_waveform = port->driverWaveform(in_rf);
if (driver_waveform) {
const Vertex *in_vertex = graph->pinLoadVertex(in_pin);
GraphDelayCalc *graph_dcalc = sta->graphDelayCalc();
Slew in_slew = graph_dcalc->edgeFromSlew(in_vertex, in_rf,
dcalc_arg.arc()->role(), dcalc_ap);
LibertyLibrary *library = port->libertyLibrary();
float vdd;
bool vdd_exists;
library->supplyVoltage("VDD", vdd, vdd_exists);
if (!vdd_exists)
report->error(1751, "VDD not defined in library %s", library->name());
Waveform in_waveform = driver_waveform->waveform(in_slew);
// Delay time axis.
FloatSeq *time_values = new FloatSeq;
for (float time : *in_waveform.axis1()->values())
time_values->push_back(time + dcalc_arg.inputDelay());
TableAxisPtr time_axis = make_shared<TableAxis>(TableAxisVariable::time, time_values);
// Scale the waveform from 0:vdd.
FloatSeq *scaled_values = new FloatSeq;
for (float value : *in_waveform.values()) {
float scaled_value = (in_rf == RiseFall::rise())
? value * vdd
: (1.0 - value) * vdd;
scaled_values->push_back(scaled_value);
}
return Waveform(scaled_values, time_axis);
}
}
return Waveform();
}
} // namespace

View File

@ -22,37 +22,25 @@
namespace sta {
class StaState;
class Corner;
class DcalcAnalysisPt;
class ArcDcalcArg;
// Abstract class for the graph delay calculator traversal to interface
// Abstract class for delay calculation waveforms for ploting.
class ArcDcalcWaveforms
{
public:
virtual Table1 inputWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Corner *corner,
const MinMax *min_max);
virtual Table1 drvrWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Corner *corner,
const MinMax *min_max) = 0;
virtual Table1 loadWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max) = 0;
virtual Table1 drvrRampWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max);
// Record waveform for drvr/load pin.
virtual void watchPin(const Pin *pin) = 0;
virtual void clearWatchPins() = 0;
virtual PinSeq watchPins() const = 0;
virtual Waveform watchWaveform(const Pin *pin) = 0;
protected:
Waveform inputWaveform(ArcDcalcArg &dcalc_arg,
const DcalcAnalysisPt *dcalc_ap,
const StaState *sta);
};
} // namespace

View File

@ -16,9 +16,11 @@
#include "ArcDelayCalc.hh"
#include "Units.hh"
#include "Liberty.hh"
#include "TimingArc.hh"
#include "Network.hh"
#include "Graph.hh"
namespace sta {
@ -48,12 +50,67 @@ ArcDelayCalc::gateDelay(const TimingArc *arc,
////////////////////////////////////////////////////////////////
ArcDcalcArg
makeArcDcalcArg(const char *inst_name,
const char *in_port_name,
const char *in_rf_name,
const char *drvr_port_name,
const char *drvr_rf_name,
const char *input_delay_str,
const StaState *sta)
{
Report *report = sta->report();
const Network *network = sta->sdcNetwork();
const Instance *inst = network->findInstance(inst_name);
if (inst) {
const Pin *in_pin = network->findPin(inst, in_port_name);
if (in_pin) {
const RiseFall *in_rf = RiseFall::find(in_rf_name);
if (in_rf) {
const Pin *drvr_pin = network->findPin(inst, drvr_port_name);
if (drvr_pin) {
const RiseFall *drvr_rf = RiseFall::find(drvr_rf_name);
if (drvr_rf) {
float input_delay = strtof(input_delay_str, nullptr);
input_delay = sta->units()->timeUnit()->userToSta(input_delay);
const Graph *graph = sta->graph();
Edge *edge;
const TimingArc *arc;
graph->gateEdgeArc(in_pin, in_rf, drvr_pin, drvr_rf, edge, arc);
if (edge)
return ArcDcalcArg(in_pin, drvr_pin, edge, arc, input_delay);
else {
const Network *network = sta->network();
const Instance *inst = network->instance(in_pin);
report->warn(2100, "no timing arc for %s input/driver pins.",
network->pathName(inst));
}
}
else
report->warn(2101, "%s not a valid rise/fall.", drvr_rf_name);
}
else
report->warn(2102, "Pin %s/%s not found.", inst_name, drvr_port_name);
}
else
report->warn(2103, "%s not a valid rise/fall.", in_rf_name);
}
else
report->warn(2104, "Pin %s/%s not found.", inst_name, in_port_name);
}
else
report->warn(2105, "Instance %s not found.", inst_name);
return ArcDcalcArg();
}
ArcDcalcArg::ArcDcalcArg() :
in_pin_(nullptr),
drvr_pin_(nullptr),
edge_(nullptr),
arc_(nullptr),
in_slew_(0.0),
load_cap_(0.0),
parasitic_(nullptr),
input_delay_(0.0)
{
@ -64,12 +121,14 @@ ArcDcalcArg::ArcDcalcArg(const Pin *in_pin,
Edge *edge,
const TimingArc *arc,
const Slew in_slew,
float load_cap,
const Parasitic *parasitic) :
in_pin_(in_pin),
drvr_pin_(drvr_pin),
edge_(edge),
arc_(arc),
in_slew_(in_slew),
load_cap_(load_cap),
parasitic_(parasitic),
input_delay_(0.0)
{
@ -85,6 +144,7 @@ ArcDcalcArg::ArcDcalcArg(const Pin *in_pin,
edge_(edge),
arc_(arc),
in_slew_(0.0),
load_cap_(0.0),
parasitic_(nullptr),
input_delay_(input_delay)
{
@ -96,6 +156,7 @@ ArcDcalcArg::ArcDcalcArg(const ArcDcalcArg &arg) :
edge_(arg.edge_),
arc_(arg.arc_),
in_slew_(arg.in_slew_),
load_cap_(arg.load_cap_),
parasitic_(arg.parasitic_),
input_delay_(arg.input_delay_)
{
@ -107,6 +168,12 @@ ArcDcalcArg::inEdge() const
return arc_->fromEdge()->asRiseFall();
}
Vertex *
ArcDcalcArg::drvrVertex(const Graph *graph) const
{
return edge_->to(graph);
}
LibertyCell *
ArcDcalcArg::drvrCell() const
{
@ -150,6 +217,18 @@ ArcDcalcArg::setParasitic(const Parasitic *parasitic)
parasitic_ = parasitic;
}
void
ArcDcalcArg::setLoadCap(float load_cap)
{
load_cap_ = load_cap;
}
void
ArcDcalcArg::setInputDelay(float input_delay)
{
input_delay_ = input_delay;
}
////////////////////////////////////////////////////////////////
ArcDcalcResult::ArcDcalcResult() :

View File

@ -115,6 +115,7 @@ public:
ArnoldiDelayCalc(StaState *sta);
virtual ~ArnoldiDelayCalc();
ArcDelayCalc *copy() override;
const char *name() const override { return "arnoldi"; }
Parasitic *findParasitic(const Pin *drvr_pin,
const RiseFall *rf,
const DcalcAnalysisPt *dcalc_ap) override;
@ -382,7 +383,7 @@ ArnoldiDelayCalc::gateDelay(const Pin *drvr_pin,
ConcreteParasitic *cparasitic =
reinterpret_cast<ConcreteParasitic*>(const_cast<Parasitic*>(parasitic));
rcmodel_ = dynamic_cast<rcmodel*>(cparasitic);
GateTableModel *table_model = gateTableModel(arc, dcalc_ap);
GateTableModel *table_model = arc->gateTableModel(dcalc_ap);
if (table_model && rcmodel_) {
const Pvt *pvt = pinPvt(drvr_pin, dcalc_ap);
return gateDelaySlew(drvr_cell, arc, table_model, in_slew, load_pin_index_map, pvt);

View File

@ -52,6 +52,7 @@ CcsCeffDelayCalc::CcsCeffDelayCalc(StaState *sta) :
// Includes the Vh:Vdd region.
region_count_(0),
vl_fail_(false),
watch_pin_values_(network_),
capacitance_unit_(units_->capacitanceUnit()),
table_dcalc_(makeDmpCeffElmoreDelayCalc(sta))
{
@ -82,7 +83,7 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
parasitic_ = parasitic;
output_waveforms_ = nullptr;
GateTableModel *table_model = gateTableModel(arc, dcalc_ap);
GateTableModel *table_model = arc->gateTableModel(dcalc_ap);
if (table_model && parasitic) {
OutputWaveforms *output_waveforms = table_model->outputWaveforms();
parasitics_->piModel(parasitic, c2_, rpi_, c1_);
@ -95,25 +96,25 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
bool vdd_exists;
LibertyCell *drvr_cell = arc->to()->libertyCell();
const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary();
const RiseFall *rf = arc->toEdge()->asRiseFall();
drvr_rf_ = arc->toEdge()->asRiseFall();
drvr_library->supplyVoltage("VDD", vdd_, vdd_exists);
if (!vdd_exists)
report_->error(1700, "VDD not defined in library %s", drvr_library->name());
vth_ = drvr_library->outputThreshold(rf) * vdd_;
vl_ = drvr_library->slewLowerThreshold(rf) * vdd_;
vh_ = drvr_library->slewUpperThreshold(rf) * vdd_;
vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_;
vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_;
vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_;
drvr_cell->ensureVoltageWaveforms();
drvr_cell->ensureVoltageWaveforms(dcalc_ap);
in_slew_ = delayAsFloat(in_slew);
output_waveforms_ = output_waveforms;
ref_time_ = output_waveforms_->referenceTime(in_slew_);
debugPrint(debug_, "ccs_dcalc", 1, "%s %s",
drvr_cell->name(),
rf->asString());
drvr_rf_->asString());
ArcDelay gate_delay;
Slew drvr_slew;
gateDelaySlew(drvr_library, rf, gate_delay, drvr_slew);
return makeResult(drvr_library, rf, gate_delay, drvr_slew, load_pin_index_map);
gateDelaySlew(drvr_library, drvr_rf_, gate_delay, drvr_slew);
return makeResult(drvr_library,drvr_rf_,gate_delay,drvr_slew,load_pin_index_map);
}
}
return table_dcalc_->gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic,
@ -312,9 +313,7 @@ CcsCeffDelayCalc::makeResult(const LibertyLibrary *drvr_library,
dcalc_result.setGateDelay(gate_delay);
dcalc_result.setDrvrSlew(drvr_slew);
for (auto load_pin_index : load_pin_index_map) {
const Pin *load_pin = load_pin_index.first;
size_t load_idx = load_pin_index.second;
for (const auto [load_pin, load_idx] : load_pin_index_map) {
ArcDelay wire_delay;
Slew load_slew;
loadDelaySlew(load_pin, drvr_library, rf, drvr_slew, wire_delay, load_slew);
@ -466,69 +465,75 @@ CcsCeffDelayCalc::findVlTime(double v,
// Waveform accessors for swig/tcl.
Table1
CcsCeffDelayCalc::drvrWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Corner *corner,
const MinMax *min_max)
void
CcsCeffDelayCalc::watchPin(const Pin *pin)
{
bool dcalc_success = makeWaveformPreamble(in_pin, in_rf, drvr_pin,
drvr_rf, corner, min_max);
if (dcalc_success)
return drvrWaveform(in_slew_, drvr_rf);
watch_pin_values_[pin] = FloatSeq();
}
void
CcsCeffDelayCalc::clearWatchPins()
{
watch_pin_values_.clear();
}
PinSeq
CcsCeffDelayCalc::watchPins() const
{
PinSeq pins;
for (const auto& [pin, values] : watch_pin_values_)
pins.push_back(pin);
return pins;
}
Waveform
CcsCeffDelayCalc::watchWaveform(const Pin *pin)
{
if (pin == drvr_pin_)
return drvrWaveform();
else
return loadWaveform(pin);
}
Waveform
CcsCeffDelayCalc::drvrWaveform()
{
if (output_waveforms_) {
// Stitch together the ccs waveforms for each region.
FloatSeq *drvr_times = new FloatSeq;
FloatSeq *drvr_volts = new FloatSeq;
for (size_t i = 0; i < region_count_; i++) {
double t1 = region_begin_times_[i];
double t2 = region_end_times_[i];
size_t time_steps = 10;
double time_step = (t2 - t1) / time_steps;
double time_offset = region_time_offsets_[i];
for (size_t s = 0; s <= time_steps; s++) {
double t = t1 + s * time_step;
drvr_times->push_back(t - time_offset);
double v = output_waveforms_->timeVoltage(in_slew_, region_ceff_[i], t);
if (drvr_rf_ == RiseFall::fall())
v = vdd_ - v;
drvr_volts->push_back(v);
}
}
TableAxisPtr drvr_time_axis = make_shared<TableAxis>(TableAxisVariable::time,
drvr_times);
Table1 drvr_table(drvr_volts, drvr_time_axis);
return drvr_table;
}
else
return Table1();
}
Table1
CcsCeffDelayCalc::drvrWaveform(const Slew &in_slew,
const RiseFall *drvr_rf)
Waveform
CcsCeffDelayCalc::loadWaveform(const Pin *load_pin)
{
// Stitch together the ccs waveforms for each region.
FloatSeq *drvr_times = new FloatSeq;
FloatSeq *drvr_volts = new FloatSeq;
for (size_t i = 0; i < region_count_; i++) {
double t1 = region_begin_times_[i];
double t2 = region_end_times_[i];
size_t time_steps = 10;
double time_step = (t2 - t1) / time_steps;
double time_offset = region_time_offsets_[i];
for (size_t s = 0; s <= time_steps; s++) {
double t = t1 + s * time_step;
drvr_times->push_back(t - time_offset);
double v = output_waveforms_->timeVoltage(delayAsFloat(in_slew),
region_ceff_[i], t);
if (drvr_rf == RiseFall::fall())
v = vdd_ - v;
drvr_volts->push_back(v);
}
}
TableAxisPtr drvr_time_axis = make_shared<TableAxis>(TableAxisVariable::time,
drvr_times);
Table1 drvr_table(drvr_volts, drvr_time_axis);
return drvr_table;
}
// For debugging
Table1
CcsCeffDelayCalc::loadWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max)
{
bool elmore_exists = false;
float elmore = 0.0;
if (parasitic_) {
if (output_waveforms_) {
bool elmore_exists = false;
float elmore = 0.0;
parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists);
bool dcalc_success = makeWaveformPreamble(in_pin, in_rf, drvr_pin,
drvr_rf, corner, min_max);
if (dcalc_success
&& elmore_exists) {
if (elmore_exists) {
FloatSeq *load_times = new FloatSeq;
FloatSeq *load_volts = new FloatSeq;
double t_vh = findVlTime(vh_, elmore);
@ -540,7 +545,7 @@ CcsCeffDelayCalc::loadWaveform(const Pin *in_pin,
double ignore;
vl(t, elmore, v, ignore);
double v1 = (drvr_rf == RiseFall::rise()) ? v : vdd_ - v;
double v1 = (drvr_rf_ == RiseFall::rise()) ? v : vdd_ - v;
load_volts->push_back(v1);
}
TableAxisPtr load_time_axis = make_shared<TableAxis>(TableAxisVariable::time,
@ -552,7 +557,7 @@ CcsCeffDelayCalc::loadWaveform(const Pin *in_pin,
return Table1();
}
Table1
Waveform
CcsCeffDelayCalc::drvrRampWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,

View File

@ -23,15 +23,19 @@ namespace sta {
using std::vector;
typedef map<const Pin*, FloatSeq, PinIdLess> WatchPinValuesMap;
ArcDelayCalc *
makeCcsCeffDelayCalc(StaState *sta);
class CcsCeffDelayCalc : public LumpedCapDelayCalc, public ArcDcalcWaveforms
class CcsCeffDelayCalc : public LumpedCapDelayCalc,
public ArcDcalcWaveforms
{
public:
CcsCeffDelayCalc(StaState *sta);
virtual ~CcsCeffDelayCalc();
ArcDelayCalc *copy() override;
const char *name() const override { return "ccs_ceff"; }
ArcDcalcResult gateDelay(const Pin *drvr_pin,
const TimingArc *arc,
@ -49,26 +53,11 @@ public:
const DcalcAnalysisPt *dcalc_ap,
int digits) override;
Table1 drvrWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Corner *corner,
const MinMax *min_max) override;
Table1 loadWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max) override;
Table1 drvrRampWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max) override;
// Record waveform for drvr/load pin.
void watchPin(const Pin *pin) override;
void clearWatchPins() override;
PinSeq watchPins() const override;
Waveform watchWaveform(const Pin *pin) override;
protected:
typedef vector<double> Region;
@ -99,14 +88,23 @@ protected:
// Return values.
ArcDelay &delay,
Slew &slew);
double findVlTime(double v,
double elmore);
bool makeWaveformPreamble(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Corner *corner,
const MinMax *min_max);
double findVlTime(double v,
double elmore);
Waveform drvrWaveform();
Waveform loadWaveform(const Pin *load_pin);
Waveform drvrRampWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max);
void vl(double t,
double elmore,
// Return values.
@ -114,11 +112,10 @@ protected:
double &dvl_dt);
double vl(double t,
double elmore);
Table1 drvrWaveform(const Slew &in_slew,
const RiseFall *drvr_rf);
void fail(const char *reason);
const Pin *drvr_pin_;
const RiseFall *drvr_rf_;
double in_slew_;
double load_cap_;
const Parasitic *parasitic_;
@ -148,6 +145,8 @@ protected:
Region region_ramp_times_;
Region region_ramp_slopes_;
bool vl_fail_;
// Waveform recording.
WatchPinValuesMap watch_pin_values_;
const Unit *capacitance_unit_;
// Delay calculator to use when ccs waveforms are missing from liberty.

View File

@ -23,7 +23,6 @@
#include "TimingArc.hh"
#include "Liberty.hh"
#include "Sdc.hh"
#include "Parasitics.hh"
#include "DcalcAnalysisPt.hh"
#include "Network.hh"
#include "Corner.hh"
@ -31,14 +30,14 @@
#include "GraphDelayCalc.hh"
#include "DmpDelayCalc.hh"
// Lawrence Pillage - “Electronic Circuit & System Simulation Methods” 1998
// McGraw-Hill, Inc. New York, NY.
namespace sta {
using std::abs;
using std::make_shared;
// Lawrence Pillage - “Electronic Circuit & System Simulation Methods” 1998
// McGraw-Hill, Inc. New York, NY.
ArcDelayCalc *
makeCcsSimDelayCalc(StaState *sta)
{
@ -51,9 +50,8 @@ CcsSimDelayCalc::CcsSimDelayCalc(StaState *sta) :
load_pin_index_map_(network_),
dcalc_failed_(false),
pin_node_map_(network_),
make_waveforms_(false),
waveform_drvr_pin_(nullptr),
waveform_load_pin_(nullptr),
node_index_map_(ParasiticNodeLess(parasitics_, network_)),
watch_pin_values_(network_),
table_dcalc_(makeDmpCeffElmoreDelayCalc(sta))
{
}
@ -114,9 +112,7 @@ CcsSimDelayCalc::inputPortDelay(const Pin *drvr_pin,
pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap);
}
for (auto load_pin_index : load_pin_index_map) {
const Pin *load_pin = load_pin_index.first;
size_t load_idx = load_pin_index.second;
for (const auto [load_pin, load_idx] : load_pin_index_map) {
ArcDelay wire_delay = 0.0;
Slew load_slew = in_slew;
bool elmore_exists = false;
@ -143,22 +139,19 @@ CcsSimDelayCalc::gateDelay(const Pin *drvr_pin,
const DcalcAnalysisPt *dcalc_ap)
{
ArcDcalcArgSeq dcalc_args;
dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, parasitic);
ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_cap,
load_pin_index_map, dcalc_ap);
dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, load_cap, parasitic);
ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_pin_index_map, dcalc_ap);
return dcalc_results[0];
}
ArcDcalcResultSeq
CcsSimDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap)
{
dcalc_args_ = &dcalc_args;
load_pin_index_map_ = load_pin_index_map;
drvr_count_ = dcalc_args.size();
load_cap_ = load_cap;
dcalc_ap_ = dcalc_ap;
drvr_rf_ = dcalc_args[0].arc()->toEdge()->asRiseFall();
dcalc_failed_ = false;
@ -171,14 +164,14 @@ CcsSimDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) {
ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx];
GateTableModel *table_model = gateTableModel(dcalc_arg.arc(), dcalc_ap);
GateTableModel *table_model = dcalc_arg.arc()->gateTableModel(dcalc_ap);
if (table_model && dcalc_arg.parasitic()) {
OutputWaveforms *output_waveforms = table_model->outputWaveforms();
float in_slew = delayAsFloat(dcalc_arg.inSlew());
float in_slew = dcalc_arg.inSlewFlt();
if (output_waveforms
// Bounds check because extrapolating waveforms does not work for shit.
&& output_waveforms->slewAxis()->inBounds(in_slew)
&& output_waveforms->capAxis()->inBounds(load_cap)) {
&& output_waveforms->capAxis()->inBounds(dcalc_arg.loadCap())) {
output_waveforms_[drvr_idx] = output_waveforms;
ref_time_[drvr_idx] = output_waveforms->referenceTime(in_slew);
debugPrint(debug_, "ccs_dcalc", 1, "%s %s",
@ -191,7 +184,7 @@ CcsSimDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
drvr_library->supplyVoltage("VDD", vdd_, vdd_exists);
if (!vdd_exists)
report_->error(1720, "VDD not defined in library %s", drvr_library->name());
drvr_cell->ensureVoltageWaveforms();
drvr_cell->ensureVoltageWaveforms(dcalc_ap);
if (drvr_idx == 0) {
vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_;
vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_;
@ -220,8 +213,7 @@ CcsSimDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
dcalc_arg.setParasitic(pi_elmore);
}
}
dcalc_results = table_dcalc_->gateDelays(dcalc_args, load_cap,
load_pin_index_map, dcalc_ap);
dcalc_results = table_dcalc_->gateDelays(dcalc_args, load_pin_index_map, dcalc_ap);
}
else {
simulate(dcalc_args);
@ -245,9 +237,7 @@ CcsSimDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
delayAsString(drvr_slew, this));
dcalc_result.setLoadCount(load_pin_index_map.size());
for (auto load_pin_index : load_pin_index_map) {
const Pin *load_pin = load_pin_index.first;
size_t load_idx = load_pin_index.second;
for (const auto [load_pin, load_idx] : load_pin_index_map) {
size_t load_node = pin_node_map_[load_pin];
ThresholdTimes &wire_times = threshold_times_[load_node];
ThresholdTimes &drvr_times = threshold_times_[drvr_node];
@ -291,10 +281,10 @@ CcsSimDelayCalc::simulate(ArcDcalcArgSeq &dcalc_args)
for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) {
ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx];
// Find initial ceff.
ceff_[drvr_idx] = load_cap_;
ceff_[drvr_idx] = dcalc_arg.loadCap();
// voltageTime is always for a rising waveform so 0.0v is initial voltage.
drvr_current_[drvr_idx] =
output_waveforms_[drvr_idx]->voltageCurrent(delayAsFloat(dcalc_arg.inSlew()),
output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlewFlt(),
ceff_[drvr_idx], 0.0);
}
// Initial time depends on ceff which impact delay, so use a sim step
@ -310,8 +300,7 @@ CcsSimDelayCalc::simulate(ArcDcalcArgSeq &dcalc_args)
// Limit in case load voltage waveforms don't get to final value.
double time_end = time_begin + maxTime();
if (make_waveforms_)
recordWaveformStep(time_begin);
recordWaveformStep(time_begin);
for (double time = time_begin; time <= time_end; time += time_step_) {
stampConductances();
@ -328,12 +317,10 @@ CcsSimDelayCalc::simulate(ArcDcalcArgSeq &dcalc_args)
updateCeffIdrvr();
measureThresholds(time);
if (make_waveforms_)
recordWaveformStep(time);
recordWaveformStep(time);
bool loads_finished = true;
for (auto load_node1 : pin_node_map_) {
size_t load_node = load_node1.second;
for (const auto [load, load_node] : pin_node_map_) {
if ((drvr_rf_ == RiseFall::rise()
&& voltages_[load_node] < vh_ + (vdd_ - vh_) * .5)
|| (drvr_rf_ == RiseFall::fall()
@ -356,14 +343,14 @@ double
CcsSimDelayCalc::timeStep()
{
// Needs to use LTE for time step dynamic control.
return drive_resistance_ * load_cap_ * .02;
return drive_resistance_ * (*dcalc_args_)[0].loadCap() * .02;
}
double
CcsSimDelayCalc::maxTime()
{
return (*dcalc_args_)[0].inSlewFlt()
+ (drive_resistance_ + resistance_sum_) * load_cap_ * 2;
+ (drive_resistance_ + resistance_sum_) * (*dcalc_args_)[0].loadCap() * 2;
}
void
@ -383,9 +370,6 @@ CcsSimDelayCalc::initSim()
// Reset waveform recording.
times_.clear();
drvr_voltages_.clear();
load_voltages_.clear();
measure_thresholds_ = {vl_, vth_, vh_};
}
@ -406,7 +390,7 @@ CcsSimDelayCalc::findNodeCount()
const Pin *pin = parasitics_->pin(node);
if (pin) {
pin_node_map_[pin] = node_idx;
debugPrint(debug_, "ccs_dcalc", 1, "pin %s node %lu",
debugPrint(debug_, "ccs_dcalc", 1, "pin %s node %zu",
network_->pathName(pin),
node_idx);
}
@ -660,10 +644,8 @@ CcsSimDelayCalc::updateCeffIdrvr()
void
CcsSimDelayCalc::measureThresholds(double time)
{
for (auto pin_node1 : pin_node_map_) {
size_t pin_node = pin_node1.second;
measureThresholds(time, pin_node);
}
for (const auto [pin, node] : pin_node_map_)
measureThresholds(time, node);
}
void
@ -677,7 +659,7 @@ CcsSimDelayCalc::measureThresholds(double time,
if ((v_prev < th && th <= v)
|| (v_prev > th && th >= v)) {
double t_cross = time - time_step_ + (th - v_prev) * time_step_ / (v - v_prev);
debugPrint(debug_, "ccs_measure", 1, "node %lu cross %.2f %s",
debugPrint(debug_, "ccs_measure", 1, "node %zu cross %.2f %s",
n,
th,
delayAsString(t_cross, this));
@ -689,12 +671,13 @@ CcsSimDelayCalc::measureThresholds(double time,
void
CcsSimDelayCalc::recordWaveformStep(double time)
{
times_.push_back(time);
size_t drvr_node = pin_node_map_[waveform_drvr_pin_];
drvr_voltages_.push_back(voltages_[drvr_node]);
if (waveform_load_pin_) {
size_t load_node = pin_node_map_[waveform_load_pin_];
load_voltages_.push_back(voltages_[load_node]);
if (!watch_pin_values_.empty()) {
times_.push_back(time);
for (auto& [pin, waveform] : watch_pin_values_) {
size_t node = pin_node_map_[pin];
double pin_v = voltages_[node];
waveform.push_back(pin_v);
}
}
}
@ -710,7 +693,7 @@ CcsSimDelayCalc::reportGateDelay(const Pin *drvr_pin,
const DcalcAnalysisPt *dcalc_ap,
int digits)
{
GateTimingModel *model = gateModel(arc, dcalc_ap);
GateTableModel *model = arc->gateTableModel(dcalc_ap);
if (model) {
float in_slew1 = delayAsFloat(in_slew);
return model->reportGateDelay(pinPvt(drvr_pin, dcalc_ap), in_slew1, load_cap,
@ -721,92 +704,39 @@ CcsSimDelayCalc::reportGateDelay(const Pin *drvr_pin,
////////////////////////////////////////////////////////////////
// Waveform accessors for swig/tcl.
Table1
CcsSimDelayCalc::drvrWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Corner *corner,
const MinMax *min_max)
void
CcsSimDelayCalc::watchPin(const Pin *pin)
{
makeWaveforms(in_pin, in_rf, drvr_pin, drvr_rf, nullptr, corner, min_max);
TableAxisPtr time_axis = make_shared<TableAxis>(TableAxisVariable::time,
new FloatSeq(times_));
Table1 waveform(new FloatSeq(drvr_voltages_), time_axis);
return waveform;
}
Table1
CcsSimDelayCalc::loadWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max)
{
makeWaveforms(in_pin, in_rf, drvr_pin, drvr_rf, load_pin, corner, min_max);
TableAxisPtr time_axis = make_shared<TableAxis>(TableAxisVariable::time,
new FloatSeq(times_));
Table1 waveform(new FloatSeq(load_voltages_), time_axis);
return waveform;
}
Table1
CcsSimDelayCalc::inputWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Corner *corner,
const MinMax *min_max)
{
LibertyPort *port = network_->libertyPort(in_pin);
if (port) {
DriverWaveform *driver_waveform = port->driverWaveform(in_rf);
const Vertex *in_vertex = graph_->pinLoadVertex(in_pin);
DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
float in_slew = delayAsFloat(graph_->slew(in_vertex, in_rf, dcalc_ap->index()));
LibertyLibrary *library = port->libertyLibrary();
float vdd;
bool vdd_exists;
library->supplyVoltage("VDD", vdd, vdd_exists);
if (!vdd_exists)
report_->error(1721, "VDD not defined in library %s", library->name());
Table1 in_waveform = driver_waveform->waveform(in_slew);
// Scale the waveform from 0:vdd.
FloatSeq *scaled_values = new FloatSeq;
for (float value : *in_waveform.values())
scaled_values->push_back(value * vdd);
return Table1(scaled_values, in_waveform.axis1ptr());
}
return Table1();
watch_pin_values_[pin] = FloatSeq();
}
void
CcsSimDelayCalc::makeWaveforms(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max)
CcsSimDelayCalc::clearWatchPins()
{
Edge *edge;
const TimingArc *arc;
graph_->gateEdgeArc(in_pin, in_rf, drvr_pin, drvr_rf, edge, arc);
if (arc) {
DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
const Parasitic *parasitic = findParasitic(drvr_pin, drvr_rf, dcalc_ap);
if (parasitic) {
make_waveforms_ = true;
waveform_drvr_pin_ = drvr_pin;
waveform_load_pin_ = load_pin;
Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin);
graph_delay_calc_->findDriverArcDelays(drvr_vertex, edge, arc, dcalc_ap, this);
make_waveforms_ = false;
waveform_drvr_pin_ = nullptr;
waveform_load_pin_ = nullptr;
}
watch_pin_values_.clear();
}
PinSeq
CcsSimDelayCalc::watchPins() const
{
PinSeq pins;
for (const auto& [pin, values] : watch_pin_values_)
pins.push_back(pin);
return pins;
}
Waveform
CcsSimDelayCalc::watchWaveform(const Pin *pin)
{
for (ArcDcalcArg &dcalc_arg : *dcalc_args_) {
if (dcalc_arg.inPin() == pin)
return inputWaveform(dcalc_arg, dcalc_ap_, this);
}
FloatSeq &voltages = watch_pin_values_[pin];
TableAxisPtr time_axis = make_shared<TableAxis>(TableAxisVariable::time,
new FloatSeq(times_));
Table1 waveform(new FloatSeq(voltages), time_axis);
return waveform;
}
////////////////////////////////////////////////////////////////

View File

@ -20,6 +20,7 @@
#include <Eigen/SparseCore>
#include <Eigen/SparseLU>
#include "Parasitics.hh"
#include "LumpedCapDelayCalc.hh"
#include "ArcDcalcWaveforms.hh"
@ -39,19 +40,22 @@ using Eigen::Index;
using Eigen::SparseLU;
typedef Map<const Pin*, size_t, PinIdLess> PinNodeMap;
typedef Map<const ParasiticNode*, size_t> NodeIndexMap;
typedef map<const ParasiticNode*, size_t, ParasiticNodeLess> NodeIndexMap;
typedef Map<const Pin*, size_t> PortIndexMap;
typedef SparseMatrix<double> MatrixSd;
typedef map<const Pin*, FloatSeq, PinIdLess> WatchPinValuesMap;
ArcDelayCalc *
makeCcsSimDelayCalc(StaState *sta);
class CcsSimDelayCalc : public DelayCalcBase, public ArcDcalcWaveforms
class CcsSimDelayCalc : public DelayCalcBase,
public ArcDcalcWaveforms
{
public:
CcsSimDelayCalc(StaState *sta);
~CcsSimDelayCalc();
ArcDelayCalc *copy() override;
const char *name() const override { return "ccs_sim"; }
Parasitic *findParasitic(const Pin *drvr_pin,
const RiseFall *rf,
const DcalcAnalysisPt *dcalc_ap) override;
@ -73,7 +77,6 @@ public:
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) override;
ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) override;
string reportGateDelay(const Pin *drvr_pin,
@ -85,23 +88,11 @@ public:
const DcalcAnalysisPt *dcalc_ap,
int digits) override;
Table1 inputWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Corner *corner,
const MinMax *min_max) override;
Table1 drvrWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Corner *corner,
const MinMax *min_max) override;
Table1 loadWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max) override;
// Record waveform for drvr/load pin.
void watchPin(const Pin *pin) override;
void clearWatchPins() override;
PinSeq watchPins() const override;
Waveform watchWaveform(const Pin *pin) override;
protected:
void simulate(ArcDcalcArgSeq &dcalc_args);
@ -145,13 +136,7 @@ protected:
ArcDelay &delay,
Slew &slew);
void recordWaveformStep(double time);
void makeWaveforms(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max);
void reportMatrix(const char *name,
MatrixSd &matrix);
void reportMatrix(const char *name,
@ -167,7 +152,6 @@ protected:
ArcDcalcArgSeq *dcalc_args_;
size_t drvr_count_;
float load_cap_;
const DcalcAnalysisPt *dcalc_ap_;
const Parasitic *parasitic_network_;
const RiseFall *drvr_rf_;
@ -205,11 +189,7 @@ protected:
SparseLU<MatrixSd> solver_;
// Waveform recording.
bool make_waveforms_;
const Pin *waveform_drvr_pin_;
const Pin *waveform_load_pin_;
FloatSeq drvr_voltages_;
FloatSeq load_voltages_;
WatchPinValuesMap watch_pin_values_;
FloatSeq times_;
size_t drvr_idx_;

View File

@ -82,10 +82,8 @@ StringSeq
delayCalcNames()
{
StringSeq names;
for (auto name_dcalc : *delay_calcs) {
const char *name = name_dcalc.first;
for (const auto [name, make_dcalc] : *delay_calcs)
names.push_back(name);
}
return names;
}

View File

@ -62,88 +62,4 @@ report_delay_calc_cmd(Edge *edge,
return Sta::sta()->reportDelayCalc(edge, arc, corner, min_max, digits);
}
////////////////////////////////////////////////////////////////
Table1
ccs_input_waveform(const Pin *in_pin,
const RiseFall *in_rf,
const Corner *corner,
const MinMax *min_max)
{
cmdLinkedNetwork();
Sta *sta = Sta::sta();
ArcDcalcWaveforms *arc_dcalc = dynamic_cast<ArcDcalcWaveforms*>(sta->arcDelayCalc());
if (arc_dcalc)
return arc_dcalc->inputWaveform(in_pin, in_rf, corner, min_max);
else
return Table1();
}
Table1
ccs_driver_waveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Corner *corner,
const MinMax *min_max)
{
cmdLinkedNetwork();
Sta *sta = Sta::sta();
ArcDcalcWaveforms *arc_dcalc = dynamic_cast<ArcDcalcWaveforms*>(sta->arcDelayCalc());
if (arc_dcalc)
return arc_dcalc->drvrWaveform(in_pin, in_rf, drvr_pin, drvr_rf, corner, min_max);
else
return Table1();
}
Table1
ccs_driver_ramp_waveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max)
{
cmdLinkedNetwork();
Sta *sta = Sta::sta();
ArcDcalcWaveforms *arc_dcalc = dynamic_cast<ArcDcalcWaveforms*>(sta->arcDelayCalc());
if (arc_dcalc)
return arc_dcalc->drvrRampWaveform(in_pin, in_rf, drvr_pin, drvr_rf,
load_pin, corner, min_max);
else
return Table1();
}
Table1
ccs_load_waveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max)
{
cmdLinkedNetwork();
Sta *sta = Sta::sta();
ArcDcalcWaveforms *arc_dcalc = dynamic_cast<ArcDcalcWaveforms*>(sta->arcDelayCalc());
if (arc_dcalc)
return arc_dcalc->loadWaveform(in_pin, in_rf, drvr_pin, drvr_rf,
load_pin, corner, min_max);
else
return Table1();
}
void
set_prima_reduce_order(size_t order)
{
cmdLinkedNetwork();
Sta *sta = Sta::sta();
PrimaDelayCalc *dcalc = dynamic_cast<PrimaDelayCalc*>(sta->arcDelayCalc());
if (dcalc) {
dcalc->setPrimaReduceOrder(order);
sta->delaysInvalid();
}
}
%} // inline

View File

@ -148,17 +148,17 @@ proc set_assigned_delay { args } {
if [info exists keys(-from)] {
set from_pins [get_port_pins_error "from_pins" $keys(-from)]
} else {
sta_error 181 ""set_assigned_delay" missing -from argument."
sta_error 181 "set_assigned_delay missing -from argument."
}
if [info exists keys(-to)] {
set to_pins [get_port_pins_error "to_pins" $keys(-to)]
} else {
sta_error 182 ""set_assigned_delay" missing -to argument."
sta_error 182 "set_assigned_delay missing -to argument."
}
set delay [lindex $args 0]
if {![string is double $delay]} {
sta_error 183 ""set_assigned_delay" delay is not a float."
sta_error 183 "set_assigned_delay delay is not a float."
}
set delay [time_ui_sta $delay]
@ -283,7 +283,7 @@ proc set_assigned_check { args } {
}
set check_value [lindex $args 0]
if { ![string is double $check_value] } {
sta_error 192 ""set_assigned_check" check_value is not a float."
sta_error 192 "set_assigned_check check_value is not a float."
}
set check_value [time_ui_sta $check_value]

View File

@ -22,9 +22,11 @@
#include "TableModel.hh"
#include "Network.hh"
#include "Parasitics.hh"
#include "Graph.hh"
#include "Sdc.hh"
#include "Corner.hh"
#include "DcalcAnalysisPt.hh"
#include "GraphDelayCalc.hh"
namespace sta {
@ -64,36 +66,6 @@ DelayCalcBase::reduceParasitic(const Parasitic *parasitic_network,
delete pin_iter;
}
TimingModel *
DelayCalcBase::model(const TimingArc *arc,
const DcalcAnalysisPt *dcalc_ap) const
{
const OperatingConditions *op_cond = dcalc_ap->operatingConditions();
const TimingArc *corner_arc = arc->cornerArc(dcalc_ap->libertyIndex());
return corner_arc->model(op_cond);
}
GateTimingModel *
DelayCalcBase::gateModel(const TimingArc *arc,
const DcalcAnalysisPt *dcalc_ap) const
{
return dynamic_cast<GateTimingModel*>(model(arc, dcalc_ap));
}
GateTableModel *
DelayCalcBase::gateTableModel(const TimingArc *arc,
const DcalcAnalysisPt *dcalc_ap) const
{
return dynamic_cast<GateTableModel*>(model(arc, dcalc_ap));
}
CheckTimingModel *
DelayCalcBase::checkModel(const TimingArc *arc,
const DcalcAnalysisPt *dcalc_ap) const
{
return dynamic_cast<CheckTimingModel*>(model(arc, dcalc_ap));
}
void
DelayCalcBase::finishDrvrPin()
{
@ -182,7 +154,7 @@ DelayCalcBase::checkDelay(const Pin *check_pin,
float related_out_cap,
const DcalcAnalysisPt *dcalc_ap)
{
CheckTimingModel *model = checkModel(arc, dcalc_ap);
CheckTimingModel *model = arc->checkModel(dcalc_ap);
if (model) {
float from_slew1 = delayAsFloat(from_slew);
float to_slew1 = delayAsFloat(to_slew);
@ -203,7 +175,7 @@ DelayCalcBase::reportCheckDelay(const Pin *check_pin,
const DcalcAnalysisPt *dcalc_ap,
int digits)
{
CheckTimingModel *model = checkModel(arc, dcalc_ap);
CheckTimingModel *model = arc->checkModel(dcalc_ap);
if (model) {
float from_slew1 = delayAsFloat(from_slew);
float to_slew1 = delayAsFloat(to_slew);
@ -225,4 +197,33 @@ DelayCalcBase::pinPvt(const Pin *pin,
return pvt;
}
void
DelayCalcBase::setDcalcArgParasiticSlew(ArcDcalcArg &gate,
const DcalcAnalysisPt *dcalc_ap)
{
const Pin *drvr_pin = gate.drvrPin();
if (drvr_pin) {
const Parasitic *parasitic;
float load_cap;
graph_delay_calc_->parasiticLoad(drvr_pin, gate.drvrEdge(), dcalc_ap,
nullptr, this, load_cap,
parasitic);
gate.setLoadCap(load_cap);
gate.setParasitic(parasitic);
const Pin *in_pin = gate.inPin();
const Vertex *in_vertex = graph_->pinLoadVertex(in_pin);
const Slew &in_slew = graph_delay_calc_->edgeFromSlew(in_vertex, gate.inEdge(),
gate.edge(), dcalc_ap);
gate.setInSlew(in_slew);
}
}
void
DelayCalcBase::setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates,
const DcalcAnalysisPt *dcalc_ap)
{
for (ArcDcalcArg &gate : gates)
setDcalcArgParasiticSlew(gate, dcalc_ap);
}
} // namespace

View File

@ -22,6 +22,7 @@ namespace sta {
class GateTableModel;
// ArcDelayCalc helper functions.
class DelayCalcBase : public ArcDelayCalc
{
public:
@ -32,7 +33,10 @@ public:
const Net *net,
const Corner *corner,
const MinMaxAll *min_max) override;
void setDcalcArgParasiticSlew(ArcDcalcArg &gate,
const DcalcAnalysisPt *dcalc_ap) override;
void setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates,
const DcalcAnalysisPt *dcalc_ap) override;
ArcDelay checkDelay(const Pin *check_pin,
const TimingArc *arc,
const Slew &from_slew,
@ -50,14 +54,6 @@ public:
int digits) override;
protected:
GateTimingModel *gateModel(const TimingArc *arc,
const DcalcAnalysisPt *dcalc_ap) const;
GateTableModel *gateTableModel(const TimingArc *arc,
const DcalcAnalysisPt *dcalc_ap) const;
CheckTimingModel *checkModel(const TimingArc *arc,
const DcalcAnalysisPt *dcalc_ap) const;
TimingModel *model(const TimingArc *arc,
const DcalcAnalysisPt *dcalc_ap) const;
// Find the liberty library to use for logic/slew thresholds.
LibertyLibrary *thresholdLibrary(const Pin *load_pin);
// Adjust load_delay and load_slew from driver thresholds to load thresholds.

View File

@ -1501,7 +1501,7 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin,
const LibertyCell *drvr_cell = arc->from()->libertyCell();
const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary();
GateTableModel *table_model = gateTableModel(arc, dcalc_ap);
GateTableModel *table_model = arc->gateTableModel(dcalc_ap);
if (table_model && parasitic) {
float in_slew1 = delayAsFloat(in_slew);
float c2, rpi, c1;
@ -1516,9 +1516,7 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin,
dcalc_result.setGateDelay(gate_delay);
dcalc_result.setDrvrSlew(drvr_slew);
for (auto load_pin_index : load_pin_index_map) {
const Pin *load_pin = load_pin_index.first;
size_t load_idx = load_pin_index.second;
for (const auto [load_pin, load_idx] : load_pin_index_map) {
ArcDelay wire_delay;
Slew load_slew;
loadDelaySlew(load_pin, drvr_slew, rf, drvr_library, parasitic,
@ -1595,7 +1593,7 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin,
int digits)
{
gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, load_pin_index_map, dcalc_ap);
GateTimingModel *model = gateModel(arc, dcalc_ap);
GateTableModel *model = arc->gateTableModel(dcalc_ap);
float c_eff = 0.0;
string result;
if (parasitic && dmp_alg_) {

View File

@ -36,6 +36,7 @@ class DmpCeffElmoreDelayCalc : public DmpCeffDelayCalc
public:
DmpCeffElmoreDelayCalc(StaState *sta);
ArcDelayCalc *copy() override;
const char *name() const override { return "dmp_ceff_elmore"; }
ArcDcalcResult inputPortDelay(const Pin *port_pin,
float in_slew,
const RiseFall *rf,
@ -81,9 +82,7 @@ DmpCeffElmoreDelayCalc::inputPortDelay(const Pin *,
{
ArcDcalcResult dcalc_result(load_pin_index_map.size());
LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
for (auto load_pin_index : load_pin_index_map) {
const Pin *load_pin = load_pin_index.first;
size_t load_idx = load_pin_index.second;
for (auto [load_pin, load_idx] : load_pin_index_map) {
ArcDelay wire_delay = 0.0;
Slew load_slew = in_slew;
bool elmore_exists = false;
@ -130,6 +129,7 @@ class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc
public:
DmpCeffTwoPoleDelayCalc(StaState *sta);
ArcDelayCalc *copy() override;
const char *name() const override { return "dmp_ceff_two_pole"; }
Parasitic *findParasitic(const Pin *drvr_pin,
const RiseFall *rf,
const DcalcAnalysisPt *dcalc_ap) override;
@ -257,9 +257,7 @@ DmpCeffTwoPoleDelayCalc::inputPortDelay(const Pin *,
ArcDelay wire_delay = 0.0;
Slew load_slew = in_slew;
LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
for (auto load_pin_index : load_pin_index_map) {
const Pin *load_pin = load_pin_index.first;
size_t load_idx = load_pin_index.second;
for (const auto [load_pin, load_idx] : load_pin_index_map) {
if (parasitics_->isPiPoleResidue(parasitic)) {
const Parasitic *pole_residue = parasitics_->findPoleResidue(parasitic, load_pin);
if (pole_residue) {

View File

@ -366,12 +366,12 @@ GraphDelayCalc::seedNoDrvrCellSlew(Vertex *drvr_vertex,
float drive_res;
drive->driveResistance(rf, cnst_min_max, drive_res, exists);
const Parasitic *parasitic;
float cap;
float load_cap;
parasiticLoad(drvr_pin, rf, dcalc_ap, nullptr, arc_delay_calc,
cap, parasitic);
load_cap, parasitic);
if (exists) {
drive_delay = cap * drive_res;
slew = cap * drive_res;
drive_delay = load_cap * drive_res;
slew = load_cap * drive_res;
}
const MinMax *slew_min_max = dcalc_ap->slewMinMax();
if (!drvr_vertex->slewAnnotated(rf, slew_min_max))
@ -912,8 +912,7 @@ GraphDelayCalc::findDriverArcDelays(Vertex *drvr_vertex,
edge, arc, dcalc_ap,
arc_delay_calc);
ArcDcalcResultSeq dcalc_results =
arc_delay_calc->gateDelays(dcalc_args, load_cap, load_pin_index_map,
dcalc_ap);
arc_delay_calc->gateDelays(dcalc_args, load_pin_index_map, dcalc_ap);
for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) {
ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx];
ArcDcalcResult &dcalc_result = dcalc_results[drvr_idx];
@ -963,9 +962,12 @@ GraphDelayCalc::makeArcDcalcArgs(Vertex *drvr_vertex,
const RiseFall *drvr_rf = arc1->toEdge()->asRiseFall();
const Slew in_slew = edgeFromSlew(from_vertex, from_rf, edge1, dcalc_ap);
const Pin *drvr_pin1 = drvr_vertex1->pin();
Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin1, drvr_rf,
dcalc_ap);
dcalc_args.emplace_back(from_pin, drvr_pin1, edge1, arc1, in_slew, parasitic);
float load_cap;
const Parasitic *parasitic;
parasiticLoad(drvr_pin1, drvr_rf, dcalc_ap, multi_drvr, arc_delay_calc,
load_cap, parasitic);
dcalc_args.emplace_back(from_pin, drvr_pin1, edge1, arc1, in_slew,
load_cap, parasitic);
}
}
return dcalc_args;
@ -1061,7 +1063,7 @@ GraphDelayCalc::annotateDelaySlew(Edge *edge,
// Merge slews.
const Slew &drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index);
const MinMax *slew_min_max = dcalc_ap->slewMinMax();
if (delayGreater(gate_slew, drvr_slew, dcalc_ap->slewMinMax(), this)
if (delayGreater(gate_slew, drvr_slew, slew_min_max, this)
&& !drvr_vertex->slewAnnotated(drvr_rf, slew_min_max)
&& !edge->role()->isLatchDtoQ())
graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew);
@ -1380,14 +1382,23 @@ GraphDelayCalc::initWireDelays(Vertex *drvr_vertex)
}
}
// Use clock slew for register/latch clk->q edges.
Slew
GraphDelayCalc::edgeFromSlew(const Vertex *from_vertex,
const RiseFall *from_rf,
const Edge *edge,
const DcalcAnalysisPt *dcalc_ap)
{
const TimingRole *role = edge->role();
return edgeFromSlew(from_vertex, from_rf, edge->role(), dcalc_ap);
}
// Use clock slew for register/latch clk->q edges.
Slew
GraphDelayCalc::edgeFromSlew(const Vertex *from_vertex,
const RiseFall *from_rf,
const TimingRole *role,
const DcalcAnalysisPt *dcalc_ap)
{
if (role->genericRole() == TimingRole::regClkToQ()
&& clk_network_->isIdealClock(from_vertex->pin()))
return clk_network_->idealClkSlew(from_vertex->pin(), from_rf,

View File

@ -122,7 +122,7 @@ LumpedCapDelayCalc::gateDelay(const Pin *drvr_pin,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap)
{
GateTimingModel *model = gateModel(arc, dcalc_ap);
GateTimingModel *model = arc->gateModel(dcalc_ap);
debugPrint(debug_, "delay_calc", 3,
" in_slew = %s load_cap = %s lumped",
delayAsString(in_slew, this),
@ -155,9 +155,7 @@ LumpedCapDelayCalc::makeResult(const LibertyLibrary *drvr_library,
dcalc_result.setGateDelay(gate_delay);
dcalc_result.setDrvrSlew(drvr_slew);
for (auto load_pin_index : load_pin_index_map) {
const Pin *load_pin = load_pin_index.first;
size_t load_idx = load_pin_index.second;
for (const auto [load_pin, load_idx] : load_pin_index_map) {
ArcDelay wire_delay = 0.0;
thresholdAdjust(load_pin, drvr_library, rf, wire_delay, drvr_slew);
dcalc_result.setWireDelay(load_idx, wire_delay);
@ -176,7 +174,7 @@ LumpedCapDelayCalc::reportGateDelay(const Pin *check_pin,
const DcalcAnalysisPt *dcalc_ap,
int digits)
{
GateTimingModel *model = gateModel(arc, dcalc_ap);
GateTimingModel *model = arc->gateModel(dcalc_ap);
if (model) {
float in_slew1 = delayAsFloat(in_slew);
return model->reportGateDelay(pinPvt(check_pin, dcalc_ap), in_slew1, load_cap,

View File

@ -27,6 +27,7 @@ class LumpedCapDelayCalc : public ParallelDelayCalc
public:
LumpedCapDelayCalc(StaState *sta);
ArcDelayCalc *copy() override;
const char *name() const override { return "lumped_cap"; }
Parasitic *findParasitic(const Pin *drvr_pin,
const RiseFall *rf,
const DcalcAnalysisPt *dcalc_ap) override;

View File

@ -33,26 +33,24 @@ ParallelDelayCalc::ParallelDelayCalc(StaState *sta):
ArcDcalcResultSeq
ParallelDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap)
{
if (dcalc_args.size() == 1) {
ArcDcalcArg &dcalc_arg = dcalc_args[0];
ArcDcalcResult dcalc_result = gateDelay(dcalc_arg.drvrPin(), dcalc_arg.arc(),
dcalc_arg.inSlew(),
load_cap, dcalc_arg.parasitic(),
dcalc_arg.inSlew(), dcalc_arg.loadCap(),
dcalc_arg.parasitic(),
load_pin_index_map, dcalc_ap);
ArcDcalcResultSeq dcalc_results;
dcalc_results.push_back(dcalc_result);
return dcalc_results;
}
return gateDelaysParallel(dcalc_args, load_cap, load_pin_index_map, dcalc_ap);
return gateDelaysParallel(dcalc_args, load_pin_index_map, dcalc_ap);
}
ArcDcalcResultSeq
ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap)
{
@ -74,7 +72,7 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args,
ArcDelay intrinsic_delay = intrinsic_result.gateDelay();
intrinsic_delays[drvr_idx] = intrinsic_result.gateDelay();
ArcDcalcResult gate_result = gateDelay(drvr_pin, arc, in_slew, load_cap,
ArcDcalcResult gate_result = gateDelay(drvr_pin, arc, in_slew, dcalc_arg.loadCap(),
dcalc_arg.parasitic(),
load_pin_index_map, dcalc_ap);
ArcDelay gate_delay = gate_result.gateDelay();
@ -88,8 +86,7 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args,
slew_sum += 1.0 / drvr_slew;
dcalc_result.setLoadCount(load_pin_index_map.size());
for (auto load_pin_index : load_pin_index_map) {
size_t load_idx = load_pin_index.second;
for (const auto [load_pin, load_idx] : load_pin_index_map) {
dcalc_result.setWireDelay(load_idx, gate_result.wireDelay(load_idx));
dcalc_result.setLoadSlew(load_idx, gate_result.loadSlew(load_idx));
}

View File

@ -29,12 +29,10 @@ class ParallelDelayCalc : public DelayCalcBase
public:
ParallelDelayCalc(StaState *sta);
ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) override;
protected:
ArcDcalcResultSeq gateDelaysParallel(ArcDcalcArgSeq &dcalc_args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap);
};

View File

@ -57,6 +57,7 @@ PrimaDelayCalc::PrimaDelayCalc(StaState *sta) :
dcalc_args_(nullptr),
load_pin_index_map_(nullptr),
pin_node_map_(network_),
node_index_map_(ParasiticNodeLess(parasitics_, network_)),
prima_order_(3),
make_waveforms_(false),
waveform_drvr_pin_(nullptr),
@ -71,6 +72,7 @@ PrimaDelayCalc::PrimaDelayCalc(const PrimaDelayCalc &dcalc) :
dcalc_args_(nullptr),
load_pin_index_map_(nullptr),
pin_node_map_(network_),
node_index_map_(ParasiticNodeLess(parasitics_, network_)),
prima_order_(dcalc.prima_order_),
make_waveforms_(false),
waveform_drvr_pin_(nullptr),
@ -184,22 +186,19 @@ PrimaDelayCalc::gateDelay(const Pin *drvr_pin,
const DcalcAnalysisPt *dcalc_ap)
{
ArcDcalcArgSeq dcalc_args;
dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, parasitic);
ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_cap,
load_pin_index_map, dcalc_ap);
dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, load_cap, parasitic);
ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_pin_index_map, dcalc_ap);
return dcalc_results[0];
}
ArcDcalcResultSeq
PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap)
{
dcalc_args_ = &dcalc_args;
load_pin_index_map_ = &load_pin_index_map;
drvr_count_ = dcalc_args.size();
load_cap_ = load_cap;
dcalc_ap_ = dcalc_ap;
drvr_rf_ = dcalc_args[0].arc()->toEdge()->asRiseFall();
parasitic_network_ = dcalc_args[0].parasitic();
@ -208,14 +207,14 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
output_waveforms_.resize(drvr_count_);
for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) {
ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx];
GateTableModel *table_model = gateTableModel(dcalc_arg.arc(), dcalc_ap);
GateTableModel *table_model = dcalc_arg.arc()->gateTableModel(dcalc_ap);
if (table_model && dcalc_arg.parasitic()) {
OutputWaveforms *output_waveforms = table_model->outputWaveforms();
Slew in_slew = dcalc_arg.inSlew();
if (output_waveforms
// Bounds check because extrapolating waveforms does not work for shit.
&& output_waveforms->slewAxis()->inBounds(in_slew)
&& output_waveforms->capAxis()->inBounds(load_cap)) {
&& output_waveforms->capAxis()->inBounds(dcalc_arg.loadCap())) {
output_waveforms_[drvr_idx] = output_waveforms;
debugPrint(debug_, "ccs_dcalc", 1, "%s %s",
dcalc_arg.drvrCell()->name(),
@ -226,7 +225,7 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
drvr_library->supplyVoltage("VDD", vdd_, vdd_exists);
if (!vdd_exists)
report_->error(1720, "VDD not defined in library %s", drvr_library->name());
drvr_cell->ensureVoltageWaveforms();
drvr_cell->ensureVoltageWaveforms(dcalc_ap);
if (drvr_idx == 0) {
vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_;
vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_;
@ -241,7 +240,7 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
}
if (failed)
return tableDcalcResults(load_cap);
return tableDcalcResults();
else {
simulate();
return dcalcResults();
@ -249,7 +248,7 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
}
ArcDcalcResultSeq
PrimaDelayCalc::tableDcalcResults(float load_cap)
PrimaDelayCalc::tableDcalcResults()
{
for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) {
ArcDcalcArg &dcalc_arg = (*dcalc_args_)[drvr_idx];
@ -260,8 +259,7 @@ PrimaDelayCalc::tableDcalcResults(float load_cap)
dcalc_arg.setParasitic(parasitic);
}
}
return table_dcalc_->gateDelays(*dcalc_args_, load_cap, *load_pin_index_map_,
dcalc_ap_);
return table_dcalc_->gateDelays(*dcalc_args_, *load_pin_index_map_, dcalc_ap_);
}
void
@ -904,7 +902,7 @@ PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin,
const DcalcAnalysisPt *dcalc_ap,
int digits)
{
GateTimingModel *model = gateModel(arc, dcalc_ap);
GateTimingModel *model = arc->gateModel(dcalc_ap);
if (model) {
float in_slew1 = delayAsFloat(in_slew);
return model->reportGateDelay(pinPvt(drvr_pin, dcalc_ap), in_slew1, load_cap,
@ -952,96 +950,6 @@ PrimaDelayCalc::watchWaveform(const Pin *pin)
////////////////////////////////////////////////////////////////
// Waveform accessors for swig/tcl.
Table1
PrimaDelayCalc::drvrWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Corner *corner,
const MinMax *min_max)
{
makeWaveforms(in_pin, in_rf, drvr_pin, drvr_rf, nullptr, corner, min_max);
TableAxisPtr time_axis = make_shared<TableAxis>(TableAxisVariable::time,
new FloatSeq(times_));
Table1 waveform(new FloatSeq(drvr_voltages_), time_axis);
return waveform;
}
Table1
PrimaDelayCalc::loadWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max)
{
makeWaveforms(in_pin, in_rf, drvr_pin, drvr_rf, load_pin, corner, min_max);
TableAxisPtr time_axis = make_shared<TableAxis>(TableAxisVariable::time,
new FloatSeq(times_));
Table1 waveform(new FloatSeq(load_voltages_), time_axis);
return waveform;
}
Table1
PrimaDelayCalc::inputWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Corner *corner,
const MinMax *min_max)
{
LibertyPort *port = network_->libertyPort(in_pin);
if (port) {
DriverWaveform *driver_waveform = port->driverWaveform(in_rf);
const Vertex *in_vertex = graph_->pinLoadVertex(in_pin);
DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
Slew in_slew = graph_->slew(in_vertex, in_rf, dcalc_ap->index());
LibertyLibrary *library = port->libertyLibrary();
float vdd;
bool vdd_exists;
library->supplyVoltage("VDD", vdd, vdd_exists);
if (!vdd_exists)
report_->error(1751, "VDD not defined in library %s", library->name());
Table1 in_waveform = driver_waveform->waveform(in_slew);
// Scale the waveform from 0:vdd.
FloatSeq *scaled_values = new FloatSeq;
for (float value : *in_waveform.values())
scaled_values->push_back(value * vdd);
return Table1(scaled_values, in_waveform.axis1ptr());
}
return Table1();
}
void
PrimaDelayCalc::makeWaveforms(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max)
{
Edge *edge;
const TimingArc *arc;
graph_->gateEdgeArc(in_pin, in_rf, drvr_pin, drvr_rf, edge, arc);
if (arc) {
DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
const Parasitic *parasitic = findParasitic(drvr_pin, drvr_rf, dcalc_ap);
if (parasitic) {
make_waveforms_ = true;
waveform_drvr_pin_ = drvr_pin;
waveform_load_pin_ = load_pin;
Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin);
graph_delay_calc_->findDriverArcDelays(drvr_vertex, edge, arc, dcalc_ap, this);
make_waveforms_ = false;
waveform_drvr_pin_ = nullptr;
waveform_load_pin_ = nullptr;
}
}
}
////////////////////////////////////////////////////////////////
void
PrimaDelayCalc::reportMatrix(const char *name,
MatrixSd &matrix)

View File

@ -24,6 +24,7 @@
#include "Map.hh"
#include "LumpedCapDelayCalc.hh"
#include "ArcDcalcWaveforms.hh"
#include "Parasitics.hh"
namespace sta {
@ -41,7 +42,7 @@ using Eigen::Index;
using std::map;
typedef Map<const Pin*, size_t, PinIdLess> PinNodeMap;
typedef Map<const ParasiticNode*, size_t> NodeIndexMap;
typedef map<const ParasiticNode*, size_t, ParasiticNodeLess> NodeIndexMap;
typedef Map<const Pin*, size_t> PortIndexMap;
typedef SparseMatrix<double> MatrixSd;
typedef Map<const Pin*, VectorXd, PinIdLess> PinLMap;
@ -61,6 +62,7 @@ public:
~PrimaDelayCalc();
ArcDelayCalc *copy() override;
void copyState(const StaState *sta) override;
const char *name() const override { return "prima"; }
void setPrimaReduceOrder(size_t order);
Parasitic *findParasitic(const Pin *drvr_pin,
const RiseFall *rf,
@ -83,7 +85,6 @@ public:
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) override;
ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) override;
string reportGateDelay(const Pin *drvr_pin,
@ -96,31 +97,13 @@ public:
int digits) override;
// Record waveform for drvr/load pin.
void watchPin(const Pin *pin);
void clearWatchPins();
PinSeq watchPins() const;
Waveform watchWaveform(const Pin *pin);
void watchPin(const Pin *pin) override;
void clearWatchPins() override;
PinSeq watchPins() const override;
Waveform watchWaveform(const Pin *pin) override;
Waveform inputWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Corner *corner,
const MinMax *min_max) override;
Waveform drvrWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Corner *corner,
const MinMax *min_max) override;
Waveform loadWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max) override;
protected:
ArcDcalcResultSeq tableDcalcResults(float load_cap);
ArcDcalcResultSeq tableDcalcResults();
void simulate();
void simulate1(const MatrixSd &G,
const MatrixSd &C,

View File

@ -62,6 +62,18 @@ UnitDelayCalc::reduceParasitic(const Parasitic *,
{
}
void
UnitDelayCalc::setDcalcArgParasiticSlew(ArcDcalcArg &,
const DcalcAnalysisPt *)
{
}
void
UnitDelayCalc::setDcalcArgParasiticSlew(ArcDcalcArgSeq &,
const DcalcAnalysisPt *)
{
}
ArcDcalcResult
UnitDelayCalc::inputPortDelay(const Pin *,
float,
@ -87,7 +99,6 @@ UnitDelayCalc::gateDelay(const Pin *,
ArcDcalcResultSeq
UnitDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
float,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *)
{

View File

@ -26,6 +26,7 @@ class UnitDelayCalc : public ArcDelayCalc
public:
UnitDelayCalc(StaState *sta);
ArcDelayCalc *copy() override;
const char *name() const override { return "unit"; }
Parasitic *findParasitic(const Pin *drvr_pin,
const RiseFall *rf,
const DcalcAnalysisPt *dcalc_ap) override;
@ -37,6 +38,10 @@ public:
const Net *net,
const Corner *corner,
const MinMaxAll *min_max) override;
void setDcalcArgParasiticSlew(ArcDcalcArg &gate,
const DcalcAnalysisPt *dcalc_ap) override;
void setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates,
const DcalcAnalysisPt *dcalc_ap) override;
ArcDcalcResult inputPortDelay(const Pin *port_pin,
float in_slew,
const RiseFall *rf,
@ -52,7 +57,6 @@ public:
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) override;
ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) override;
ArcDelay checkDelay(const Pin *check_pin,

View File

@ -3,6 +3,13 @@ OpenSTA Timing Analyzer Release Notes
This file summarizes user visible changes for each release.
Release 2.6.0 2024/07/??
-------------------------
The version of c++ used by OpenSTA is now 17.
The USE_CUDD and USE_TCL_READLINE options default to ON.
Release 2.5.0 2024/01/17
-------------------------

View File

@ -757,10 +757,8 @@ Graph::makeArcDelayTables(DcalcAPIndex ap_count)
{
if (have_arc_delays_) {
arc_delays_.resize(ap_count);
for (DcalcAPIndex i = 0; i < ap_count; i++) {
DelayTable *table = new DelayTable();
arc_delays_[i] = table;
}
for (DcalcAPIndex i = 0; i < ap_count; i++)
arc_delays_[i] = new DelayTable();
}
}
@ -1041,7 +1039,7 @@ Graph::setPeriodCheckAnnotation(const Pin *pin,
float period)
{
if (period_check_annotations_ == nullptr)
period_check_annotations_ = new PeriodCheckAnnotations;
period_check_annotations_ = new PeriodCheckAnnotations(network_);
float *periods = period_check_annotations_->findKey(pin);
if (periods == nullptr) {
periods = new float[ap_count_];
@ -1057,10 +1055,8 @@ void
Graph::removePeriodCheckAnnotations()
{
if (period_check_annotations_) {
for (auto pin_floats : *period_check_annotations_) {
float *periods = pin_floats.second;
for (const auto [pin, periods] : *period_check_annotations_)
delete [] periods;
}
delete period_check_annotations_;
period_check_annotations_ = nullptr;
}

View File

@ -37,7 +37,7 @@ VertexNameLess::operator()(const Vertex *vertex1,
////////////////////////////////////////////////////////////////
EdgeLess::EdgeLess(const Network *network,
Graph *graph) :
Graph *&graph) :
pin_less_(network),
graph_(graph)
{

View File

@ -40,6 +40,10 @@ class Corner;
class Parasitic;
class DcalcAnalysisPt;
class MultiDrvrNet;
class ArcDcalcArg;
typedef std::vector<ArcDcalcArg*> ArcDcalcArgPtrSeq;
typedef std::vector<ArcDcalcArg> ArcDcalcArgSeq;
// Driver load pin -> index in driver loads.
typedef map<const Pin *, size_t, PinIdLess> LoadPinIndexMap;
@ -56,6 +60,7 @@ public:
Edge *edge,
const TimingArc *arc,
const Slew in_slew,
float load_cap,
const Parasitic *parasitic);
ArcDcalcArg(const Pin *in_pin,
const Pin *drvr_pin,
@ -65,6 +70,7 @@ public:
const Pin *inPin() const { return in_pin_; }
const RiseFall *inEdge() const;
const Pin *drvrPin() const { return drvr_pin_; }
Vertex *drvrVertex(const Graph *graph) const;
LibertyCell *drvrCell() const;
const LibertyLibrary *drvrLibrary() const;
const RiseFall *drvrEdge() const;
@ -74,9 +80,12 @@ public:
Slew inSlew() const { return in_slew_; }
float inSlewFlt() const;
void setInSlew(Slew in_slew);
const Parasitic *parasitic() { return parasitic_; }
const Parasitic *parasitic() const { return parasitic_; }
void setParasitic(const Parasitic *parasitic);
float loadCap() const { return load_cap_; }
void setLoadCap(float load_cap);
float inputDelay() const { return input_delay_; }
void setInputDelay(float input_delay);
protected:
const Pin *in_pin_;
@ -84,10 +93,21 @@ protected:
Edge *edge_;
const TimingArc *arc_;
Slew in_slew_;
float load_cap_;
const Parasitic *parasitic_;
float input_delay_;
};
ArcDcalcArg
makeArcDcalcArg(const char *inst_name,
const char *in_port_name,
const char *in_rf_name,
const char *drvr_port_name,
const char *drvr_rf_name,
const char *input_delay_str,
const StaState *sta);
// Arc delay calc result.
class ArcDcalcResult
{
@ -127,6 +147,9 @@ typedef vector<ArcDcalcResult> ArcDcalcResultSeq;
// DmpCeffElmoreDelayCalc
// DmpCeffTwoPoleDelayCalc
// ArnoldiDelayCalc
// CcsCeffDelayCalc
// CcsSimfDelayCalc
// PrimafDelayCalc
// Abstract class for the graph delay calculator traversal to interface
// to a delay calculator primitive.
@ -136,6 +159,7 @@ public:
explicit ArcDelayCalc(StaState *sta);
virtual ~ArcDelayCalc() {}
virtual ArcDelayCalc *copy() = 0;
virtual const char *name() const = 0;
// Find the parasitic for drvr_pin that is acceptable to the delay
// calculator by probing parasitics_.
@ -154,6 +178,11 @@ public:
const Net *net,
const Corner *corner,
const MinMaxAll *min_max) = 0;
// Set the in_slew, load_cap, parasitic for gates.
virtual void setDcalcArgParasiticSlew(ArcDcalcArg &gate,
const DcalcAnalysisPt *dcalc_ap) = 0;
virtual void setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates,
const DcalcAnalysisPt *dcalc_ap) = 0;
// Find the wire delays and slews for an input port without a driving cell.
// This call primarily initializes the load delay/slew iterator.
virtual ArcDcalcResult inputPortDelay(const Pin *port_pin,
@ -172,6 +201,7 @@ public:
const Parasitic *parasitic,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) = 0;
// deprecated 2024-02-27
virtual void gateDelay(const TimingArc *arc,
const Slew &in_slew,
float load_cap,
@ -185,7 +215,6 @@ public:
// Find gate delays and slews for parallel gates.
virtual ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) = 0;

View File

@ -49,8 +49,8 @@ template <class ENUM>
EnumNameMap<ENUM>::EnumNameMap(initializer_list<pair<const ENUM, string>> enum_names) :
enum_map_(enum_names)
{
for (auto iter = enum_map_.begin(); iter != enum_map_.end(); iter++)
name_map_[iter->second] = iter->first;
for (const auto& [key, name] : enum_map_)
name_map_[name] = key;
}
template <class ENUM>

View File

@ -46,7 +46,7 @@ typedef ArrayTable<Required> RequiredsTable;
typedef ArrayTable<PathVertexRep> PrevPathsTable;
typedef Map<const Pin*, Vertex*> PinVertexMap;
typedef Iterator<Edge*> VertexEdgeIterator;
typedef Map<const Pin*, float*> PeriodCheckAnnotations;
typedef Map<const Pin*, float*, PinIdLess> PeriodCheckAnnotations;
typedef Vector<DelayTable*> DelayTableSeq;
typedef ObjectId EdgeId;
typedef ObjectId ArrivalId;
@ -352,13 +352,13 @@ protected:
EdgeId in_edges_; // Edges to this vertex.
EdgeId out_edges_; // Edges from this vertex.
// 4 bytes
// 32 bits
unsigned int tag_group_index_:tag_group_index_bits; // 24
// Each bit corresponds to a different BFS queue.
unsigned int bfs_in_queue_:int(BfsIndex::bits); // 4
unsigned int slew_annotated_:slew_annotated_bits;
// 4 bytes (32 bits)
// 32 bits
unsigned int level_:Graph::vertex_level_bits;
// Levelization search state.
// LevelColor gcc barfs if this is dcl'd.
@ -369,6 +369,8 @@ protected:
// This flag distinguishes the driver and load vertices.
bool is_bidirect_drvr_:1;
bool is_reg_clk_:1;
// 15 bits
bool is_disabled_constraint_:1;
bool is_gated_clk_enable_:1;
// Constrained by timing check edge.
@ -379,7 +381,6 @@ protected:
bool has_downstream_clk_pin_:1;
bool crpr_path_pruning_disabled_:1;
bool requireds_pruned_:1;
unsigned object_idx_:VertexTable::idx_bits;
private:
@ -441,6 +442,7 @@ protected:
EdgeId vertex_out_next_; // Vertex out edges doubly linked list.
EdgeId vertex_out_prev_;
ArcId arc_delays_;
// 16 bits
bool delay_annotation_is_incremental_:1;
bool is_bidirect_inst_path_:1;
bool is_bidirect_net_path_:1;

View File

@ -37,13 +37,13 @@ class EdgeLess
{
public:
EdgeLess(const Network *network,
Graph *graph);
Graph *&graph);
bool operator()(const Edge *edge1,
const Edge *edge2) const;
private:
const PinPathNameLess pin_less_;
Graph *graph_;
Graph *&graph_;
};
void

View File

@ -120,6 +120,10 @@ public:
const RiseFall *from_rf,
const Edge *edge,
const DcalcAnalysisPt *dcalc_ap);
Slew edgeFromSlew(const Vertex *from_vertex,
const RiseFall *from_rf,
const TimingRole *role,
const DcalcAnalysisPt *dcalc_ap);
protected:
void seedInvalidDelays();

View File

@ -73,7 +73,7 @@ typedef Map<const OperatingConditions*, LibertyCell*> ScaledCellMap;
typedef Map<const OperatingConditions*, LibertyPort*> ScaledPortMap;
typedef Map<const char *, ModeDef*, CharPtrLess> ModeDefMap;
typedef Map<const char *, ModeValueDef*, CharPtrLess> ModeValueMap;
typedef Map<TimingArcSet*, LatchEnable*> LatchEnableMap;
typedef Map<const TimingArcSet*, LatchEnable*> LatchEnableMap;
typedef Vector<LatchEnable*> LatchEnableSeq;
typedef Map<const char *, OcvDerate*, CharPtrLess> OcvDerateMap;
typedef Vector<InternalPowerAttrs*> InternalPowerAttrsSeq;
@ -473,10 +473,10 @@ public:
bool hasInferedRegTimingArcs() const { return has_infered_reg_timing_arcs_; }
TestCell *testCell() const { return test_cell_; }
bool isLatchData(LibertyPort *port);
void latchEnable(TimingArcSet *arc_set,
void latchEnable(const TimingArcSet *arc_set,
// Return values.
LibertyPort *&enable_port,
FuncExpr *&enable_func,
const LibertyPort *&enable_port,
const FuncExpr *&enable_func,
const RiseFall *&enable_rf) const;
const RiseFall *latchCheckEnableEdge(TimingArcSet *check_set);
bool isDisabledConstraint() const { return is_disabled_constraint_; }
@ -532,7 +532,7 @@ public:
// Check all liberty cells to make sure they exist
// for all the defined corners.
static void checkLibertyCorners();
void ensureVoltageWaveforms();
void ensureVoltageWaveforms(const DcalcAnalysisPt *dcalc_ap);
protected:
void addPort(ConcretePort *port);
@ -802,20 +802,20 @@ public:
DriverWaveform *driverWaveform(const RiseFall *rf) const;
void setDriverWaveform(DriverWaveform *driver_waveform,
const RiseFall *rf);
void setClkTreeDelay(const TableModel *model,
const RiseFall *from_rf,
const RiseFall *to_rf,
const MinMax *min_max);
// Should be deprecated.
float clkTreeDelay(float in_slew,
const RiseFall *from_rf,
const MinMax *min_max) const;
float clkTreeDelay(float in_slew,
const RiseFall *from_rf,
const RiseFall *to_rf,
const MinMax *min_max) const;
// Assumes input slew of 0.0.
RiseFallMinMax clkTreeDelays() const;
float clkTreeDelay(float in_slew,
const RiseFall *from_rf,
const MinMax *min_max) const;
void setClkTreeDelay(const TableModel *model,
const RiseFall *from_rf,
const RiseFall *to_rf,
const MinMax *min_max);
// deprecated 2024-06-22
RiseFallMinMax clkTreeDelays() const __attribute__ ((deprecated));
// deprecated 2024-02-27
RiseFallMinMax clockTreePathDelays() const __attribute__ ((deprecated));
static bool equiv(const LibertyPort *port1,

View File

@ -101,9 +101,7 @@ public:
void
deleteKeysContents()
{
for (auto key_value : this) {
KEY key = key_value.first;
VALUE value = key_value.second;
for (const auto [key, value] : this) {
delete key;
delete value;
}

View File

@ -108,9 +108,9 @@ public:
bool matches(const MinMax *min_max) const;
bool matches(const MinMaxAll *min_max) const;
static MinMaxAll *find(const char *min_max);
// for (auto min_max : min_max->range()) {}
// for (const auto min_max : min_max->range()) {}
const std::vector<MinMax*> &range() const { return range_; }
// for (auto mm_index : min_max->rangeIndex()) {}
// for (const auto mm_index : min_max->rangeIndex()) {}
const std::vector<int> &rangeIndex() const { return range_index_; }
private:

View File

@ -513,6 +513,7 @@ public:
LibertyPort *port,
Net *net) = 0;
// makePin/connectPin replaced by connect.
// deprecated 2018-09-28
virtual void connectPin(Pin *pin,
Net *net) __attribute__ ((deprecated));
// Disconnect pin from net.

View File

@ -175,6 +175,7 @@ public:
// Find the parasitic node connected to pin.
virtual ParasiticNode *findParasiticNode(const Parasitic *parasitic,
const Pin *pin) const = 0;
// deprecated 2024-02-27
virtual ParasiticNode *findNode(const Parasitic *parasitic,
const Pin *pin) const __attribute__ ((deprecated));
// Make a subnode of the parasitic network net connected to pin.
@ -188,6 +189,7 @@ public:
virtual const Pin *pin(const ParasiticNode *node) const = 0;
virtual const Net *net(const ParasiticNode *node,
const Network *network) const = 0;
virtual unsigned netId(const ParasiticNode *node) const = 0;
virtual bool isExternal(const ParasiticNode *node) const = 0;
// Node capacitance to ground.
virtual float nodeGndCap(const ParasiticNode *node) const = 0;
@ -308,4 +310,17 @@ private:
float coupling_cap_factor_;
};
class ParasiticNodeLess
{
public:
ParasiticNodeLess(const Parasitics *parasitics,
const Network *network);
ParasiticNodeLess(const ParasiticNodeLess &less);
bool operator()(const ParasiticNode *node1,
const ParasiticNode *node2) const;
private:
const Parasitics *parasitics_;
const Network *network_;
};
} // namespace

View File

@ -136,7 +136,8 @@ public:
virtual PathDelay *pathDelay() const;
// This returns the crpr signed with respect to the check type.
// Positive for setup, negative for hold.
virtual Crpr commonClkPessimism(const StaState *sta) const;
virtual Crpr checkCrpr(const StaState *sta) const;
virtual Crpr crpr(const StaState *sta) const;
virtual MultiCyclePath *multiCyclePath() const;
virtual TimingArc *checkArc() const { return nullptr; }
// PathEndDataCheck data clock path.
@ -244,7 +245,7 @@ public:
virtual float targetNonInterClkUncertainty(const StaState *sta) const;
virtual float interClkUncertainty(const StaState *sta) const;
virtual float targetClkUncertainty(const StaState *sta) const;
virtual Crpr commonClkPessimism(const StaState *sta) const;
virtual Crpr crpr(const StaState *sta) const;
virtual Required requiredTime(const StaState *sta) const;
virtual Slack slack(const StaState *sta) const;
virtual Slack slackNoCrpr(const StaState *sta) const;
@ -426,7 +427,7 @@ public:
virtual Arrival targetClkArrivalNoCrpr(const StaState *sta) const;
virtual Delay targetClkDelay(const StaState *sta) const;
virtual Delay targetClkInsertionDelay(const StaState *sta) const;
virtual Crpr commonClkPessimism(const StaState *sta) const;
virtual Crpr crpr(const StaState *sta) const;
virtual int exceptPathCmp(const PathEnd *path_end,
const StaState *sta) const;

View File

@ -955,6 +955,13 @@ public:
float &wire_cap,
float &fanout,
bool &has_net_load) const;
void pinCaps(const Pin *pin,
const RiseFall *rf,
const Corner *corner,
const MinMax *min_max,
float &pin_cap,
float &wire_cap,
float &fanout) const;
void portExtFanout(const Port *port,
const Corner *corner,
const MinMax *min_max,
@ -1235,13 +1242,6 @@ protected:
void annotateHierClkLatency();
void annotateHierClkLatency(const Pin *hpin,
ClockLatency *latency);
void pinCaps(const Pin *pin,
const RiseFall *rf,
const Corner *corner,
const MinMax *min_max,
float &pin_cap,
float &wire_cap,
float &fanout) const;
void netCaps(const Pin *drvr_pin,
const RiseFall *rf,
const Corner *corner,
@ -1256,7 +1256,8 @@ protected:
const RiseFall *rf,
const Corner *corner,
const MinMax *min_max);
float portCapacitance(Instance *inst, LibertyPort *port,
float portCapacitance(Instance *inst,
LibertyPort *port,
const RiseFall *rf,
const Corner *corner,
const MinMax *min_max) const;

View File

@ -85,7 +85,7 @@ typedef MinMaxAll SetupHoldAll;
typedef Vector<ExceptionThru*> ExceptionThruSeq;
typedef Set<LibertyPortPair, LibertyPortPairLess> LibertyPortPairSet;
typedef Map<const Instance*, DisabledInstancePorts*> DisabledInstancePortsMap;
typedef Map<const LibertyCell*, DisabledCellPorts*> DisabledCellPortsMap;
typedef Map<LibertyCell*, DisabledCellPorts*> DisabledCellPortsMap;
typedef MinMaxValues<float> ClockUncertainties;
typedef Set<ExceptionPath*> ExceptionPathSet;
typedef PinPair EdgePins;

View File

@ -28,6 +28,8 @@
#include "VertexVisitor.hh"
#include "SearchClass.hh"
#include "PowerClass.hh"
#include "ArcDelayCalc.hh"
#include "CircuitSim.hh"
struct Tcl_Interp;
@ -773,7 +775,7 @@ public:
const RiseFallBoth *rf,
float slew);
void writeSdf(const char *filename,
Corner *corner,
const Corner *corner,
char divider,
bool include_typ,
int digits,
@ -938,7 +940,7 @@ public:
// bug that should be reported.
void updateTiming(bool full);
// Invalidate all delay calculations. Arrivals also invalidated.
void delaysInvalid();
void delaysInvalid() const;
// Invalidate all arrival and required times.
void arrivalsInvalid();
PinSet startpointPins();
@ -1285,6 +1287,24 @@ public:
const Corner *corner);
PwrActivity findClkedActivity(const Pin *pin);
void writeGateSpice(ArcDcalcArgSeq gates,
const char *spice_filename,
const char *subckt_filename,
const char *lib_subckt_filename,
const char *model_filename,
const char *power_name,
const char *gnd_name,
CircuitSim ckt_sim,
const Corner *corner,
const MinMax *min_max);
void writeGateGnuplot(ArcDcalcArgSeq gates,
PinSet plot_pins,
const char *spice_waveform_filename,
const char *csv_filename,
const char *gnuplot_filename,
const Corner *corner,
const MinMax *min_max);
void writeTimingModel(const char *lib_name,
const char *cell_name,
const char *filename,

View File

@ -64,7 +64,5 @@ sourceTclFile(const char *filename,
bool echo,
bool verbose,
Tcl_Interp *interp);
bool
is_regular_file(const char *filename);
} // namespace

View File

@ -39,6 +39,7 @@ class Table1;
typedef Vector<float> FloatSeq;
typedef Vector<FloatSeq*> FloatTable;
typedef Vector<Table1*> Table1Seq;
typedef Table1 Waveform;
TableAxisVariable
stringTableAxisVariable(const char *variable);
@ -66,6 +67,7 @@ public:
// Return values.
ArcDelay &gate_delay,
Slew &drvr_slew) const override;
// deprecated 2024-01-07
// related_out_cap arg removed.
void gateDelay(const Pvt *pvt,
float in_slew,
@ -455,9 +457,10 @@ public:
// Return values.
size_t &index,
bool &exists) const;
size_t findAxisClosestIndex(float value) const;
FloatSeq *values() const { return values_; }
float min() const { return (*values_)[0]; }
float max() const { return (*values_)[values_->size() - 1]; }
float min() const;
float max() const;
private:
TableAxisVariable variable_;
@ -492,26 +495,43 @@ public:
const RiseFall *rf() const { return rf_; }
const TableAxis *slewAxis() const { return slew_axis_.get(); }
const TableAxis *capAxis() const { return cap_axis_.get(); }
Table1 voltageWaveform(float in_slew,
float load_cap);
float voltageTime(float in_slew,
float load_cap,
float voltage);
const Table1 *currentWaveform(float slew,
float cap);
// Make voltage wavefroms from liberty time/current values.
// Required before voltageTime, timeVoltage, voltageCurrent.
void makeVoltageWaveforms(float vdd);
float timeCurrent(float slew,
float cap,
float time);
float timeVoltage(float slew,
float cap,
float time);
float voltageTime(float in_slew,
float load_cap,
float voltage);
float voltageCurrent(float slew,
float cap,
float volt);
float referenceTime(float slew);
void makeVoltageWaveforms(float vdd);
float beginTime(float slew,
float cap);
float endTime(float slew,
float cap);
static bool checkAxes(const TableTemplate *tbl_template);
Table1 currentWaveform(float slew,
float cap);
// Waveform closest to slew/cap; no interpolation.
const Table1 *currentWaveformRaw(float slew,
float cap);
Table1 voltageWaveform(float in_slew,
float load_cap);
// Waveform closest to slew/cap; no interpolation.
const Table1 *voltageWaveformRaw(float slew,
float cap);
Table1 voltageCurrentWaveform(float slew,
float cap);
// V/I for last segment of min slew/max cap.
float finalResistance();
private:
void findVoltages(size_t wave_index,
float cap);
@ -519,27 +539,30 @@ private:
float cap,
float axis_value,
Table1Seq &waveforms);
float voltageTime1(float voltage,
float beginEndTime(float slew,
float cap,
bool begin);
double voltageTime1(double volt,
double dx1,
double dx2,
size_t wave_index00,
size_t wave_index01,
size_t wave_index10,
size_t wave_index11);
float voltageTime2(float volt,
size_t wave_index);
void waveformMinMaxTime(float slew,
float cap,
Table1Seq &waveforms,
// Return values.
float &min_time,
float &max_time);
// Row.
TableAxisPtr slew_axis_;
// Column.
TableAxisPtr cap_axis_;
const RiseFall *rf_;
Table1Seq current_waveforms_;
Table1Seq current_waveforms_; // from liberty
Table1Seq voltage_waveforms_;
Table1Seq voltage_currents_;
FloatTable voltage_times_;
Table1 *ref_times_;
float vdd_;
static constexpr size_t voltage_waveform_step_count_ = 20;
static constexpr size_t voltage_waveform_step_count_ = 100;
};
class DriverWaveform

View File

@ -27,6 +27,8 @@ namespace sta {
class TimingArcAttrs;
class WireTimingArc;
class GateTableModel;
class DcalcAnalysisPt;
typedef int TimingArcIndex;
typedef Vector<TimingArc*> TimingArcSeq;
@ -147,7 +149,7 @@ public:
TimingRole *role() const { return role_; };
TimingSense sense() const;
// Rise/fall if the arc set is rising_edge or falling_edge.
RiseFall *isRisingFallingEdge() const;
const RiseFall *isRisingFallingEdge() const;
size_t arcCount() const { return arcs_.size(); }
TimingArcSeq &arcs() { return arcs_; }
// Return 1 or 2 arcs matching from transition.
@ -235,8 +237,11 @@ public:
TimingSense sense() const;
// Index in TimingArcSet.
unsigned index() const { return index_; }
TimingModel *model(const OperatingConditions *op_cond) const;
TimingModel *model() const { return model_; }
GateTimingModel *gateModel(const DcalcAnalysisPt *dcalc_ap) const;
CheckTimingModel *checkModel(const DcalcAnalysisPt *dcalc_ap) const;
GateTableModel *gateTableModel() const;
GateTableModel *gateTableModel(const DcalcAnalysisPt *dcalc_ap) const;
const TimingArc *cornerArc(int ap_index) const;
void setCornerArc(TimingArc *corner_arc,
int ap_index);
@ -247,6 +252,7 @@ public:
const TimingArc *arc2);
protected:
TimingModel *model(const DcalcAnalysisPt *dcalc_ap) const;
void setIndex(unsigned index);
void addScaledModel(const OperatingConditions *op_cond,
TimingModel *scaled_model);

View File

@ -97,9 +97,9 @@ public:
RiseFall *asRiseFall() const { return as_rise_fall_; }
// Find transition corresponding to string.
static RiseFallBoth *find(const char *tr_str);
// for (auto tr : min_max->range()) {}
// for (const auto rf : rf->range()) {}
const std::vector<RiseFall*> &range() const { return range_; }
// for (auto tr_index : min_max->rangeIndex()) {}
// for (const auto rf_index : rf->rangeIndex()) {}
const std::vector<int> &rangeIndex() const { return range_index_; }
static const int index_count = 3;

View File

@ -121,10 +121,8 @@ LibertyLibrary::~LibertyLibrary()
delete units_;
ocv_derate_map_.deleteContents();
for (auto name_volt : supply_voltage_map_) {
const char *supply_name = name_volt.first;
for (auto [supply_name, volt] : supply_voltage_map_)
stringDelete(supply_name);
}
delete buffers_;
delete inverters_;
driver_waveform_map_.deleteContents();
@ -204,8 +202,8 @@ BusDclSeq
LibertyLibrary::busDcls() const
{
BusDclSeq dcls;
for (auto name_dcl : bus_dcls_)
dcls.push_back(name_dcl.second);
for (auto [name, dcl] : bus_dcls_)
dcls.push_back(dcl);
return dcls;
}
@ -228,10 +226,8 @@ LibertyLibrary::tableTemplates() const
{
TableTemplateSeq tbl_templates;
for (int type = 0; type < table_template_type_count; type++) {
for (auto name_template : template_maps_[type]) {
TableTemplate *tbl_template = name_template.second;
for (auto [name, tbl_template] : template_maps_[type])
tbl_templates.push_back(tbl_template);
}
}
return tbl_templates;
}
@ -1312,8 +1308,7 @@ LibertyCell::finish(bool infer_latches,
void
LibertyCell::findDefaultCondArcs()
{
for (auto port_pair_set : port_timing_arc_set_map_) {
TimingArcSetSeq *sets = port_pair_set.second;
for (auto [port_pair, sets] : port_timing_arc_set_map_) {
bool has_cond_arcs = false;
for (auto set : *sets) {
if (set->cond()) {
@ -1908,10 +1903,10 @@ LibertyCell::isLatchData(LibertyPort *port)
}
void
LibertyCell::latchEnable(TimingArcSet *d_to_q_set,
LibertyCell::latchEnable(const TimingArcSet *d_to_q_set,
// Return values.
LibertyPort *&enable_port,
FuncExpr *&enable_func,
const LibertyPort *&enable_port,
const FuncExpr *&enable_func,
const RiseFall *&enable_edge) const
{
LatchEnable *latch_enable = latch_d_to_q_map_.findKey(d_to_q_set);
@ -1938,7 +1933,7 @@ LibertyCell::latchCheckEnableEdge(TimingArcSet *check_set)
}
void
LibertyCell::ensureVoltageWaveforms()
LibertyCell::ensureVoltageWaveforms(const DcalcAnalysisPt *dcalc_ap)
{
if (!have_voltage_waveforms_) {
float vdd = 0.0; // shutup gcc
@ -1948,7 +1943,7 @@ LibertyCell::ensureVoltageWaveforms()
criticalError(1120, "library missing vdd");
for (TimingArcSet *arc_set : timingArcSets()) {
for (TimingArc *arc : arc_set->arcs()) {
GateTableModel*model = dynamic_cast<GateTableModel*>(arc->model());
GateTableModel *model = arc->gateTableModel(dcalc_ap);
if (model) {
OutputWaveforms *output_waveforms = model->outputWaveforms();
if (output_waveforms)

View File

@ -695,22 +695,22 @@ LibertyReader::endLibraryAttrs(LibertyGroup *group)
}
bool missing_threshold = false;
for (auto tr : RiseFall::range()) {
int tr_index = tr->index();
if (!have_input_threshold_[tr_index]) {
libWarn(1145, group, "input_threshold_pct_%s not found.", tr->name());
for (auto rf : RiseFall::range()) {
int rf_index = rf->index();
if (!have_input_threshold_[rf_index]) {
libWarn(1145, group, "input_threshold_pct_%s not found.", rf->name());
missing_threshold = true;
}
if (!have_output_threshold_[tr_index]) {
libWarn(1146, group, "output_threshold_pct_%s not found.", tr->name());
if (!have_output_threshold_[rf_index]) {
libWarn(1146, group, "output_threshold_pct_%s not found.", rf->name());
missing_threshold = true;
}
if (!have_slew_lower_threshold_[tr_index]) {
libWarn(1147, group, "slew_lower_threshold_pct_%s not found.", tr->name());
if (!have_slew_lower_threshold_[rf_index]) {
libWarn(1147, group, "slew_lower_threshold_pct_%s not found.", rf->name());
missing_threshold = true;
}
if (!have_slew_upper_threshold_[tr_index]) {
libWarn(1148, group, "slew_upper_threshold_pct_%s not found.", tr->name());
if (!have_slew_upper_threshold_[rf_index]) {
libWarn(1148, group, "slew_upper_threshold_pct_%s not found.", rf->name());
missing_threshold = true;
}
}
@ -5110,13 +5110,13 @@ LibertyReader::endOcvDerateFactors(LibertyGroup *)
{
if (ocv_derate_) {
for (auto early_late : derate_type_->range()) {
for (auto tr : rf_type_->range()) {
for (auto rf : rf_type_->range()) {
if (path_type_ == PathType::clk_and_data) {
ocv_derate_->setDerateTable(tr, early_late, PathType::clk, table_);
ocv_derate_->setDerateTable(tr, early_late, PathType::data, table_);
ocv_derate_->setDerateTable(rf, early_late, PathType::clk, table_);
ocv_derate_->setDerateTable(rf, early_late, PathType::data, table_);
}
else
ocv_derate_->setDerateTable(tr, early_late, path_type_, table_);
ocv_derate_->setDerateTable(rf, early_late, path_type_, table_);
}
}
}

View File

@ -31,6 +31,9 @@ using std::max;
using std::abs;
using std::make_shared;
size_t
findValueIndex(float value,
const FloatSeq *values);
static void
deleteSigmaModels(TableModel *models[EarlyLate::index_count]);
static string
@ -1425,6 +1428,25 @@ TableAxis::~TableAxis()
delete values_;
}
float
TableAxis::min() const
{
if (!values_->empty())
return (*values_)[0];
else
return 0.0;
}
float
TableAxis::max() const
{
size_t size = values_->size();
if (size > 0)
return (*values_)[values_->size() - 1];
else
return 0.0;
}
bool
TableAxis::inBounds(float value) const
{
@ -1434,14 +1456,22 @@ TableAxis::inBounds(float value) const
&& value <= (*values_)[size - 1];
}
// Bisection search.
size_t
TableAxis::findAxisIndex(float value) const
{
size_t size = values_->size();
if (size <= 1 || value <= (*values_)[0])
return findValueIndex(value, values_);
}
// Bisection search.
// Assumes values are monotonically increasing.
size_t
findValueIndex(float value,
const FloatSeq *values)
{
size_t size = values->size();
if (size <= 1 || value <= (*values)[0])
return 0;
else if (value >= (*values_)[size - 1])
else if (value >= (*values)[size - 1])
// Return max_index-1 for value too large so interpolation pts are index,index+1.
return size - 2;
else {
@ -1449,7 +1479,7 @@ TableAxis::findAxisIndex(float value) const
int upper = size;
while (upper - lower > 1) {
int mid = (upper + lower) >> 1;
if (value >= (*values_)[mid])
if (value >= (*values)[mid])
lower = mid;
else
upper = mid;
@ -1486,6 +1516,31 @@ TableAxis::findAxisIndex(float value,
exists = false;
}
size_t
TableAxis::findAxisClosestIndex(float value) const
{
size_t size = values_->size();
if (size <= 1 || value <= (*values_)[0])
return 0;
else if (value >= (*values_)[size - 1])
return size - 1;
else {
int lower = -1;
int upper = size;
while (upper - lower > 1) {
int mid = (upper + lower) >> 1;
if (value >= (*values_)[mid])
lower = mid;
else
upper = mid;
}
if ((value - (*values_)[lower]) < ((*values_)[upper] - value))
return lower;
else
return upper;
}
}
const char *
TableAxis::variableString() const
{
@ -1588,7 +1643,6 @@ OutputWaveforms::~OutputWaveforms()
current_waveforms_.deleteContents();
voltage_waveforms_.deleteContents();
voltage_currents_.deleteContents();
voltage_times_.deleteContents();
delete ref_times_;
}
@ -1616,10 +1670,9 @@ OutputWaveforms::makeVoltageWaveforms(float vdd)
size_t size = current_waveforms_.size();
voltage_waveforms_.resize(size);
voltage_currents_.resize(size);
voltage_times_.resize(size);
size_t cap_count = cap_axis_->size();
for (size_t slew_index = 0; slew_index < slew_axis_->size(); slew_index++) {
for (size_t cap_index = 0; cap_index < cap_axis_->size(); cap_index++) {
for (size_t cap_index = 0; cap_index < cap_count; cap_index++) {
size_t wave_index = slew_index * cap_count + cap_index;
findVoltages(wave_index, cap_axis_->axisValue(cap_index));
}
@ -1650,6 +1703,7 @@ OutputWaveforms::findVoltages(size_t wave_index,
prev_time = time;
prev_current = current;
}
(*volts)[volts->size() - 1] = vdd_;
Table1 *volt_table = new Table1(volts, currents->axis1ptr());
voltage_waveforms_[wave_index] = volt_table;
@ -1660,39 +1714,33 @@ OutputWaveforms::findVoltages(size_t wave_index,
FloatSeq *currents1 = new FloatSeq(*currents->values());
Table1 *volt_currents = new Table1(currents1, volt_axis);
voltage_currents_[wave_index] = volt_currents;
// Sample the voltage waveform at uniform intervals to speed up
// voltage time lookup.
FloatSeq *voltage_times = new FloatSeq;
float volt_step = vdd_ / voltage_waveform_step_count_;
size_t i = 0;
float time0 = time_axis->axisValue(i);
float volt0 = (*volts)[i];
i = 1;
float time1 = time_axis->axisValue(i);
float volt1 = (*volts)[i];
for (size_t v = 0; v <= voltage_waveform_step_count_; v++) {
float volt3 = v * volt_step;
while (volt3 > volt1 && i < volts->size() - 1) {
time0 = time1;
volt0 = volt1;
i++;
time1 = time_axis->axisValue(i);
volt1 = (*volts)[i];
}
float time3 = time0 + (time1 - time0) * (volt3 - volt0) / (volt1 - volt0);
voltage_times->push_back(time3);
}
voltage_times_[wave_index] = voltage_times;
}
const Table1 *
Table1
OutputWaveforms::currentWaveform(float slew,
float cap)
{
size_t slew_index = slew_axis_->findAxisIndex(slew);
size_t cap_index = cap_axis_->findAxisIndex(cap);
size_t wave_index = slew_index * cap_axis_->size() + cap_index;
FloatSeq *times = new FloatSeq;
FloatSeq *currents = new FloatSeq;
for (size_t i = 0; i <= voltage_waveform_step_count_; i++) {
float volt = i * vdd_ / voltage_waveform_step_count_;
float time = voltageTime(slew, cap, volt);
float current = voltageCurrent(slew, cap, volt);
times->push_back(time);
currents->push_back(current);
}
TableAxisPtr time_axis = make_shared<TableAxis>(TableAxisVariable::time, times);
return Table1(currents, time_axis);
}
const Table1 *
OutputWaveforms::currentWaveformRaw(float slew,
float cap)
{
size_t slew_index = slew_axis_->findAxisClosestIndex(slew);
size_t cap_index = cap_axis_->findAxisClosestIndex(cap);
size_t cap_count = cap_axis_->size();
size_t wave_index = slew_index * cap_count + cap_index;
return current_waveforms_[wave_index];
}
@ -1701,7 +1749,9 @@ OutputWaveforms::timeCurrent(float slew,
float cap,
float time)
{
return waveformValue(slew, cap, time, current_waveforms_);
// Current waveform is not monotonic, so use volt/current correspondence.
float volt = timeVoltage(slew, cap, time);
return voltageCurrent(slew, cap, volt);
}
float
@ -1709,7 +1759,81 @@ OutputWaveforms::timeVoltage(float slew,
float cap,
float time)
{
return waveformValue(slew, cap, time, voltage_waveforms_);
size_t slew_index = slew_axis_->findAxisIndex(slew);
size_t cap_index = cap_axis_->findAxisIndex(cap);
size_t cap_count = cap_axis_->size();
size_t wave_index00 = slew_index * cap_count + cap_index;
size_t wave_index01 = slew_index * cap_count + (cap_index + 1);
size_t wave_index10 = (slew_index + 1) * cap_count + cap_index;
size_t wave_index11 = (slew_index + 1) * cap_count + (cap_index + 1);
size_t index1 = slew_index;
size_t index2 = cap_index;
double x1 = slew;
double x2 = cap;
double x1l = slew_axis_->axisValue(index1);
double x1u = slew_axis_->axisValue(index1 + 1);
double dx1 = (x1 - x1l) / (x1u - x1l);
double x2l = cap_axis_->axisValue(index2);
double x2u = cap_axis_->axisValue(index2 + 1);
double dx2 = (x2 - x2l) / (x2u - x2l);
double v_lo = 0.0;
double v_hi = vdd_;
double v_mid = (v_hi + v_lo) * 0.5;
double time_mid;
while (v_hi - v_lo > .001) {
time_mid = voltageTime1(v_mid, dx1, dx2, wave_index00, wave_index01,
wave_index10, wave_index11);
if (time > time_mid) {
v_lo = v_mid;
v_mid = (v_hi + v_lo) * 0.5;
}
else {
v_hi = v_mid;
v_mid = (v_hi + v_lo) * 0.5;
}
}
return v_mid;
}
double
OutputWaveforms::voltageTime1(double volt,
double dx1,
double dx2,
size_t wave_index00,
size_t wave_index01,
size_t wave_index10,
size_t wave_index11)
{
double y00 = voltageTime2(volt, wave_index00);
double y01 = voltageTime2(volt, wave_index01);
double y10 = voltageTime2(volt, wave_index10);
double y11 = voltageTime2(volt, wave_index11);
double time
= (1 - dx1) * (1 - dx2) * y00
+ dx1 * (1 - dx2) * y10
+ dx1 * dx2 * y11
+ (1 - dx1) * dx2 * y01;
return time;
}
float
OutputWaveforms::voltageTime2(float volt,
size_t wave_index)
{
const Table1 *voltage_waveform = voltage_waveforms_[wave_index];
const FloatSeq *voltages = voltage_waveform->values();
size_t index1 = findValueIndex(volt, voltages);
float volt_lo = (*voltages)[index1];
float volt_hi = (*voltages)[index1 + 1];
float dv = volt_hi - volt_lo;
const TableAxis *time_axis = voltage_waveform->axis1();
float time_lo = time_axis->axisValue(index1);
float time_hi = time_axis->axisValue(index1 + 1);
float dt = time_hi - time_lo;
return time_lo + dt * (volt - volt_lo) / dv;
}
float
@ -1773,14 +1897,11 @@ Table1
OutputWaveforms::voltageWaveform(float slew,
float cap)
{
float min_time, max_time;
waveformMinMaxTime(slew, cap, voltage_waveforms_, min_time, max_time);
float time_step = (max_time - min_time) / voltage_waveform_step_count_;
FloatSeq *times = new FloatSeq;
FloatSeq *volts = new FloatSeq;
for (size_t i = 0; i < voltage_waveform_step_count_; i++) {
float time = min_time + i * time_step;
float volt = timeVoltage(slew, cap, time);
for (size_t i = 0; i <= voltage_waveform_step_count_; i++) {
float volt = i * vdd_ / voltage_waveform_step_count_;
float time = voltageTime(slew, cap, volt);
times->push_back(time);
volts->push_back(volt);
}
@ -1788,42 +1909,21 @@ OutputWaveforms::voltageWaveform(float slew,
return Table1(volts, time_axis);
}
void
OutputWaveforms::waveformMinMaxTime(float slew,
float cap,
Table1Seq &waveforms,
// Return values.
float &min_time,
float &max_time)
const Table1 *
OutputWaveforms::voltageWaveformRaw(float slew,
float cap)
{
size_t slew_index = slew_axis_->findAxisIndex(slew);
size_t cap_index = cap_axis_->findAxisIndex(cap);
size_t slew_index = slew_axis_->findAxisClosestIndex(slew);
size_t cap_index = cap_axis_->findAxisClosestIndex(cap);
size_t cap_count = cap_axis_->size();
size_t wave_index00 = slew_index * cap_count + cap_index;
size_t wave_index01 = slew_index * cap_count + (cap_index + 1);
size_t wave_index10 = (slew_index + 1) * cap_count + cap_index;
size_t wave_index11 = (slew_index + 1) * cap_count + (cap_index + 1);
const Table1 *waveform00 = waveforms[wave_index00];
const Table1 *waveform01 = waveforms[wave_index01];
const Table1 *waveform10 = waveforms[wave_index10];
const Table1 *waveform11 = waveforms[wave_index11];
min_time = waveform00->axis1()->min();
min_time = min(min_time, waveform01->axis1()->min());
min_time = min(min_time, waveform10->axis1()->min());
min_time = min(min_time, waveform11->axis1()->min());
max_time = waveform00->axis1()->max();
max_time = max(max_time, waveform01->axis1()->max());
max_time = max(max_time, waveform10->axis1()->max());
max_time = max(max_time, waveform11->axis1()->max());
size_t wave_index = slew_index * cap_count + cap_index;
return voltage_waveforms_[wave_index];
}
float
OutputWaveforms::voltageTime(float slew,
float cap,
float volt)
float cap,
float volt)
{
size_t slew_index = slew_axis_->findAxisIndex(slew);
size_t cap_index = cap_axis_->findAxisIndex(cap);
@ -1845,33 +1945,109 @@ OutputWaveforms::voltageTime(float slew,
double x2u = cap_axis_->axisValue(index2 + 1);
double dx2 = (x2 - x2l) / (x2u - x2l);
double y00 = voltageTime1(volt, wave_index00);
double y01 = voltageTime1(volt, wave_index01);
double y10 = voltageTime1(volt, wave_index10);
double y11 = voltageTime1(volt, wave_index11);
double time
= (1 - dx1) * (1 - dx2) * y00
+ dx1 * (1 - dx2) * y10
+ dx1 * dx2 * y11
+ (1 - dx1) * dx2 * y01;
double time = voltageTime1(volt, dx1, dx2, wave_index00, wave_index01,
wave_index10, wave_index11);
return time;
}
float
OutputWaveforms::voltageTime1(float voltage,
size_t wave_index)
OutputWaveforms::beginTime(float slew,
float cap)
{
FloatSeq *voltage_times = voltage_times_[wave_index];
float volt_step = vdd_ / voltage_waveform_step_count_;
size_t volt_idx = voltage / volt_step;
if (volt_idx >= voltage_times->size() - 1)
return (*voltage_times)[voltage_times->size() - 1];
else {
double time0 = (*voltage_times)[volt_idx];
double time1 = (*voltage_times)[volt_idx + 1];
double time = time0 + (time1 - time0) * (voltage - volt_step * volt_idx);
return time;
return beginEndTime(slew, cap, true);
}
float
OutputWaveforms::endTime(float slew,
float cap)
{
return beginEndTime(slew, cap, false);
}
float
OutputWaveforms::beginEndTime(float slew,
float cap,
bool begin)
{
size_t slew_index = slew_axis_->findAxisIndex(slew);
size_t cap_index = cap_axis_->findAxisIndex(cap);
size_t cap_count = cap_axis_->size();
size_t wave_index00 = slew_index * cap_count + cap_index;
size_t wave_index01 = slew_index * cap_count + (cap_index + 1);
size_t wave_index10 = (slew_index + 1) * cap_count + cap_index;
size_t wave_index11 = (slew_index + 1) * cap_count + (cap_index + 1);
const Table1 *waveform00 = current_waveforms_[wave_index00];
const Table1 *waveform01 = current_waveforms_[wave_index01];
const Table1 *waveform10 = current_waveforms_[wave_index10];
const Table1 *waveform11 = current_waveforms_[wave_index11];
// Interpolate waveform samples at voltage steps.
size_t index1 = slew_index;
size_t index2 = cap_index;
float x1 = slew;
float x2 = cap;
float x1l = slew_axis_->axisValue(index1);
float x1u = slew_axis_->axisValue(index1 + 1);
float dx1 = (x1 - x1l) / (x1u - x1l);
float x2l = cap_axis_->axisValue(index2);
float x2u = cap_axis_->axisValue(index2 + 1);
float dx2 = (x2 - x2l) / (x2u - x2l);
float y00, y01, y10, y11;
if (begin) {
y00 = waveform00->axis1()->min();
y01 = waveform01->axis1()->min();
y10 = waveform10->axis1()->min();
y11 = waveform11->axis1()->min();
}
else {
y00 = waveform00->axis1()->max();
y01 = waveform01->axis1()->max();
y10 = waveform10->axis1()->max();
y11 = waveform11->axis1()->max();
}
float wave_value
= (1 - dx1) * (1 - dx2) * y00
+ dx1 * (1 - dx2) * y10
+ dx1 * dx2 * y11
+ (1 - dx1) * dx2 * y01;
return wave_value;
}
Table1
OutputWaveforms::voltageCurrentWaveform(float slew,
float cap)
{
FloatSeq *volts = new FloatSeq;
FloatSeq *currents = new FloatSeq;
for (size_t i = 0; i < voltage_waveform_step_count_; i++) {
float volt = i * vdd_ / voltage_waveform_step_count_;
float current = voltageCurrent(slew, cap, volt);
volts->push_back(volt);
currents->push_back(current);
}
TableAxisPtr volt_axis =
make_shared<TableAxis>(TableAxisVariable::input_voltage, volts);
return Table1(currents, volt_axis);
}
// Incremental resistance at final value of waveform.
// This corresponds to the pulldown/pullup that holds the output to the rail
// after the waveform has transitioned to the final value.
float
OutputWaveforms::finalResistance()
{
size_t slew_index = 0;
size_t cap_count = cap_axis_->size();
size_t cap_index = cap_count - 1;
size_t wave_index = slew_index * cap_count + cap_index;
const Table1 *voltage_currents = voltage_currents_[wave_index];
FloatSeq *voltages = voltage_currents->axis1()->values();
FloatSeq *currents = voltage_currents->values();
size_t idx_last1 = voltages->size() - 2;
return (vdd_ - (*voltages)[idx_last1]) / abs((*currents)[idx_last1]);
}
////////////////////////////////////////////////////////////////

View File

@ -21,6 +21,8 @@
#include "TimingRole.hh"
#include "Liberty.hh"
#include "TimingArc.hh"
#include "DcalcAnalysisPt.hh"
#include "TableModel.hh"
namespace sta {
@ -316,13 +318,13 @@ TimingArcSet::sense() const
return attrs_->timingSense();
}
RiseFall *
const RiseFall *
TimingArcSet::isRisingFallingEdge() const
{
int arc_count = arcs_.size();
if (arc_count == 2) {
RiseFall *from_rf1 = arcs_[0]->fromEdge()->asRiseFall();
RiseFall *from_rf2 = arcs_[1]->fromEdge()->asRiseFall();
const RiseFall *from_rf1 = arcs_[0]->fromEdge()->asRiseFall();
const RiseFall *from_rf2 = arcs_[1]->fromEdge()->asRiseFall();
if (from_rf1 == from_rf2)
return from_rf1;
}
@ -545,18 +547,42 @@ TimingArc::~TimingArc()
delete scaled_models_;
}
TimingModel *
TimingArc::model(const OperatingConditions *op_cond) const
GateTimingModel *
TimingArc::gateModel(const DcalcAnalysisPt *dcalc_ap) const
{
if (scaled_models_) {
TimingModel *model = scaled_models_->findKey(op_cond);
if (model)
return model;
else
return model_;
return dynamic_cast<GateTimingModel*>(model(dcalc_ap));
}
GateTableModel *
TimingArc::gateTableModel() const
{
return dynamic_cast<GateTableModel*>(model_);
}
GateTableModel *
TimingArc::gateTableModel(const DcalcAnalysisPt *dcalc_ap) const
{
return dynamic_cast<GateTableModel*>(model(dcalc_ap));
}
CheckTimingModel *
TimingArc::checkModel(const DcalcAnalysisPt *dcalc_ap) const
{
return dynamic_cast<CheckTimingModel*>(model(dcalc_ap));
}
TimingModel *
TimingArc::model(const DcalcAnalysisPt *dcalc_ap) const
{
const TimingArc *corner_arc = cornerArc(dcalc_ap->libertyIndex());
ScaledTimingModelMap *scaled_models = corner_arc->scaled_models_;
if (scaled_models) {
const OperatingConditions *op_cond = dcalc_ap->operatingConditions();
TimingModel *scaled_model = scaled_models->findKey(op_cond);
if (scaled_model)
return scaled_model;
}
else
return model_;
return corner_arc->model();
}
void

View File

@ -320,8 +320,9 @@ public:
int from() const { return from_; }
int to() const { return to_; }
ConcretePortSeq &members() { return members_; }
const ConcretePortSeq &members() const { return members_; }
void setDirection(PortDirection *direction);
PortDirection *direction() { return direction_; }
PortDirection *direction() const { return direction_; }
private:
int from_;
@ -385,9 +386,7 @@ ConcreteCell::groupBusPorts(const char bus_brkt_left,
}
// Make the bus ports.
for (auto name_bus : bus_map) {
string bus_name = name_bus.first;
BusPort &bus_port = name_bus.second;
for (const auto& [bus_name, bus_port] : bus_map) {
int from = bus_port.from();
int to = bus_port.to();
size_t size = to - from + 1;

View File

@ -246,10 +246,8 @@ ConcretePiElmore::unannotatedLoads(const Pin *drvr_pin,
const Parasitics *parasitics) const
{
PinSet loads = parasitics->loads(drvr_pin);
for (auto pin_elmore : loads_) {
const Pin *load = pin_elmore.first;
for (const auto [load, elmore] : loads_)
loads.erase(load);
}
return loads;
}
@ -371,10 +369,8 @@ ConcretePiPoleResidue::unannotatedLoads(const Pin *drvr_pin,
const Parasitics *parasitics) const
{
PinSet loads = parasitics->loads(drvr_pin);
for (auto pin_pole_residue : load_pole_residue_) {
const Pin *load = pin_pole_residue.first;
for (const auto& [load, pole_residue] : load_pole_residue_)
loads.erase(load);
}
return loads;
}
@ -395,6 +391,7 @@ ConcreteParasiticNode::ConcreteParasiticNode(const Pin *pin,
bool is_external) :
is_net_(false),
is_external_(is_external),
id_(0),
cap_(0.0)
{
net_pin_.pin_ = pin;
@ -506,14 +503,10 @@ ConcreteParasiticNetwork::~ConcreteParasiticNetwork()
void
ConcreteParasiticNetwork::deleteNodes()
{
for (auto id_node : sub_nodes_) {
ConcreteParasiticNode *node = id_node.second;
for (const auto& [id, node] : sub_nodes_)
delete node;
}
for (auto pin_node : pin_nodes_) {
ConcreteParasiticNode *node = pin_node.second;
for (const auto& [pin, node] : pin_nodes_)
delete node;
}
}
void
@ -547,14 +540,10 @@ ParasiticNodeSeq
ConcreteParasiticNetwork::nodes() const
{
ParasiticNodeSeq nodes;
for (auto pin_node : pin_nodes_) {
ParasiticNode *node = pin_node.second;
for (const auto [pin, node] : pin_nodes_)
nodes.push_back(node);
}
for (auto id_node : sub_nodes_) {
ParasiticNode *node = id_node.second;
for (const auto& [id, node] : sub_nodes_)
nodes.push_back(node);
}
return nodes;
}
@ -562,14 +551,12 @@ float
ConcreteParasiticNetwork::capacitance() const
{
float cap = 0.0;
for (auto id_node : sub_nodes_) {
ConcreteParasiticNode *node = id_node.second;
for (const auto& [id, node] : sub_nodes_) {
if (!node->isExternal())
cap += node->capacitance();
}
for (auto pin_node : pin_nodes_) {
ConcreteParasiticNode *node = pin_node.second;
for (const auto [pin, node] : pin_nodes_) {
if (!node->isExternal())
cap += node->capacitance();
}
@ -617,7 +604,8 @@ ConcreteParasiticNetwork::ensureParasiticNode(const Net *net,
if (id_node == sub_nodes_.end()) {
node = new ConcreteParasiticNode(net, id, net != net_);
sub_nodes_[net_id] = node;
max_node_id_ = max((int) max_node_id_, id);
if (net == net_)
max_node_id_ = max((int) max_node_id_, id);
}
else
node = id_node->second;
@ -790,8 +778,7 @@ ConcreteParasitics::deleteParasitics()
{
int ap_count = corners_->parasiticAnalysisPtCount();
int ap_rf_count = ap_count * RiseFall::index_count;
for (auto drvr_parasitics : drvr_parasitic_map_) {
ConcreteParasitic **parasitics = drvr_parasitics.second;
for (const auto [drvr, parasitics] : drvr_parasitic_map_) {
if (parasitics) {
for (int i = 0; i < ap_rf_count; i++)
delete parasitics[i];
@ -800,8 +787,7 @@ ConcreteParasitics::deleteParasitics()
}
drvr_parasitic_map_.clear();
for (auto net_parasitics : parasitic_network_map_) {
ConcreteParasiticNetwork **parasitics = net_parasitics.second;
for (const auto [net, parasitics] : parasitic_network_map_) {
if (parasitics) {
for (int i = 0; i < ap_count; i++)
delete parasitics[i];
@ -817,8 +803,8 @@ ConcreteParasitics::deleteParasitics(const Pin *drvr_pin,
{
ConcreteParasitic **parasitics = drvr_parasitic_map_[drvr_pin];
if (parasitics) {
for (auto tr : RiseFall::range()) {
int ap_rf_index = parasiticAnalysisPtIndex(ap, tr);
for (auto rf : RiseFall::range()) {
int ap_rf_index = parasiticAnalysisPtIndex(ap, rf);
delete parasitics[ap_rf_index];
parasitics[ap_rf_index] = nullptr;
}
@ -1247,8 +1233,10 @@ ConcreteParasitics::makeParasiticNetwork(const Net *net,
ConcreteParasiticNetwork *parasitic = parasitics[ap_index];
if (parasitic) {
delete parasitic;
for (const Pin *drvr_pin : *network_->drivers(net))
deleteParasitics(drvr_pin, ap);
if (net) {
for (const Pin *drvr_pin : *network_->drivers(net))
deleteParasitics(drvr_pin, ap);
}
}
parasitic = new ConcreteParasiticNetwork(net, includes_pin_caps, network_);
parasitics[ap_index] = parasitic;
@ -1446,6 +1434,14 @@ ConcreteParasitics::net(const ParasiticNode *node,
return cnode->net(network);
}
unsigned
ConcreteParasitics::netId(const ParasiticNode *node) const
{
const ConcreteParasiticNode *cnode =
static_cast<const ConcreteParasiticNode*>(node);
return cnode->id();
}
bool
ConcreteParasitics::isExternal(const ParasiticNode *node) const
{

View File

@ -133,6 +133,7 @@ public:
const Pin *pin(const ParasiticNode *node) const override;
const Net *net(const ParasiticNode *node,
const Network *network) const override;
unsigned netId(const ParasiticNode *node) const override;
bool isExternal(const ParasiticNode *node) const override;
float nodeGndCap(const ParasiticNode *node) const override;

View File

@ -43,7 +43,7 @@ typedef std::map<const Pin*, float> ConcreteElmoreLoadMap;
typedef std::map<const Pin*, ConcretePoleResidue> ConcretePoleResidueMap;
typedef std::map<NetIdPair,ConcreteParasiticNode*,
NetIdPairLess> ConcreteParasiticSubNodeMap;
typedef std::map<const Pin*, ConcreteParasiticNode*, PinIdLess> ConcreteParasiticPinNodeMap;
typedef std::map<const Pin*,ConcreteParasiticNode*,PinIdLess> ConcreteParasiticPinNodeMap;
typedef std::set<ParasiticNode*> ParasiticNodeSet;
typedef std::set<ParasiticResistor*> ParasiticResistorSet;
typedef std::vector<ParasiticResistor*> ParasiticResistorSeq;
@ -262,6 +262,7 @@ public:
float capacitance() const { return cap_; }
const char *name(const Network *network) const;
const Net *net(const Network *network) const;
unsigned id() const { return id_; }
bool isExternal() const { return is_external_; }
const Pin *pin() const;
void incrCapacitance(float cap);

View File

@ -18,6 +18,7 @@
#include "Wireload.hh"
#include "Liberty.hh"
#include "PortDirection.hh"
#include "Network.hh"
#include "Sdc.hh"
#include "Parasitics.hh"
@ -147,7 +148,7 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin,
bool &elmore_use_load_cap)
{
if (wireload_res == 0.0
|| fanout == 0) {
|| fanout == 0.0) {
// No resistance, so load is capacitance only.
c2 = wireload_cap + net_pin_cap;
rpi = 0.0;
@ -168,22 +169,19 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin,
network_->connectedPinIterator(drvr_pin);
while (load_iter->hasNext()) {
const Pin *load_pin = load_iter->next();
Port *port = network_->port(load_pin);
double cap = 0.0;
// Bidirects don't count themselves as loads.
if (load_pin != drvr_pin && network_->isLoad(load_pin)) {
Port *port = network_->port(load_pin);
double load_cap = 0.0;
if (network_->isLeaf(load_pin))
load_cap = sdc_->pinCapacitance(load_pin, rf, corner, min_max);
else if (network_->isTopLevelPort(load_pin))
load_cap = sdc_->portExtCap(port, rf, corner, min_max);
else
report_->critical(1050, "load pin not leaf or top level");
double cap = load_cap + cap_fanout;
double y2_ = res_fanout * cap * cap;
y1 += cap;
y2 += -y2_;
y3 += y2_ * res_fanout * cap;
}
if (load_pin == drvr_pin)
cap = sdc_->portExtCap(port, rf, corner, min_max);
else if (network_->isLeaf(load_pin))
cap = sdc_->pinCapacitance(load_pin, rf, corner, min_max) + cap_fanout;
else if (network_->isTopLevelPort(load_pin))
cap = sdc_->portExtCap(port, rf, corner, min_max) + cap_fanout;
double y2_ = res_fanout * cap * cap;
y1 += cap;
y2 += -y2_;
y3 += y2_ * res_fanout * cap;
}
delete load_iter;
@ -196,6 +194,8 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin,
else {
c1 = static_cast<float>(y2 * y2 / y3);
c2 = static_cast<float>(y1 - y2 * y2 / y3);
if (c2 < 0.0)
c2 = 0.0;
rpi = static_cast<float>(-y3 * y3 / (y2 * y2 * y2));
}
elmore_res = static_cast<float>(res_fanout);

View File

@ -203,29 +203,32 @@ Parasitics::makeWireloadNetwork(const Pin *drvr_pin,
const MinMax *min_max,
const ParasiticAnalysisPt *ap)
{
Parasitic *parasitic = nullptr;
const Net *net = findParasiticNet(drvr_pin);
Parasitic *parasitic = makeParasiticNetwork(net, false, ap);
const OperatingConditions *op_cond = sdc_->operatingConditions(min_max);
float wireload_cap, wireload_res;
wireload->findWireload(fanout, op_cond, wireload_cap, wireload_res);
if (net) {
parasitic = makeParasiticNetwork(net, false, ap);
const OperatingConditions *op_cond = sdc_->operatingConditions(min_max);
float wireload_cap, wireload_res;
wireload->findWireload(fanout, op_cond, wireload_cap, wireload_res);
WireloadTree tree = WireloadTree::balanced;
if (op_cond)
tree = op_cond->wireloadTree();
switch (tree) {
case WireloadTree::worst_case:
makeWireloadNetworkWorst(parasitic, drvr_pin, net, wireload_cap,
wireload_res, fanout);
break;
case WireloadTree::balanced:
makeWireloadNetworkBalanced(parasitic, drvr_pin, wireload_cap,
wireload_res, fanout);
break;
case WireloadTree::best_case:
case WireloadTree::unknown:
makeWireloadNetworkBest(parasitic, drvr_pin, wireload_cap,
wireload_res, fanout);
break;
WireloadTree tree = WireloadTree::balanced;
if (op_cond)
tree = op_cond->wireloadTree();
switch (tree) {
case WireloadTree::worst_case:
makeWireloadNetworkWorst(parasitic, drvr_pin, net, wireload_cap,
wireload_res, fanout);
break;
case WireloadTree::balanced:
makeWireloadNetworkBalanced(parasitic, drvr_pin, wireload_cap,
wireload_res, fanout);
break;
case WireloadTree::best_case:
case WireloadTree::unknown:
makeWireloadNetworkBest(parasitic, drvr_pin, wireload_cap,
wireload_res, fanout);
break;
}
}
return parasitic;
}
@ -324,4 +327,38 @@ ParasiticAnalysisPt::setCouplingCapFactor(float factor)
coupling_cap_factor_ = factor;
}
////////////////////////////////////////////////////////////////
ParasiticNodeLess::ParasiticNodeLess(const Parasitics *parasitics,
const Network *network) :
parasitics_(parasitics),
network_(network)
{
}
ParasiticNodeLess::ParasiticNodeLess(const ParasiticNodeLess &less) :
parasitics_(less.parasitics_),
network_(less.network_)
{
}
bool
ParasiticNodeLess::operator()(const ParasiticNode *node1,
const ParasiticNode *node2) const
{
const Pin *pin1 = parasitics_->pin(node1);
const Pin *pin2 = parasitics_->pin(node2);
const Net *net1 = parasitics_->net(node1, network_);
const Net *net2 = parasitics_->net(node2, network_);
unsigned id1 = parasitics_->netId(node1);
unsigned id2 = parasitics_->netId(node2);
return (pin1 == nullptr && pin2)
|| (pin1 && pin2
&& network_->id(pin1) < network_->id(pin2))
|| (pin1 == nullptr && pin2 == nullptr
&& (network_->id(net1) < network_->id(net2)
|| (net1 == net2
&& id1 < id2)));
}
} // namespace

View File

@ -37,16 +37,20 @@ proc_redirect read_spef {
set reduce [info exists flags(-reduce)]
if { [info exists flags(-quiet)] } {
# deprecated 2024-02-08
sta_warn 272 "read_spef -quiet is deprecated."
}
if { [info exists keys(-reduce_to)] } {
# deprecated 2024-02-08
sta_warn 273 "read_spef -reduce_to is deprecated. Use -reduce instead."
set reduce 1
}
if { [info exists flags(-delete_after_reduce)] } {
# deprecated 2024-02-08
sta_warn 274 "read_spef -delete_after_reduce is deprecated."
}
if { [info exists flags(-save)] } {
# deprecated 2024-02-08
sta_warn 275 "read_spef -save is deprecated."
}

View File

@ -183,7 +183,7 @@ ReduceToPi::reducePiDfs(const Pin *drvr_pin,
&& resistor != from_res) {
if (isVisited(onode)) {
// Resistor loop.
debugPrint(debug_, "parasitic_reduce", 2, " loop detected thru resistor %lu",
debugPrint(debug_, "parasitic_reduce", 2, " loop detected thru resistor %zu",
parasitics_->id(resistor));
markLoopResistor(resistor);
}

View File

@ -80,14 +80,14 @@ ReportParasiticAnnotation::report()
void
ReportParasiticAnnotation::reportAnnotationCounts()
{
report_->reportLine("Found %lu unannotated drivers.", unannotated_.size());
report_->reportLine("Found %zu unannotated drivers.", unannotated_.size());
if (report_unannotated_) {
sort(unannotated_, PinPathNameLess(network_));
for (const Pin *drvr_pin : unannotated_)
report_->reportLine(" %s", network_->pathName(drvr_pin));
}
report_->reportLine("Found %lu partially unannotated drivers.",
report_->reportLine("Found %zu partially unannotated drivers.",
partially_annotated_.size());
if (report_unannotated_) {
sort(partially_annotated_, PinPathNameLess(network_));

View File

@ -120,10 +120,8 @@ SpefReader::~SpefReader()
design_flow_ = nullptr;
}
for (auto index_name : name_map_) {
char *name = index_name.second;
for (const auto [index, name] : name_map_)
stringDelete(name);
}
}
void
@ -496,7 +494,9 @@ SpefReader::findParasiticNode(char *name,
int id = atoi(id_str);
if (local_only
&& !network_->isConnected(net, net_))
warn(1653, "%s not connected to net %s.", name, network_->pathName(net_));
warn(1653, "%s not connected to net %s.",
name,
network_->pathName(net_));
return parasitics_->ensureParasiticNode(parasitic_, net, id, network_);
}
else

View File

@ -586,12 +586,10 @@ Power::evalBddActivity(DdNode *bdd,
const Instance *inst)
{
float activity = 0.0;
for (auto port_var : bdd_.portVarMap()) {
const LibertyPort *port = port_var.first;
for (const auto [port, var_node] : bdd_.portVarMap()) {
const Pin *pin = findLinkPin(inst, port);
if (pin) {
PwrActivity var_activity = findActivity(pin);
DdNode *var_node = port_var.second;
unsigned int var_index = Cudd_NodeReadIndex(var_node);
DdNode *diff = Cudd_bddBooleanDiff(bdd_.cuddMgr(), bdd, var_index);
Cudd_Ref(diff);

View File

@ -105,7 +105,7 @@ ReadVcdActivities::readActivities()
setActivities();
else
report_->warn(1450, "VCD max time is zero.");
report_->reportLine("Annotated %lu pin activities.", annotated_pins_.size());
report_->reportLine("Annotated %zu pin activities.", annotated_pins_.size());
}
void

View File

@ -41,8 +41,8 @@ DeratingFactors::setFactor(PathClkOrData clk_data,
const EarlyLate *early_late,
float factor)
{
for (auto tr1 : rf->range())
factors_[int(clk_data)].setValue(tr1, early_late, factor);
for (auto rf1 : rf->range())
factors_[int(clk_data)].setValue(rf1, early_late, factor);
}
void

View File

@ -20,17 +20,17 @@ namespace sta {
InputDrive::InputDrive()
{
for (auto tr_index : RiseFall::rangeIndex()) {
for (auto rf_index : RiseFall::rangeIndex()) {
for (auto mm_index : MinMax::rangeIndex())
drive_cells_[tr_index][mm_index] = nullptr;
drive_cells_[rf_index][mm_index] = nullptr;
}
}
InputDrive::~InputDrive()
{
for (auto tr_index : RiseFall::rangeIndex()) {
for (auto rf_index : RiseFall::rangeIndex()) {
for (auto mm_index : MinMax::rangeIndex()) {
InputDriveCell *drive_cell = drive_cells_[tr_index][mm_index];
InputDriveCell *drive_cell = drive_cells_[rf_index][mm_index];
delete drive_cell;
}
}
@ -210,8 +210,8 @@ InputDriveCell::setToPort(const LibertyPort *to_port)
void
InputDriveCell::setFromSlews(float *from_slews)
{
for (auto tr_index : RiseFall::rangeIndex())
from_slews_[tr_index] = from_slews[tr_index];
for (auto rf_index : RiseFall::rangeIndex())
from_slews_[rf_index] = from_slews[rf_index];
}
bool

View File

@ -271,15 +271,12 @@ Sdc::deleteConstraints()
inst_min_pulse_width_map_.deleteContentsClear();
clk_min_pulse_width_map_.deleteContentsClear();
for (auto pin_data_check : data_checks_from_map_) {
DataCheckSet *checks = pin_data_check.second;
for (auto [pin, checks] : data_checks_from_map_) {
checks->deleteContents();
delete checks;
}
for (auto pin_data_check : data_checks_to_map_) {
DataCheckSet *checks = pin_data_check.second;
for (auto [pin, checks] : data_checks_to_map_)
delete checks;
}
input_delays_.deleteContents();
input_delay_pin_map_.deleteContents();
@ -323,9 +320,7 @@ Sdc::removeNetLoadCaps()
void
Sdc::removeLibertyAnnotations()
{
for (auto cell_port : disabled_cell_ports_) {
DisabledCellPorts *disable = cell_port.second;
LibertyCell *cell = disable->cell();
for (auto [cell, disable] : disabled_cell_ports_) {
if (disable->all())
cell->setIsDisabledConstraint(false);
@ -1957,8 +1952,8 @@ void
Sdc::ensureClkGroupExclusions()
{
if (clk_group_exclusions_.empty()) {
for (auto name_clk_groups : clk_groups_name_map_)
makeClkGroupExclusions(name_clk_groups.second);
for (const auto [name, clk_groups] : clk_groups_name_map_)
makeClkGroupExclusions(clk_groups);
}
}
@ -2071,8 +2066,7 @@ Sdc::removeClockGroupsLogicallyExclusive(const char *name)
removeClockGroups(groups);
}
else {
for (auto name_group : clk_groups_name_map_) {
ClockGroups *groups = name_group.second;
for (const auto [name, groups] : clk_groups_name_map_) {
if (groups->logicallyExclusive())
removeClockGroups(groups);
}
@ -2088,8 +2082,7 @@ Sdc::removeClockGroupsPhysicallyExclusive(const char *name)
removeClockGroups(groups);
}
else {
for (auto name_group : clk_groups_name_map_) {
ClockGroups *groups = name_group.second;
for (const auto [name, groups] : clk_groups_name_map_) {
if (groups->physicallyExclusive())
removeClockGroups(groups);
}
@ -2105,8 +2098,7 @@ Sdc::removeClockGroupsAsynchronous(const char *name)
removeClockGroups(groups);
}
else {
for (auto name_group : clk_groups_name_map_) {
ClockGroups *groups = name_group.second;
for (const auto [name, groups] : clk_groups_name_map_) {
if (groups->asynchronous())
removeClockGroups(groups);
}
@ -2126,10 +2118,8 @@ Sdc::removeClockGroups(ClockGroups *groups)
void
Sdc::clockGroupsDeleteClkRefs(Clock *clk)
{
for (auto name_group : clk_groups_name_map_) {
ClockGroups *groups = name_group.second;
for (const auto [name, groups] : clk_groups_name_map_)
groups->removeClock(clk);
}
clearClkGroupExclusions();
}
@ -4034,9 +4024,7 @@ Sdc::clearGroupPathMap()
{
// GroupPath exceptions are deleted with other exceptions.
// Delete group_path name strings.
for (auto name_groups : group_path_map_) {
const char *name = name_groups.first;
GroupPathSet *groups = name_groups.second;
for (auto [name, groups] : group_path_map_) {
stringDelete(name);
groups->deleteContents();
delete groups;

View File

@ -382,7 +382,7 @@ WriteSdc::writeClocks() const
{
// Write clocks in the order they were defined because generated
// clocks depend on master clocks having been previously defined.
for (auto clk : sdc_->clocks_) {
for (const auto clk : sdc_->clocks_) {
if (clk->isGenerated())
writeGeneratedClock(clk);
else
@ -852,8 +852,8 @@ void
WriteSdc::writeClockSenses() const
{
Vector<PinClockPair> pin_clks;
for (auto iter : sdc_->clk_sense_map_)
pin_clks.push_back(iter.first);
for (const auto& [pin_clk, sense] : sdc_->clk_sense_map_)
pin_clks.push_back(pin_clk);
// Sort by pin/clk pair so regressions results are stable.
sort(pin_clks, PinClockPairNameLess(sdc_network_));
@ -937,10 +937,8 @@ ClockGroupLess::operator()(const ClockGroup *clk_group1,
void
WriteSdc::writeClockGroups() const
{
for (auto &name_groups : sdc_->clk_groups_name_map_) {
ClockGroups *clk_groups = name_groups.second;
for (const auto [name, clk_groups] : sdc_->clk_groups_name_map_)
writeClockGroups(clk_groups);
}
}
void
@ -1408,8 +1406,7 @@ void
WriteSdc::writeDataChecks() const
{
Vector<DataCheck*> checks;
for (auto pin_checks : sdc_->data_checks_to_map_) {
DataCheckSet *checks1 = pin_checks.second;
for (const auto [pin, checks1] : sdc_->data_checks_to_map_) {
for (DataCheck *check : *checks1)
checks.push_back(check);
}
@ -1512,9 +1509,7 @@ void
WriteSdc::writeNetLoads() const
{
int corner_index = 0; // missing corner arg
for (auto net_cap : sdc_->net_wire_cap_maps_[corner_index]) {
const Net *net = net_cap.first;
MinMaxFloatValues &caps = net_cap.second;
for (const auto [net, caps] : sdc_->net_wire_cap_maps_[corner_index]) {
float min_cap, max_cap;
bool min_exists, max_exists;
caps.value(MinMax::min(), min_cap, min_exists);
@ -1725,10 +1720,8 @@ void
WriteSdc::writeNetResistances() const
{
NetSeq nets;
for (auto net_res : sdc_->netResistances()) {
const Net *net = net_res.first;
for (const auto [net, res] : sdc_->netResistances())
nets.push_back(net);
}
sort(nets, NetPathNameLess(sdc_network_));
for (const Net *net : nets) {
float min_res, max_res;
@ -1843,10 +1836,8 @@ void
WriteSdc::sortedLogicValuePins(LogicValueMap &value_map,
PinSeq &pins) const
{
for (auto pin_value : value_map) {
const Pin *pin = pin_value.first;
for (const auto [pin, value] : value_map)
pins.push_back(pin);
}
// Sort pins.
sort(pins, PinPathNameLess(sdc_network_));
}
@ -1860,9 +1851,7 @@ WriteSdc::writeDeratings() const
if (factors)
writeDerating(factors);
for (auto net_derating : sdc_->net_derating_factors_) {
const Net *net = net_derating.first;
DeratingFactorsNet *factors = net_derating.second;
for (const auto [net, factors] : sdc_->net_derating_factors_) {
WriteGetNet write_net(net, this);
for (auto early_late : EarlyLate::range()) {
writeDerating(factors, TimingDerateType::net_delay, early_late,
@ -1870,16 +1859,12 @@ WriteSdc::writeDeratings() const
}
}
for (auto inst_derating : sdc_->inst_derating_factors_) {
const Instance *inst = inst_derating.first;
DeratingFactorsCell *factors = inst_derating.second;
for (const auto [inst, factors] : sdc_->inst_derating_factors_) {
WriteGetInstance write_inst(inst, this);
writeDerating(factors, &write_inst);
}
for (auto cell_derating : sdc_->cell_derating_factors_) {
const LibertyCell *cell = cell_derating.first;
DeratingFactorsCell *factors = cell_derating.second;
for (const auto [cell, factors] : sdc_->cell_derating_factors_) {
WriteGetLibCell write_cell(cell, this);
writeDerating(factors, &write_cell);
}
@ -2029,12 +2014,10 @@ WriteSdc::writeVoltages() const
gzprintf(stream_, "set_voltage %.3f\n", voltage_max);
}
for (auto net_volt : sdc_->net_voltage_map_) {
const Net *net = net_volt.first;
MinMaxFloatValues &values = net_volt.second;
values.value(MinMax::max(), voltage_max, exists_max);
for (const auto& [net, volts] : sdc_->net_voltage_map_) {
volts.value(MinMax::max(), voltage_max, exists_max);
if (exists_max) {
values.value(MinMax::min(), voltage_min, exists_min);
volts.value(MinMax::min(), voltage_min, exists_min);
if (exists_min)
gzprintf(stream_, "set_voltage -object_list %s -min %.3f %.3f\n",
sdc_network_->pathName(net),
@ -2065,23 +2048,17 @@ WriteSdc::writeDesignRules() const
void
WriteSdc::writeMinPulseWidths() const
{
for (auto pin_widths : sdc_->pin_min_pulse_width_map_) {
const Pin *pin = pin_widths.first;
RiseFallValues *min_widths = pin_widths.second;
for (const auto [pin, min_widths] : sdc_->pin_min_pulse_width_map_) {
WriteGetPin write_obj(pin, false, this);
writeMinPulseWidths(min_widths, write_obj);
}
for (auto inst_widths : sdc_->inst_min_pulse_width_map_) {
const Instance *inst = inst_widths.first;
RiseFallValues *min_widths = inst_widths.second;
for (const auto [inst, min_widths] : sdc_->inst_min_pulse_width_map_) {
WriteGetInstance write_obj(inst, this);
writeMinPulseWidths(min_widths, write_obj);
}
for (auto clk_widths : sdc_->clk_min_pulse_width_map_) {
const Clock *clk = clk_widths.first;
RiseFallValues *min_widths = clk_widths.second;
for (const auto [clk, min_widths] : sdc_->clk_min_pulse_width_map_) {
WriteGetClock write_obj(clk, this);
writeMinPulseWidths(min_widths, write_obj);
}
@ -2123,9 +2100,7 @@ WriteSdc::writeMinPulseWidth(const char *hi_low,
void
WriteSdc::writeLatchBorowLimits() const
{
for (auto pin_borrow : sdc_->pin_latch_borrow_limit_map_) {
const Pin *pin = pin_borrow.first;
float limit = pin_borrow.second;
for (const auto [pin, limit] : sdc_->pin_latch_borrow_limit_map_) {
gzprintf(stream_, "set_max_time_borrow ");
writeTime(limit);
gzprintf(stream_, " ");
@ -2133,9 +2108,7 @@ WriteSdc::writeLatchBorowLimits() const
gzprintf(stream_, "\n");
}
for (auto inst_borrow : sdc_->inst_latch_borrow_limit_map_) {
const Instance *inst = inst_borrow.first;
float limit = inst_borrow.second;
for (const auto [inst, limit] : sdc_->inst_latch_borrow_limit_map_) {
gzprintf(stream_, "set_max_time_borrow ");
writeTime(limit);
gzprintf(stream_, " ");
@ -2143,9 +2116,7 @@ WriteSdc::writeLatchBorowLimits() const
gzprintf(stream_, "\n");
}
for (auto clk_borrow : sdc_->clk_latch_borrow_limit_map_) {
const Clock *clk = clk_borrow.first;
float limit = clk_borrow.second;
for (const auto [clk, limit] : sdc_->clk_latch_borrow_limit_map_) {
gzprintf(stream_, "set_max_time_borrow ");
writeTime(limit);
gzprintf(stream_, " ");
@ -2266,12 +2237,10 @@ WriteSdc::writeCapLimits(const MinMax *min_max,
gzprintf(stream_, " [current_design]\n");
}
for (auto port_limit : sdc_->port_cap_limit_map_) {
const Port *port = port_limit.first;
MinMaxFloatValues values = port_limit.second;
for (const auto [port, limits] : sdc_->port_cap_limit_map_) {
float cap;
bool exists;
values.value(min_max, cap, exists);
limits.value(min_max, cap, exists);
if (exists) {
gzprintf(stream_, "%s ", cmd);
writeCapacitance(cap);
@ -2281,12 +2250,10 @@ WriteSdc::writeCapLimits(const MinMax *min_max,
}
}
for (auto pin_limit : sdc_->pin_cap_limit_map_) {
const Pin *pin = pin_limit.first;
MinMaxFloatValues values = pin_limit.second;
for (const auto [pin, limits] : sdc_->pin_cap_limit_map_) {
float cap;
bool exists;
values.value(min_max, cap, exists);
limits.value(min_max, cap, exists);
if (exists) {
gzprintf(stream_, "%s ", cmd);
writeCapacitance(cap);

View File

@ -45,7 +45,7 @@ public:
SdfWriter(StaState *sta);
~SdfWriter();
void write(const char *filename,
Corner *corner,
const Corner *corner,
char sdf_divider,
bool include_typ,
int digits,
@ -122,7 +122,7 @@ private:
void
writeSdf(const char *filename,
Corner *corner,
const Corner *corner,
char sdf_divider,
bool include_typ,
int digits,
@ -151,7 +151,7 @@ SdfWriter::~SdfWriter()
void
SdfWriter::write(const char *filename,
Corner *corner,
const Corner *corner,
char sdf_divider,
bool include_typ,
int digits,

View File

@ -23,7 +23,7 @@ class Corner;
void
writeSdf(const char *filename,
Corner *corner,
const Corner *corner,
char divider,
bool include_typ,
int digits,

View File

@ -168,10 +168,8 @@ Bdd::varIndexPort(int var_index)
void
Bdd::clearVarMap()
{
for (auto port_node : bdd_port_var_map_) {
DdNode *var_node = port_node.second;
for (const auto [port, var_node] : bdd_port_var_map_)
Cudd_RecursiveDeref(cudd_mgr_, var_node);
}
bdd_port_var_map_.clear();
bdd_var_idx_port_map_.clear();
}

View File

@ -70,7 +70,7 @@ BfsIterator::clear()
Level level = first_level_;
while (levelLessOrEqual(level, last_level_)) {
VertexSeq &level_vertices = queue_[level];
for (auto vertex : level_vertices) {
for (Vertex *vertex : level_vertices) {
if (vertex)
vertex->setBfsInQueue(bfs_index_, false);
}
@ -88,7 +88,7 @@ BfsIterator::reportEntries(const Network *network)
VertexSeq &level_vertices = queue_[level];
if (!level_vertices.empty()) {
report_->reportLine("Level %d", level);
for (auto vertex : level_vertices) {
for (Vertex *vertex : level_vertices) {
if (vertex)
report_->reportLine(" %s", vertex->name(network));
}
@ -101,7 +101,7 @@ void
BfsIterator::deleteEntries(Level level)
{
VertexSeq &level_vertices = queue_[level];
for (auto vertex : level_vertices) {
for (Vertex *vertex : level_vertices) {
if (vertex)
vertex->setBfsInQueue(bfs_index_, false);
}
@ -281,7 +281,7 @@ BfsIterator::checkInQueue(Vertex *vertex)
{
Level level = vertex->level();
if (static_cast<Level>(queue_.size()) > level) {
for (auto v : queue_[level]) {
for (Vertex *v : queue_[level]) {
if (v == vertex) {
if (vertex->bfsInQueue(bfs_index_))
return;
@ -308,7 +308,7 @@ BfsIterator::remove(Vertex *vertex)
Level level = vertex->level();
if (vertex->bfsInQueue(bfs_index_)
&& static_cast<Level>(queue_.size()) > level) {
for (auto &v : queue_[level]) {
for (Vertex *v : queue_[level]) {
if (v == vertex) {
v = nullptr;
vertex->setBfsInQueue(bfs_index_, false);

View File

@ -411,7 +411,7 @@ MinPulseWidthCheck::width(const StaState *sta) const
{
return closeArrival(sta) + closeOffset(sta)
- open_path_.arrival(sta)
+ commonClkPessimism(sta);
+ checkCrpr(sta);
}
float
@ -458,7 +458,7 @@ minPulseWidth(const Path *path,
}
Crpr
MinPulseWidthCheck::commonClkPessimism(const StaState *sta) const
MinPulseWidthCheck::checkCrpr(const StaState *sta) const
{
CheckCrpr *check_crpr = sta->search()->checkCrpr();
PathVertex close;

View File

@ -80,7 +80,7 @@ public:
float closeOffset(const StaState *sta) const;
const ClockEdge *openClkEdge(const StaState *sta) const;
const ClockEdge *closeClkEdge(const StaState *sta) const;
Crpr commonClkPessimism(const StaState *sta) const;
Crpr checkCrpr(const StaState *sta) const;
protected:
// Open path of the pulse.

View File

@ -281,8 +281,7 @@ ClkSkews::findWorstClkSkew(const Corner *corner,
clks.push_back(clk);
ClkSkewMap skews = findClkSkew(clks, corner, setup_hold, include_internal_latency);
float worst_skew = 0.0;
for (auto clk_skew_itr : skews) {
ClkSkew &clk_skew = clk_skew_itr.second;
for (const auto& [clk, clk_skew] : skews) {
float skew = clk_skew.skew();
if (abs(skew) > abs(worst_skew))
worst_skew = skew;
@ -318,9 +317,7 @@ ClkSkews::findClkSkew(ConstClockSeq &clks,
// Reduce skews from each register source.
for (size_t i = 0; i < partial_skews.size(); i++) {
for (auto clk_skew_itr : partial_skews[i]) {
const Clock *clk = clk_skew_itr.first;
auto partial_skew = clk_skew_itr.second;
for (auto& [clk, partial_skew] : partial_skews[i]) {
auto ins = skews.insert(std::make_pair(clk, partial_skew));
if (!ins.second) {
ClkSkew &final_skew = ins.first->second;

View File

@ -676,10 +676,10 @@ Genclks::seedSrcPins(Clock *gclk,
for (auto path_ap : corners_->pathAnalysisPts()) {
const MinMax *min_max = path_ap->pathMinMax();
const EarlyLate *early_late = min_max;
for (auto tr : RiseFall::range()) {
Tag *tag = makeTag(gclk, master_clk, master_pin, tr, src_filter,
for (auto rf : RiseFall::range()) {
Tag *tag = makeTag(gclk, master_clk, master_pin, rf, src_filter,
path_ap);
Arrival insert = search_->clockInsertion(master_clk, master_pin, tr,
Arrival insert = search_->clockInsertion(master_clk, master_pin, rf,
min_max, early_late, path_ap);
tag_bldr.setArrival(tag, insert, nullptr);
}

View File

@ -473,9 +473,9 @@ Latches::latchDtoQEnable(Edge *d_q_edge,
state = LatchEnableState::open;
LibertyCell *cell = network_->libertyCell(inst);
if (cell) {
TimingArcSet *d_q_set = d_q_edge->timingArcSet();
LibertyPort *enable_port;
FuncExpr *enable_func;
const TimingArcSet *d_q_set = d_q_edge->timingArcSet();
const LibertyPort *enable_port;
const FuncExpr *enable_func;
cell->latchEnable(d_q_set, enable_port, enable_func, enable_rf);
if (enable_port) {
Pin *enable_pin = network_->findPin(inst, enable_port);

View File

@ -395,9 +395,7 @@ void
MakeTimingModel::makeSetupHoldTimingArcs(const Pin *input_pin,
const ClockEdgeDelays &clk_margins)
{
for (auto clk_edge_margins : clk_margins) {
const ClockEdge *clk_edge = clk_edge_margins.first;
RiseFallMinMax &margins = clk_edge_margins.second;
for (const auto& [clk_edge, margins] : clk_margins) {
for (MinMax *min_max : MinMax::range()) {
bool setup = (min_max == MinMax::max());
TimingArcAttrsPtr attrs = nullptr;
@ -442,9 +440,7 @@ void
MakeTimingModel::makeInputOutputTimingArcs(const Pin *input_pin,
OutputPinDelays &output_pin_delays)
{
for (auto out_pin_delay : output_pin_delays) {
const Pin *output_pin = out_pin_delay.first;
OutputDelays &output_delays = out_pin_delay.second;
for (const auto& [output_pin, output_delays] : output_pin_delays) {
TimingArcAttrsPtr attrs = nullptr;
for (RiseFall *output_rf : RiseFall::range()) {
MinMax *min_max = MinMax::max();
@ -499,9 +495,7 @@ MakeTimingModel::findClkedOutputPaths()
delayAsFloat(delay, min_max, sta_));
}
}
for (auto clk_edge_delay : clk_delays) {
const ClockEdge *clk_edge = clk_edge_delay.first;
RiseFallMinMax &delays = clk_edge_delay.second;
for (const auto& [clk_edge, delays] : clk_delays) {
for (const Pin *clk_pin : clk_edge->clock()->pins()) {
LibertyPort *clk_port = modelPort(clk_pin);
if (clk_port) {
@ -659,9 +653,6 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin,
{
const DcalcAnalysisPt *dcalc_ap = corner_->findDcalcAnalysisPt(min_max_);
const Pvt *pvt = dcalc_ap->operatingConditions();
const OperatingConditions *op_cond = dcalc_ap->operatingConditions();
int lib_index = dcalc_ap->libertyIndex();
PinSet *drvrs = network_->drivers(network_->net(network_->term(output_pin)));
const Pin *drvr_pin = *drvrs->begin();
const LibertyPort *drvr_port = network_->libertyPort(drvr_pin);
@ -680,8 +671,7 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin,
drvr_arc->fromEdge()->asRiseFall(),
dcalc_ap->index());
float in_slew1 = delayAsFloat(in_slew);
TimingModel *drvr_model = drvr_arc->cornerArc(lib_index)->model(op_cond);
GateTableModel *drvr_gate_model = dynamic_cast<GateTableModel*>(drvr_model);
GateTableModel *drvr_gate_model = drvr_arc->gateTableModel(dcalc_ap);
if (drvr_gate_model) {
float output_load_cap = graph_delay_calc_->loadCap(output_pin, dcalc_ap);
ArcDelay drvr_self_delay;

View File

@ -259,7 +259,16 @@ PathEnd::borrow(const StaState *) const
}
Crpr
PathEnd::commonClkPessimism(const StaState *) const
PathEnd::checkCrpr(const StaState *sta) const
{
if (checkRole(sta)->genericRole() == TimingRole::hold())
return -crpr(sta);
else
return crpr(sta);;
}
Crpr
PathEnd::crpr(const StaState *) const
{
return 0.0;
}
@ -618,7 +627,7 @@ Arrival
PathEndClkConstrained::targetClkArrival(const StaState *sta) const
{
return targetClkArrivalNoCrpr(sta)
+ commonClkPessimism(sta);
+ checkCrpr(sta);
}
Arrival
@ -689,24 +698,21 @@ PathEndClkConstrained::targetClkUncertainty(const StaState *sta) const
}
Crpr
PathEndClkConstrained::commonClkPessimism(const StaState *sta) const
PathEndClkConstrained::crpr(const StaState *sta) const
{
if (!crpr_valid_) {
CheckCrpr *check_crpr = sta->search()->checkCrpr();
crpr_ = check_crpr->checkCrpr(path_.path(), targetClkPath());
crpr_valid_ = true;
}
if (checkRole(sta)->genericRole() == TimingRole::hold())
return -crpr_;
else
return crpr_;
return crpr_;
}
Required
PathEndClkConstrained::requiredTime(const StaState *sta) const
{
return requiredTimeNoCrpr(sta)
+ commonClkPessimism(sta);
+ checkCrpr(sta);
}
Required
@ -1014,8 +1020,7 @@ PathEndCheck::exceptPathCmp(const PathEnd *path_end,
Delay
PathEndCheck::clkSkew(const StaState *sta)
{
commonClkPessimism(sta);
return sourceClkDelay(sta) - targetClkDelay(sta) - crpr_
return sourceClkDelay(sta) - targetClkDelay(sta) - crpr(sta)
// Uncertainty decreases slack, but increases skew.
- checkTgtClkUncertainty(&clk_path_, clk_path_.clkEdge(sta), checkRole(sta), sta);
}
@ -1373,13 +1378,12 @@ PathEndOutputDelay::targetClkArrivalNoCrpr(const StaState *sta) const
}
Crpr
PathEndOutputDelay::commonClkPessimism(const StaState *sta) const
PathEndOutputDelay::crpr(const StaState *sta) const
{
if (!crpr_valid_) {
CheckCrpr *check_crpr = sta->search()->checkCrpr();
crpr_ = check_crpr->outputDelayCrpr(path_.path(), targetClkEdge(sta));
if (checkRole(sta)->genericRole() == TimingRole::hold())
crpr_ = -crpr_;
crpr_valid_ = true;
}
return crpr_;
}

View File

@ -255,8 +255,7 @@ PathGroups::makeGroups(int group_count,
{
int mm_index = min_max->index();
if (setup_hold) {
for (auto name_group : sdc_->groupPaths()) {
const char *name = name_group.first;
for (const auto [name, group] : sdc_->groupPaths()) {
if (reportGroup(name, group_names)) {
PathGroup *group = PathGroup::makePathGroupSlack(name, group_count,
endpoint_count, unique_pins,
@ -840,7 +839,7 @@ public:
virtual void visit(Vertex *vertex);
private:
VisitPathEnds *visit_path_ends_;
VisitPathEnds visit_path_ends_;
PathEndVisitor *path_end_visitor_;
const Corner *corner_;
const MinMaxAll *min_max_;
@ -851,7 +850,7 @@ MakeEndpointPathEnds::MakeEndpointPathEnds(PathEndVisitor *path_end_visitor,
const Corner *corner,
const MinMaxAll *min_max,
const StaState *sta) :
visit_path_ends_(new VisitPathEnds(sta)),
visit_path_ends_(sta),
path_end_visitor_(path_end_visitor->copy()),
corner_(corner),
min_max_(min_max),
@ -860,7 +859,7 @@ MakeEndpointPathEnds::MakeEndpointPathEnds(PathEndVisitor *path_end_visitor,
}
MakeEndpointPathEnds::MakeEndpointPathEnds(const MakeEndpointPathEnds &make_path_ends) :
visit_path_ends_(new VisitPathEnds(make_path_ends.sta_)),
visit_path_ends_(make_path_ends.sta_),
path_end_visitor_(make_path_ends.path_end_visitor_->copy()),
corner_(make_path_ends.corner_),
min_max_(make_path_ends.min_max_),
@ -870,7 +869,6 @@ MakeEndpointPathEnds::MakeEndpointPathEnds(const MakeEndpointPathEnds &make_path
MakeEndpointPathEnds::~MakeEndpointPathEnds()
{
delete visit_path_ends_;
delete path_end_visitor_;
}
@ -883,8 +881,7 @@ MakeEndpointPathEnds::copy() const
void
MakeEndpointPathEnds::visit(Vertex *vertex)
{
visit_path_ends_->visitPathEnds(vertex, corner_, min_max_, true,
path_end_visitor_);
visit_path_ends_.visitPathEnds(vertex, corner_, min_max_, true, path_end_visitor_);
}
////////////////////////////////////////////////////////////////
@ -904,7 +901,7 @@ PathGroups::makeGroupPathEnds(VertexSet *endpoints,
Vector<MakeEndpointPathEnds> visitors(thread_count_,
MakeEndpointPathEnds(visitor, corner,
min_max, this));
for (auto endpoint : *endpoints) {
for (const auto endpoint : *endpoints) {
dispatch_queue_->dispatch( [endpoint, &visitors](int i)
{ visitors[i].visit(endpoint); } );
}

View File

@ -1192,7 +1192,7 @@ ReportPath::reportVerbose(MinPulseWidthCheck *check)
reportLine(pin_name, delay_zero, close_arrival, close_el);
if (sdc_->crprEnabled()) {
Crpr pessimism = check->commonClkPessimism(this);
Crpr pessimism = check->checkCrpr(this);
close_arrival += pessimism;
reportLine("clock reconvergence pessimism", pessimism, close_arrival, close_el);
}
@ -1803,7 +1803,7 @@ ReportPath::clkRegLatchDesc(const PathEnd *end)
TimingRole *role = arc_set->role();
if (role == TimingRole::regClkToQ()
|| role == TimingRole::latchEnToQ()) {
RiseFall *arc_rf = arc_set->isRisingFallingEdge();
const RiseFall *arc_rf = arc_set->isRisingFallingEdge();
clk_set = arc_set;
if (arc_rf == check_clk_rf)
clk_rf_set = arc_set;
@ -2320,7 +2320,7 @@ ReportPath::reportCommonClkPessimism(const PathEnd *end,
Arrival &clk_arrival)
{
if (sdc_->crprEnabled()) {
Crpr pessimism = end->commonClkPessimism(this);
Crpr pessimism = end->checkCrpr(this);
clk_arrival += pessimism;
reportLine("clock reconvergence pessimism", pessimism, clk_arrival,
end->clkEarlyLate(this));
@ -3250,8 +3250,8 @@ ReportPath::edgeRegLatchDesc(Edge *first_edge,
Instance *inst = network_->instance(first_edge->to(graph_)->pin());
LibertyCell *cell = network_->libertyCell(inst);
if (cell) {
LibertyPort *enable_port;
FuncExpr *enable_func;
const LibertyPort *enable_port;
const FuncExpr *enable_func;
const RiseFall *enable_rf;
cell->latchEnable(first_edge->timingArcSet(),
enable_port, enable_func, enable_rf);

View File

@ -905,8 +905,7 @@ Search::visitStartpoints(VertexVisitor *visitor)
}
delete pin_iter;
for (auto iter : sdc_->inputDelayPinMap()) {
const Pin *pin = iter.first;
for (const auto [pin, input_delays] : sdc_->inputDelayPinMap()) {
// Already hit these.
if (!network_->isTopLevelPort(pin)) {
Vertex *vertex = graph_->pinDrvrVertex(pin);
@ -1674,8 +1673,7 @@ Search::seedInputArrivals(ClockSet *clks)
{
// Input arrivals can be on internal pins, so iterate over the pins
// that have input arrivals rather than the top level input pins.
for (auto iter : sdc_->inputDelayPinMap()) {
const Pin *pin = iter.first;
for (const auto [pin, input_delays] : sdc_->inputDelayPinMap()) {
if (!sdc_->isLeafPinClock(pin)) {
Vertex *vertex = graph_->pinDrvrVertex(pin);
seedInputArrival(pin, vertex, clks);
@ -2928,7 +2926,7 @@ Search::reportClkInfos() const
sort(clk_infos, ClkInfoLess(this));
for (ClkInfo *clk_info : clk_infos)
report_->reportLine("ClkInfo %s", clk_info->asString(this));
report_->reportLine("%lu clk infos", clk_info_set_->size());
report_->reportLine("%zu clk infos", clk_info_set_->size());
}
ClkInfo *

View File

@ -682,9 +682,7 @@ Sim::propagateConstants(bool thru_sequentials)
void
Sim::setConstraintConstPins(LogicValueMap &value_map)
{
for (auto pin_value : value_map) {
const Pin *pin = pin_value.first;
LogicValue value = pin_value.second;
for (const auto [pin, value] : value_map) {
debugPrint(debug_, "sim", 2, "case pin %s = %c",
network_->pathName(pin),
logicValueString(value));
@ -1219,10 +1217,9 @@ isModeDisabled(Edge *edge,
if (cond_value == LogicValue::zero) {
// For a mode value to be disabled by having a value of
// logic zero one mode value must logic one.
for (auto name_mode : *mode_def->values()) {
ModeValueDef *value_def1 = name_mode.second;
if (value_def1) {
FuncExpr *cond1 = value_def1->cond();
for (const auto [name, value_def] : *mode_def->values()) {
if (value_def) {
FuncExpr *cond1 = value_def->cond();
if (cond1) {
LogicValue cond_value1 = sim->evalExpr(cond1, inst);
if (cond_value1 == LogicValue::one) {

View File

@ -349,8 +349,7 @@ Sta::updateComponentsState()
corners_->copyState(this);
levelize_->copyState(this);
parasitics_->copyState(this);
if (arc_delay_calc_)
arc_delay_calc_->copyState(this);
arc_delay_calc_->copyState(this);
sim_->copyState(this);
search_->copyState(this);
latches_->copyState(this);
@ -531,8 +530,7 @@ Sta::~Sta()
delete search_;
delete latches_;
delete parasitics_;
if (arc_delay_calc_)
delete arc_delay_calc_;
delete arc_delay_calc_;
delete graph_delay_calc_;
delete sim_;
delete levelize_;
@ -776,8 +774,7 @@ Sta::setAnalysisType(AnalysisType analysis_type)
{
if (analysis_type != sdc_->analysisType()) {
sdc_->setAnalysisType(analysis_type);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
search_->deletePathGroups();
corners_->analysisTypeChanged();
if (graph_)
@ -797,8 +794,7 @@ Sta::setOperatingConditions(OperatingConditions *op_cond,
{
sdc_->setOperatingConditions(op_cond, min_max);
corners_->operatingConditionsChanged();
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
}
const Pvt *
@ -1000,8 +996,7 @@ void
Sta::setWireloadMode(WireloadMode mode)
{
sdc_->setWireloadMode(mode);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
}
void
@ -1009,8 +1004,7 @@ Sta::setWireload(Wireload *wireload,
const MinMaxAll *min_max)
{
sdc_->setWireload(wireload, min_max);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
}
void
@ -1018,8 +1012,7 @@ Sta::setWireloadSelection(WireloadSelection *selection,
const MinMaxAll *min_max)
{
sdc_->setWireloadSelection(selection, min_max);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
}
void
@ -1148,8 +1141,7 @@ void
Sta::setPropagatedClock(Clock *clk)
{
sdc_->setPropagatedClock(clk);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
clkPinsInvalid();
}
@ -1157,8 +1149,7 @@ void
Sta::removePropagatedClock(Clock *clk)
{
sdc_->removePropagatedClock(clk);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
clkPinsInvalid();
}
@ -1166,8 +1157,7 @@ void
Sta::setPropagatedClock(Pin *pin)
{
sdc_->setPropagatedClock(pin);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
clkPinsInvalid();
}
@ -1175,8 +1165,7 @@ void
Sta::removePropagatedClock(Pin *pin)
{
sdc_->removePropagatedClock(pin);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
clkPinsInvalid();
}
@ -1609,8 +1598,7 @@ Sta::disableAfter()
{
// Levelization respects disabled edges.
levelize_->invalid();
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
}
////////////////////////////////////////////////////////////////
@ -1837,8 +1825,7 @@ Sta::setLogicValue(Pin *pin,
// fails. This could be more incremental if the graph delay
// calculator searched thru disabled edges but ignored their
// results.
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
}
void
@ -1854,8 +1841,7 @@ Sta::setCaseAnalysis(Pin *pin,
// simply invaldating the delays downstream from the constant pin
// fails. This could be handled incrementally by invalidating delays
// on the output of gates one level downstream.
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
}
void
@ -1870,8 +1856,7 @@ Sta::removeCaseAnalysis(Pin *pin)
// simply invaldating the delays downstream from the constant pin
// fails. This could be handled incrementally by invalidating delays
// on the output of gates one level downstream.
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
}
void
@ -2131,8 +2116,7 @@ void
Sta::constraintsChanged()
{
levelize_->invalid();
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
sim_->constantsInvalid();
}
@ -2215,10 +2199,8 @@ Sta::pocvEnabled() const
void
Sta::setPocvEnabled(bool enabled)
{
if (enabled != pocv_enabled_) {
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
}
if (enabled != pocv_enabled_)
delaysInvalid();
pocv_enabled_ = enabled;
updateComponentsState();
}
@ -2258,8 +2240,7 @@ Sta::setPresetClrArcsEnabled(bool enable)
{
if (sdc_->presetClrArcsEnabled() != enable) {
levelize_->invalid();
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
}
sdc_->setPresetClrArcsEnabled(enable);
}
@ -2274,8 +2255,7 @@ void
Sta::setCondDefaultArcsEnabled(bool enabled)
{
if (sdc_->condDefaultArcsEnabled() != enabled) {
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
sdc_->setCondDefaultArcsEnabled(enabled);
}
}
@ -2291,8 +2271,7 @@ Sta::setBidirectInstPathsEnabled(bool enabled)
{
if (sdc_->bidirectInstPathsEnabled() != enabled) {
levelize_->invalid();
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
sdc_->setBidirectInstPathsEnabled(enabled);
}
}
@ -2307,8 +2286,7 @@ void
Sta::setBidirectNetPathsEnabled(bool enabled)
{
if (sdc_->bidirectNetPathsEnabled() != enabled) {
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
sdc_->setBidirectNetPathsEnabled(enabled);
}
}
@ -2656,7 +2634,7 @@ Sta::findClkDelays(const Clock *clk,
////////////////////////////////////////////////////////////////
void
Sta::delaysInvalid()
Sta::delaysInvalid() const
{
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
@ -3106,8 +3084,8 @@ Sta::vertexSlacks(Vertex *vertex,
Slack (&slacks)[RiseFall::index_count][MinMax::index_count])
{
findRequired(vertex);
for(int rf_index : RiseFall::rangeIndex()) {
for(MinMax *min_max : MinMax::range()) {
for (int rf_index : RiseFall::rangeIndex()) {
for (const MinMax *min_max : MinMax::range()) {
slacks[rf_index][min_max->index()] = MinMax::min()->initValue();
}
}
@ -3327,8 +3305,7 @@ Sta::setArcDelayCalc(const char *delay_calc_name)
arc_delay_calc_ = makeDelayCalc(delay_calc_name, sta_);
// Update pointers to arc_delay_calc.
updateComponentsState();
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
}
void
@ -3639,7 +3616,7 @@ Sta::setAnnotatedSlew(Vertex *vertex,
void
Sta::writeSdf(const char *filename,
Corner *corner,
const Corner *corner,
char divider,
bool include_typ,
int digits,
@ -3656,7 +3633,7 @@ void
Sta::removeDelaySlewAnnotations()
{
graph_->removeDelaySlewAnnotations();
graph_delay_calc_->delaysInvalid();
delaysInvalid();
}
LogicValue
@ -3768,7 +3745,7 @@ void
Sta::removeNetLoadCaps() const
{
sdc_->removeNetLoadCaps();
graph_delay_calc_->delaysInvalid();
delaysInvalid();
}
void
@ -3920,8 +3897,7 @@ Sta::readSpef(const char *filename,
pin_cap_included, keep_coupling_caps,
coupling_cap_factor, reduce,
corner, min_max, this);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
return success;
}
@ -4023,8 +3999,7 @@ void
Sta::deleteParasitics()
{
parasitics_->deleteParasitics();
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
delaysInvalid();
}
Parasitic *

View File

@ -90,7 +90,7 @@ void
TagGroup::report(const StaState *sta) const
{
Report *report = sta->report();
report->reportLine("Group %u hash = %lu", index_, hash_);
report->reportLine("Group %u hash = %zu", index_, hash_);
arrivalMapReport(arrival_map_, sta);
}

View File

@ -40,7 +40,7 @@
#include "PathRef.hh"
#include "PathExpanded.hh"
#include "StaState.hh"
#include "Sim.hh"
#include "search/Sim.hh"
#include "WriteSpice.hh"
namespace sta {
@ -137,6 +137,8 @@ private:
// Input clock waveform cycles.
int clk_cycle_count_;
InstanceSet written_insts_;
using WriteSpice::writeHeader;
using WriteSpice::writePrintStmt;
using WriteSpice::writeSubckts;
@ -177,12 +179,14 @@ WritePathSpice::WritePathSpice(Path *path,
CircuitSim ckt_sim,
const StaState *sta) :
WriteSpice(spice_filename, subckt_filename, lib_subckt_filename,
model_filename, power_name, gnd_name, ckt_sim, sta),
model_filename, power_name, gnd_name, ckt_sim,
path->dcalcAnalysisPt(sta), sta),
path_(path),
path_expanded_(sta),
clk_cycle_count_(3)
clk_cycle_count_(3),
written_insts_(network_)
{
initPowerGnd( path_->dcalcAnalysisPt(this));
initPowerGnd();
}
void
@ -253,18 +257,17 @@ float
WritePathSpice::pathMaxTime()
{
float max_time = 0.0;
DcalcAPIndex dcalc_ap_index = path_->dcalcAnalysisPt(this)->index();
for (size_t i = 0; i < path_expanded_.size(); i++) {
PathRef *path = path_expanded_.path(i);
const RiseFall *rf = path->transition(this);
Vertex *vertex = path->vertex(this);
float path_max_slew = railToRailSlew(findSlew(vertex,rf,nullptr,dcalc_ap_index),rf);
float path_max_slew = railToRailSlew(findSlew(vertex,rf,nullptr), rf);
if (vertex->isDriver(network_)) {
VertexOutEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
Vertex *load = edge->to(graph_);
float load_slew = railToRailSlew(findSlew(load, rf, nullptr, dcalc_ap_index),rf);
float load_slew = railToRailSlew(findSlew(load, rf, nullptr), rf);
if (load_slew > path_max_slew)
path_max_slew = load_slew;
}
@ -387,9 +390,8 @@ float
WritePathSpice::findSlew(Path *path)
{
Vertex *vertex = path->vertex(this);
DcalcAPIndex dcalc_ap_index = path->dcalcAnalysisPt(this)->index();
const RiseFall *rf = path->transition(this);
return findSlew(vertex, rf, nullptr, dcalc_ap_index);
return findSlew(vertex, rf, nullptr);
}
float
@ -398,8 +400,7 @@ WritePathSpice::findSlew(Path *path,
TimingArc *next_arc)
{
Vertex *vertex = path->vertex(this);
DcalcAPIndex dcalc_ap_index = path->dcalcAnalysisPt(this)->index();
return findSlew(vertex, rf, next_arc, dcalc_ap_index);
return findSlew(vertex, rf, next_arc);
}
////////////////////////////////////////////////////////////////
@ -496,7 +497,7 @@ WritePathSpice::writeGateStage(Stage stage)
const char *load_pin_name = stageLoadPinName(stage);
string subckt_name = "stage" + std::to_string(stage);
Instance *inst = stageInstance(stage);
const Instance *inst = stageInstance(stage);
LibertyPort *input_port = stageGateInputPort(stage);
LibertyPort *drvr_port = stageDrvrPort(stage);
@ -511,10 +512,9 @@ WritePathSpice::writeGateStage(Stage stage)
network_->pathName(inst),
input_port->name(),
drvr_port->name());
writeSubcktInst(input_pin);
writeSubcktInst(inst);
PathRef *drvr_path = stageDrvrPath(stage);
DcalcAPIndex dcalc_ap_index = drvr_path->dcalcAnalysisPt(this)->index();
const RiseFall *drvr_rf = drvr_path->transition(this);
Edge *gate_edge = stageGateEdge(stage);
@ -523,11 +523,20 @@ WritePathSpice::writeGateStage(Stage stage)
gatePortValues(input_pin, drvr_pin, drvr_rf, gate_edge,
port_values, is_clked);
const Clock *clk = (is_clked) ? stageDrvrPath(stage)->clock(this) : nullptr;
writeSubcktInstVoltSrcs(input_pin, port_values, clk, dcalc_ap_index);
PinSet inputs(network_);
inputs.insert(input_pin);
writeSubcktInstVoltSrcs(inst, port_values, inputs);
streamPrint(spice_stream_, "\n");
writeSubcktInstLoads(drvr_pin, load_pin);
PinSet drvr_loads(network_);
PinConnectedPinIterator *pin_iter = network_->connectedPinIterator(drvr_pin);
while (pin_iter->hasNext()) {
const Pin *load_pin = pin_iter->next();
drvr_loads.insert(load_pin);
}
delete pin_iter;
writeSubcktInstLoads(drvr_pin, load_pin, drvr_loads, written_insts_);
writeStageParasitics(stage);
streamPrint(spice_stream_, ".ends\n\n");
}
@ -538,9 +547,14 @@ WritePathSpice::writeStageParasitics(Stage stage)
PathRef *drvr_path = stageDrvrPath(stage);
DcalcAnalysisPt *dcalc_ap = drvr_path->dcalcAnalysisPt(this);
ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt();
const Pin *drvr_pin = stageDrvrPin(stage);
const Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap);
if (parasitic == nullptr) {
const RiseFall *drvr_rf = drvr_path->transition(this);
parasitic = parasitics_->findPiElmore(drvr_pin, drvr_rf, parasitic_ap);
}
NetSet coupling_nets;
writeDrvrParasitics(stageDrvrPin(stage), drvr_path->transition(this),
coupling_nets, parasitic_ap);
writeDrvrParasitics(drvr_pin, parasitic, coupling_nets);
}
////////////////////////////////////////////////////////////////

View File

@ -14,7 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "WriteSpice.hh"
#include "spice/WriteSpice.hh"
#include <algorithm> // swap
#include "Debug.hh"
#include "Units.hh"
@ -27,7 +29,7 @@
#include "Liberty.hh"
#include "Network.hh"
#include "Graph.hh"
#include "Sim.hh"
#include "search/Sim.hh"
#include "Clock.hh"
#include "PathVertex.hh"
#include "DcalcAnalysisPt.hh"
@ -36,6 +38,8 @@
namespace sta {
using std::ifstream;
using std::swap;
using std::set;
Net *
pinNet(const Pin *pin,
@ -77,6 +81,7 @@ WriteSpice::WriteSpice(const char *spice_filename,
const char *power_name,
const char *gnd_name,
CircuitSim ckt_sim,
const DcalcAnalysisPt *dcalc_ap,
const StaState *sta) :
StaState(sta),
spice_filename_(spice_filename),
@ -86,23 +91,23 @@ WriteSpice::WriteSpice(const char *spice_filename,
power_name_(power_name),
gnd_name_(gnd_name),
ckt_sim_(ckt_sim),
dcalc_ap_(dcalc_ap),
default_library_(network_->defaultLibertyLibrary()),
short_ckt_resistance_(.0001),
cap_index_(1),
res_index_(1),
volt_index_(1),
next_node_index_(1),
bdd_(sta)
{
}
void
WriteSpice::initPowerGnd(const DcalcAnalysisPt *dcalc_ap)
WriteSpice::initPowerGnd()
{
bool exists = false;
default_library_->supplyVoltage(power_name_, power_voltage_, exists);
if (!exists) {
const OperatingConditions *op_cond = dcalc_ap->operatingConditions();
const OperatingConditions *op_cond = dcalc_ap_->operatingConditions();
if (op_cond == nullptr)
op_cond = network_->defaultLibertyLibrary()->defaultOperatingConditions();
power_voltage_ = op_cond->voltage();
@ -182,7 +187,7 @@ WriteSpice::writeGnuplotFile(StdStringSeq &node_nanes)
csv_filename.c_str());
for (size_t i = 3; i <= node_nanes.size() + 1; i++) {
streamPrint(gnuplot_stream, ",\\\n");
streamPrint(gnuplot_stream, "'' using 1:%lu with lines", i);
streamPrint(gnuplot_stream, "'' using 1:%zu with lines", i);
}
streamPrint(gnuplot_stream, "\n");
streamPrint(gnuplot_stream, "pause mouse close\n");
@ -312,9 +317,8 @@ WriteSpice::findCellSubckts(StdStringSet &cell_names)
////////////////////////////////////////////////////////////////
void
WriteSpice::writeSubcktInst(const Pin *input_pin)
WriteSpice::writeSubcktInst(const Instance *inst)
{
const Instance *inst = network_->instance(input_pin);
const char *inst_name = network_->pathName(inst);
LibertyCell *cell = network_->libertyCell(inst);
const char *cell_name = cell->name();
@ -340,23 +344,20 @@ WriteSpice::writeSubcktInst(const Pin *input_pin)
// Power/ground and input voltage sources.
void
WriteSpice::writeSubcktInstVoltSrcs(const Pin *input_pin,
WriteSpice::writeSubcktInstVoltSrcs(const Instance *inst,
LibertyPortLogicValues &port_values,
const Clock *clk,
DcalcAPIndex dcalc_ap_index)
const PinSet &excluded_input_pins)
{
const Instance *inst = network_->instance(input_pin);
LibertyCell *cell = network_->libertyCell(inst);
const char *cell_name = cell->name();
StringVector &spice_port_names = cell_spice_port_names_[cell_name];
const LibertyPort *input_port = network_->libertyPort(input_pin);
const char *inst_name = network_->pathName(inst);
debugPrint(debug_, "write_spice", 2, "subckt %s", cell->name());
for (string subckt_port_sname : spice_port_names) {
const char *subckt_port_name = subckt_port_sname.c_str();
LibertyPort *port = cell->findLibertyPort(subckt_port_name);
const Pin *pin = port ? network_->findPin(inst, port) : nullptr;
LibertyPgPort *pg_port = cell->findPgPort(subckt_port_name);
debugPrint(debug_, "write_spice", 2, " port %s%s",
subckt_port_name,
@ -369,10 +370,9 @@ WriteSpice::writeSubcktInstVoltSrcs(const Pin *input_pin,
else if (stringEq(subckt_port_name, gnd_name_))
writeVoltageSource(inst_name, subckt_port_name, gnd_voltage_);
else if (port
&& port != input_port
&& excluded_input_pins.find(pin) == excluded_input_pins.end()
&& port->direction()->isAnyInput()) {
// Input voltage to sensitize path from gate input to output.
const Pin *pin = network_->findPin(inst, port);
// Look for tie high/low or propagated constant values.
LogicValue port_value = sim_->logicValue(pin);
if (port_value == LogicValue::unknown) {
@ -395,11 +395,7 @@ WriteSpice::writeSubcktInstVoltSrcs(const Pin *input_pin,
power_voltage_);
break;
case LogicValue::rise:
writeClkedStepSource(pin, RiseFall::rise(), clk, dcalc_ap_index);
break;
case LogicValue::fall:
writeClkedStepSource(pin, RiseFall::fall(), clk, dcalc_ap_index);
break;
}
}
@ -465,34 +461,14 @@ WriteSpice::pgPortVoltage(LibertyPgPort *pg_port)
return voltage;
}
// PWL voltage source that rises half way into the first clock cycle.
void
WriteSpice::writeClkedStepSource(const Pin *pin,
const RiseFall *rf,
const Clock *clk,
DcalcAPIndex dcalc_ap_index)
{
Vertex *vertex = graph_->pinLoadVertex(pin);
float slew = findSlew(vertex, rf, nullptr, dcalc_ap_index);
float time = clkWaveformTimeOffset(clk) + clk->period() / 2.0;
writeRampVoltSource(pin, rf, time, slew);
}
float
WriteSpice::clkWaveformTimeOffset(const Clock *clk)
{
return clk->period() / 10;
}
////////////////////////////////////////////////////////////////
float
WriteSpice::findSlew(Vertex *vertex,
const RiseFall *rf,
TimingArc *next_arc,
DcalcAPIndex dcalc_ap_index)
TimingArc *next_arc)
{
float slew = delayAsFloat(graph_->slew(vertex, rf, dcalc_ap_index));
float slew = delayAsFloat(graph_->slew(vertex, rf, dcalc_ap_->index()));
if (slew == 0.0 && next_arc)
slew = slewAxisMinValue(next_arc);
if (slew == 0.0)
@ -504,7 +480,7 @@ WriteSpice::findSlew(Vertex *vertex,
float
WriteSpice::slewAxisMinValue(TimingArc *arc)
{
GateTableModel *gate_model = dynamic_cast<GateTableModel*>(arc->model());
GateTableModel *gate_model = arc->gateTableModel(dcalc_ap_);
if (gate_model) {
const TableModel *model = gate_model->delayModel();
const TableAxis *axis1 = model->axis1();
@ -532,27 +508,20 @@ WriteSpice::slewAxisMinValue(TimingArc *arc)
void
WriteSpice::writeDrvrParasitics(const Pin *drvr_pin,
const RiseFall *drvr_rf,
const NetSet &aggressor_nets,
const ParasiticAnalysisPt *parasitic_ap)
const Parasitic *parasitic,
const NetSet &coupling_nets)
{
Net *net = network_->net(drvr_pin);
const char *net_name = net ? network_->pathName(net) : network_->pathName(drvr_pin);
streamPrint(spice_stream_, "* Net %s\n", net_name);
Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap);
node_map_.clear();
next_node_index_ = 1;
if (parasitic)
writeParasiticNetwork(drvr_pin, parasitic, aggressor_nets);
if (parasitics_->isParasiticNetwork(parasitic))
writeParasiticNetwork(drvr_pin, parasitic, coupling_nets);
else if (parasitics_->isPiElmore(parasitic))
writePiElmore(drvr_pin, parasitic);
else {
parasitic = parasitics_->findPiElmore(drvr_pin, drvr_rf, parasitic_ap);
if (parasitic)
writePiElmore(drvr_pin, parasitic);
else {
streamPrint(spice_stream_, "* Net has no parasitics.\n");
writeNullParasitic(drvr_pin);
}
streamPrint(spice_stream_, "* Net has no parasitics.\n");
writeNullParasitic(drvr_pin);
}
}
@ -561,7 +530,7 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin,
const Parasitic *parasitic,
const NetSet &coupling_nets)
{
Set<const Pin*> reachable_pins;
set<const Pin*> reachable_pins;
// Sort resistors for consistent regression results.
ParasiticResistorSeq resistors = parasitics_->resistors(parasitic);
sort(resistors.begin(), resistors.end(),
@ -575,8 +544,8 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin,
ParasiticNode *node2 = parasitics_->node2(resistor);
streamPrint(spice_stream_, "R%d %s %s %.3e\n",
res_index_++,
nodeName(node1),
nodeName(node2),
parasitics_->name(node1),
parasitics_->name(node2),
resistance);
// Necessary but not sufficient. Need a DFS.
@ -595,7 +564,7 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin,
if (pin != drvr_pin
&& network_->isLoad(pin)
&& !network_->isHierarchical(pin)
&& !reachable_pins.hasKey(pin)) {
&& reachable_pins.find(pin) == reachable_pins.end()) {
streamPrint(spice_stream_, "R%d %s %s %.3e\n",
res_index_++,
network_->pathName(drvr_pin),
@ -605,6 +574,7 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin,
}
delete pin_iter;
// Grounded node capacitors.
// Sort nodes for consistent regression results.
ParasiticNodeSeq nodes = parasitics_->nodes(parasitic);
sort(nodes.begin(), nodes.end(),
@ -621,12 +591,12 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin,
if (cap > 0.0) {
streamPrint(spice_stream_, "C%d %s 0 %.3e\n",
cap_index_++,
nodeName(node),
parasitics_->name(node),
cap);
}
}
// Sort coupling capacitors consistent regression results.
// Sort coupling capacitors for consistent regression results.
ParasiticCapacitorSeq capacitors = parasitics_->capacitors(parasitic);
sort(capacitors.begin(), capacitors.end(),
[=] (const ParasiticCapacitor *c1,
@ -640,21 +610,21 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin,
float cap = parasitics_->value(capacitor);
const Net *net1 = node1 ? parasitics_->net(node1, network_) : nullptr;
const Net *net2 = node2 ? parasitics_->net(node2, network_) : nullptr;
const ParasiticNode *net_node = nullptr;
const char *coupling_name;
if (net1 == net) {
net_node = node1;
coupling_name = net2 && coupling_nets.hasKey(net2) ? nodeName(node2) : "0";
if (net2 == net) {
swap(net1, net2);
swap(node1, node2);
}
else if (net2 == net) {
net_node = node2;
coupling_name = net1 && coupling_nets.hasKey(net1) ? nodeName(node1) : "0";
}
if (net_node)
if (net2 && coupling_nets.hasKey(net2))
// Write half the capacitance because the coupled net will do the same.
streamPrint(spice_stream_, "C%d %s %s %.3e\n",
cap_index_++,
nodeName(net_node),
coupling_name,
parasitics_->name(node1),
parasitics_->name(node2),
cap * .5);
else
streamPrint(spice_stream_, "C%d %s 0 %.3e\n",
cap_index_++,
parasitics_->name(node1),
cap);
}
}
@ -674,27 +644,6 @@ pinNet(const Pin *pin,
return net;
}
const char *
WriteSpice::nodeName(const ParasiticNode *node)
{
const Pin *pin = parasitics_->pin(node);
if (pin)
return parasitics_->name(node);
else {
int node_index;
auto index_itr = node_map_.find(node);
if (index_itr == node_map_.end()) {
node_index = next_node_index_++;
node_map_[node] = node_index;
}
else
node_index = index_itr->second;
const Net *net = parasitics_->net(node, network_);
const char *net_name = network_->pathName(net);
return stringPrintTmp("%s:%d", net_name, node_index);
}
}
void
WriteSpice::writePiElmore(const Pin *drvr_pin,
const Parasitic *parasitic)
@ -1081,21 +1030,23 @@ WriteSpice::seqPortValues(Sequential *seq,
LibertyPortLogicValues &port_values)
{
FuncExpr *data = seq->data();
// SHOULD choose values for all ports of data to make output rise/fall
// matching rf.
LibertyPort *port = onePort(data);
if (port) {
TimingSense sense = data->portTimingSense(port);
switch (sense) {
case TimingSense::positive_unate:
if (rf == RiseFall::rise())
port_values[port] = LogicValue::rise;
port_values[port] = LogicValue::one;
else
port_values[port] = LogicValue::fall;
port_values[port] = LogicValue::zero;
break;
case TimingSense::negative_unate:
if (rf == RiseFall::rise())
port_values[port] = LogicValue::fall;
port_values[port] = LogicValue::zero;
else
port_values[port] = LogicValue::rise;
port_values[port] = LogicValue::one;
break;
case TimingSense::non_unate:
case TimingSense::none:
@ -1153,20 +1104,25 @@ WriteSpice::drvrLoads(const Pin *drvr_pin)
void
WriteSpice::writeSubcktInstLoads(const Pin *drvr_pin,
const Pin *exclude)
const Pin *path_load,
const PinSet &excluded_input_pins,
InstanceSet &written_insts)
{
streamPrint(spice_stream_, "* Load pins\n");
PinSeq drvr_loads = drvrLoads(drvr_pin);
// Do not sensitize side load gates.
LibertyPortLogicValues port_values;
for (const Pin *load_pin : drvr_loads) {
if (load_pin != exclude
const Instance *load_inst = network_->instance(load_pin);
if (load_pin != path_load
&& network_->direction(load_pin)->isAnyInput()
&& !network_->isHierarchical(load_pin)
&& !network_->isTopLevelPort(load_pin)) {
writeSubcktInst(load_pin);
writeSubcktInstVoltSrcs(load_pin, port_values, nullptr, 0);
&& !network_->isTopLevelPort(load_pin)
&& !written_insts.hasKey(load_inst)) {
writeSubcktInst(load_inst);
writeSubcktInstVoltSrcs(load_inst, port_values, excluded_input_pins);
streamPrint(spice_stream_, "\n");
written_insts.insert(load_inst);
}
}
}
@ -1264,4 +1220,25 @@ streamPrint(ofstream &stream,
va_end(args);
}
////////////////////////////////////////////////////////////////
// Unused
// PWL voltage source that rises half way into the first clock cycle.
void
WriteSpice::writeClkedStepSource(const Pin *pin,
const RiseFall *rf,
const Clock *clk)
{
Vertex *vertex = graph_->pinLoadVertex(pin);
float slew = findSlew(vertex, rf, nullptr);
float time = clkWaveformTimeOffset(clk) + clk->period() / 2.0;
writeRampVoltSource(pin, rf, time, slew);
}
float
WriteSpice::clkWaveformTimeOffset(const Clock *clk)
{
return clk->period() / 10;
}
} // namespace

View File

@ -50,10 +50,11 @@ public:
const char *power_name,
const char *gnd_name,
CircuitSim ckt_sim,
const DcalcAnalysisPt *dcalc_ap,
const StaState *sta);
protected:
void initPowerGnd(const DcalcAnalysisPt *dcalc_ap);
void initPowerGnd();
void writeHeader(string &title,
float max_time,
float time_step);
@ -63,11 +64,10 @@ protected:
void findCellSubckts(StdStringSet &cell_names);
void recordSpicePortNames(const char *cell_name,
StringVector &tokens);
void writeSubcktInst(const Pin *input_pin);
void writeSubcktInstVoltSrcs(const Pin *input_pin,
void writeSubcktInst(const Instance *inst);
void writeSubcktInstVoltSrcs(const Instance *inst,
LibertyPortLogicValues &port_values,
const Clock *clk,
DcalcAPIndex dcalc_ap_index);
const PinSet &excluded_input_pins);
float pgPortVoltage(LibertyPgPort *pg_port);
void writeVoltageSource(const char *inst_name,
const char *port_name,
@ -79,20 +79,21 @@ protected:
float voltage);
void writeClkedStepSource(const Pin *pin,
const RiseFall *rf,
const Clock *clk,
DcalcAPIndex dcalc_ap_index);
const Clock *clk);
void writeDrvrParasitics(const Pin *drvr_pin,
const RiseFall *drvr_rf,
// Nets with parasitics to include coupling caps to.
const NetSet &coupling_nets,
const ParasiticAnalysisPt *parasitic_ap);
void writeDrvrParasitics(const Pin *drvr_pin,
const Parasitic *parasitic,
const NetSet &coupling_nets);
void writeParasiticNetwork(const Pin *drvr_pin,
const Parasitic *parasitic,
const NetSet &aggressor_nets);
void writePiElmore(const Pin *drvr_pin,
const Parasitic *parasitic);
void writeNullParasitic(const Pin *drvr_pin);
const char *nodeName(const ParasiticNode *node);
void writeVoltageSource(const char *node_name,
float voltage);
@ -126,8 +127,7 @@ protected:
const char *spiceTrans(const RiseFall *rf);
float findSlew(Vertex *vertex,
const RiseFall *rf,
TimingArc *next_arc,
DcalcAPIndex dcalc_ap_index);
TimingArc *next_arc);
float slewAxisMinValue(TimingArc *arc);
float clkWaveformTimeOffset(const Clock *clk);
@ -151,7 +151,9 @@ protected:
// Return values.
LibertyPortLogicValues &port_values);
void writeSubcktInstLoads(const Pin *drvr_pin,
const Pin *exclude);
const Pin *path_load,
const PinSet &excluded_input_pins,
InstanceSet &written_insts);
PinSeq drvrLoads(const Pin *drvr_pin);
void writeSubcktInstVoltSrcs();
string replaceFileExt(string filename,
@ -164,6 +166,7 @@ protected:
const char *power_name_;
const char *gnd_name_;
CircuitSim ckt_sim_;
const DcalcAnalysisPt *dcalc_ap_;
ofstream spice_stream_;
LibertyLibrary *default_library_;
@ -177,8 +180,6 @@ protected:
int cap_index_;
int res_index_;
int volt_index_;
ParasiticNodeMap node_map_;
int next_node_index_;
CellSpicePortNames cell_spice_port_names_;
Bdd bdd_;
};

43
spice/WriteSpice.i Normal file
View File

@ -0,0 +1,43 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, 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/>.
%module write_gate_spice
%{
#include "spice/WritePathSpice.hh"
%}
%inline %{
void
write_path_spice_cmd(PathRef *path,
const char *spice_filename,
const char *subckt_filename,
const char *lib_subckt_filename,
const char *model_filename,
const char *power_name,
const char *gnd_name,
CircuitSim ckt_sim)
{
Sta *sta = Sta::sta();
writePathSpice(path, spice_filename, subckt_filename,
lib_subckt_filename, model_filename,
power_name, gnd_name, ckt_sim, sta);
}
%} // inline

279
spice/WriteSpice.tcl Normal file
View File

@ -0,0 +1,279 @@
# OpenSTA, Static Timing Analyzer
# Copyright (c) 2024, 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/>.
namespace eval sta {
define_cmd_args "write_path_spice" { -path_args path_args\
-spice_directory spice_directory\
-lib_subckt_file lib_subckts_file\
-model_file model_file\
-power power\
-ground ground\
[-simulator hspice|ngspice|xyce]}
proc write_path_spice { args } {
parse_key_args "write_path_spice" args \
keys {-spice_directory -lib_subckt_file -model_file \
-power -ground -path_args -simulator} \
flags {}
if { [info exists keys(-spice_directory)] } {
set spice_dir [file nativename $keys(-spice_directory)]
if { ![file exists $spice_dir] } {
sta_error 1920 "Directory $spice_dir not found."
}
if { ![file isdirectory $spice_dir] } {
sta_error 1921 "$spice_dir is not a directory."
}
if { ![file writable $spice_dir] } {
sta_error 1922 "Cannot write in $spice_dir."
}
} else {
sta_error 1923 "No -spice_directory specified."
}
if { [info exists keys(-lib_subckt_file)] } {
set lib_subckt_file [file nativename $keys(-lib_subckt_file)]
if { ![file readable $lib_subckt_file] } {
sta_error 1924 "-lib_subckt_file $lib_subckt_file is not readable."
}
} else {
sta_error 1925 "No -lib_subckt_file specified."
}
if { [info exists keys(-model_file)] } {
set model_file [file nativename $keys(-model_file)]
if { ![file readable $model_file] } {
sta_error 1926 "-model_file $model_file is not readable."
}
} else {
sta_error 1927 "No -model_file specified."
}
if { [info exists keys(-power)] } {
set power $keys(-power)
} else {
sta_error 1928 "No -power specified."
}
if { [info exists keys(-ground)] } {
set ground $keys(-ground)
} else {
sta_error 1929 "No -ground specified."
}
set ckt_sim [parse_ckt_sim_key keys]
if { ![info exists keys(-path_args)] } {
sta_error 1930 "No -path_args specified."
}
set path_args $keys(-path_args)
set path_ends [eval [concat find_timing_paths $path_args]]
if { $path_ends == {} } {
sta_error 1931 "No paths found for -path_args $path_args."
} else {
set path_index 1
foreach path_end $path_ends {
set path [$path_end path]
set path_name "path_$path_index"
set spice_file [file join $spice_dir "$path_name.sp"]
set subckt_file [file join $spice_dir "$path_name.subckt"]
write_path_spice_cmd $path $spice_file $subckt_file \
$lib_subckt_file $model_file $power $ground $ckt_sim
incr path_index
}
}
}
set ::ckt_sims {hspice ngspice xyce}
proc parse_ckt_sim_key { keys_var } {
upvar 1 $keys_var keys
global ckt_sims
set ckt_sim "ngspice"
if { [info exists keys(-simulator)] } {
set ckt_sim [file nativename $keys(-simulator)]
if { [lsearch $ckt_sims $ckt_sim] == -1 } {
sta_error 1910 "Unknown circuit simulator"
}
}
return $ckt_sim
}
################################################################
define_cmd_args "write_gate_spice" \
{ -gates {{instance input_port driver_port edge [delay]}...}\
-spice_filename spice_filename\
-lib_subckt_file lib_subckts_file\
-model_file model_file\
-power power\
-ground ground\
[-simulator hspice|ngspice|xyce]\
[-corner corner]\
[-min] [-max]}
proc write_gate_spice { args } {
parse_key_args "write_gate_spice" args \
keys {-gates -spice_filename -lib_subckt_file -model_file \
-power -ground -simulator -corner}\
flags {-measure_stmts -min -max}
if { [info exists keys(-gates)] } {
set gates $keys(-gates)
} else {
sta_error 1932 "Missing -gates argument."
}
if { [info exists keys(-spice_filename)] } {
set spice_file [file nativename $keys(-spice_filename)]
set spice_dir [file dirname $spice_file]
if { ![file writable $spice_dir] } {
sta_error 1903 "Cannot write $spice_dir."
}
} else {
sta_error 1904 "No -spice_filename specified."
}
if { [info exists keys(-lib_subckt_file)] } {
set lib_subckt_file [file nativename $keys(-lib_subckt_file)]
if { ![file readable $lib_subckt_file] } {
sta_error 1905 "-lib_subckt_file $lib_subckt_file is not readable."
}
} else {
sta_error 1906 "No -lib_subckt_file specified."
}
if { [info exists keys(-model_file)] } {
set model_file [file nativename $keys(-model_file)]
if { ![file readable $model_file] } {
sta_error 1907 "-model_file $model_file is not readable."
}
} else {
sta_error 1908 "No -model_file specified."
}
if { [info exists keys(-power)] } {
set power $keys(-power)
} else {
sta_error 1909 "No -power specified."
}
if { [info exists keys(-ground)] } {
set ground $keys(-ground)
} else {
sta_error 1915 "No -ground specified."
}
set ckt_sim [parse_ckt_sim_key keys]
set corner [parse_corner keys]
set min_max [parse_min_max_flags flags]
check_argc_eq0 "write_gate_spice" $args
set spice_dir [file dirname $spice_file]
set spice_root [file rootname [file tail $spice_file]]
set subckt_file [file join $spice_dir "$spice_root.subckt"]
write_gate_spice_cmd $gates $spice_file $subckt_file \
$lib_subckt_file $model_file $power $ground $ckt_sim \
$corner $min_max
}
################################################################
# plot_pins defaults to input_pin, driver_pina and load pins for each driver.
define_cmd_args "write_gate_gnuplot" \
{ -gates {{instance input_port driver_port edge [delay]}...}\
-plot_pins plot_pins\
-plot_basename plot_basename\
[-corner corner] [-min] [-max]}
proc write_gate_gnuplot { args } {
parse_key_args "write_gate_gnuplot" args \
keys {-gates -plot_pins -plot_basename -spice_waveforms -corner} \
flags {-min -max}
if { [info exists keys(-gates)] } {
set gates $keys(-gates)
} else {
sta_error 1933 "Missing -gates argument."
}
if { [info exists keys(-plot_pins)] } {
set plot_pins [get_port_pins_error "-plot_pins" $keys(-plot_pins)]
} else {
set plot_pins {}
set plot_all_loads 0
set gate_idx 0
foreach gate $gates {
set in_pin [parse_gate_in_pin $gate]
set drvr_pin [parse_gate_drvr_pin $gate]
lappend plot_pins $in_pin
lappend plot_pins $drvr_pin
# Only plot driver loads.
if { $plot_all_loads || $gate_idx == 0 } {
set pin_iter [$drvr_pin connected_pin_iterator]
while { [$pin_iter has_next] } {
set pin [$pin_iter next]
if { [$pin is_load] } {
lappend plot_pins $pin
}
}
$pin_iter finish
}
incr gate_idx
}
}
if { [info exists keys(-plot_basename)] } {
set plot_base [file nativename $keys(-plot_basename)]
set plot_dir [file dirname $plot_base]
if { ![file writable $plot_dir] } {
sta_error 1913 "Cannot write $plot_dir."
}
} else {
sta_error 1914 "No -plot_basename specified."
}
set gnuplot_filename "${plot_base}.gnuplot"
set csv_filename "${plot_base}.csv"
set sim_wave_filename ""
if { [info exists keys(-spice_waveforms)] } {
set sim_wave_filename $keys(-spice_waveforms)
}
set corner [parse_corner keys]
set min_max [parse_min_max_flags flags]
write_gate_gnuplot_cmd $gates $plot_pins $sim_wave_filename \
$gnuplot_filename $csv_filename $corner $min_max
}
proc parse_gate_drvr_pin { gate_arg } {
lassign $gate_arg inst_name in_port_name in_rf drvr_port_name drvr_rf
set inst [get_instance_error "instance" $inst_name]
set drvr_pin [$inst find_pin $drvr_port_name]
return $drvr_pin
}
proc parse_gate_in_pin { gate_arg } {
lassign $gate_arg inst_name in_port_name in_rf drvr_port_name drvr_rf
set inst [get_instance_error "instance" $inst_name]
set in_pin [$inst find_pin $in_port_name]
return $in_pin
}
# sta namespace end.
}

77
spice/Xyce.cc Normal file
View File

@ -0,0 +1,77 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, 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/>.
#include "Xyce.hh"
#include <fstream>
#include <sstream>
#include <memory>
#include "Error.hh"
namespace sta {
using std::string;
using std::ifstream;
using std::getline;
using std::stringstream;
using std::vector;
using std::make_shared;
void
readXyceCsv(const char *csv_filename,
// Return values.
StdStringSeq &titles,
WaveformSeq &waveforms)
{
ifstream file(csv_filename);
if (file.is_open()) {
string line;
// Read the header line.
getline(file, line);
stringstream ss(line);
string field;
size_t col = 0;
while (getline(ss, field, ',')) {
// Skip TIME title.
if (col > 0)
titles.push_back(field);
col++;
}
vector<FloatSeq> values(titles.size() + 1);
while (getline(file, line)) {
stringstream ss(line);
size_t col = 0;
while (getline(ss, field, ',')) {
float value = std::stof(field);
values[col].push_back(value);
col++;
}
}
file.close();
TableAxisPtr time_axis = make_shared<TableAxis>(TableAxisVariable::time,
new FloatSeq(values[0]));
for (size_t var = 1; var < values.size(); var++)
waveforms.emplace_back(new FloatSeq(values[var]), time_axis);
}
else
throw FileNotReadable(csv_filename);
}
} // namespace

35
spice/Xyce.hh Normal file
View File

@ -0,0 +1,35 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, 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/>.
#pragma once
#include <string>
#include <vector>
#include "TableModel.hh"
namespace sta {
typedef std::vector<std::string> StdStringSeq;
typedef std::vector<Table1> WaveformSeq;
void
readXyceCsv(const char *csv_filename,
// Return values.
StdStringSeq &titles,
WaveformSeq &waveforms);
} // namespace

View File

@ -400,12 +400,21 @@ proc parse_corner { keys_var } {
upvar 1 $keys_var keys
if { [info exists keys(-corner)] } {
set corner_name $keys(-corner)
set corner [find_corner $corner_name]
if { $corner == "NULL" } {
sta_error 102 "$corner_name is not the name of process corner."
set corner_arg $keys(-corner)
if { [is_object $corner_arg] } {
set object_type [object_type $corner_arg]
if { $object_type == "Corner" } {
return $corner_arg
} else {
sta_error 144 "corner object type '$object_type' is not a corner."
}
} else {
return $corner
set corner [find_corner $corner_arg]
if { $corner == "NULL" } {
sta_error 102 "$corner_arg is not the name of process corner."
} else {
return $corner
}
}
} elseif { [multi_corner] } {
sta_error 103 "-corner keyword required with multi-corner analysis."
@ -510,7 +519,7 @@ proc parse_min_max_all_flags { flags_var } {
} elseif { [info exists flags(-max)] && ![info exists flags(-min)] } {
return "max"
} else {
return "all"
return "min_max"
}
}
@ -518,13 +527,13 @@ proc parse_min_max_all_flags { flags_var } {
proc parse_min_max_all_check_flags { flags_var } {
upvar 1 $flags_var flags
if { [info exists flags(-min)] && [info exists flags(-max)] } {
return "all"
return "min_max"
} elseif { [info exists flags(-min)] && ![info exists flags(-max)] } {
return "min"
} elseif { [info exists flags(-max)] && ![info exists flags(-min)] } {
return "max"
} else {
return "all"
return "min_max"
}
}
@ -550,7 +559,7 @@ proc parse_early_late_all_flags { flags_var } {
} elseif { [info exists flags(-late)] && ![info exists flags(-early)] } {
return "max"
} else {
return "all"
return "min_max"
}
}

View File

@ -167,10 +167,10 @@ proc set_unit_values { unit key suffix key_var } {
set scale [unit_prefix_scale $unit $prefix ]
set_cmd_unit_scale $unit $scale
} else {
sta_error 343 "unknown unit $unit prefix '${arg_prefix}'."
sta_error 166 "unknown unit $unit prefix '${arg_prefix}'."
}
} else {
sta_error 501 "incorrect unit suffix '$arg_suffix'."
sta_error 167 "incorrect unit suffix '$arg_suffix'."
}
if [info exists keys(-digits)] {
set_cmd_unit_digits $unit $keys(-digits)

View File

@ -26,9 +26,11 @@ proc_redirect report_instance {
check_argc_eq1 "report_instance" $args
if { [info exists flags(-connections)] } {
# deprecated 2024-01-17
sta_warn 233 "report_instance -connections is deprecated."
}
if { [info exists flags(-verbose)] } {
# deprecated 2024-01-17
sta_warn 234 "report_instance -verbose is deprecated."
}
set instance_path [lindex $args 0]
@ -183,12 +185,15 @@ proc_redirect report_net {
check_argc_eq1 "report_net" $args
if { [info exists flags(-connections)] } {
# deprecated 2024-01-17
sta_warn 235 "report_net -connections is deprecated."
}
if { [info exists flags(-verbose)] } {
# deprecated 2024-01-17
sta_warn 236 "report_net -verbose is deprecated."
}
if { [info exists flags(-hier_pins)] } {
# deprecated 2024-01-17
sta_warn 237 "report_net -hier_pins is deprecated."
}

View File

@ -133,38 +133,6 @@ proc parse_connect_pin { arg } {
return [list $inst $port]
}
proc connect_pins { net pins } {
sta_warn 251 "connect_pins is deprecated. Use connect_pin."
# Visit the pins to make sure command will succeed.
set insts_ports [parse_connect_pins $pins]
if { $insts_ports == 0 } {
return 0
}
set net [get_net_arg "net" $net]
if { $net == "NULL" } {
return 0
}
foreach {inst port} $insts_ports {
connect_pin_cmd $inst $port $net
}
return 1
}
proc parse_connect_pins { arg } {
set path_regexp [path_regexp]
set inst_ports {}
# Copy backslashes that will be removed by foreach.
set arg [string map {\\ \\\\} $arg]
foreach obj $arg {
set inst_port [parse_connect_pin $obj]
if { $inst_port == 0 } {
return 0
}
set inst_ports [concat $inst_ports $inst_port]
}
return $inst_ports
}
################################################################
define_cmd_args "disconnect_pin" {net -all|pin}

View File

@ -1300,7 +1300,7 @@ proc set_clock_gating_check { args } {
flags {-rise -fall -high -low}
check_argc_eq0or1 "set_clock_gating_check" $args
set tr [parse_rise_fall_flags flags]
set rf [parse_rise_fall_flags flags]
set active_value ""
if {[info exists flags(-high)] && [info exists flags(-low)]} {
@ -1315,20 +1315,20 @@ proc set_clock_gating_check { args } {
sta_error 397 "missing -setup or -hold argument."
}
if [info exists keys(-hold)] {
set_clock_gating_check1 $args $tr "min" $keys(-hold) $active_value
set_clock_gating_check1 $args $rf "min" $keys(-hold) $active_value
}
if [info exists keys(-setup)] {
set_clock_gating_check1 $args $tr "max" $keys(-setup) $active_value
set_clock_gating_check1 $args $rf "max" $keys(-setup) $active_value
}
}
proc set_clock_gating_check1 { args tr setup_hold margin active_value } {
proc set_clock_gating_check1 { args rf setup_hold margin active_value } {
set margin [time_ui_sta $margin]
if { [llength $args] == 0 } {
if { $active_value != "" } {
sta_error 398 "-high and -low only permitted for pins and instances."
}
set_clock_gating_check_cmd $tr $setup_hold $margin
set_clock_gating_check_cmd $rf $setup_hold $margin
} elseif { [llength $args] == 1 } {
parse_clk_inst_port_pin_arg [lindex $args 0] clks insts pins
@ -1336,18 +1336,18 @@ proc set_clock_gating_check1 { args tr setup_hold margin active_value } {
sta_error 399 "-high and -low only permitted for pins and instances."
}
foreach clk $clks {
set_clock_gating_check_clk_cmd $clk $tr $setup_hold $margin
set_clock_gating_check_clk_cmd $clk $rf $setup_hold $margin
}
if { $active_value == "" } {
set active_value "X"
}
foreach pin $pins {
set_clock_gating_check_pin_cmd $pin $tr $setup_hold \
set_clock_gating_check_pin_cmd $pin $rf $setup_hold \
$margin $active_value
}
foreach inst $insts {
set_clock_gating_check_instance_cmd $inst $tr $setup_hold \
set_clock_gating_check_instance_cmd $inst $rf $setup_hold \
$margin $active_value
}
}
@ -1486,7 +1486,7 @@ proc set_clock_latency { args } {
parse_clk_port_pin_arg $objects clks pins
set tr [parse_rise_fall_flags flags]
set rf [parse_rise_fall_flags flags]
set min_max [parse_min_max_all_flags flags]
set pin_clk "NULL"
@ -1502,14 +1502,14 @@ proc set_clock_latency { args } {
set early_late [parse_early_late_all_flags flags]
foreach clk $clks {
set_clock_insertion_cmd $clk "NULL" $tr $min_max $early_late $delay
set_clock_insertion_cmd $clk "NULL" $rf $min_max $early_late $delay
}
foreach pin $pins {
# Source only allowed on clocks and clock pins.
if { ![is_clock_src $pin] } {
sta_error 409 "-source '[get_full_name $pin]' is not a clock pin."
}
set_clock_insertion_cmd $pin_clk $pin $tr $min_max $early_late $delay
set_clock_insertion_cmd $pin_clk $pin $rf $min_max $early_late $delay
}
} else {
# Latency.
@ -1518,10 +1518,10 @@ proc set_clock_latency { args } {
}
foreach clk $clks {
set_clock_latency_cmd $clk "NULL" $tr $min_max $delay
set_clock_latency_cmd $clk "NULL" $rf $min_max $delay
}
foreach pin $pins {
set_clock_latency_cmd $pin_clk $pin $tr $min_max $delay
set_clock_latency_cmd $pin_clk $pin $rf $min_max $delay
}
}
}
@ -1647,7 +1647,7 @@ proc set_clock_transition { args } {
parse_key_args "set_clock_transition" args keys {} \
flags {-rise -fall -max -min}
set tr [parse_rise_fall_flags flags]
set rf [parse_rise_fall_flags flags]
set min_max [parse_min_max_all_flags flags]
check_argc_eq2 "set_clock_transition" $args
@ -1658,7 +1658,7 @@ proc set_clock_transition { args } {
if { [$clk is_virtual] } {
sta_warn 419 "transition time can not be specified for virtual clocks."
} else {
set_clock_slew_cmd $clk $tr $min_max [time_ui_sta $slew]
set_clock_slew_cmd $clk $rf $min_max [time_ui_sta $slew]
}
}
}
@ -2353,7 +2353,7 @@ proc set_port_delay { cmd sta_cmd cmd_args port_dirs } {
set clk_rf "rise"
}
set tr [parse_rise_fall_flags flags]
set rf [parse_rise_fall_flags flags]
set min_max [parse_min_max_all_flags flags]
set add [info exists flags(-add_delay)]
set source_latency_included [info exists flags(-source_latency_included)]
@ -2366,7 +2366,7 @@ proc set_port_delay { cmd sta_cmd cmd_args port_dirs } {
} elseif { $clk != "NULL" && [lsearch [$clk sources] $pin] != -1 } {
sta_warn 441 "$cmd relative to a clock defined on the same port/pin not allowed."
} else {
$sta_cmd $pin $tr $clk $clk_rf $ref_pin\
$sta_cmd $pin $rf $clk $clk_rf $ref_pin\
$source_latency_included $network_latency_included \
$min_max $add $delay
}
@ -2667,11 +2667,11 @@ proc unset_port_delay { cmd swig_cmd cmd_args } {
set clk_rf "rise"
}
set tr [parse_rise_fall_flags flags]
set rf [parse_rise_fall_flags flags]
set min_max [parse_min_max_all_flags flags]
foreach pin $pins {
$swig_cmd $pin $tr $clk $clk_rf $min_max
$swig_cmd $pin $rf $clk $clk_rf $min_max
}
}
@ -2751,7 +2751,7 @@ define_cmd_args "set_drive" {[-rise] [-fall] [-min] [-max] \
proc set_drive { args } {
parse_key_args "set_drive" args keys {} flags {-rise -fall -min -max}
set tr [parse_rise_fall_flags flags]
set rf [parse_rise_fall_flags flags]
set min_max [parse_min_max_all_check_flags flags]
check_argc_eq2 "set_drive" $args
@ -2761,7 +2761,7 @@ proc set_drive { args } {
set res [resistance_ui_sta $res]
set ports [get_ports_error "ports" [lindex $args 1]]
foreach port $ports {
set_drive_resistance_cmd $port $tr $min_max $res
set_drive_resistance_cmd $port $rf $min_max $res
}
}
@ -2780,7 +2780,7 @@ proc set_driving_cell { args } {
-input_transition_rise -input_transition_fall} \
flags {-rise -fall -min -max -dont_scale -no_design_rule}
set tr [parse_rise_fall_flags flags]
set rf [parse_rise_fall_flags flags]
set min_max [parse_min_max_all_flags flags]
# -cell is an undocumented non-sdc alias for -lib_cell.
@ -2871,7 +2871,7 @@ proc set_driving_cell { args } {
set ports [get_ports_error "ports" [lindex $args 0]]
foreach port $ports {
set_drive_cell_cmd $library $cell $port $from_port \
$from_slew_rise $from_slew_fall $to_port $tr $min_max
$from_slew_rise $from_slew_fall $to_port $rf $min_max
}
}
@ -2898,7 +2898,7 @@ proc set_input_transition { args } {
parse_key_args "set_input_transition" args keys {-clock} \
flags {-rise -fall -max -min -clock_fall}
set tr [parse_rise_fall_flags flags]
set rf [parse_rise_fall_flags flags]
set min_max [parse_min_max_all_flags flags]
@ -2917,7 +2917,7 @@ proc set_input_transition { args } {
}
foreach port $ports {
set_input_slew_cmd $port $tr $min_max $slew
set_input_slew_cmd $port $rf $min_max $slew
}
}
@ -3082,7 +3082,7 @@ proc set_max_transition { args } {
set objects [lindex $args 1]
parse_clk_cell_port_args $objects clks cells ports
set tr [parse_rise_fall_flags flags]
set rf [parse_rise_fall_flags flags]
set path_types {}
if { ![info exists flags(-clock_path)] \
@ -3108,7 +3108,7 @@ proc set_max_transition { args } {
# -clock_path/-data_path and transition only apply to clock objects.
foreach path_type $path_types {
foreach clk $clks {
set_slew_limit_clk $clk $tr $path_type "max" $slew
set_slew_limit_clk $clk $rf $path_type "max" $slew
}
}
foreach cell $cells {
@ -3176,7 +3176,7 @@ proc set_timing_derate { args } {
sta_warn 469 "derating factor greater than 2.0."
}
set tr [parse_rise_fall_flags flags]
set rf [parse_rise_fall_flags flags]
set early_late [parse_early_late_flags flags]
set path_types {}
@ -3214,7 +3214,7 @@ proc set_timing_derate { args } {
}
foreach net $nets {
foreach path_type $path_types {
set_timing_derate_net_cmd $net $path_type $tr $early_late $derate
set_timing_derate_net_cmd $net $path_type $rf $early_late $derate
}
}
}
@ -3227,11 +3227,11 @@ proc set_timing_derate { args } {
foreach path_type $path_types {
foreach inst $insts {
set_timing_derate_inst_cmd $inst $derate_type $path_type \
$tr $early_late $derate
$rf $early_late $derate
}
foreach libcell $libcells {
set_timing_derate_cell_cmd $libcell $derate_type $path_type \
$tr $early_late $derate
$rf $early_late $derate
}
}
}
@ -3244,7 +3244,7 @@ proc set_timing_derate { args } {
}
foreach derate_type $derate_types {
foreach path_type $path_types {
set_timing_derate_cmd $derate_type $path_type $tr $early_late $derate
set_timing_derate_cmd $derate_type $path_type $rf $early_late $derate
}
}
}
@ -3266,13 +3266,13 @@ proc parse_from_arg { keys_var arg_error_var } {
if [info exists keys(-from)] {
set key "-from"
set tr "rise_fall"
set rf "rise_fall"
} elseif [info exists keys(-rise_from)] {
set key "-rise_from"
set tr "rise"
set rf "rise"
} elseif [info exists keys(-fall_from)] {
set key "-fall_from"
set tr "fall"
set rf "fall"
} else {
return "NULL"
}
@ -3283,7 +3283,7 @@ proc parse_from_arg { keys_var arg_error_var } {
sta_warn 471 "no valid objects specified for $key."
return "NULL"
}
return [make_exception_from $from_pins $from_clks $from_insts $tr]
return [make_exception_from $from_pins $from_clks $from_insts $rf]
}
# "arg_error" is set to notify the caller to cleanup and post error.
@ -3294,18 +3294,18 @@ proc parse_thrus_arg { args_var arg_error_var } {
set args_rtn {}
while { $args != {} } {
set arg [lindex $args 0]
set tr ""
set rf ""
if { $arg == "-through" } {
set tr "rise_fall"
set rf "rise_fall"
set key "-through"
} elseif { $arg == "-rise_through" } {
set tr "rise"
set rf "rise"
set key "-rise_through"
} elseif { $arg == "-fall_through" } {
set tr "fall"
set rf "fall"
set key "-fall_through"
}
if { $tr != "" } {
if { $rf != "" } {
if { [llength $args] > 1 } {
set args [lrange $args 1 end]
set arg [lindex $args 0]
@ -3315,7 +3315,7 @@ proc parse_thrus_arg { args_var arg_error_var } {
set arg_error 1
sta_warn 472 "no valid objects specified for $key"
} else {
lappend thrus [make_exception_thru $pins $nets $insts $tr]
lappend thrus [make_exception_thru $pins $nets $insts $rf]
}
}
} else {
@ -3673,7 +3673,7 @@ proc set_pvt { args } {
check_argc_eq1 "set_pvt" $args
set insts [get_instances_error "instances" [lindex $args 0]]
if { $min_max == "all" } {
if { $min_max == "min_max" } {
set_pvt_min_max $insts "min" keys
set_pvt_min_max $insts "max" keys
} else {

Some files were not shown because too many files have changed in this diff Show More