rel 2.6.0
Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
parent
948266945c
commit
d0287e5a47
|
|
@ -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
|
||||
|
|
|
|||
55
README.md
55
README.md
|
|
@ -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`).
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,5 +23,6 @@
|
|||
%include "NetworkEdit.i"
|
||||
%include "Sdf.i"
|
||||
%include "DelayCalc.i"
|
||||
%include "WriteSpice.i"
|
||||
%include "Parasitics.i"
|
||||
%include "Power.i"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() :
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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_) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 *)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
-------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ VertexNameLess::operator()(const Vertex *vertex1,
|
|||
////////////////////////////////////////////////////////////////
|
||||
|
||||
EdgeLess::EdgeLess(const Network *network,
|
||||
Graph *graph) :
|
||||
Graph *&graph) :
|
||||
pin_less_(network),
|
||||
graph_(graph)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -64,7 +64,5 @@ sourceTclFile(const char *filename,
|
|||
bool echo,
|
||||
bool verbose,
|
||||
Tcl_Interp *interp);
|
||||
bool
|
||||
is_regular_file(const char *filename);
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
32
sdc/Sdc.cc
32
sdc/Sdc.cc
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class Corner;
|
|||
|
||||
void
|
||||
writeSdf(const char *filename,
|
||||
Corner *corner,
|
||||
const Corner *corner,
|
||||
char divider,
|
||||
bool include_typ,
|
||||
int digits,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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); } );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 *
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 *
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
|
@ -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
|
||||
|
|
@ -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_;
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
86
tcl/Sdc.tcl
86
tcl/Sdc.tcl
|
|
@ -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
Loading…
Reference in New Issue