Update from upstream
Signed-off-by: Matt Liberty <mliberty@precisioninno.com>
This commit is contained in:
parent
2e59563717
commit
6c969fd8c6
150
CMakeLists.txt
150
CMakeLists.txt
|
|
@ -31,6 +31,7 @@ project(STA VERSION 2.5.0
|
|||
option(USE_CUDD "Use CUDD BDD package")
|
||||
option(CUDD_DIR "CUDD BDD package directory")
|
||||
|
||||
# Turn on to debug compiler args.
|
||||
set(CMAKE_VERBOSE_MAKEFILE OFF)
|
||||
|
||||
set(STA_HOME ${PROJECT_SOURCE_DIR})
|
||||
|
|
@ -65,6 +66,7 @@ set(STA_SOURCE
|
|||
dcalc/ArcDcalcWaveforms.cc
|
||||
dcalc/ArnoldiDelayCalc.cc
|
||||
dcalc/ArnoldiReduce.cc
|
||||
dcalc/CcsCeffDelayCalc.cc
|
||||
dcalc/DcalcAnalysisPt.cc
|
||||
dcalc/DelayCalc.cc
|
||||
dcalc/DelayCalcBase.cc
|
||||
|
|
@ -145,6 +147,7 @@ set(STA_SOURCE
|
|||
sdf/SdfReader.cc
|
||||
sdf/SdfWriter.cc
|
||||
|
||||
search/Bdd.cc
|
||||
search/Bfs.cc
|
||||
search/CheckMaxSkews.cc
|
||||
search/CheckMinPeriods.cc
|
||||
|
|
@ -154,6 +157,7 @@ set(STA_SOURCE
|
|||
search/CheckSlewLimits.cc
|
||||
search/CheckTiming.cc
|
||||
search/ClkInfo.cc
|
||||
search/ClkLatency.cc
|
||||
search/ClkNetwork.cc
|
||||
search/ClkSkew.cc
|
||||
search/Corner.cc
|
||||
|
|
@ -188,6 +192,7 @@ set(STA_SOURCE
|
|||
search/VisitPathGroupVertices.cc
|
||||
search/WorstSlack.cc
|
||||
search/WritePathSpice.cc
|
||||
search/WriteSpice.cc
|
||||
|
||||
power/Power.cc
|
||||
power/ReadVcdActivities.cc
|
||||
|
|
@ -300,77 +305,6 @@ bison_target(SdfParser ${STA_HOME}/sdf/SdfParse.yy ${CMAKE_CURRENT_BINARY_DIR}/S
|
|||
add_flex_bison_dependency(SdfLex SdfParser)
|
||||
|
||||
|
||||
################################################################
|
||||
|
||||
find_package(SWIG 3.0 REQUIRED)
|
||||
include(UseSWIG)
|
||||
|
||||
set(STA_SWIG_FILE app/StaApp.i)
|
||||
|
||||
set_property(SOURCE ${STA_SWIG_FILE}
|
||||
PROPERTY CPLUSPLUS ON
|
||||
)
|
||||
# Ubuntu 18.04 cmake version 3.10.2 does not support the
|
||||
# COMPILE_OPTIONS and INCLUDE_DIRECTORIES properties, so cram
|
||||
# them into SWIG_FLAGS for the time being.
|
||||
set_property(SOURCE ${STA_SWIG_FILE}
|
||||
PROPERTY SWIG_FLAGS
|
||||
-module sta
|
||||
-namespace -prefix sta
|
||||
-I${STA_HOME}/tcl
|
||||
-I${STA_HOME}/sdf
|
||||
-I${STA_HOME}/dcalc
|
||||
-I${STA_HOME}/parasitics
|
||||
-I${STA_HOME}/power
|
||||
-I${STA_HOME}/verilog
|
||||
)
|
||||
|
||||
set(SWIG_FILES
|
||||
${STA_HOME}/dcalc/DelayCalc.i
|
||||
${STA_HOME}/parasitics/Parasitics.i
|
||||
${STA_HOME}/power/Power.i
|
||||
${STA_HOME}/sdf/Sdf.i
|
||||
${STA_HOME}/tcl/Exception.i
|
||||
${STA_HOME}/tcl/StaTcl.i
|
||||
${STA_HOME}/tcl/StaTclTypes.i
|
||||
${STA_HOME}/tcl/NetworkEdit.i
|
||||
${STA_HOME}/verilog/Verilog.i
|
||||
)
|
||||
|
||||
set_property(SOURCE ${STA_SWIG_FILE}
|
||||
PROPERTY DEPENDS ${SWIG_FILES}
|
||||
)
|
||||
|
||||
swig_add_library(sta_swig
|
||||
LANGUAGE tcl
|
||||
TYPE STATIC
|
||||
SOURCES ${STA_SWIG_FILE}
|
||||
)
|
||||
|
||||
get_target_property(STA_SWIG_CXX_FILE sta_swig SOURCES)
|
||||
|
||||
set_source_files_properties(${STA_SWIG_CXX_FILE}
|
||||
PROPERTIES
|
||||
# No simple way to modify the swig template that emits code full of warnings
|
||||
# so suppress them.
|
||||
COMPILE_OPTIONS "-Wno-cast-qual;-Wno-missing-braces;-Wno-deprecated-declarations"
|
||||
)
|
||||
|
||||
target_link_libraries(sta_swig
|
||||
PUBLIC
|
||||
OpenSTA
|
||||
)
|
||||
|
||||
# result build/CMakeFiles/sta_swig.dir/StaAppTCL_wrap.cxx
|
||||
target_include_directories(sta_swig
|
||||
PUBLIC
|
||||
include/sta
|
||||
|
||||
PRIVATE
|
||||
${STA_HOME}
|
||||
${TCL_INCLUDE_PATH}
|
||||
)
|
||||
|
||||
################################################################
|
||||
|
||||
set(STA_TCL_INIT ${CMAKE_CURRENT_BINARY_DIR}/StaTclInitVar.cc)
|
||||
|
|
@ -469,6 +403,80 @@ configure_file(${STA_HOME}/util/StaConfig.hh.cmake
|
|||
${STA_HOME}/include/sta/StaConfig.hh
|
||||
)
|
||||
|
||||
###########################################################
|
||||
# Swig
|
||||
###########################################################
|
||||
|
||||
find_package(SWIG 3.0 REQUIRED)
|
||||
include(UseSWIG)
|
||||
|
||||
set(STA_SWIG_FILE app/StaApp.i)
|
||||
|
||||
set_property(SOURCE ${STA_SWIG_FILE}
|
||||
PROPERTY CPLUSPLUS ON
|
||||
)
|
||||
# Ubuntu 18.04 cmake version 3.10.2 does not support the
|
||||
# COMPILE_OPTIONS and INCLUDE_DIRECTORIES properties, so cram
|
||||
# them into SWIG_FLAGS for the time being.
|
||||
set_property(SOURCE ${STA_SWIG_FILE}
|
||||
PROPERTY SWIG_FLAGS
|
||||
-module sta
|
||||
-namespace -prefix sta
|
||||
-I${STA_HOME}/tcl
|
||||
-I${STA_HOME}/sdf
|
||||
-I${STA_HOME}/dcalc
|
||||
-I${STA_HOME}/parasitics
|
||||
-I${STA_HOME}/power
|
||||
-I${STA_HOME}/verilog
|
||||
)
|
||||
|
||||
set(SWIG_FILES
|
||||
${STA_HOME}/dcalc/DelayCalc.i
|
||||
${STA_HOME}/parasitics/Parasitics.i
|
||||
${STA_HOME}/power/Power.i
|
||||
${STA_HOME}/sdf/Sdf.i
|
||||
${STA_HOME}/tcl/Exception.i
|
||||
${STA_HOME}/tcl/StaTcl.i
|
||||
${STA_HOME}/tcl/StaTclTypes.i
|
||||
${STA_HOME}/tcl/NetworkEdit.i
|
||||
${STA_HOME}/verilog/Verilog.i
|
||||
)
|
||||
|
||||
set_property(SOURCE ${STA_SWIG_FILE}
|
||||
PROPERTY DEPENDS ${SWIG_FILES}
|
||||
)
|
||||
|
||||
swig_add_library(sta_swig
|
||||
LANGUAGE tcl
|
||||
TYPE STATIC
|
||||
SOURCES ${STA_SWIG_FILE}
|
||||
)
|
||||
|
||||
get_target_property(STA_SWIG_CXX_FILE sta_swig SOURCES)
|
||||
|
||||
set_source_files_properties(${STA_SWIG_CXX_FILE}
|
||||
PROPERTIES
|
||||
# No simple way to modify the swig template that emits code full of warnings
|
||||
# so suppress them.
|
||||
COMPILE_OPTIONS "-Wno-cast-qual;-Wno-missing-braces;-Wno-deprecated-declarations"
|
||||
INCLUDE_DIRECTORIES "${CUDD_INCLUDE}"
|
||||
)
|
||||
|
||||
target_link_libraries(sta_swig
|
||||
PUBLIC
|
||||
OpenSTA
|
||||
)
|
||||
|
||||
# result build/CMakeFiles/sta_swig.dir/StaAppTCL_wrap.cxx
|
||||
target_include_directories(sta_swig
|
||||
PUBLIC
|
||||
include/sta
|
||||
|
||||
PRIVATE
|
||||
${STA_HOME}
|
||||
${TCL_INCLUDE_PATH}
|
||||
)
|
||||
|
||||
###########################################################
|
||||
# Library
|
||||
###########################################################
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@
|
|||
|
||||
#include "ArcDelayCalc.hh"
|
||||
|
||||
#include "Liberty.hh"
|
||||
#include "TimingArc.hh"
|
||||
#include "Network.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
ArcDelayCalc::ArcDelayCalc(StaState *sta):
|
||||
|
|
@ -53,19 +57,85 @@ ArcDcalcArg::ArcDcalcArg() :
|
|||
{
|
||||
}
|
||||
|
||||
ArcDcalcArg::ArcDcalcArg(const Pin *drvr_pin,
|
||||
ArcDcalcArg::ArcDcalcArg(const Pin *in_pin,
|
||||
const Pin *drvr_pin,
|
||||
Edge *edge,
|
||||
const TimingArc *arc,
|
||||
const Slew in_slew,
|
||||
const Parasitic *parasitic) :
|
||||
in_pin_(in_pin),
|
||||
drvr_pin_(drvr_pin),
|
||||
edge_(edge),
|
||||
arc_(arc),
|
||||
in_slew_(in_slew),
|
||||
parasitic_(parasitic)
|
||||
parasitic_(parasitic),
|
||||
input_delay_(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
ArcDcalcArg::ArcDcalcArg(const Pin *in_pin,
|
||||
const Pin *drvr_pin,
|
||||
Edge *edge,
|
||||
const TimingArc *arc,
|
||||
float input_delay) :
|
||||
in_pin_(in_pin),
|
||||
drvr_pin_(drvr_pin),
|
||||
edge_(edge),
|
||||
arc_(arc),
|
||||
in_slew_(0.0),
|
||||
parasitic_(nullptr),
|
||||
input_delay_(input_delay)
|
||||
{
|
||||
}
|
||||
|
||||
ArcDcalcArg::ArcDcalcArg(const ArcDcalcArg &arg) :
|
||||
in_pin_(arg.in_pin_),
|
||||
drvr_pin_(arg.drvr_pin_),
|
||||
edge_(arg.edge_),
|
||||
arc_(arg.arc_),
|
||||
in_slew_(arg.in_slew_),
|
||||
parasitic_(arg.parasitic_),
|
||||
input_delay_(arg.input_delay_)
|
||||
{
|
||||
}
|
||||
|
||||
const RiseFall *
|
||||
ArcDcalcArg::inEdge() const
|
||||
{
|
||||
return arc_->fromEdge()->asRiseFall();
|
||||
}
|
||||
|
||||
LibertyCell *
|
||||
ArcDcalcArg::drvrCell() const
|
||||
{
|
||||
|
||||
return arc_->to()->libertyCell();
|
||||
}
|
||||
|
||||
const LibertyLibrary *
|
||||
ArcDcalcArg::drvrLibrary() const
|
||||
{
|
||||
return arc_->to()->libertyLibrary();
|
||||
}
|
||||
|
||||
const RiseFall *
|
||||
ArcDcalcArg::drvrEdge() const
|
||||
{
|
||||
return arc_->toEdge()->asRiseFall();
|
||||
}
|
||||
|
||||
const Net *
|
||||
ArcDcalcArg::drvrNet(const Network *network) const
|
||||
{
|
||||
return network->net(drvr_pin_);
|
||||
}
|
||||
|
||||
void
|
||||
ArcDcalcArg::setInSlew(Slew in_slew)
|
||||
{
|
||||
in_slew_ = in_slew;
|
||||
}
|
||||
|
||||
void
|
||||
ArcDcalcArg::setParasitic(const Parasitic *parasitic)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -314,7 +314,8 @@ ArnoldiReduce::findPt(ParasiticNode *node)
|
|||
rcmodel *
|
||||
ArnoldiReduce::makeRcmodelDrv()
|
||||
{
|
||||
ParasiticNode *drv_node = parasitics_->findNode(parasitic_network_, drvr_pin_);
|
||||
ParasiticNode *drv_node =
|
||||
parasitics_->findParasiticNode(parasitic_network_, drvr_pin_);
|
||||
ts_point *pdrv = findPt(drv_node);
|
||||
makeRcmodelDfs(pdrv);
|
||||
getRC();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,678 @@
|
|||
// 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 "CcsCeffDelayCalc.hh"
|
||||
|
||||
#include "Debug.hh"
|
||||
#include "Units.hh"
|
||||
#include "Liberty.hh"
|
||||
#include "TimingArc.hh"
|
||||
#include "Network.hh"
|
||||
#include "Graph.hh"
|
||||
#include "Corner.hh"
|
||||
#include "DcalcAnalysisPt.hh"
|
||||
#include "Parasitics.hh"
|
||||
#include "GraphDelayCalc.hh"
|
||||
#include "DmpDelayCalc.hh"
|
||||
#include "FindRoot.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
// Implementaion based on:
|
||||
// "Gate Delay Estimation with Library Compatible Current Source Models
|
||||
// and Effective Capacitance", D. Garyfallou et al,
|
||||
// IEEE Transactions on Very Large Scale Integration (VLSI) Systems, March 2021
|
||||
|
||||
using std::abs;
|
||||
using std::exp;
|
||||
using std::make_shared;
|
||||
|
||||
ArcDelayCalc *
|
||||
makeCcsCeffDelayCalc(StaState *sta)
|
||||
{
|
||||
return new CcsCeffDelayCalc(sta);
|
||||
}
|
||||
|
||||
CcsCeffDelayCalc::CcsCeffDelayCalc(StaState *sta) :
|
||||
LumpedCapDelayCalc(sta),
|
||||
output_waveforms_(nullptr),
|
||||
// Includes the Vh:Vdd region.
|
||||
region_count_(0),
|
||||
vl_fail_(false),
|
||||
capacitance_unit_(units_->capacitanceUnit()),
|
||||
table_dcalc_(makeDmpCeffElmoreDelayCalc(sta))
|
||||
{
|
||||
}
|
||||
|
||||
CcsCeffDelayCalc::~CcsCeffDelayCalc()
|
||||
{
|
||||
delete table_dcalc_;
|
||||
}
|
||||
|
||||
ArcDelayCalc *
|
||||
CcsCeffDelayCalc::copy()
|
||||
{
|
||||
return new CcsCeffDelayCalc(this);
|
||||
}
|
||||
|
||||
ArcDcalcResult
|
||||
CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
|
||||
const TimingArc *arc,
|
||||
const Slew &in_slew,
|
||||
float load_cap,
|
||||
const Parasitic *parasitic,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap)
|
||||
{
|
||||
bool vdd_exists;
|
||||
LibertyCell *drvr_cell = arc->to()->libertyCell();
|
||||
const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary();
|
||||
const RiseFall *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_;
|
||||
in_slew_ = in_slew;
|
||||
load_cap_ = load_cap;
|
||||
parasitic_ = parasitic;
|
||||
drvr_cell->ensureVoltageWaveforms();
|
||||
output_waveforms_ = nullptr;
|
||||
|
||||
GateTableModel *table_model = gateTableModel(arc, dcalc_ap);
|
||||
if (table_model && parasitic) {
|
||||
OutputWaveforms *output_waveforms = table_model->outputWaveforms();
|
||||
parasitics_->piModel(parasitic, c2_, rpi_, c1_);
|
||||
if (output_waveforms
|
||||
&& rpi_ > 0.0 && c1_ > 0.0
|
||||
// Bounds check because extrapolating waveforms does not work for shit.
|
||||
&& output_waveforms->slewAxis()->inBounds(in_slew_)
|
||||
&& output_waveforms->capAxis()->inBounds(c2_)
|
||||
&& output_waveforms->capAxis()->inBounds(load_cap_)) {
|
||||
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());
|
||||
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);
|
||||
}
|
||||
}
|
||||
return table_dcalc_->gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic,
|
||||
load_pin_index_map, dcalc_ap);
|
||||
}
|
||||
|
||||
void
|
||||
CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf,
|
||||
// Return values.
|
||||
ArcDelay &gate_delay,
|
||||
Slew &drvr_slew)
|
||||
{
|
||||
initRegions(drvr_library, rf);
|
||||
findCsmWaveform();
|
||||
ref_time_ = output_waveforms_->referenceTime(in_slew_);
|
||||
gate_delay = region_times_[region_vth_idx_] - ref_time_;
|
||||
drvr_slew = abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]);
|
||||
debugPrint(debug_, "ccs_dcalc", 2,
|
||||
"gate_delay %s drvr_slew %s (initial)",
|
||||
delayAsString(gate_delay, this),
|
||||
delayAsString(drvr_slew, this));
|
||||
float prev_drvr_slew = delayAsFloat(drvr_slew);
|
||||
constexpr int max_iterations = 5;
|
||||
for (int iter = 0; iter < max_iterations; iter++) {
|
||||
debugPrint(debug_, "ccs_dcalc", 2, "iteration %d", iter);
|
||||
// Init drvr ramp model for vl.
|
||||
for (size_t i = 0; i <= region_count_; i++) {
|
||||
region_ramp_times_[i] = region_times_[i];
|
||||
if (i < region_count_)
|
||||
region_ramp_slopes_[i] = (region_volts_[i + 1] - region_volts_[i])
|
||||
/ (region_times_[i + 1] - region_times_[i]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < region_count_; i++) {
|
||||
double v1 = region_volts_[i];
|
||||
double v2 = region_volts_[i + 1];
|
||||
double t1 = region_times_[i];
|
||||
double t2 = region_times_[i + 1];
|
||||
|
||||
// Receiver cap Cp(l) assumed constant and included in c1.
|
||||
// Note that eqn 8 in the ref'd paper does not properly account
|
||||
// for the charge on c1 from previous segments so it does not
|
||||
// work well.
|
||||
double c1_v1, c1_v2, ignore;
|
||||
vl(t1, rpi_ * c1_, c1_v1, ignore);
|
||||
vl(t2, rpi_ * c1_, c1_v2, ignore);
|
||||
double q1 = v1 * c2_ + c1_v1 * c1_;
|
||||
double q2 = v2 * c2_ + c1_v2 * c1_;
|
||||
double ceff = (q2 - q1) / (v2 - v1);
|
||||
|
||||
debugPrint(debug_, "ccs_dcalc", 2, "ceff %s",
|
||||
capacitance_unit_->asString(ceff));
|
||||
region_ceff_[i] = ceff;
|
||||
}
|
||||
findCsmWaveform();
|
||||
gate_delay = region_times_[region_vth_idx_] - ref_time_;
|
||||
drvr_slew = abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]);
|
||||
debugPrint(debug_, "ccs_dcalc", 2,
|
||||
"gate_delay %s drvr_slew %s",
|
||||
delayAsString(gate_delay, this),
|
||||
delayAsString(drvr_slew, this));
|
||||
if (abs(delayAsFloat(drvr_slew) - prev_drvr_slew) < .01 * prev_drvr_slew)
|
||||
break;
|
||||
prev_drvr_slew = delayAsFloat(drvr_slew);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CcsCeffDelayCalc::initRegions(const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf)
|
||||
{
|
||||
// Falling waveforms are treated as rising to simplify the conditionals.
|
||||
if (rf == RiseFall::fall()) {
|
||||
vl_ = (1.0 - drvr_library->slewUpperThreshold(rf)) * vdd_;
|
||||
vh_ = (1.0 - drvr_library->slewLowerThreshold(rf)) * vdd_;
|
||||
}
|
||||
|
||||
// Includes the Vh:Vdd region.
|
||||
region_count_ = 7;
|
||||
region_volts_.resize(region_count_ + 1);
|
||||
region_ceff_.resize(region_count_ + 1);
|
||||
region_times_.resize(region_count_ + 1);
|
||||
region_begin_times_.resize(region_count_ + 1);
|
||||
region_end_times_.resize(region_count_ + 1);
|
||||
region_time_offsets_.resize(region_count_ + 1);
|
||||
region_ramp_times_.resize(region_count_ + 1);
|
||||
region_ramp_slopes_.resize(region_count_ + 1);
|
||||
|
||||
region_vl_idx_ = 1;
|
||||
region_vh_idx_ = region_count_ - 1;
|
||||
|
||||
double vth_vh = (vh_ - vth_);
|
||||
switch (region_count_) {
|
||||
case 4:
|
||||
region_vth_idx_ = 2;
|
||||
region_volts_ = {0.0, vl_, vth_, vh_, vdd_};
|
||||
break;
|
||||
case 5: {
|
||||
region_vth_idx_ = 2;
|
||||
double v1 = vth_ + .7 * vth_vh;
|
||||
region_volts_ = {0.0, vl_, vth_, v1, vh_, vdd_};
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
region_vth_idx_ = 2;
|
||||
double v1 = vth_ + .3 * vth_vh;
|
||||
double v2 = vth_ + .6 * vth_vh;
|
||||
region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, vdd_};
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
region_vth_idx_ = 2;
|
||||
region_vh_idx_ = 5;
|
||||
double v1 = vth_ + .3 * vth_vh;
|
||||
double v2 = vth_ + .6 * vth_vh;
|
||||
double v3 = vh_ + .5 * (vdd_ - vh_);
|
||||
region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, v3, vdd_};
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
region_vth_idx_ = 2;
|
||||
region_vh_idx_ = 6;
|
||||
double v1 = vth_ + .25 * vth_vh;
|
||||
double v2 = vth_ + .50 * vth_vh;
|
||||
double v3 = vth_ + .75 * vth_vh;
|
||||
double v4 = vh_ + .5 * (vdd_ - vh_);
|
||||
region_volts_ = {0.0, vl_, vth_, v1, v2, v3, vh_, v4, vdd_};
|
||||
break;
|
||||
}
|
||||
case 9: {
|
||||
region_vth_idx_ = 2;
|
||||
region_vh_idx_ = 7;
|
||||
double v1 = vth_ + .2 * vth_vh;
|
||||
double v2 = vth_ + .4 * vth_vh;
|
||||
double v3 = vth_ + .6 * vth_vh;
|
||||
double v4 = vth_ + .8 * vth_vh;
|
||||
double v5 = vh_ + .5 * (vdd_ - vh_);
|
||||
region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, vdd_};
|
||||
break;
|
||||
}
|
||||
case 10: {
|
||||
region_vth_idx_ = 2;
|
||||
region_vh_idx_ = 7;
|
||||
double v1 = vth_ + .2 * vth_vh;
|
||||
double v2 = vth_ + .4 * vth_vh;
|
||||
double v3 = vth_ + .6 * vth_vh;
|
||||
double v4 = vth_ + .8 * vth_vh;
|
||||
double v5 = vh_ + .3 * (vdd_ - vh_);
|
||||
double v6 = vh_ + .6 * (vdd_ - vh_);
|
||||
region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, v6, vdd_};
|
||||
break;
|
||||
}
|
||||
default:
|
||||
report_->error(1701, "unsupported ccs region count.");
|
||||
break;
|
||||
}
|
||||
fill(region_ceff_.begin(), region_ceff_.end(), c2_ + c1_);
|
||||
}
|
||||
|
||||
void
|
||||
CcsCeffDelayCalc::findCsmWaveform()
|
||||
{
|
||||
for (size_t i = 0; i < region_count_; i++) {
|
||||
double t1 = output_waveforms_->voltageTime(in_slew_, region_ceff_[i],
|
||||
region_volts_[i]);
|
||||
double t2 = output_waveforms_->voltageTime(in_slew_, region_ceff_[i],
|
||||
region_volts_[i + 1]);
|
||||
region_begin_times_[i] = t1;
|
||||
region_end_times_[i] = t2;
|
||||
double time_offset = (i == 0)
|
||||
? 0.0
|
||||
: t1 - (region_end_times_[i - 1] - region_time_offsets_[i - 1]);
|
||||
region_time_offsets_[i] = time_offset;
|
||||
|
||||
if (i == 0)
|
||||
region_times_[i] = t1 - time_offset;
|
||||
region_times_[i + 1] = t2 - time_offset;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
ArcDcalcResult
|
||||
CcsCeffDelayCalc::makeResult(const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf,
|
||||
ArcDelay &gate_delay,
|
||||
Slew &drvr_slew,
|
||||
const LoadPinIndexMap &load_pin_index_map)
|
||||
{
|
||||
ArcDcalcResult dcalc_result(load_pin_index_map.size());
|
||||
debugPrint(debug_, "ccs_dcalc", 2,
|
||||
"gate_delay %s drvr_slew %s",
|
||||
delayAsString(gate_delay, this),
|
||||
delayAsString(drvr_slew, this));
|
||||
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;
|
||||
ArcDelay wire_delay;
|
||||
Slew load_slew;
|
||||
loadDelaySlew(load_pin, drvr_library, rf, drvr_slew, wire_delay, load_slew);
|
||||
dcalc_result.setWireDelay(load_idx, wire_delay);
|
||||
dcalc_result.setLoadSlew(load_idx, load_slew);
|
||||
}
|
||||
return dcalc_result;
|
||||
}
|
||||
|
||||
void
|
||||
CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin,
|
||||
const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf,
|
||||
Slew &drvr_slew,
|
||||
// Return values.
|
||||
ArcDelay &wire_delay,
|
||||
Slew &load_slew)
|
||||
{
|
||||
ArcDelay wire_delay1 = 0.0;
|
||||
Slew load_slew1 = drvr_slew;
|
||||
bool elmore_exists = false;
|
||||
float elmore = 0.0;
|
||||
if (parasitic_
|
||||
&& parasitics_->isPiElmore(parasitic_))
|
||||
parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists);
|
||||
|
||||
if (elmore_exists &&
|
||||
(elmore == 0.0
|
||||
// Elmore delay is small compared to driver slew.
|
||||
|| elmore < delayAsFloat(drvr_slew) * 1e-3)) {
|
||||
wire_delay1 = elmore;
|
||||
load_slew1 = drvr_slew;
|
||||
}
|
||||
else
|
||||
loadDelaySlew(load_pin, drvr_slew, elmore, wire_delay1, load_slew1);
|
||||
|
||||
thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew);
|
||||
wire_delay = wire_delay1;
|
||||
load_slew = load_slew1;
|
||||
}
|
||||
|
||||
void
|
||||
CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin,
|
||||
Slew &drvr_slew,
|
||||
float elmore,
|
||||
// Return values.
|
||||
ArcDelay &delay,
|
||||
Slew &slew)
|
||||
{
|
||||
for (size_t i = 0; i <= region_count_; i++) {
|
||||
region_ramp_times_[i] = region_times_[i];
|
||||
if (i < region_count_)
|
||||
region_ramp_slopes_[i] = (region_volts_[i + 1] - region_volts_[i])
|
||||
/ (region_times_[i + 1] - region_times_[i]);
|
||||
}
|
||||
|
||||
vl_fail_ = false;
|
||||
double t_vl = findVlTime(vl_, elmore);
|
||||
double t_vth = findVlTime(vth_, elmore);
|
||||
double t_vh = findVlTime(vh_, elmore);
|
||||
if (!vl_fail_) {
|
||||
delay = t_vth - region_times_[region_vth_idx_];
|
||||
slew = t_vh - t_vl;
|
||||
}
|
||||
else {
|
||||
delay = elmore;
|
||||
slew = drvr_slew;
|
||||
fail("load delay threshold crossing");
|
||||
}
|
||||
debugPrint(debug_, "ccs_dcalc", 2,
|
||||
"load %s delay %s slew %s",
|
||||
network_->pathName(load_pin),
|
||||
delayAsString(delay, this),
|
||||
delayAsString(slew, this));
|
||||
}
|
||||
|
||||
// Elmore (one pole) response to ramp with slope slew.
|
||||
static void
|
||||
rampElmoreV(double t,
|
||||
double slew,
|
||||
double elmore,
|
||||
// Return values.
|
||||
double &v,
|
||||
double &dv_dt)
|
||||
{
|
||||
double exp_t = 1.0 - exp2(-t / elmore);
|
||||
v = slew * (t - elmore * exp_t);
|
||||
// First derivative of loadVl dv/dt.
|
||||
dv_dt = slew * exp_t;
|
||||
}
|
||||
|
||||
// Elmore (one pole) response to 2 segment ramps [0, vth] slew1, [vth, vdd] slew2.
|
||||
void
|
||||
CcsCeffDelayCalc::vl(double t,
|
||||
double elmore,
|
||||
// Return values.
|
||||
double &vl,
|
||||
double &dvl_dt)
|
||||
{
|
||||
vl = 0.0;
|
||||
dvl_dt = 0.0;
|
||||
for (size_t i = 0; i < region_count_; i++) {
|
||||
double t_begin = region_ramp_times_[i];
|
||||
double t_end = region_ramp_times_[i + 1];
|
||||
double ramp_slope = region_ramp_slopes_[i];
|
||||
if (t >= t_begin) {
|
||||
double v, dv_dt;
|
||||
rampElmoreV(t - t_begin, ramp_slope, elmore, v, dv_dt);
|
||||
vl += v;
|
||||
dvl_dt += dv_dt;
|
||||
}
|
||||
if (t > t_end) {
|
||||
double v, dv_dt;
|
||||
rampElmoreV(t - t_end, ramp_slope, elmore, v, dv_dt);
|
||||
vl -= v;
|
||||
dvl_dt -= dv_dt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for debugging
|
||||
double
|
||||
CcsCeffDelayCalc::vl(double t,
|
||||
double elmore)
|
||||
{
|
||||
double vl1, dvl_dt;
|
||||
vl(t, elmore, vl1, dvl_dt);
|
||||
return vl1;
|
||||
}
|
||||
|
||||
double
|
||||
CcsCeffDelayCalc::findVlTime(double v,
|
||||
double elmore)
|
||||
{
|
||||
double t_init = region_ramp_times_[0];
|
||||
double t_final = region_ramp_times_[region_count_];
|
||||
bool root_fail = false;
|
||||
double time = findRoot([=] (double t,
|
||||
double &y,
|
||||
double &dy) {
|
||||
vl(t, elmore, y, dy);
|
||||
y -= v;
|
||||
}, t_init, t_final + elmore * 3.0, .001, 20, root_fail);
|
||||
vl_fail_ |= root_fail;
|
||||
return time;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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)
|
||||
{
|
||||
bool dcalc_success = makeWaveformPreamble(in_pin, in_rf, drvr_pin,
|
||||
drvr_rf, corner, min_max);
|
||||
if (dcalc_success)
|
||||
return drvrWaveform(in_slew_, drvr_rf);
|
||||
else
|
||||
return Table1();
|
||||
}
|
||||
|
||||
Table1
|
||||
CcsCeffDelayCalc::drvrWaveform(const Slew &in_slew,
|
||||
const RiseFall *drvr_rf)
|
||||
{
|
||||
// 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_) {
|
||||
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) {
|
||||
FloatSeq *load_times = new FloatSeq;
|
||||
FloatSeq *load_volts = new FloatSeq;
|
||||
double t_vh = findVlTime(vh_, elmore);
|
||||
double dt = t_vh / 20.0;
|
||||
double v_final = vh_ + (vdd_ - vh_) * .8;
|
||||
double v = 0.0;
|
||||
for (double t = 0; v < v_final; t += dt) {
|
||||
load_times->push_back(t);
|
||||
|
||||
double ignore;
|
||||
vl(t, elmore, v, ignore);
|
||||
double v1 = (drvr_rf == RiseFall::rise()) ? v : vdd_ - v;
|
||||
load_volts->push_back(v1);
|
||||
}
|
||||
TableAxisPtr load_time_axis = make_shared<TableAxis>(TableAxisVariable::time,
|
||||
load_times);
|
||||
Table1 load_table(load_volts, load_time_axis);
|
||||
return load_table;
|
||||
}
|
||||
}
|
||||
return Table1();
|
||||
}
|
||||
|
||||
Table1
|
||||
CcsCeffDelayCalc::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)
|
||||
{
|
||||
bool elmore_exists = false;
|
||||
float elmore = 0.0;
|
||||
if (parasitic_) {
|
||||
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) {
|
||||
FloatSeq *load_times = new FloatSeq;
|
||||
FloatSeq *load_volts = new FloatSeq;
|
||||
for (size_t j = 0; j <= region_count_; j++) {
|
||||
double t = region_ramp_times_[j];
|
||||
load_times->push_back(t);
|
||||
double v = 0.0;
|
||||
for (size_t i = 0; i < region_count_; i++) {
|
||||
double t_begin = region_ramp_times_[i];
|
||||
double t_end = region_ramp_times_[i + 1];
|
||||
double ramp_slope = region_ramp_slopes_[i];
|
||||
if (t >= t_begin)
|
||||
v += (t - t_begin) * ramp_slope;
|
||||
if (t > t_end)
|
||||
v -= (t - t_end) * ramp_slope;
|
||||
}
|
||||
double v1 = (drvr_rf == RiseFall::rise()) ? v : vdd_ - v;
|
||||
load_volts->push_back(v1);
|
||||
}
|
||||
TableAxisPtr load_time_axis = make_shared<TableAxis>(TableAxisVariable::time,
|
||||
load_times);
|
||||
Table1 load_table(load_volts, load_time_axis);
|
||||
return load_table;
|
||||
}
|
||||
}
|
||||
return Table1();
|
||||
}
|
||||
|
||||
bool
|
||||
CcsCeffDelayCalc::makeWaveformPreamble(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max)
|
||||
{
|
||||
Vertex *in_vertex = graph_->pinLoadVertex(in_pin);
|
||||
Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin);
|
||||
Edge *edge = nullptr;
|
||||
VertexInEdgeIterator edge_iter(drvr_vertex, graph_);
|
||||
while (edge_iter.hasNext()) {
|
||||
edge = edge_iter.next();
|
||||
Vertex *from_vertex = edge->from(graph_);
|
||||
const Pin *from_pin = from_vertex->pin();
|
||||
if (from_pin == in_pin)
|
||||
break;
|
||||
}
|
||||
if (edge) {
|
||||
TimingArc *arc = nullptr;
|
||||
for (TimingArc *arc1 : edge->timingArcSet()->arcs()) {
|
||||
if (arc1->fromEdge()->asRiseFall() == in_rf
|
||||
&& arc1->toEdge()->asRiseFall() == drvr_rf) {
|
||||
arc = arc1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (arc) {
|
||||
DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
|
||||
const Slew &in_slew = graph_->slew(in_vertex, in_rf, dcalc_ap->index());
|
||||
parasitic_ = arc_delay_calc_->findParasitic(drvr_pin, drvr_rf, dcalc_ap);
|
||||
if (parasitic_) {
|
||||
parasitics_->piModel(parasitic_, c2_, rpi_, c1_);
|
||||
LoadPinIndexMap load_pin_index_map =
|
||||
graph_delay_calc_->makeLoadPinIndexMap(drvr_vertex);
|
||||
gateDelay(drvr_pin, arc, in_slew, load_cap_, parasitic_,
|
||||
load_pin_index_map, dcalc_ap);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
string
|
||||
CcsCeffDelayCalc::reportGateDelay(const Pin *drvr_pin,
|
||||
const TimingArc *arc,
|
||||
const Slew &in_slew,
|
||||
float load_cap,
|
||||
const Parasitic *parasitic,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap,
|
||||
int digits)
|
||||
{
|
||||
Parasitic *pi_elmore = nullptr;
|
||||
const RiseFall *rf = arc->toEdge()->asRiseFall();
|
||||
if (parasitic && !parasitics_->isPiElmore(parasitic)) {
|
||||
const ParasiticAnalysisPt *ap = dcalc_ap->parasiticAnalysisPt();
|
||||
pi_elmore = parasitics_->reduceToPiElmore(parasitic, drvr_pin_, rf,
|
||||
dcalc_ap->corner(),
|
||||
dcalc_ap->constraintMinMax(), ap);
|
||||
}
|
||||
string report = table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap,
|
||||
pi_elmore, load_pin_index_map,
|
||||
dcalc_ap, digits);
|
||||
parasitics_->deleteDrvrReducedParasitics(drvr_pin);
|
||||
return report;
|
||||
}
|
||||
|
||||
void
|
||||
CcsCeffDelayCalc::fail(const char *reason)
|
||||
{
|
||||
// Report failures with a unique debug flag.
|
||||
if (debug_->check("ccs_dcalc", 1) || debug_->check("dcalc_error", 1))
|
||||
report_->reportLine("delay_calc: CCS failed - %s", reason);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
// 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 "LumpedCapDelayCalc.hh"
|
||||
#include "ArcDcalcWaveforms.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
using std::vector;
|
||||
|
||||
ArcDelayCalc *
|
||||
makeCcsCeffDelayCalc(StaState *sta);
|
||||
|
||||
class CcsCeffDelayCalc : public LumpedCapDelayCalc, public ArcDcalcWaveforms
|
||||
{
|
||||
public:
|
||||
CcsCeffDelayCalc(StaState *sta);
|
||||
virtual ~CcsCeffDelayCalc();
|
||||
ArcDelayCalc *copy() override;
|
||||
|
||||
ArcDcalcResult gateDelay(const Pin *drvr_pin,
|
||||
const TimingArc *arc,
|
||||
const Slew &in_slew,
|
||||
float load_cap,
|
||||
const Parasitic *parasitic,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
string reportGateDelay(const Pin *drvr_pin,
|
||||
const TimingArc *arc,
|
||||
const Slew &in_slew,
|
||||
float load_cap,
|
||||
const Parasitic *parasitic,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
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;
|
||||
|
||||
protected:
|
||||
typedef vector<double> Region;
|
||||
|
||||
void gateDelaySlew(const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf,
|
||||
// Return values.
|
||||
ArcDelay &gate_delay,
|
||||
Slew &drvr_slew);
|
||||
void initRegions(const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf);
|
||||
void findCsmWaveform();
|
||||
ArcDcalcResult makeResult(const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf,
|
||||
ArcDelay &gate_delay,
|
||||
Slew &drvr_slew,
|
||||
const LoadPinIndexMap &load_pin_index_map);
|
||||
void loadDelaySlew(const Pin *load_pin,
|
||||
const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf,
|
||||
Slew &drvr_slew,
|
||||
// Return values.
|
||||
ArcDelay &wire_delay,
|
||||
Slew &load_slew);
|
||||
void loadDelaySlew(const Pin *load_pin,
|
||||
Slew &drvr_slew,
|
||||
float elmore,
|
||||
// Return values.
|
||||
ArcDelay &delay,
|
||||
Slew &slew);
|
||||
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);
|
||||
void vl(double t,
|
||||
double elmore,
|
||||
// Return values.
|
||||
double &vl,
|
||||
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_;
|
||||
double in_slew_;
|
||||
double load_cap_;
|
||||
const Parasitic *parasitic_;
|
||||
|
||||
OutputWaveforms *output_waveforms_;
|
||||
double ref_time_;
|
||||
float vdd_;
|
||||
float vth_;
|
||||
float vl_;
|
||||
float vh_;
|
||||
|
||||
float c2_;
|
||||
float rpi_;
|
||||
float c1_;
|
||||
|
||||
size_t region_count_;
|
||||
size_t region_vl_idx_;
|
||||
size_t region_vth_idx_;
|
||||
size_t region_vh_idx_;
|
||||
|
||||
Region region_volts_;
|
||||
Region region_ceff_;
|
||||
Region region_times_;
|
||||
Region region_begin_times_;
|
||||
Region region_end_times_;
|
||||
Region region_time_offsets_;
|
||||
Region region_ramp_times_;
|
||||
Region region_ramp_slopes_;
|
||||
bool vl_fail_;
|
||||
|
||||
const Unit *capacitance_unit_;
|
||||
// Delay calculator to use when ccs waveforms are missing from liberty.
|
||||
ArcDelayCalc *table_dcalc_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
@ -22,6 +22,7 @@
|
|||
#include "LumpedCapDelayCalc.hh"
|
||||
#include "DmpDelayCalc.hh"
|
||||
#include "ArnoldiDelayCalc.hh"
|
||||
#include "CcsCeffDelayCalc.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
|
|
@ -37,6 +38,7 @@ registerDelayCalcs()
|
|||
registerDelayCalc("dmp_ceff_elmore", makeDmpCeffElmoreDelayCalc);
|
||||
registerDelayCalc("dmp_ceff_two_pole", makeDmpCeffTwoPoleDelayCalc);
|
||||
registerDelayCalc("arnoldi", makeArnoldiDelayCalc);
|
||||
registerDelayCalc("ccs_ceff", makeCcsCeffDelayCalc);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -16,6 +16,13 @@
|
|||
|
||||
namespace eval sta {
|
||||
|
||||
define_cmd_args "report_dcalc" \
|
||||
{[-from from_pin] [-to to_pin] [-corner corner] [-min] [-max] [-digits digits]}
|
||||
|
||||
proc_redirect report_dcalc {
|
||||
report_dcalc_cmd "report_dcalc" $args "-digits"
|
||||
}
|
||||
|
||||
# Allow any combination of -from/-to pins.
|
||||
proc report_dcalc_cmd { cmd cmd_args digits_key } {
|
||||
global sta_report_default_digits
|
||||
|
|
|
|||
|
|
@ -954,14 +954,14 @@ GraphDelayCalc::makeArcDcalcArgs(Vertex *drvr_vertex,
|
|||
// Shockingly one fpga vendor connects outputs with no timing arcs together.
|
||||
if (edge1) {
|
||||
Vertex *from_vertex = edge1->from(graph_);
|
||||
const Pin *from_pin = from_vertex->pin();
|
||||
const RiseFall *from_rf = arc1->fromEdge()->asRiseFall();
|
||||
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.push_back(ArcDcalcArg(drvr_pin1, edge1, arc1, in_slew,
|
||||
parasitic));
|
||||
dcalc_args.emplace_back(from_pin, drvr_pin1, edge1, arc1, in_slew, parasitic);
|
||||
}
|
||||
}
|
||||
return dcalc_args;
|
||||
|
|
|
|||
BIN
doc/OpenSTA.odt
BIN
doc/OpenSTA.odt
Binary file not shown.
BIN
doc/OpenSTA.pdf
BIN
doc/OpenSTA.pdf
Binary file not shown.
|
|
@ -498,6 +498,37 @@ Graph::deleteOutEdge(Vertex *vertex,
|
|||
Graph::edge(next)->vertex_out_prev_ = prev;
|
||||
}
|
||||
|
||||
void
|
||||
Graph::gateEdgeArc(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
// Return values.
|
||||
Edge *&edge,
|
||||
const TimingArc *&arc) const
|
||||
{
|
||||
Vertex *in_vertex = pinLoadVertex(in_pin);
|
||||
Vertex *drvr_vertex = pinDrvrVertex(drvr_pin);
|
||||
// Iterate over load drivers to avoid driver fanout^2.
|
||||
VertexInEdgeIterator edge_iter(drvr_vertex, this);
|
||||
while (edge_iter.hasNext()) {
|
||||
Edge *edge1 = edge_iter.next();
|
||||
if (edge1->from(this) == in_vertex) {
|
||||
TimingArcSet *arc_set = edge1->timingArcSet();
|
||||
for (TimingArc *arc1 : arc_set->arcs()) {
|
||||
if (arc1->fromEdge()->asRiseFall() == in_rf
|
||||
&& arc1->toEdge()->asRiseFall() == drvr_rf) {
|
||||
edge = edge1;
|
||||
arc = arc1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
edge = nullptr;
|
||||
arc = nullptr;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
Arrival *
|
||||
|
|
|
|||
|
|
@ -50,24 +50,41 @@ class ArcDcalcArg
|
|||
{
|
||||
public:
|
||||
ArcDcalcArg();
|
||||
ArcDcalcArg(const Pin *drvr_pin,
|
||||
Edge *edge,
|
||||
const TimingArc *arc,
|
||||
const Slew in_slew,
|
||||
const Parasitic *parasitic);
|
||||
ArcDcalcArg(const ArcDcalcArg &arg);
|
||||
ArcDcalcArg(const Pin *in_pin,
|
||||
const Pin *drvr_pin,
|
||||
Edge *edge,
|
||||
const TimingArc *arc,
|
||||
const Slew in_slew,
|
||||
const Parasitic *parasitic);
|
||||
ArcDcalcArg(const Pin *in_pin,
|
||||
const Pin *drvr_pin,
|
||||
Edge *edge,
|
||||
const TimingArc *arc,
|
||||
float in_delay);
|
||||
const Pin *inPin() const { return in_pin_; }
|
||||
const RiseFall *inEdge() const;
|
||||
const Pin *drvrPin() const { return drvr_pin_; }
|
||||
LibertyCell *drvrCell() const;
|
||||
const LibertyLibrary *drvrLibrary() const;
|
||||
const RiseFall *drvrEdge() const;
|
||||
const Net *drvrNet(const Network *network) const;
|
||||
Edge *edge() const { return edge_; }
|
||||
const TimingArc *arc() const { return arc_; }
|
||||
Slew inSlew() const { return in_slew_; }
|
||||
void setInSlew(Slew in_slew);
|
||||
const Parasitic *parasitic() { return parasitic_; }
|
||||
void setParasitic(const Parasitic *parasitic);
|
||||
float inputDelay() const { return input_delay_; }
|
||||
|
||||
protected:
|
||||
const Pin *in_pin_;
|
||||
const Pin *drvr_pin_;
|
||||
Edge *edge_;
|
||||
const TimingArc *arc_;
|
||||
Slew in_slew_;
|
||||
const Parasitic *parasitic_;
|
||||
float input_delay_;
|
||||
};
|
||||
|
||||
// Arc delay calc result.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
// 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 <map>
|
||||
|
||||
#include "StaState.hh"
|
||||
#include "LibertyClass.hh"
|
||||
|
||||
#if CUDD
|
||||
#include "cudd.h"
|
||||
#else
|
||||
struct DdNode;
|
||||
struct DdManager;
|
||||
#endif
|
||||
|
||||
namespace sta {
|
||||
|
||||
typedef std::map<const LibertyPort*, DdNode*> BddPortVarMap;
|
||||
typedef std::map<unsigned, const LibertyPort*> BddVarIdxPortMap;
|
||||
|
||||
class Bdd : public StaState
|
||||
{
|
||||
public:
|
||||
Bdd(const StaState *sta);
|
||||
~Bdd();
|
||||
DdNode *funcBdd(const FuncExpr *expr);
|
||||
DdNode *findNode(const LibertyPort *port);
|
||||
const LibertyPort *nodePort(DdNode *node);
|
||||
DdNode *ensureNode(const LibertyPort *port);
|
||||
const LibertyPort *varIndexPort(int var_index);
|
||||
BddPortVarMap &portVarMap() { return bdd_port_var_map_; }
|
||||
|
||||
void clearVarMap();
|
||||
DdManager *cuddMgr() const { return cudd_mgr_; }
|
||||
|
||||
private:
|
||||
DdManager *cudd_mgr_;
|
||||
BddPortVarMap bdd_port_var_map_;
|
||||
BddVarIdxPortMap bdd_var_idx_port_map_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// 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
|
||||
|
||||
namespace sta {
|
||||
|
||||
enum class CircuitSim { hspice, ngspice, xyce };
|
||||
|
||||
} // namespace
|
||||
|
|
@ -93,12 +93,12 @@ funcExprNot(FuncExpr *expr);
|
|||
class FuncExprPortIterator : public Iterator<LibertyPort*>
|
||||
{
|
||||
public:
|
||||
explicit FuncExprPortIterator(FuncExpr *expr);
|
||||
explicit FuncExprPortIterator(const FuncExpr *expr);
|
||||
virtual bool hasNext() { return iter_.hasNext(); }
|
||||
virtual LibertyPort *next() { return iter_.next(); }
|
||||
|
||||
private:
|
||||
void findPorts(FuncExpr *expr);
|
||||
void findPorts(const FuncExpr *expr);
|
||||
|
||||
LibertyPortSet ports_;
|
||||
LibertyPortSet::ConstIterator iter_;
|
||||
|
|
|
|||
|
|
@ -115,7 +115,6 @@ public:
|
|||
uint32_t count);
|
||||
PathVertexRep *prevPaths(Vertex *vertex) const;
|
||||
void clearPrevPaths();
|
||||
// Slews are reported slews in seconds.
|
||||
// Reported slew are the same as those in the liberty tables.
|
||||
// reported_slews = measured_slews / slew_derate_from_library
|
||||
// Measured slews are between slew_lower_threshold and slew_upper_threshold.
|
||||
|
|
@ -141,6 +140,15 @@ public:
|
|||
void makeWireEdgesThruPin(const Pin *hpin);
|
||||
virtual void makeWireEdgesFromPin(const Pin *drvr_pin);
|
||||
virtual void deleteEdge(Edge *edge);
|
||||
// Find the edge and timing arc on a gate between in_pin and drvr_pin.
|
||||
void gateEdgeArc(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
// Return values.
|
||||
Edge *&edge,
|
||||
const TimingArc *&arc) const;
|
||||
|
||||
virtual ArcDelay arcDelay(const Edge *edge,
|
||||
const TimingArc *arc,
|
||||
DcalcAPIndex ap_index) const;
|
||||
|
|
|
|||
|
|
@ -92,6 +92,14 @@ public:
|
|||
float &wire_cap,
|
||||
float &fanout,
|
||||
bool &has_set_load) const;
|
||||
void parasiticLoad(const Pin *drvr_pin,
|
||||
const RiseFall *rf,
|
||||
const DcalcAnalysisPt *dcalc_ap,
|
||||
const MultiDrvrNet *multi_drvr,
|
||||
ArcDelayCalc *arc_delay_calc,
|
||||
// Return values.
|
||||
float &cap,
|
||||
const Parasitic *¶sitic) const;
|
||||
LoadPinIndexMap makeLoadPinIndexMap(Vertex *drvr_vertex);
|
||||
void findDriverArcDelays(Vertex *drvr_vertex,
|
||||
Edge *edge,
|
||||
|
|
@ -231,14 +239,6 @@ protected:
|
|||
const RiseFall *rf,
|
||||
const DcalcAnalysisPt *dcalc_ap,
|
||||
ArcDelayCalc *arc_delay_calc) const;
|
||||
void parasiticLoad(const Pin *drvr_pin,
|
||||
const RiseFall *rf,
|
||||
const DcalcAnalysisPt *dcalc_ap,
|
||||
const MultiDrvrNet *multi_drvr,
|
||||
ArcDelayCalc *arc_delay_calc,
|
||||
// Return values.
|
||||
float &cap,
|
||||
const Parasitic *¶sitic) const;
|
||||
void parasiticLoad(const Pin *drvr_pin,
|
||||
const RiseFall *rf,
|
||||
const DcalcAnalysisPt *dcalc_ap,
|
||||
|
|
|
|||
|
|
@ -319,7 +319,6 @@ public:
|
|||
DriverWaveform *findDriverWaveform(const char *name);
|
||||
DriverWaveform *driverWaveformDefault() { return driver_waveform_default_; }
|
||||
void addDriverWaveform(DriverWaveform *driver_waveform);
|
||||
void ensureVoltageWaveforms();
|
||||
|
||||
protected:
|
||||
float degradeWireSlew(const TableModel *model,
|
||||
|
|
@ -371,7 +370,6 @@ protected:
|
|||
DriverWaveformMap driver_waveform_map_;
|
||||
// Unnamed driver waveform.
|
||||
DriverWaveform *driver_waveform_default_;
|
||||
bool have_voltage_waveforms_;
|
||||
|
||||
static constexpr float input_threshold_default_ = .5;
|
||||
static constexpr float output_threshold_default_ = .5;
|
||||
|
|
@ -533,6 +531,7 @@ public:
|
|||
// Check all liberty cells to make sure they exist
|
||||
// for all the defined corners.
|
||||
static void checkLibertyCorners();
|
||||
void ensureVoltageWaveforms();
|
||||
|
||||
protected:
|
||||
void addPort(ConcretePort *port);
|
||||
|
|
@ -608,6 +607,7 @@ protected:
|
|||
bool leakage_power_exists_;
|
||||
LibertyPgPortMap pg_port_map_;
|
||||
bool has_internal_ports_;
|
||||
bool have_voltage_waveforms_;
|
||||
|
||||
private:
|
||||
friend class LibertyLibrary;
|
||||
|
|
@ -795,7 +795,15 @@ public:
|
|||
DriverWaveform *driverWaveform(const RiseFall *rf) const;
|
||||
void setDriverWaveform(DriverWaveform *driver_waveform,
|
||||
const RiseFall *rf);
|
||||
RiseFallMinMax clockTreePathDelays();
|
||||
void setClkTreeDelay(const TableModel *model,
|
||||
const RiseFall *rf,
|
||||
const MinMax *min_max);
|
||||
float clkTreeDelay(float in_slew,
|
||||
const RiseFall *rf,
|
||||
const MinMax *min_max) const;
|
||||
// Assumes input slew of 0.0.
|
||||
RiseFallMinMax clkTreeDelays() const;
|
||||
RiseFallMinMax clockTreePathDelays() const; // __attribute__ ((deprecated))
|
||||
|
||||
static bool equiv(const LibertyPort *port1,
|
||||
const LibertyPort *port2);
|
||||
|
|
@ -837,6 +845,8 @@ protected:
|
|||
Vector<LibertyPort*> corner_ports_;
|
||||
ReceiverModelPtr receiver_model_;
|
||||
DriverWaveform *driver_waveform_[RiseFall::index_count];
|
||||
// Redundant with clock_tree_path_delay timing arcs but faster to access.
|
||||
const TableModel *clk_tree_delay_[RiseFall::index_count][MinMax::index_count];
|
||||
|
||||
unsigned int min_pulse_width_exists_:RiseFall::index_count;
|
||||
bool min_period_exists_:1;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ typedef Iterator<Port*> PortMemberIterator;
|
|||
typedef Vector<const Pin*> PinSeq;
|
||||
typedef Vector<const Instance*> InstanceSeq;
|
||||
typedef Vector<const Net*> NetSeq;
|
||||
typedef std::vector<const Net*> ConstNetSeq;
|
||||
typedef Iterator<Instance*> InstanceChildIterator;
|
||||
typedef Iterator<Pin*> InstancePinIterator;
|
||||
typedef Iterator<Net*> InstanceNetIterator;
|
||||
|
|
|
|||
|
|
@ -163,11 +163,20 @@ public:
|
|||
// True if the parasitic network caps include pin capacitances.
|
||||
virtual bool includesPinCaps(const Parasitic *parasitic) const = 0;
|
||||
// Parasitic network component builders.
|
||||
virtual ParasiticNode *findParasiticNode(Parasitic *parasitic,
|
||||
const Net *net,
|
||||
int id,
|
||||
const Network *network) const = 0;
|
||||
// Make a subnode of the parasitic network net.
|
||||
virtual ParasiticNode *ensureParasiticNode(Parasitic *parasitic,
|
||||
const Net *net,
|
||||
int id,
|
||||
const Network *network) = 0;
|
||||
// Find the parasitic node connected to pin.
|
||||
virtual ParasiticNode *findParasiticNode(const Parasitic *parasitic,
|
||||
const Pin *pin) const = 0;
|
||||
virtual ParasiticNode *findNode(const Parasitic *parasitic,
|
||||
const Pin *pin) const __attribute__ ((deprecated));
|
||||
// Make a subnode of the parasitic network net connected to pin.
|
||||
virtual ParasiticNode *ensureParasiticNode(Parasitic *parasitic,
|
||||
const Pin *pin,
|
||||
|
|
@ -175,14 +184,11 @@ public:
|
|||
// Increment the grounded capacitance on node.
|
||||
virtual void incrCap(ParasiticNode *node,
|
||||
float cap) = 0;
|
||||
virtual const char *name(const ParasiticNode *node) = 0;
|
||||
virtual const char *name(const ParasiticNode *node) const = 0;
|
||||
virtual const Pin *pin(const ParasiticNode *node) const = 0;
|
||||
virtual const Net *net(const ParasiticNode *node,
|
||||
const Network *network) const = 0;
|
||||
virtual bool isExternal(const ParasiticNode *node) const = 0;
|
||||
// Find the parasitic node connected to pin.
|
||||
virtual ParasiticNode *findNode(const Parasitic *parasitic,
|
||||
const Pin *pin) const = 0;
|
||||
// Node capacitance to ground.
|
||||
virtual float nodeGndCap(const ParasiticNode *node) const = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
namespace sta {
|
||||
|
||||
class Power;
|
||||
|
||||
enum class PwrActivityOrigin
|
||||
{
|
||||
global,
|
||||
|
|
|
|||
|
|
@ -75,7 +75,9 @@ public:
|
|||
typedef Vector<float> FloatSeq;
|
||||
typedef Vector<int> IntSeq;
|
||||
typedef Vector<Clock*> ClockSeq;
|
||||
typedef std::vector<const Clock*> ConstClockSeq;
|
||||
typedef Set<Clock*, ClockIndexLess> ClockSet;
|
||||
typedef std::set<const Clock*, ClockIndexLess> ConstClockSet;
|
||||
typedef ClockSet ClockGroup;
|
||||
typedef Vector<PinSet*> PinSetSeq;
|
||||
typedef MinMax SetupHold;
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ class MaxSkewCheck;
|
|||
class CharPtrLess;
|
||||
class SearchPred;
|
||||
class BfsFwdIterator;
|
||||
class ClkDelays;
|
||||
|
||||
// Tag compare using tag matching (tagMatch) critera.
|
||||
class TagMatchLess
|
||||
|
|
@ -116,7 +117,6 @@ typedef Vector<PathVertex> PathVertexSeq;
|
|||
typedef Vector<Slack> SlackSeq;
|
||||
typedef Delay Crpr;
|
||||
typedef Vector<PathRef> PathRefSeq;
|
||||
typedef MinMaxValues<Delay> ClkDelays[RiseFall::index_count][RiseFall::index_count];
|
||||
|
||||
enum class ReportPathFormat { full,
|
||||
full_clock,
|
||||
|
|
|
|||
|
|
@ -57,8 +57,6 @@ class SearchPred;
|
|||
class Corner;
|
||||
class ClkSkews;
|
||||
class ReportField;
|
||||
class Power;
|
||||
class PowerResult;
|
||||
class EquivCells;
|
||||
|
||||
typedef InstanceSeq::Iterator SlowDrvrIterator;
|
||||
|
|
@ -913,15 +911,17 @@ public:
|
|||
void reportPath(Path *path);
|
||||
|
||||
// Report clk skews for clks.
|
||||
void reportClkSkew(ClockSet *clks,
|
||||
void reportClkSkew(ConstClockSeq clks,
|
||||
const Corner *corner,
|
||||
const SetupHold *setup_hold,
|
||||
int digits);
|
||||
float findWorstClkSkew(const SetupHold *setup_hold);
|
||||
|
||||
void reportClkLatency(ConstClockSeq clks,
|
||||
const Corner *corner,
|
||||
int digits);
|
||||
// Find min/max/rise/fall delays for clk.
|
||||
void findClkDelays(const Clock *clk,
|
||||
// Return values.
|
||||
ClkDelays &delays);
|
||||
ClkDelays findClkDelays(const Clock *clk);
|
||||
|
||||
// Update arrival times for all pins.
|
||||
// If necessary updateTiming propagates arrivals around latch
|
||||
|
|
|
|||
|
|
@ -318,6 +318,7 @@ public:
|
|||
TableAxisPtr axis1);
|
||||
virtual ~Table1();
|
||||
Table1(Table1 &&table);
|
||||
Table1(const Table1 &table);
|
||||
Table1 &operator= (Table1 &&table);
|
||||
int order() const override { return 1; }
|
||||
const TableAxis *axis1() const override { return axis1_.get(); }
|
||||
|
|
|
|||
|
|
@ -16,16 +16,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
#include "StringSet.hh"
|
||||
#include "CircuitSim.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
using std::string;
|
||||
using std::set;
|
||||
|
||||
class Path;
|
||||
class StaState;
|
||||
|
||||
|
|
@ -41,11 +36,9 @@ writePathSpice(Path *path,
|
|||
const char *lib_subckt_filename,
|
||||
// Device model file included in spice file.
|
||||
const char *model_filename,
|
||||
// Nets off of path to include in the spice run.
|
||||
StdStringSet *off_path_pin_names,
|
||||
const char *power_name,
|
||||
const char *gnd_name,
|
||||
bool measure_stmts,
|
||||
CircuitSim ckt_sim,
|
||||
StaState *sta);
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -345,14 +345,14 @@ funcExprNot(FuncExpr *expr)
|
|||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
FuncExprPortIterator::FuncExprPortIterator(FuncExpr *expr)
|
||||
FuncExprPortIterator::FuncExprPortIterator(const FuncExpr *expr)
|
||||
{
|
||||
findPorts(expr);
|
||||
iter_.init(ports_);
|
||||
}
|
||||
|
||||
void
|
||||
FuncExprPortIterator::findPorts(FuncExpr *expr)
|
||||
FuncExprPortIterator::findPorts(const FuncExpr *expr)
|
||||
{
|
||||
if (expr) {
|
||||
if (expr->op() == FuncExpr::op_port)
|
||||
|
|
|
|||
|
|
@ -84,8 +84,7 @@ LibertyLibrary::LibertyLibrary(const char *name,
|
|||
default_ocv_derate_(nullptr),
|
||||
buffers_(nullptr),
|
||||
inverters_(nullptr),
|
||||
driver_waveform_default_(nullptr),
|
||||
have_voltage_waveforms_(false)
|
||||
driver_waveform_default_(nullptr)
|
||||
{
|
||||
// Scalar templates are builtin.
|
||||
for (int i = 0; i != table_template_type_count; i++) {
|
||||
|
|
@ -895,33 +894,6 @@ LibertyLibrary::addDriverWaveform(DriverWaveform *driver_waveform)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
LibertyLibrary::ensureVoltageWaveforms()
|
||||
{
|
||||
if (!have_voltage_waveforms_) {
|
||||
float vdd = 0.0f;
|
||||
bool vdd_exists;
|
||||
supplyVoltage("VDD", vdd, vdd_exists);
|
||||
if (!vdd_exists || vdd == 0.0)
|
||||
criticalError(1120, "library missing vdd");
|
||||
LibertyCellIterator cell_iter(this);
|
||||
while (cell_iter.hasNext()) {
|
||||
LibertyCell *cell = cell_iter.next();
|
||||
for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, nullptr)) {
|
||||
for (TimingArc *arc : arc_set->arcs()) {
|
||||
GateTableModel*model = dynamic_cast<GateTableModel*>(arc->model());
|
||||
if (model) {
|
||||
OutputWaveforms *output_waveforms = model->outputWaveforms();
|
||||
if (output_waveforms)
|
||||
output_waveforms->makeVoltageWaveforms(vdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
have_voltage_waveforms_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
LibertyCellIterator::LibertyCellIterator(const LibertyLibrary *library) :
|
||||
|
|
@ -969,7 +941,8 @@ LibertyCell::LibertyCell(LibertyLibrary *library,
|
|||
is_disabled_constraint_(false),
|
||||
leakage_power_(0.0),
|
||||
leakage_power_exists_(false),
|
||||
has_internal_ports_(false)
|
||||
has_internal_ports_(false),
|
||||
have_voltage_waveforms_(false)
|
||||
{
|
||||
liberty_cell_ = this;
|
||||
}
|
||||
|
|
@ -1101,13 +1074,13 @@ LibertyCell::setIsMemory(bool is_memory)
|
|||
}
|
||||
|
||||
void
|
||||
LibertyCell::LibertyCell::setIsPad(bool is_pad)
|
||||
LibertyCell::setIsPad(bool is_pad)
|
||||
{
|
||||
is_pad_ = is_pad;
|
||||
}
|
||||
|
||||
void
|
||||
LibertyCell::LibertyCell::setIsClockCell(bool is_clock_cell)
|
||||
LibertyCell::setIsClockCell(bool is_clock_cell)
|
||||
{
|
||||
is_clock_cell_ = is_clock_cell;
|
||||
}
|
||||
|
|
@ -1919,6 +1892,29 @@ LibertyCell::latchCheckEnableEdge(TimingArcSet *check_set)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
LibertyCell::ensureVoltageWaveforms()
|
||||
{
|
||||
if (!have_voltage_waveforms_) {
|
||||
float vdd = 0.0;
|
||||
bool vdd_exists;
|
||||
liberty_library_->supplyVoltage("VDD", vdd, vdd_exists);
|
||||
if (!vdd_exists || vdd == 0.0)
|
||||
criticalError(1120, "library missing vdd");
|
||||
for (TimingArcSet *arc_set : timingArcSets()) {
|
||||
for (TimingArc *arc : arc_set->arcs()) {
|
||||
GateTableModel*model = dynamic_cast<GateTableModel*>(arc->model());
|
||||
if (model) {
|
||||
OutputWaveforms *output_waveforms = model->outputWaveforms();
|
||||
if (output_waveforms)
|
||||
output_waveforms->makeVoltageWaveforms(vdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
have_voltage_waveforms_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
LibertyCellPortIterator::LibertyCellPortIterator(const LibertyCell *cell) :
|
||||
|
|
@ -2005,6 +2001,10 @@ LibertyPort::LibertyPort(LibertyCell *cell,
|
|||
liberty_port_ = this;
|
||||
min_pulse_width_[RiseFall::riseIndex()] = 0.0;
|
||||
min_pulse_width_[RiseFall::fallIndex()] = 0.0;
|
||||
for (auto rf_index : RiseFall::rangeIndex()) {
|
||||
for (auto mm_index : MinMax::rangeIndex())
|
||||
clk_tree_delay_[rf_index][mm_index] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
LibertyPort::~LibertyPort()
|
||||
|
|
@ -2598,31 +2598,47 @@ LibertyPort::setDriverWaveform(DriverWaveform *driver_waveform,
|
|||
}
|
||||
|
||||
RiseFallMinMax
|
||||
LibertyPort::clockTreePathDelays()
|
||||
LibertyPort::clockTreePathDelays() const
|
||||
{
|
||||
return clkTreeDelays();
|
||||
}
|
||||
|
||||
RiseFallMinMax
|
||||
LibertyPort::clkTreeDelays() const
|
||||
{
|
||||
RiseFallMinMax delays;
|
||||
const TimingArcSetSeq &arc_sets = liberty_cell_->timingArcSets(nullptr, this);
|
||||
for (TimingArcSet *arc_set : arc_sets) {
|
||||
TimingRole *role = arc_set->role();
|
||||
if (role == TimingRole::clockTreePathMin()
|
||||
|| role == TimingRole::clockTreePathMax()) {
|
||||
for (TimingArc *arc : arc_set->arcs()) {
|
||||
TimingModel *model = arc->model();
|
||||
GateTimingModel *gate_model = dynamic_cast<GateTimingModel*>(model);
|
||||
ArcDelay delay;
|
||||
Slew slew;
|
||||
gate_model->gateDelay(nullptr, 0.0, 0.0, false, delay, slew);
|
||||
const RiseFall *rf = arc->toEdge()->asRiseFall();
|
||||
const MinMax *min_max = (role == TimingRole::clockTreePathMin())
|
||||
? MinMax::min()
|
||||
: MinMax::max();
|
||||
delays.setValue(rf, min_max, delayAsFloat(delay));
|
||||
for (const RiseFall *rf : RiseFall::range()) {
|
||||
for (const MinMax *min_max : MinMax::range()) {
|
||||
const TableModel *model = clk_tree_delay_[rf->index()][min_max->index()];
|
||||
if (model) {
|
||||
float delay = model->findValue(0.0, 0.0, 0.0);
|
||||
delays.setValue(rf, min_max, delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
return delays;
|
||||
}
|
||||
|
||||
float
|
||||
LibertyPort::clkTreeDelay(float in_slew,
|
||||
const RiseFall *rf,
|
||||
const MinMax *min_max) const
|
||||
{
|
||||
const TableModel *model = clk_tree_delay_[rf->index()][min_max->index()];
|
||||
if (model)
|
||||
return model->findValue(in_slew, 0.0, 0.0);
|
||||
else
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void
|
||||
LibertyPort::setClkTreeDelay(const TableModel *model,
|
||||
const RiseFall *rf,
|
||||
const MinMax *min_max)
|
||||
{
|
||||
clk_tree_delay_[rf->index()][min_max->index()] = model;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
LibertyPortSeq
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
#include "TimingRole.hh"
|
||||
#include "FuncExpr.hh"
|
||||
#include "TimingArc.hh"
|
||||
#include "TimingModel.hh"
|
||||
#include "TableModel.hh"
|
||||
#include "InternalPower.hh"
|
||||
#include "LeakagePower.hh"
|
||||
#include "Sequential.hh"
|
||||
|
|
@ -269,10 +271,10 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell,
|
|||
attrs);
|
||||
case TimingType::min_clock_tree_path:
|
||||
return makeClockTreePathArcs(cell, to_port, TimingRole::clockTreePathMin(),
|
||||
attrs);
|
||||
MinMax::min(), attrs);
|
||||
case TimingType::max_clock_tree_path:
|
||||
return makeClockTreePathArcs(cell, to_port, TimingRole::clockTreePathMax(),
|
||||
attrs);
|
||||
MinMax::max(), attrs);
|
||||
case TimingType::min_pulse_width:
|
||||
case TimingType::minimum_period:
|
||||
case TimingType::nochange_high_high:
|
||||
|
|
@ -633,13 +635,17 @@ TimingArcSet *
|
|||
LibertyBuilder::makeClockTreePathArcs(LibertyCell *cell,
|
||||
LibertyPort *to_port,
|
||||
TimingRole *role,
|
||||
const MinMax *min_max,
|
||||
TimingArcAttrsPtr attrs)
|
||||
{
|
||||
TimingArcSet *arc_set = makeTimingArcSet(cell, nullptr, to_port, role, attrs);
|
||||
for (auto to_rf : RiseFall::range()) {
|
||||
TimingModel *model = attrs->model(to_rf);
|
||||
if (model)
|
||||
if (model) {
|
||||
makeTimingArc(arc_set, nullptr, to_rf->asTransition(), model);
|
||||
const GateTableModel *gate_model = dynamic_cast<GateTableModel *>(model);
|
||||
to_port->setClkTreeDelay(gate_model->delayModel(), to_rf, min_max);
|
||||
}
|
||||
}
|
||||
return arc_set;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "MinMax.hh"
|
||||
#include "Vector.hh"
|
||||
#include "Transition.hh"
|
||||
#include "LibertyClass.hh"
|
||||
|
|
@ -81,6 +82,7 @@ public:
|
|||
TimingArcSet *makeClockTreePathArcs(LibertyCell *cell,
|
||||
LibertyPort *to_port,
|
||||
TimingRole *role,
|
||||
const MinMax *min_max,
|
||||
TimingArcAttrsPtr attrs);
|
||||
|
||||
protected:
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ LibertyReader::defineVisitors()
|
|||
&LibertyReader::visitClockGatingIntegratedCell);
|
||||
defineAttrVisitor("area", &LibertyReader::visitArea);
|
||||
defineAttrVisitor("dont_use", &LibertyReader::visitDontUse);
|
||||
defineAttrVisitor("is_macro", &LibertyReader::visitIsMacro);
|
||||
defineAttrVisitor("is_macro_cell", &LibertyReader::visitIsMacro);
|
||||
defineAttrVisitor("is_memory", &LibertyReader::visitIsMemory);
|
||||
defineAttrVisitor("is_pad", &LibertyReader::visitIsPad);
|
||||
defineAttrVisitor("is_clock_cell", &LibertyReader::visitIsClockCell);
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ LibertyWriter::writeCell(const LibertyCell *cell)
|
|||
if (area > 0.0)
|
||||
fprintf(stream_, " area : %.3f \n", area);
|
||||
if (cell->isMacro())
|
||||
fprintf(stream_, " is_macro : true;\n");
|
||||
fprintf(stream_, " is_macro_cell : true;\n");
|
||||
if (cell->interfaceTiming())
|
||||
fprintf(stream_, " interface_timing : true;\n");
|
||||
|
||||
|
|
|
|||
|
|
@ -819,6 +819,13 @@ Table1::Table1(Table1 &&table) :
|
|||
table.axis1_ = nullptr;
|
||||
}
|
||||
|
||||
Table1::Table1(const Table1 &table) :
|
||||
Table(),
|
||||
values_(new FloatSeq(*table.values_)),
|
||||
axis1_(table.axis1_)
|
||||
{
|
||||
}
|
||||
|
||||
Table1::~Table1()
|
||||
{
|
||||
delete values_;
|
||||
|
|
|
|||
|
|
@ -487,8 +487,11 @@ ConcreteParasiticCapacitor::ConcreteParasiticCapacitor(size_t id,
|
|||
////////////////////////////////////////////////////////////////
|
||||
|
||||
ConcreteParasiticNetwork::ConcreteParasiticNetwork(const Net *net,
|
||||
bool includes_pin_caps) :
|
||||
bool includes_pin_caps,
|
||||
const Network *network) :
|
||||
net_(net),
|
||||
sub_nodes_(network),
|
||||
pin_nodes_(network),
|
||||
max_node_id_(0),
|
||||
includes_pin_caps_(includes_pin_caps)
|
||||
{
|
||||
|
|
@ -580,6 +583,29 @@ ConcreteParasiticNetwork::capacitance() const
|
|||
return cap;
|
||||
}
|
||||
|
||||
ConcreteParasiticNode *
|
||||
ConcreteParasiticNetwork::findParasiticNode(const Net *net,
|
||||
int id,
|
||||
const Network *) const
|
||||
{
|
||||
NetIdPair net_id(net, id);
|
||||
auto id_node = sub_nodes_.find(net_id);
|
||||
if (id_node == sub_nodes_.end())
|
||||
return nullptr;
|
||||
else
|
||||
return id_node->second;
|
||||
}
|
||||
|
||||
ConcreteParasiticNode *
|
||||
ConcreteParasiticNetwork::findParasiticNode(const Pin *pin) const
|
||||
{
|
||||
auto pin_node = pin_nodes_.find(pin);
|
||||
if (pin_node == pin_nodes_.end())
|
||||
return nullptr;
|
||||
else
|
||||
return pin_node->second;
|
||||
}
|
||||
|
||||
ConcreteParasiticNode *
|
||||
ConcreteParasiticNetwork::ensureParasiticNode(const Net *net,
|
||||
int id,
|
||||
|
|
@ -623,22 +649,12 @@ ConcreteParasiticNetwork::ensureParasiticNode(const Pin *pin,
|
|||
return node;
|
||||
}
|
||||
|
||||
ConcreteParasiticNode *
|
||||
ConcreteParasiticNetwork::findNode(const Pin *pin) const
|
||||
{
|
||||
auto pin_node = pin_nodes_.find(pin);
|
||||
if (pin_node == pin_nodes_.end())
|
||||
return nullptr;
|
||||
else
|
||||
return pin_node->second;
|
||||
}
|
||||
|
||||
PinSet
|
||||
ConcreteParasiticNetwork::unannotatedLoads(const Pin *drvr_pin,
|
||||
const Parasitics *parasitics) const
|
||||
{
|
||||
PinSet loads = parasitics->loads(drvr_pin);
|
||||
ParasiticNode *drvr_node = findNode(drvr_pin);
|
||||
ParasiticNode *drvr_node = findParasiticNode(drvr_pin);
|
||||
if (drvr_node) {
|
||||
ParasiticNodeResistorMap resistor_map =
|
||||
parasitics->parasiticNodeResistorMap(this);
|
||||
|
|
@ -713,6 +729,11 @@ ConcreteParasiticNetwork::disconnectPin(const Pin *pin,
|
|||
}
|
||||
}
|
||||
|
||||
NetIdPairLess::NetIdPairLess(const Network *network) :
|
||||
net_less_(network)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
NetIdPairLess::operator()(const NetIdPair &net_id1,
|
||||
const NetIdPair &net_id2) const
|
||||
|
|
@ -721,7 +742,7 @@ NetIdPairLess::operator()(const NetIdPair &net_id1,
|
|||
const Net *net2 = net_id2.first;
|
||||
int id1 = net_id1.second;
|
||||
int id2 = net_id2.second;
|
||||
return net1 < net2
|
||||
return net_less_(net1, net2)
|
||||
|| (net1 == net2
|
||||
&& id1 < id2);
|
||||
}
|
||||
|
|
@ -1229,7 +1250,7 @@ ConcreteParasitics::makeParasiticNetwork(const Net *net,
|
|||
for (const Pin *drvr_pin : *network_->drivers(net))
|
||||
deleteParasitics(drvr_pin, ap);
|
||||
}
|
||||
parasitic = new ConcreteParasiticNetwork(net, includes_pin_caps);
|
||||
parasitic = new ConcreteParasiticNetwork(net, includes_pin_caps, network_);
|
||||
parasitics[ap_index] = parasitic;
|
||||
return parasitic;
|
||||
}
|
||||
|
|
@ -1286,6 +1307,17 @@ ConcreteParasitics::includesPinCaps(const Parasitic *parasitic) const
|
|||
return cparasitic->includesPinCaps();
|
||||
}
|
||||
|
||||
ParasiticNode *
|
||||
ConcreteParasitics::findParasiticNode(Parasitic *parasitic,
|
||||
const Net *net,
|
||||
int id,
|
||||
const Network *network) const
|
||||
{
|
||||
const ConcreteParasiticNetwork *cparasitic =
|
||||
static_cast<const ConcreteParasiticNetwork*>(parasitic);
|
||||
return cparasitic->findParasiticNode(net, id, network);
|
||||
}
|
||||
|
||||
ParasiticNode *
|
||||
ConcreteParasitics::ensureParasiticNode(Parasitic *parasitic,
|
||||
const Net *net,
|
||||
|
|
@ -1297,6 +1329,15 @@ ConcreteParasitics::ensureParasiticNode(Parasitic *parasitic,
|
|||
return cparasitic->ensureParasiticNode(net, id, network);
|
||||
}
|
||||
|
||||
ParasiticNode *
|
||||
ConcreteParasitics::findParasiticNode(const Parasitic *parasitic,
|
||||
const Pin *pin) const
|
||||
{
|
||||
const ConcreteParasiticNetwork *cparasitic =
|
||||
static_cast<const ConcreteParasiticNetwork*>(parasitic);
|
||||
return cparasitic->findParasiticNode(pin);
|
||||
}
|
||||
|
||||
ParasiticNode *
|
||||
ConcreteParasitics::ensureParasiticNode(Parasitic *parasitic,
|
||||
const Pin *pin,
|
||||
|
|
@ -1373,7 +1414,7 @@ ConcreteParasitics::capacitors(const Parasitic *parasitic) const
|
|||
|
||||
|
||||
const char *
|
||||
ConcreteParasitics::name(const ParasiticNode *node)
|
||||
ConcreteParasitics::name(const ParasiticNode *node) const
|
||||
{
|
||||
const ConcreteParasiticNode *cnode =
|
||||
static_cast<const ConcreteParasiticNode*>(node);
|
||||
|
|
@ -1413,15 +1454,6 @@ ConcreteParasitics::isExternal(const ParasiticNode *node) const
|
|||
return cnode->isExternal();
|
||||
}
|
||||
|
||||
ParasiticNode *
|
||||
ConcreteParasitics::findNode(const Parasitic *parasitic,
|
||||
const Pin *pin) const
|
||||
{
|
||||
const ConcreteParasiticNetwork *cparasitic =
|
||||
static_cast<const ConcreteParasiticNetwork*>(parasitic);
|
||||
return cparasitic->findNode(pin);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
size_t
|
||||
|
|
|
|||
|
|
@ -113,19 +113,23 @@ public:
|
|||
const ParasiticAnalysisPt *ap) override;
|
||||
void deleteParasiticNetworks(const Net *net) override;
|
||||
bool includesPinCaps(const Parasitic *parasitic) const override;
|
||||
ParasiticNode *findParasiticNode(Parasitic *parasitic,
|
||||
const Net *net,
|
||||
int id,
|
||||
const Network *network) const override;
|
||||
ParasiticNode *ensureParasiticNode(Parasitic *parasitic,
|
||||
const Net *net,
|
||||
int id,
|
||||
const Network *network) override;
|
||||
ParasiticNode *findParasiticNode(const Parasitic *parasitic,
|
||||
const Pin *pin) const override;
|
||||
ParasiticNode *ensureParasiticNode(Parasitic *parasitic,
|
||||
const Pin *pin,
|
||||
const Network *network) override;
|
||||
ParasiticNodeSeq nodes(const Parasitic *parasitic) const override;
|
||||
void incrCap(ParasiticNode *node,
|
||||
float cap) override;
|
||||
const char *name(const ParasiticNode *node) override;
|
||||
ParasiticNode *findNode(const Parasitic *parasitic,
|
||||
const Pin *pin) const override;
|
||||
const char *name(const ParasiticNode *node) const override;
|
||||
const Pin *pin(const ParasiticNode *node) const override;
|
||||
const Net *net(const ParasiticNode *node,
|
||||
const Network *network) const override;
|
||||
|
|
|
|||
|
|
@ -27,17 +27,23 @@ class ConcretePoleResidue;
|
|||
class ConcreteParasiticDevice;
|
||||
class ConcreteParasiticNode;
|
||||
|
||||
typedef std::map<const Pin*, float> ConcreteElmoreLoadMap;
|
||||
typedef std::map<const Pin*, ConcretePoleResidue> ConcretePoleResidueMap;
|
||||
typedef std::pair<const Net*, int> NetIdPair;
|
||||
struct NetIdPairLess
|
||||
class NetIdPairLess
|
||||
{
|
||||
public:
|
||||
NetIdPairLess(const Network *network);
|
||||
bool operator()(const NetIdPair &net_id1,
|
||||
const NetIdPair &net_id2) const;
|
||||
|
||||
private:
|
||||
const NetIdLess net_less_;
|
||||
};
|
||||
|
||||
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*> ConcreteParasiticPinNodeMap;
|
||||
typedef std::map<const Pin*, ConcreteParasiticNode*, PinIdLess> ConcreteParasiticPinNodeMap;
|
||||
typedef std::set<ParasiticNode*> ParasiticNodeSet;
|
||||
typedef std::set<ParasiticResistor*> ParasiticResistorSet;
|
||||
typedef std::vector<ParasiticResistor*> ParasiticResistorSeq;
|
||||
|
|
@ -197,17 +203,21 @@ class ConcreteParasiticNetwork : public ParasiticNetwork,
|
|||
{
|
||||
public:
|
||||
ConcreteParasiticNetwork(const Net *net,
|
||||
bool includes_pin_caps);
|
||||
bool includes_pin_caps,
|
||||
const Network *network);
|
||||
virtual ~ConcreteParasiticNetwork();
|
||||
virtual bool isParasiticNetwork() const { return true; }
|
||||
const Net *net() { return net_; }
|
||||
bool includesPinCaps() const { return includes_pin_caps_; }
|
||||
ConcreteParasiticNode *findParasiticNode(const Net *net,
|
||||
int id,
|
||||
const Network *network) const;
|
||||
ConcreteParasiticNode *ensureParasiticNode(const Net *net,
|
||||
int id,
|
||||
const Network *network);
|
||||
ConcreteParasiticNode *findParasiticNode(const Pin *pin) const;
|
||||
ConcreteParasiticNode *ensureParasiticNode(const Pin *pin,
|
||||
const Network *network);
|
||||
ConcreteParasiticNode *findNode(const Pin *pin) const;
|
||||
virtual float capacitance() const;
|
||||
ParasiticNodeSeq nodes() const;
|
||||
void disconnectPin(const Pin *pin,
|
||||
|
|
|
|||
|
|
@ -53,6 +53,13 @@ Parasitics::findParasiticNet(const Pin *pin) const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
ParasiticNode *
|
||||
Parasitics::findNode(const Parasitic *parasitic,
|
||||
const Pin *pin) const
|
||||
{
|
||||
return findParasiticNode(parasitic, pin);
|
||||
}
|
||||
|
||||
PinSet
|
||||
Parasitics::loads(const Pin *drvr_pin) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -306,7 +306,8 @@ reduceToPiElmore(const Parasitic *parasitic_network,
|
|||
StaState *sta)
|
||||
{
|
||||
Parasitics *parasitics = sta->parasitics();
|
||||
ParasiticNode *drvr_node = parasitics->findNode(parasitic_network, drvr_pin);
|
||||
ParasiticNode *drvr_node =
|
||||
parasitics->findParasiticNode(parasitic_network, drvr_pin);
|
||||
if (drvr_node) {
|
||||
debugPrint(sta->debug(), "parasitic_reduce", 1, "Reduce driver %s %s %s",
|
||||
sta->network()->pathName(drvr_pin),
|
||||
|
|
@ -456,7 +457,8 @@ reduceToPiPoleResidue2(const Parasitic *parasitic_network,
|
|||
StaState *sta)
|
||||
{
|
||||
Parasitics *parasitics = sta->parasitics();
|
||||
ParasiticNode *drvr_node = parasitics->findNode(parasitic_network, drvr_pin);
|
||||
ParasiticNode *drvr_node =
|
||||
parasitics->findParasiticNode(parasitic_network, drvr_pin);
|
||||
if (drvr_node) {
|
||||
debugPrint(sta->debug(), "parasitic_reduce", 1, "Reduce driver %s",
|
||||
sta->network()->pathName(drvr_pin));
|
||||
|
|
@ -508,7 +510,8 @@ ReduceToPiPoleResidue2::findPolesResidues(const Parasitic *parasitic_network,
|
|||
while (pin_iter->hasNext()) {
|
||||
const Pin *pin = pin_iter->next();
|
||||
if (network_->isLoad(pin)) {
|
||||
ParasiticNode *load_node = parasitics_->findNode(parasitic_network, pin);
|
||||
ParasiticNode *load_node =
|
||||
parasitics_->findParasiticNode(parasitic_network, pin);
|
||||
if (load_node) {
|
||||
findPolesResidues(pi_pole_residue, drvr_pin, pin, load_node);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -468,38 +468,40 @@ SpefReader::findParasiticNode(char *name,
|
|||
*delim = '\0';
|
||||
char *name2 = delim + 1;
|
||||
name = nameMapLookup(name);
|
||||
Instance *inst = findInstanceRelative(name);
|
||||
if (inst) {
|
||||
// <instance>:<port>
|
||||
Pin *pin = network_->findPin(inst, name2);
|
||||
if (pin) {
|
||||
if (local_only
|
||||
&& !network_->isConnected(net_, pin))
|
||||
warn(1651, "%s not connected to net %s.", name, network_->pathName(net_));
|
||||
return parasitics_->ensureParasiticNode(parasitic_, pin, network_);
|
||||
if (name) {
|
||||
Instance *inst = findInstanceRelative(name);
|
||||
if (inst) {
|
||||
// <instance>:<port>
|
||||
Pin *pin = network_->findPin(inst, name2);
|
||||
if (pin) {
|
||||
if (local_only
|
||||
&& !network_->isConnected(net_, pin))
|
||||
warn(1651, "%s not connected to net %s.", name, network_->pathName(net_));
|
||||
return parasitics_->ensureParasiticNode(parasitic_, pin, network_);
|
||||
}
|
||||
else {
|
||||
// Replace delimiter for error message.
|
||||
*delim = delimiter_;
|
||||
warn(1652, "pin %s not found.", name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Replace delimiter for error message.
|
||||
Net *net = findNet(name);
|
||||
// Replace delimiter for error messages.
|
||||
*delim = delimiter_;
|
||||
warn(1652, "pin %s not found.", name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Net *net = findNet(name);
|
||||
// Replace delimiter for error messages.
|
||||
*delim = delimiter_;
|
||||
if (net) {
|
||||
// <net>:<subnode_id>
|
||||
const char *id_str = delim + 1;
|
||||
if (isDigits(id_str)) {
|
||||
int id = atoi(id_str);
|
||||
if (local_only
|
||||
&& !network_->isConnected(net, net_))
|
||||
warn(1653, "%s not connected to net %s.", name, network_->pathName(net_));
|
||||
return parasitics_->ensureParasiticNode(parasitic_, net, id, network_);
|
||||
if (net) {
|
||||
// <net>:<subnode_id>
|
||||
const char *id_str = delim + 1;
|
||||
if (isDigits(id_str)) {
|
||||
int id = atoi(id_str);
|
||||
if (local_only
|
||||
&& !network_->isConnected(net, net_))
|
||||
warn(1653, "%s not connected to net %s.", name, network_->pathName(net_));
|
||||
return parasitics_->ensureParasiticNode(parasitic_, net, id, network_);
|
||||
}
|
||||
else
|
||||
warn(1654, "node %s not a pin or net:number", name);
|
||||
}
|
||||
else
|
||||
warn(1654, "node %s not a pin or net:number", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
141
power/Power.cc
141
power/Power.cc
|
|
@ -47,13 +47,6 @@
|
|||
#include "Bfs.hh"
|
||||
#include "ClkNetwork.hh"
|
||||
|
||||
#if CUDD
|
||||
#include "cudd.h"
|
||||
#else
|
||||
#define Cudd_Init(ignore1, ignore2, ignore3, ignore4, ignore5) nullptr
|
||||
#define Cudd_Quit(ignore1)
|
||||
#endif
|
||||
|
||||
// Related liberty not supported:
|
||||
// library
|
||||
// default_cell_leakage_power : 0;
|
||||
|
|
@ -95,15 +88,10 @@ Power::Power(StaState *sta) :
|
|||
input_activity_{0.1, 0.5, PwrActivityOrigin::input},
|
||||
seq_activity_map_(100, SeqPinHash(network_), SeqPinEqual()),
|
||||
activities_valid_(false),
|
||||
cudd_mgr_(Cudd_Init(0, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0))
|
||||
bdd_(sta)
|
||||
{
|
||||
}
|
||||
|
||||
Power::~Power()
|
||||
{
|
||||
Cudd_Quit(cudd_mgr_);
|
||||
}
|
||||
|
||||
void
|
||||
Power::setGlobalActivity(float activity,
|
||||
float duty)
|
||||
|
|
@ -524,12 +512,12 @@ Power::evalActivity(FuncExpr *expr,
|
|||
if (func_port && func_port->direction()->isInternal())
|
||||
return findSeqActivity(inst, func_port);
|
||||
else {
|
||||
DdNode *bdd = funcBdd(expr);
|
||||
DdNode *bdd = bdd_.funcBdd(expr);
|
||||
float duty = evalBddDuty(bdd, inst);
|
||||
float activity = evalBddActivity(bdd, inst);
|
||||
|
||||
Cudd_RecursiveDeref(cudd_mgr_, bdd);
|
||||
clearVarMap();
|
||||
Cudd_RecursiveDeref(bdd_.cuddMgr(), bdd);
|
||||
bdd_.clearVarMap();
|
||||
return PwrActivity(activity, duty, PwrActivityOrigin::propagated);
|
||||
}
|
||||
}
|
||||
|
|
@ -540,110 +528,19 @@ Power::evalDiffDuty(FuncExpr *expr,
|
|||
LibertyPort *from_port,
|
||||
const Instance *inst)
|
||||
{
|
||||
DdNode *bdd = funcBdd(expr);
|
||||
DdNode *var_node = bdd_port_var_map_[from_port];
|
||||
DdNode *bdd = bdd_.funcBdd(expr);
|
||||
DdNode *var_node = bdd_.findNode(from_port);
|
||||
unsigned var_index = Cudd_NodeReadIndex(var_node);
|
||||
DdNode *diff = Cudd_bddBooleanDiff(cudd_mgr_, bdd, var_index);
|
||||
DdNode *diff = Cudd_bddBooleanDiff(bdd_.cuddMgr(), bdd, var_index);
|
||||
Cudd_Ref(diff);
|
||||
float duty = evalBddDuty(diff, inst);
|
||||
|
||||
Cudd_RecursiveDeref(cudd_mgr_, diff);
|
||||
Cudd_RecursiveDeref(cudd_mgr_, bdd);
|
||||
clearVarMap();
|
||||
Cudd_RecursiveDeref(bdd_.cuddMgr(), diff);
|
||||
Cudd_RecursiveDeref(bdd_.cuddMgr(), bdd);
|
||||
bdd_.clearVarMap();
|
||||
return duty;
|
||||
}
|
||||
|
||||
DdNode *
|
||||
Power::funcBdd(const FuncExpr *expr)
|
||||
{
|
||||
DdNode *left = nullptr;
|
||||
DdNode *right = nullptr;
|
||||
DdNode *result = nullptr;
|
||||
switch (expr->op()) {
|
||||
case FuncExpr::op_port: {
|
||||
LibertyPort *port = expr->port();
|
||||
result = ensureNode(port);
|
||||
break;
|
||||
}
|
||||
case FuncExpr::op_not:
|
||||
left = funcBdd(expr->left());
|
||||
if (left)
|
||||
result = Cudd_Not(left);
|
||||
break;
|
||||
case FuncExpr::op_or:
|
||||
left = funcBdd(expr->left());
|
||||
right = funcBdd(expr->right());
|
||||
if (left && right)
|
||||
result = Cudd_bddOr(cudd_mgr_, left, right);
|
||||
else if (left)
|
||||
result = left;
|
||||
else if (right)
|
||||
result = right;
|
||||
break;
|
||||
case FuncExpr::op_and:
|
||||
left = funcBdd(expr->left());
|
||||
right = funcBdd(expr->right());
|
||||
if (left && right)
|
||||
result = Cudd_bddAnd(cudd_mgr_, left, right);
|
||||
else if (left)
|
||||
result = left;
|
||||
else if (right)
|
||||
result = right;
|
||||
break;
|
||||
case FuncExpr::op_xor:
|
||||
left = funcBdd(expr->left());
|
||||
right = funcBdd(expr->right());
|
||||
if (left && right)
|
||||
result = Cudd_bddXor(cudd_mgr_, left, right);
|
||||
else if (left)
|
||||
result = left;
|
||||
else if (right)
|
||||
result = right;
|
||||
break;
|
||||
case FuncExpr::op_one:
|
||||
result = Cudd_ReadOne(cudd_mgr_);
|
||||
break;
|
||||
case FuncExpr::op_zero:
|
||||
result = Cudd_ReadLogicZero(cudd_mgr_);
|
||||
break;
|
||||
default:
|
||||
report_->critical(1440, "unknown function operator");
|
||||
}
|
||||
if (result)
|
||||
Cudd_Ref(result);
|
||||
if (left)
|
||||
Cudd_RecursiveDeref(cudd_mgr_, left);
|
||||
if (right)
|
||||
Cudd_RecursiveDeref(cudd_mgr_, right);
|
||||
return result;
|
||||
}
|
||||
|
||||
DdNode *
|
||||
Power::ensureNode(LibertyPort *port)
|
||||
{
|
||||
DdNode *bdd = bdd_port_var_map_.findKey(port);
|
||||
if (bdd == nullptr) {
|
||||
bdd = Cudd_bddNewVar(cudd_mgr_);
|
||||
bdd_port_var_map_[port] = bdd;
|
||||
unsigned var_index = Cudd_NodeReadIndex(bdd);
|
||||
bdd_var_idx_port_map_[var_index] = port;
|
||||
Cudd_Ref(bdd);
|
||||
debugPrint(debug_, "power_activity", 2, "%s var %d", port->name(), var_index);
|
||||
}
|
||||
return bdd;
|
||||
}
|
||||
|
||||
void
|
||||
Power::clearVarMap()
|
||||
{
|
||||
for (auto port_node : bdd_port_var_map_) {
|
||||
DdNode *var_node = port_node.second;
|
||||
Cudd_RecursiveDeref(cudd_mgr_, var_node);
|
||||
}
|
||||
bdd_port_var_map_.clear();
|
||||
bdd_var_idx_port_map_.clear();
|
||||
}
|
||||
|
||||
// As suggested by
|
||||
// https://stackoverflow.com/questions/63326728/cudd-printminterm-accessing-the-individual-minterms-in-the-sum-of-products
|
||||
float
|
||||
|
|
@ -651,9 +548,9 @@ Power::evalBddDuty(DdNode *bdd,
|
|||
const Instance *inst)
|
||||
{
|
||||
if (Cudd_IsConstant(bdd)) {
|
||||
if (bdd == Cudd_ReadOne(cudd_mgr_))
|
||||
if (bdd == Cudd_ReadOne(bdd_.cuddMgr()))
|
||||
return 1.0;
|
||||
else if (bdd == Cudd_ReadLogicZero(cudd_mgr_))
|
||||
else if (bdd == Cudd_ReadLogicZero(bdd_.cuddMgr()))
|
||||
return 0.0;
|
||||
else
|
||||
criticalError(1100, "unknown cudd constant");
|
||||
|
|
@ -662,10 +559,10 @@ Power::evalBddDuty(DdNode *bdd,
|
|||
float duty0 = evalBddDuty(Cudd_E(bdd), inst);
|
||||
float duty1 = evalBddDuty(Cudd_T(bdd), inst);
|
||||
unsigned int index = Cudd_NodeReadIndex(bdd);
|
||||
int var_index = Cudd_ReadPerm(cudd_mgr_, index);
|
||||
LibertyPort *port = bdd_var_idx_port_map_[var_index];
|
||||
int var_index = Cudd_ReadPerm(bdd_.cuddMgr(), index);
|
||||
const LibertyPort *port = bdd_.varIndexPort(var_index);
|
||||
if (port->direction()->isInternal())
|
||||
return findSeqActivity(inst, port).duty();
|
||||
return findSeqActivity(inst, const_cast<LibertyPort*>(port)).duty();
|
||||
else {
|
||||
const Pin *pin = findLinkPin(inst, port);
|
||||
if (pin) {
|
||||
|
|
@ -689,17 +586,17 @@ Power::evalBddActivity(DdNode *bdd,
|
|||
const Instance *inst)
|
||||
{
|
||||
float activity = 0.0;
|
||||
for (auto port_var : bdd_port_var_map_) {
|
||||
LibertyPort *port = port_var.first;
|
||||
for (auto port_var : bdd_.portVarMap()) {
|
||||
const LibertyPort *port = port_var.first;
|
||||
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(cudd_mgr_, bdd, var_index);
|
||||
DdNode *diff = Cudd_bddBooleanDiff(bdd_.cuddMgr(), bdd, var_index);
|
||||
Cudd_Ref(diff);
|
||||
float diff_duty = evalBddDuty(diff, inst);
|
||||
Cudd_RecursiveDeref(cudd_mgr_, diff);
|
||||
Cudd_RecursiveDeref(bdd_.cuddMgr(), diff);
|
||||
float var_act = var_activity.activity() * diff_duty;
|
||||
activity += var_act;
|
||||
const Clock *clk = findClk(pin);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include "SdcClass.hh"
|
||||
#include "PowerClass.hh"
|
||||
#include "StaState.hh"
|
||||
#include "Bdd.hh"
|
||||
|
||||
struct DdNode;
|
||||
struct DdManager;
|
||||
|
|
@ -38,8 +39,6 @@ class BfsFwdIterator;
|
|||
class Vertex;
|
||||
|
||||
typedef std::pair<const Instance*, LibertyPort*> SeqPin;
|
||||
typedef Map<LibertyPort*, DdNode*> BddPortVarMap;
|
||||
typedef Map<unsigned, LibertyPort*> BddVarIdxPortMap;
|
||||
|
||||
class SeqPinHash
|
||||
{
|
||||
|
|
@ -68,7 +67,6 @@ class Power : public StaState
|
|||
{
|
||||
public:
|
||||
Power(StaState *sta);
|
||||
~Power();
|
||||
void power(const Corner *corner,
|
||||
// Return values.
|
||||
PowerResult &total,
|
||||
|
|
@ -196,10 +194,6 @@ protected:
|
|||
const Pin *&enable,
|
||||
const Pin *&clk,
|
||||
const Pin *&gclk) const;
|
||||
|
||||
DdNode *funcBdd(const FuncExpr *expr);
|
||||
DdNode *ensureNode(LibertyPort *port);
|
||||
void clearVarMap();
|
||||
float evalBddActivity(DdNode *bdd,
|
||||
const Instance *inst);
|
||||
float evalBddDuty(DdNode *bdd,
|
||||
|
|
@ -217,10 +211,7 @@ private:
|
|||
PwrActivityMap activity_map_;
|
||||
PwrSeqActivityMap seq_activity_map_;
|
||||
bool activities_valid_;
|
||||
|
||||
DdManager *cudd_mgr_;
|
||||
BddPortVarMap bdd_port_var_map_;
|
||||
BddVarIdxPortMap bdd_var_idx_port_map_;
|
||||
Bdd bdd_;
|
||||
|
||||
static constexpr int max_activity_passes_ = 100;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,179 @@
|
|||
// 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 "Bdd.hh"
|
||||
|
||||
#include "StaConfig.hh"
|
||||
#include "Report.hh"
|
||||
#include "FuncExpr.hh"
|
||||
|
||||
#if CUDD
|
||||
#include "cudd.h"
|
||||
#else
|
||||
#include <cstdint>
|
||||
#define CUDD_UNIQUE_SLOTS 0
|
||||
#define CUDD_CACHE_SLOTS 0
|
||||
DdManager *Cudd_Init(int, int, int, int, int) { return nullptr; }
|
||||
void Cudd_Quit(void *) {}
|
||||
DdNode *Cudd_Not(void *) { return nullptr; }
|
||||
DdNode *Cudd_bddOr(void *, void *, void *) { return nullptr; }
|
||||
DdNode *Cudd_bddAnd(void *, void *, void *) { return nullptr; }
|
||||
DdNode *Cudd_bddXor(void *, void *, void *) { return nullptr; }
|
||||
DdNode *Cudd_ReadOne(void *) { return nullptr; }
|
||||
DdNode *Cudd_ReadLogicZero(void *) { return nullptr; }
|
||||
DdNode *Cudd_bddNewVar(void *) { return nullptr; }
|
||||
int Cudd_NodeReadIndex(void *) { return 0;}
|
||||
void Cudd_Ref(void *) {}
|
||||
void Cudd_RecursiveDeref(void *, void *) {}
|
||||
#endif
|
||||
|
||||
namespace sta {
|
||||
|
||||
Bdd::Bdd(const StaState *sta) :
|
||||
StaState(sta),
|
||||
cudd_mgr_(Cudd_Init(0, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0))
|
||||
{
|
||||
}
|
||||
|
||||
Bdd::~Bdd()
|
||||
{
|
||||
Cudd_Quit(cudd_mgr_);
|
||||
}
|
||||
|
||||
DdNode *
|
||||
Bdd::funcBdd(const FuncExpr *expr)
|
||||
{
|
||||
DdNode *left = nullptr;
|
||||
DdNode *right = nullptr;
|
||||
DdNode *result = nullptr;
|
||||
switch (expr->op()) {
|
||||
case FuncExpr::op_port: {
|
||||
LibertyPort *port = expr->port();
|
||||
result = ensureNode(port);
|
||||
break;
|
||||
}
|
||||
case FuncExpr::op_not:
|
||||
left = funcBdd(expr->left());
|
||||
if (left)
|
||||
result = Cudd_Not(left);
|
||||
break;
|
||||
case FuncExpr::op_or:
|
||||
left = funcBdd(expr->left());
|
||||
right = funcBdd(expr->right());
|
||||
if (left && right)
|
||||
result = Cudd_bddOr(cudd_mgr_, left, right);
|
||||
else if (left)
|
||||
result = left;
|
||||
else if (right)
|
||||
result = right;
|
||||
break;
|
||||
case FuncExpr::op_and:
|
||||
left = funcBdd(expr->left());
|
||||
right = funcBdd(expr->right());
|
||||
if (left && right)
|
||||
result = Cudd_bddAnd(cudd_mgr_, left, right);
|
||||
else if (left)
|
||||
result = left;
|
||||
else if (right)
|
||||
result = right;
|
||||
break;
|
||||
case FuncExpr::op_xor:
|
||||
left = funcBdd(expr->left());
|
||||
right = funcBdd(expr->right());
|
||||
if (left && right)
|
||||
result = Cudd_bddXor(cudd_mgr_, left, right);
|
||||
else if (left)
|
||||
result = left;
|
||||
else if (right)
|
||||
result = right;
|
||||
break;
|
||||
case FuncExpr::op_one:
|
||||
result = Cudd_ReadOne(cudd_mgr_);
|
||||
break;
|
||||
case FuncExpr::op_zero:
|
||||
result = Cudd_ReadLogicZero(cudd_mgr_);
|
||||
break;
|
||||
default:
|
||||
report_->critical(1440, "unknown function operator");
|
||||
}
|
||||
if (result)
|
||||
Cudd_Ref(result);
|
||||
if (left)
|
||||
Cudd_RecursiveDeref(cudd_mgr_, left);
|
||||
if (right)
|
||||
Cudd_RecursiveDeref(cudd_mgr_, right);
|
||||
return result;
|
||||
}
|
||||
|
||||
DdNode *
|
||||
Bdd::findNode(const LibertyPort *port)
|
||||
{
|
||||
auto port_var = bdd_port_var_map_.find(port);
|
||||
if (port_var == bdd_port_var_map_.end())
|
||||
return nullptr;
|
||||
else
|
||||
return port_var->second;
|
||||
}
|
||||
|
||||
DdNode *
|
||||
Bdd::ensureNode(const LibertyPort *port)
|
||||
{
|
||||
auto port_var = bdd_port_var_map_.find(port);
|
||||
DdNode *node = nullptr;
|
||||
if (port_var == bdd_port_var_map_.end()) {
|
||||
node = Cudd_bddNewVar(cudd_mgr_);
|
||||
bdd_port_var_map_[port] = node;
|
||||
unsigned var_index = Cudd_NodeReadIndex(node);
|
||||
bdd_var_idx_port_map_[var_index] = port;
|
||||
Cudd_Ref(node);
|
||||
}
|
||||
else
|
||||
node = port_var->second;
|
||||
return node;
|
||||
}
|
||||
|
||||
const LibertyPort *
|
||||
Bdd::nodePort(DdNode *node)
|
||||
{
|
||||
auto port_index = bdd_var_idx_port_map_.find(Cudd_NodeReadIndex(node));
|
||||
if (port_index == bdd_var_idx_port_map_.end())
|
||||
return nullptr;
|
||||
else
|
||||
return port_index->second;
|
||||
}
|
||||
|
||||
const LibertyPort *
|
||||
Bdd::varIndexPort(int var_index)
|
||||
{
|
||||
auto index_port = bdd_var_idx_port_map_.find(var_index);
|
||||
if (index_port == bdd_var_idx_port_map_.end())
|
||||
return nullptr;
|
||||
else
|
||||
return index_port->second;
|
||||
}
|
||||
|
||||
void
|
||||
Bdd::clearVarMap()
|
||||
{
|
||||
for (auto port_node : bdd_port_var_map_) {
|
||||
DdNode *var_node = port_node.second;
|
||||
Cudd_RecursiveDeref(cudd_mgr_, var_node);
|
||||
}
|
||||
bdd_port_var_map_.clear();
|
||||
bdd_var_idx_port_map_.clear();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
// 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 "MinMax.hh"
|
||||
#include "StaState.hh"
|
||||
#include "Transition.hh"
|
||||
#include "PathVertex.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
class ClkDelays
|
||||
{
|
||||
public:
|
||||
ClkDelays();
|
||||
void delay(const RiseFall *src_rf,
|
||||
const RiseFall *end_rf,
|
||||
const MinMax *min_max,
|
||||
// Return values.
|
||||
float &insertion,
|
||||
float &delay,
|
||||
float &lib_clk_delay,
|
||||
float &latency,
|
||||
PathVertex &path,
|
||||
bool &exists);
|
||||
void latency(const RiseFall *src_rf,
|
||||
const RiseFall *end_rf,
|
||||
const MinMax *min_max,
|
||||
// Return values.
|
||||
float &delay,
|
||||
bool &exists);
|
||||
static float latency(PathVertex *clk_path,
|
||||
StaState *sta);
|
||||
void setLatency(const RiseFall *src_rf,
|
||||
const RiseFall *end_rf,
|
||||
const MinMax *min_max,
|
||||
PathVertex *path,
|
||||
StaState *sta);
|
||||
|
||||
private:
|
||||
static float insertionDelay(PathVertex *clk_path,
|
||||
StaState *sta);
|
||||
static float delay(PathVertex *clk_path,
|
||||
StaState *sta);
|
||||
static float clkTreeDelay(PathVertex *clk_path,
|
||||
StaState *sta);
|
||||
|
||||
float insertion_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count];
|
||||
float delay_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count];
|
||||
float lib_clk_delay_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count];
|
||||
float latency_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count];
|
||||
PathVertex path_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count];
|
||||
bool exists_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count];
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
// 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 "ClkLatency.hh"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Report.hh"
|
||||
#include "Debug.hh"
|
||||
#include "Units.hh"
|
||||
#include "Liberty.hh"
|
||||
#include "Network.hh"
|
||||
#include "Clock.hh"
|
||||
#include "Graph.hh"
|
||||
#include "PathVertex.hh"
|
||||
#include "StaState.hh"
|
||||
#include "Search.hh"
|
||||
#include "PathAnalysisPt.hh"
|
||||
#include "ClkInfo.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
ClkLatency::ClkLatency(StaState *sta) :
|
||||
StaState(sta)
|
||||
{
|
||||
}
|
||||
|
||||
ClkDelays
|
||||
ClkLatency::findClkDelays(const Clock *clk,
|
||||
const Corner *corner)
|
||||
{
|
||||
ConstClockSeq clks;
|
||||
clks.push_back(clk);
|
||||
ClkDelayMap clk_delay_map = findClkDelays(clks, corner);
|
||||
return clk_delay_map[clk];
|
||||
}
|
||||
|
||||
void
|
||||
ClkLatency::reportClkLatency(ConstClockSeq clks,
|
||||
const Corner *corner,
|
||||
int digits)
|
||||
{
|
||||
ClkDelayMap clk_delay_map = findClkDelays(clks, corner);
|
||||
|
||||
// Sort the clocks to report in a stable order.
|
||||
ConstClockSeq sorted_clks;
|
||||
for (const Clock *clk : clks)
|
||||
sorted_clks.push_back(clk);
|
||||
std::sort(sorted_clks.begin(), sorted_clks.end(), ClkNameLess());
|
||||
|
||||
for (const Clock *clk : sorted_clks) {
|
||||
ClkDelays clk_delays = clk_delay_map[clk];
|
||||
reportClkLatency(clk, clk_delays, digits);
|
||||
report_->reportBlankLine();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ClkLatency::reportClkLatency(const Clock *clk,
|
||||
ClkDelays &clk_delays,
|
||||
int digits)
|
||||
{
|
||||
Unit *time_unit = units_->timeUnit();
|
||||
report_->reportLine("Clock %s", clk->name());
|
||||
for (const RiseFall *src_rf : RiseFall::range()) {
|
||||
for (const RiseFall *end_rf : RiseFall::range()) {
|
||||
PathVertex path_min;
|
||||
float insertion_min;
|
||||
float delay_min;
|
||||
float lib_clk_delay_min;
|
||||
float latency_min;
|
||||
bool exists_min;
|
||||
clk_delays.delay(src_rf, end_rf, MinMax::min(), insertion_min,
|
||||
delay_min, lib_clk_delay_min, latency_min,
|
||||
path_min, exists_min);
|
||||
PathVertex path_max;
|
||||
float insertion_max;
|
||||
float delay_max;
|
||||
float lib_clk_delay_max;
|
||||
float latency_max;
|
||||
bool exists_max;
|
||||
clk_delays.delay(src_rf, end_rf, MinMax::max(), insertion_max,
|
||||
delay_max, lib_clk_delay_max, latency_max,
|
||||
path_max, exists_max);
|
||||
if (exists_min & exists_max) {
|
||||
report_->reportLine("%s -> %s",
|
||||
src_rf->name(),
|
||||
end_rf->name());
|
||||
report_->reportLine(" min max");
|
||||
|
||||
report_->reportLine("%7s %7s source latency",
|
||||
time_unit->asString(insertion_min, digits),
|
||||
time_unit->asString(insertion_max, digits));
|
||||
report_->reportLine("%7s %7s network latency %s",
|
||||
time_unit->asString(delay_min, digits),
|
||||
"",
|
||||
sdc_network_->pathName(path_min.pin(this)));
|
||||
report_->reportLine("%7s %7s network latency %s",
|
||||
"",
|
||||
time_unit->asString(delay_max, digits),
|
||||
sdc_network_->pathName(path_max.pin(this)));
|
||||
if (lib_clk_delay_min != 0.0
|
||||
|| lib_clk_delay_max != 0.0)
|
||||
report_->reportLine("%7s %7s liberty clock tree delay",
|
||||
time_unit->asString(lib_clk_delay_min, digits),
|
||||
time_unit->asString(lib_clk_delay_max, digits));
|
||||
report_->reportLine("---------------");
|
||||
report_->reportLine("%7s %7s latency",
|
||||
time_unit->asString(latency_min, digits),
|
||||
time_unit->asString(latency_max, digits));
|
||||
float skew = latency_max - latency_min;
|
||||
report_->reportLine(" %7s skew",
|
||||
time_unit->asString(skew, digits));
|
||||
report_->reportBlankLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClkDelayMap
|
||||
ClkLatency::findClkDelays(ConstClockSeq clks,
|
||||
const Corner *corner)
|
||||
{
|
||||
ClkDelayMap clk_delay_map;
|
||||
// Make entries for the relevant clocks to filter path clocks.
|
||||
for (const Clock *clk : clks)
|
||||
clk_delay_map[clk];
|
||||
for (Vertex *clk_vertex : *graph_->regClkVertices()) {
|
||||
VertexPathIterator path_iter(clk_vertex, this);
|
||||
while (path_iter.hasNext()) {
|
||||
PathVertex *path = path_iter.next();
|
||||
const ClockEdge *path_clk_edge = path->clkEdge(this);
|
||||
const PathAnalysisPt *path_ap = path->pathAnalysisPt(this);
|
||||
if (path_clk_edge
|
||||
&& (corner == nullptr
|
||||
|| path_ap->corner() == corner)) {
|
||||
const Clock *path_clk = path_clk_edge->clock();
|
||||
auto delays_itr = clk_delay_map.find(path_clk);
|
||||
if (delays_itr != clk_delay_map.end()) {
|
||||
ClkDelays &clk_delays = delays_itr->second;
|
||||
const RiseFall *clk_rf = path_clk_edge->transition();
|
||||
const MinMax *min_max = path->minMax(this);
|
||||
const RiseFall *end_rf = path->transition(this);
|
||||
float latency = ClkDelays::latency(path, this);
|
||||
float clk_latency;
|
||||
bool exists;
|
||||
clk_delays.latency(clk_rf, end_rf, min_max, clk_latency, exists);
|
||||
if (!exists || min_max->compare(latency, clk_latency))
|
||||
clk_delays.setLatency(clk_rf, end_rf, min_max, path, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return clk_delay_map;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
ClkDelays::ClkDelays()
|
||||
{
|
||||
for (auto src_rf_index : RiseFall::rangeIndex()) {
|
||||
for (auto end_rf_index : RiseFall::rangeIndex()) {
|
||||
for (auto mm_index : MinMax::rangeIndex()) {
|
||||
insertion_[src_rf_index][end_rf_index][mm_index] = 0.0;
|
||||
delay_[src_rf_index][end_rf_index][mm_index] = 0.0;
|
||||
lib_clk_delay_[src_rf_index][end_rf_index][mm_index] = 0.0;
|
||||
latency_[src_rf_index][end_rf_index][mm_index] = 0.0;
|
||||
exists_[src_rf_index][end_rf_index][mm_index] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ClkDelays::delay(const RiseFall *src_rf,
|
||||
const RiseFall *end_rf,
|
||||
const MinMax *min_max,
|
||||
// Return values.
|
||||
float &insertion,
|
||||
float &delay,
|
||||
float &lib_clk_delay,
|
||||
float &latency,
|
||||
PathVertex &path,
|
||||
bool &exists)
|
||||
{
|
||||
int src_rf_index = src_rf->index();
|
||||
int end_rf_index = end_rf->index();
|
||||
int mm_index = min_max->index();
|
||||
path = path_[src_rf_index][end_rf_index][mm_index];
|
||||
insertion = insertion_[src_rf_index][end_rf_index][mm_index];
|
||||
delay = delay_[src_rf_index][end_rf_index][mm_index];
|
||||
lib_clk_delay = lib_clk_delay_[src_rf_index][end_rf_index][mm_index];
|
||||
latency = latency_[src_rf_index][end_rf_index][mm_index];
|
||||
exists = exists_[src_rf_index][end_rf_index][mm_index];
|
||||
}
|
||||
|
||||
void
|
||||
ClkDelays::latency(const RiseFall *src_rf,
|
||||
const RiseFall *end_rf,
|
||||
const MinMax *min_max,
|
||||
// Return values.
|
||||
float &latency,
|
||||
bool &exists)
|
||||
{
|
||||
int src_rf_index = src_rf->index();
|
||||
int end_rf_index = end_rf->index();
|
||||
int mm_index = min_max->index();
|
||||
latency = latency_[src_rf_index][end_rf_index][mm_index];
|
||||
exists = exists_[src_rf_index][end_rf_index][mm_index];
|
||||
}
|
||||
|
||||
void
|
||||
ClkDelays::setLatency(const RiseFall *src_rf,
|
||||
const RiseFall *end_rf,
|
||||
const MinMax *min_max,
|
||||
PathVertex *path,
|
||||
StaState *sta)
|
||||
{
|
||||
int src_rf_index = src_rf->index();
|
||||
int end_rf_index = end_rf->index();
|
||||
int mm_index = min_max->index();
|
||||
|
||||
float insertion = insertionDelay(path, sta);
|
||||
insertion_[src_rf_index][end_rf_index][mm_index] = insertion;
|
||||
|
||||
float delay1 = delay(path, sta);
|
||||
delay_[src_rf_index][end_rf_index][mm_index] = delay1;
|
||||
|
||||
float lib_clk_delay = clkTreeDelay(path, sta);
|
||||
lib_clk_delay_[src_rf_index][end_rf_index][mm_index] = lib_clk_delay;
|
||||
|
||||
float latency = insertion + delay1 + lib_clk_delay;
|
||||
latency_[src_rf_index][end_rf_index][mm_index] = latency;
|
||||
|
||||
path_[src_rf_index][end_rf_index][mm_index] = *path;
|
||||
exists_[src_rf_index][end_rf_index][mm_index] = true;
|
||||
}
|
||||
|
||||
float
|
||||
ClkDelays::latency(PathVertex *clk_path,
|
||||
StaState *sta)
|
||||
{
|
||||
|
||||
float insertion = insertionDelay(clk_path, sta);
|
||||
float delay1 = delay(clk_path, sta);
|
||||
float lib_clk_delay = clkTreeDelay(clk_path, sta);
|
||||
return insertion + delay1 + lib_clk_delay;
|
||||
}
|
||||
|
||||
float
|
||||
ClkDelays::delay(PathVertex *clk_path,
|
||||
StaState *sta)
|
||||
{
|
||||
Arrival arrival = clk_path->arrival(sta);
|
||||
const ClockEdge *path_clk_edge = clk_path->clkEdge(sta);
|
||||
return delayAsFloat(arrival) - path_clk_edge->time();
|
||||
}
|
||||
|
||||
float
|
||||
ClkDelays::insertionDelay(PathVertex *clk_path,
|
||||
StaState *sta)
|
||||
{
|
||||
const ClockEdge *clk_edge = clk_path->clkEdge(sta);
|
||||
const Clock *clk = clk_edge->clock();
|
||||
const RiseFall *clk_rf = clk_edge->transition();
|
||||
ClkInfo *clk_info = clk_path->clkInfo(sta);
|
||||
const Pin *src_pin = clk_info->clkSrc();
|
||||
const PathAnalysisPt *path_ap = clk_path->pathAnalysisPt(sta);
|
||||
const MinMax *min_max = clk_path->minMax(sta);
|
||||
return sta->search()->clockInsertion(clk, src_pin, clk_rf, min_max, min_max, path_ap);
|
||||
}
|
||||
|
||||
float
|
||||
ClkDelays::clkTreeDelay(PathVertex *clk_path,
|
||||
StaState *sta)
|
||||
{
|
||||
const Vertex *vertex = clk_path->vertex(sta);
|
||||
const Pin *pin = vertex->pin();
|
||||
const LibertyPort *port = sta->network()->libertyPort(pin);
|
||||
const MinMax *min_max = clk_path->minMax(sta);
|
||||
const RiseFall *rf = clk_path->transition(sta);
|
||||
Slew slew = clk_path->slew(sta);
|
||||
return port->clkTreeDelay(slew, rf, min_max);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// 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 <map>
|
||||
|
||||
#include "SdcClass.hh"
|
||||
#include "StaState.hh"
|
||||
#include "Transition.hh"
|
||||
#include "SearchClass.hh"
|
||||
#include "PathVertex.hh"
|
||||
#include "ClkDelays.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
typedef std::map<const Clock*, ClkDelays> ClkDelayMap;
|
||||
|
||||
// Find and report clock skews between source/target registers.
|
||||
class ClkLatency : public StaState
|
||||
{
|
||||
public:
|
||||
ClkLatency(StaState *sta);
|
||||
// Report clk latency for clks.
|
||||
void reportClkLatency(ConstClockSeq clks,
|
||||
const Corner *corner,
|
||||
int digits);
|
||||
ClkDelays findClkDelays(const Clock *clk,
|
||||
const Corner *corner);
|
||||
|
||||
protected:
|
||||
ClkDelayMap findClkDelays(ConstClockSeq clks,
|
||||
const Corner *corner);
|
||||
void reportClkLatency(const Clock *clk,
|
||||
ClkDelays &clk_delays,
|
||||
int digits);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
@ -17,12 +17,13 @@
|
|||
#include "ClkSkew.hh"
|
||||
|
||||
#include <cmath> // abs
|
||||
#include <algorithm>
|
||||
|
||||
#include "Report.hh"
|
||||
#include "Debug.hh"
|
||||
#include "Fuzzy.hh"
|
||||
#include "Units.hh"
|
||||
#include "TimingArc.hh"
|
||||
#include "Liberty.hh"
|
||||
#include "Network.hh"
|
||||
#include "Graph.hh"
|
||||
#include "Sdc.hh"
|
||||
|
|
@ -52,10 +53,15 @@ public:
|
|||
PathVertex *tgtPath() { return &tgt_path_; }
|
||||
float srcLatency(StaState *sta);
|
||||
float tgtLatency(StaState *sta);
|
||||
float srcClkTreeDelay(StaState *sta);
|
||||
float tgtClkTreeDelay(StaState *sta);
|
||||
Crpr crpr(StaState *sta);
|
||||
float skew() const { return skew_; }
|
||||
|
||||
private:
|
||||
float clkTreeDelay(PathVertex &clk_path,
|
||||
StaState *sta);
|
||||
|
||||
PathVertex src_path_;
|
||||
PathVertex tgt_path_;
|
||||
float skew_;
|
||||
|
|
@ -94,14 +100,41 @@ float
|
|||
ClkSkew::srcLatency(StaState *sta)
|
||||
{
|
||||
Arrival src_arrival = src_path_.arrival(sta);
|
||||
return delayAsFloat(src_arrival) - src_path_.clkEdge(sta)->time();
|
||||
return delayAsFloat(src_arrival) - src_path_.clkEdge(sta)->time()
|
||||
+ clkTreeDelay(src_path_, sta);
|
||||
}
|
||||
|
||||
float
|
||||
ClkSkew::srcClkTreeDelay(StaState *sta)
|
||||
{
|
||||
return clkTreeDelay(src_path_, sta);
|
||||
}
|
||||
|
||||
float
|
||||
ClkSkew::tgtLatency(StaState *sta)
|
||||
{
|
||||
Arrival tgt_arrival = tgt_path_.arrival(sta);
|
||||
return delayAsFloat(tgt_arrival) - tgt_path_.clkEdge(sta)->time();
|
||||
return delayAsFloat(tgt_arrival) - tgt_path_.clkEdge(sta)->time()
|
||||
+ clkTreeDelay(tgt_path_, sta);
|
||||
}
|
||||
|
||||
float
|
||||
ClkSkew::tgtClkTreeDelay(StaState *sta)
|
||||
{
|
||||
return clkTreeDelay(tgt_path_, sta);
|
||||
}
|
||||
|
||||
float
|
||||
ClkSkew::clkTreeDelay(PathVertex &clk_path,
|
||||
StaState *sta)
|
||||
{
|
||||
const Vertex *vertex = clk_path.vertex(sta);
|
||||
const Pin *pin = vertex->pin();
|
||||
const LibertyPort *port = sta->network()->libertyPort(pin);
|
||||
const MinMax *min_max = clk_path.minMax(sta);
|
||||
const RiseFall *rf = clk_path.transition(sta);
|
||||
Slew slew = clk_path.slew(sta);
|
||||
return port->clkTreeDelay(slew, rf, min_max);
|
||||
}
|
||||
|
||||
Crpr
|
||||
|
|
@ -119,106 +152,129 @@ ClkSkews::ClkSkews(StaState *sta) :
|
|||
}
|
||||
|
||||
void
|
||||
ClkSkews::reportClkSkew(ClockSet *clks,
|
||||
ClkSkews::reportClkSkew(ConstClockSeq clks,
|
||||
const Corner *corner,
|
||||
const SetupHold *setup_hold,
|
||||
int digits)
|
||||
{
|
||||
ClkSkewMap skews;
|
||||
findClkSkew(clks, corner, setup_hold, skews);
|
||||
ClkSkewMap skews = findClkSkew(clks, corner, setup_hold);
|
||||
|
||||
// Sort the clocks to report in a stable order.
|
||||
ClockSeq sorted_clks;
|
||||
for (Clock *clk : *clks)
|
||||
ConstClockSeq sorted_clks;
|
||||
for (const Clock *clk : clks)
|
||||
sorted_clks.push_back(clk);
|
||||
sort(sorted_clks, ClkNameLess());
|
||||
std::sort(sorted_clks.begin(), sorted_clks.end(), ClkNameLess());
|
||||
|
||||
Unit *time_unit = units_->timeUnit();
|
||||
ClockSeq::Iterator clk_iter2(sorted_clks);
|
||||
while (clk_iter2.hasNext()) {
|
||||
Clock *clk = clk_iter2.next();
|
||||
for (const Clock *clk : sorted_clks) {
|
||||
report_->reportLine("Clock %s", clk->name());
|
||||
ClkSkew *clk_skew = skews.findKey(clk);
|
||||
if (clk_skew) {
|
||||
report_->reportLine("Latency CRPR Skew");
|
||||
PathVertex *src_path = clk_skew->srcPath();
|
||||
PathVertex *tgt_path = clk_skew->tgtPath();
|
||||
report_->reportLine("%s %s",
|
||||
sdc_network_->pathName(src_path->pin(this)),
|
||||
src_path->transition(this)->asString());
|
||||
report_->reportLine("%7s",
|
||||
time_unit->asString(clk_skew->srcLatency(this), digits));
|
||||
report_->reportLine("%s %s",
|
||||
sdc_network_->pathName(tgt_path->pin(this)),
|
||||
tgt_path->transition(this)->asString());
|
||||
report_->reportLine("%7s %7s %7s",
|
||||
time_unit->asString(clk_skew->tgtLatency(this), digits),
|
||||
time_unit->asString(delayAsFloat(-clk_skew->crpr(this)),
|
||||
digits),
|
||||
time_unit->asString(clk_skew->skew(), digits));
|
||||
}
|
||||
auto skew_itr = skews.find(clk);
|
||||
if (skew_itr != skews.end())
|
||||
reportClkSkew(skew_itr->second, digits);
|
||||
else
|
||||
report_->reportLine("No launch/capture paths found.");
|
||||
report_->reportBlankLine();
|
||||
}
|
||||
}
|
||||
|
||||
skews.deleteContents();
|
||||
void
|
||||
ClkSkews::reportClkSkew(ClkSkew &clk_skew,
|
||||
int digits)
|
||||
{
|
||||
Unit *time_unit = units_->timeUnit();
|
||||
PathVertex *src_path = clk_skew.srcPath();
|
||||
PathVertex *tgt_path = clk_skew.tgtPath();
|
||||
float src_latency = clk_skew.srcLatency(this);
|
||||
float tgt_latency = clk_skew.tgtLatency(this);
|
||||
float src_clk_tree_delay = clk_skew.srcClkTreeDelay(this);
|
||||
float tgt_clk_tree_delay = clk_skew.tgtClkTreeDelay(this);
|
||||
|
||||
if (src_clk_tree_delay != 0.0)
|
||||
src_latency -= src_clk_tree_delay;
|
||||
report_->reportLine("%7s source latency %s %s",
|
||||
time_unit->asString(src_latency, digits),
|
||||
sdc_network_->pathName(src_path->pin(this)),
|
||||
src_path->transition(this)->asString());
|
||||
if (src_clk_tree_delay != 0.0)
|
||||
report_->reportLine("%7s source clock tree delay",
|
||||
time_unit->asString(src_clk_tree_delay, digits));
|
||||
|
||||
if (tgt_clk_tree_delay != 0.0)
|
||||
tgt_latency -= tgt_clk_tree_delay;
|
||||
report_->reportLine("%7s target latency %s %s",
|
||||
time_unit->asString(-tgt_latency, digits),
|
||||
sdc_network_->pathName(tgt_path->pin(this)),
|
||||
tgt_path->transition(this)->asString());
|
||||
if (tgt_clk_tree_delay != 0.0)
|
||||
report_->reportLine("%7s target clock tree delay",
|
||||
time_unit->asString(-tgt_clk_tree_delay, digits));
|
||||
|
||||
report_->reportLine("%7s CRPR",
|
||||
time_unit->asString(delayAsFloat(-clk_skew.crpr(this)),
|
||||
digits));
|
||||
report_->reportLine("--------------");
|
||||
report_->reportLine("%7s %s skew",
|
||||
time_unit->asString(clk_skew.skew(), digits),
|
||||
src_path->minMax(this) == MinMax::max() ? "setup" : "hold");
|
||||
}
|
||||
|
||||
float
|
||||
ClkSkews::findWorstClkSkew(const Corner *corner,
|
||||
const SetupHold *setup_hold)
|
||||
{
|
||||
ClockSet clks;
|
||||
for (Clock *clk : *sdc_->clocks())
|
||||
clks.insert(clk);
|
||||
ClkSkewMap skews;
|
||||
findClkSkew(&clks, corner, setup_hold, skews);
|
||||
ConstClockSeq clks;
|
||||
for (const Clock *clk : *sdc_->clocks())
|
||||
clks.push_back(clk);
|
||||
ClkSkewMap skews = findClkSkew(clks, corner, setup_hold);
|
||||
float worst_skew = 0.0;
|
||||
for (auto clk_skew_itr : skews) {
|
||||
ClkSkew *clk_skew = clk_skew_itr.second;
|
||||
float skew = clk_skew->skew();
|
||||
ClkSkew &clk_skew = clk_skew_itr.second;
|
||||
float skew = clk_skew.skew();
|
||||
if (abs(skew) > abs(worst_skew))
|
||||
worst_skew = skew;
|
||||
}
|
||||
skews.deleteContents();
|
||||
return worst_skew;
|
||||
}
|
||||
|
||||
void
|
||||
ClkSkews::findClkSkew(ClockSet *clks,
|
||||
ClkSkewMap
|
||||
ClkSkews::findClkSkew(ConstClockSeq &clks,
|
||||
const Corner *corner,
|
||||
const SetupHold *setup_hold,
|
||||
ClkSkewMap &skews)
|
||||
const SetupHold *setup_hold)
|
||||
{
|
||||
ClkSkewMap skews;
|
||||
|
||||
ConstClockSet clk_set;
|
||||
for (const Clock *clk : clks)
|
||||
clk_set.insert(clk);
|
||||
|
||||
for (Vertex *src_vertex : *graph_->regClkVertices()) {
|
||||
if (hasClkPaths(src_vertex, clks)) {
|
||||
if (hasClkPaths(src_vertex, clk_set)) {
|
||||
VertexOutEdgeIterator edge_iter(src_vertex, graph_);
|
||||
while (edge_iter.hasNext()) {
|
||||
Edge *edge = edge_iter.next();
|
||||
if (edge->role()->genericRole() == TimingRole::regClkToQ()) {
|
||||
Vertex *q_vertex = edge->to(graph_);
|
||||
RiseFall *rf = edge->timingArcSet()->isRisingFallingEdge();
|
||||
RiseFallBoth *src_rf = rf
|
||||
const RiseFall *rf = edge->timingArcSet()->isRisingFallingEdge();
|
||||
const RiseFallBoth *src_rf = rf
|
||||
? rf->asRiseFallBoth()
|
||||
: RiseFallBoth::riseFall();
|
||||
findClkSkewFrom(src_vertex, q_vertex, src_rf, clks,
|
||||
findClkSkewFrom(src_vertex, q_vertex, src_rf, clk_set,
|
||||
corner, setup_hold, skews);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return skews;
|
||||
}
|
||||
|
||||
bool
|
||||
ClkSkews::hasClkPaths(Vertex *vertex,
|
||||
ClockSet *clks)
|
||||
ConstClockSet &clks)
|
||||
{
|
||||
VertexPathIterator path_iter(vertex, this);
|
||||
while (path_iter.hasNext()) {
|
||||
PathVertex *path = path_iter.next();
|
||||
const Clock *path_clk = path->clock(this);
|
||||
if (clks->hasKey(const_cast<Clock*>(path_clk)))
|
||||
if (clks.find(path_clk) != clks.end())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -227,16 +283,14 @@ ClkSkews::hasClkPaths(Vertex *vertex,
|
|||
void
|
||||
ClkSkews::findClkSkewFrom(Vertex *src_vertex,
|
||||
Vertex *q_vertex,
|
||||
RiseFallBoth *src_rf,
|
||||
ClockSet *clks,
|
||||
const RiseFallBoth *src_rf,
|
||||
ConstClockSet &clk_set,
|
||||
const Corner *corner,
|
||||
const SetupHold *setup_hold,
|
||||
ClkSkewMap &skews)
|
||||
{
|
||||
VertexSet endpoints = findFanout(q_vertex);
|
||||
VertexSet::Iterator end_iter(endpoints);
|
||||
while (end_iter.hasNext()) {
|
||||
Vertex *end = end_iter.next();
|
||||
for (Vertex *end : endpoints) {
|
||||
VertexInEdgeIterator edge_iter(end, graph_);
|
||||
while (edge_iter.hasNext()) {
|
||||
Edge *edge = edge_iter.next();
|
||||
|
|
@ -247,12 +301,12 @@ ClkSkews::findClkSkewFrom(Vertex *src_vertex,
|
|||
|| ((setup_hold == SetupHold::min()
|
||||
&& role->genericRole() == TimingRole::hold())))) {
|
||||
Vertex *tgt_vertex = edge->from(graph_);
|
||||
RiseFall *tgt_rf1 = edge->timingArcSet()->isRisingFallingEdge();
|
||||
RiseFallBoth *tgt_rf = tgt_rf1
|
||||
const RiseFall *tgt_rf1 = edge->timingArcSet()->isRisingFallingEdge();
|
||||
const RiseFallBoth *tgt_rf = tgt_rf1
|
||||
? tgt_rf1->asRiseFallBoth()
|
||||
: RiseFallBoth::riseFall();
|
||||
findClkSkew(src_vertex, src_rf, tgt_vertex, tgt_rf,
|
||||
clks, corner, setup_hold, skews);
|
||||
clk_set, corner, setup_hold, skews);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -260,13 +314,13 @@ ClkSkews::findClkSkewFrom(Vertex *src_vertex,
|
|||
|
||||
void
|
||||
ClkSkews::findClkSkew(Vertex *src_vertex,
|
||||
RiseFallBoth *src_rf,
|
||||
const RiseFallBoth *src_rf,
|
||||
Vertex *tgt_vertex,
|
||||
RiseFallBoth *tgt_rf,
|
||||
ClockSet *clks,
|
||||
const RiseFallBoth *tgt_rf,
|
||||
ConstClockSet &clk_set,
|
||||
const Corner *corner,
|
||||
const SetupHold *setup_hold,
|
||||
ClkSkewMap &skews)
|
||||
ClkSkewMap &skews)
|
||||
{
|
||||
Unit *time_unit = units_->timeUnit();
|
||||
const SetupHold *tgt_min_max = setup_hold->opposite();
|
||||
|
|
@ -276,7 +330,7 @@ ClkSkews::findClkSkew(Vertex *src_vertex,
|
|||
const Clock *src_clk = src_path->clock(this);
|
||||
if (src_rf->matches(src_path->transition(this))
|
||||
&& src_path->minMax(this) == setup_hold
|
||||
&& clks->hasKey(const_cast<Clock*>(src_clk))) {
|
||||
&& clk_set.find(src_clk) != clk_set.end()) {
|
||||
Corner *src_corner = src_path->pathAnalysisPt(this)->corner();
|
||||
if (corner == nullptr
|
||||
|| src_corner == corner) {
|
||||
|
|
@ -290,7 +344,7 @@ ClkSkews::findClkSkew(Vertex *src_vertex,
|
|||
&& tgt_path->minMax(this) == tgt_min_max
|
||||
&& tgt_path->pathAnalysisPt(this)->corner() == src_corner) {
|
||||
ClkSkew probe(src_path, tgt_path, this);
|
||||
ClkSkew *clk_skew = skews.findKey(const_cast<Clock*>(src_clk));
|
||||
ClkSkew &clk_skew = skews[src_clk];
|
||||
debugPrint(debug_, "clk_skew", 2,
|
||||
"%s %s %s -> %s %s %s crpr = %s skew = %s",
|
||||
network_->pathName(src_path->pin(this)),
|
||||
|
|
@ -301,12 +355,9 @@ ClkSkews::findClkSkew(Vertex *src_vertex,
|
|||
time_unit->asString(probe.tgtLatency(this)),
|
||||
delayAsString(probe.crpr(this), this),
|
||||
time_unit->asString(probe.skew()));
|
||||
if (clk_skew == nullptr) {
|
||||
clk_skew = new ClkSkew(probe);
|
||||
skews[src_clk] = clk_skew;
|
||||
}
|
||||
else if (abs(probe.skew()) > abs(clk_skew->skew()))
|
||||
*clk_skew = probe;
|
||||
if (clk_skew.srcPath()->isNull()
|
||||
|| abs(probe.skew()) > abs(clk_skew.skew()))
|
||||
clk_skew = probe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -358,31 +409,4 @@ ClkSkews::findFanout(Vertex *from)
|
|||
return endpoints;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
ClkSkews::findClkDelays(const Clock *clk,
|
||||
// Return values.
|
||||
ClkDelays &delays)
|
||||
{
|
||||
for (Vertex *clk_vertex : *graph_->regClkVertices()) {
|
||||
VertexPathIterator path_iter(clk_vertex, this);
|
||||
while (path_iter.hasNext()) {
|
||||
PathVertex *path = path_iter.next();
|
||||
const ClockEdge *path_clk_edge = path->clkEdge(this);
|
||||
if (path_clk_edge) {
|
||||
const RiseFall *clk_rf = path_clk_edge->transition();
|
||||
const Clock *path_clk = path_clk_edge->clock();
|
||||
if (path_clk == clk) {
|
||||
Arrival arrival = path->arrival(this);
|
||||
Delay clk_delay = delayAsFloat(arrival) - path_clk_edge->time();
|
||||
const MinMax *min_max = path->minMax(this);
|
||||
const RiseFall *rf = path->transition(this);
|
||||
delays[clk_rf->index()][rf->index()].setValue(min_max, clk_delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -16,17 +16,19 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Map.hh"
|
||||
#include <map>
|
||||
|
||||
#include "SdcClass.hh"
|
||||
#include "StaState.hh"
|
||||
#include "Transition.hh"
|
||||
#include "SearchClass.hh"
|
||||
#include "PathVertex.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
class ClkSkew;
|
||||
|
||||
typedef Map<const Clock*, ClkSkew*> ClkSkewMap;
|
||||
typedef std::map<const Clock*, ClkSkew> ClkSkewMap;
|
||||
|
||||
// Find and report clock skews between source/target registers.
|
||||
class ClkSkews : public StaState
|
||||
|
|
@ -34,40 +36,38 @@ class ClkSkews : public StaState
|
|||
public:
|
||||
ClkSkews(StaState *sta);
|
||||
// Report clk skews for clks.
|
||||
void reportClkSkew(ClockSet *clks,
|
||||
void reportClkSkew(ConstClockSeq clks,
|
||||
const Corner *corner,
|
||||
const SetupHold *setup_hold,
|
||||
int digits);
|
||||
// Find worst clock skew between src/target registers.
|
||||
float findWorstClkSkew(const Corner *corner,
|
||||
const SetupHold *setup_hold);
|
||||
void findClkDelays(const Clock *clk,
|
||||
// Return values.
|
||||
ClkDelays &delays);
|
||||
|
||||
protected:
|
||||
void findClkSkew(ClockSet *clks,
|
||||
const Corner *corner,
|
||||
const SetupHold *setup_hold,
|
||||
ClkSkewMap &skews);
|
||||
ClkSkewMap findClkSkew(ConstClockSeq &clks,
|
||||
const Corner *corner,
|
||||
const SetupHold *setup_hold);
|
||||
bool hasClkPaths(Vertex *vertex,
|
||||
ClockSet *clks);
|
||||
ConstClockSet &clks);
|
||||
void findClkSkewFrom(Vertex *src_vertex,
|
||||
Vertex *q_vertex,
|
||||
RiseFallBoth *src_rf,
|
||||
ClockSet *clks,
|
||||
const RiseFallBoth *src_rf,
|
||||
ConstClockSet &clk_set,
|
||||
const Corner *corner,
|
||||
const SetupHold *setup_hold,
|
||||
ClkSkewMap &skews);
|
||||
void findClkSkew(Vertex *src_vertex,
|
||||
RiseFallBoth *src_rf,
|
||||
const RiseFallBoth *src_rf,
|
||||
Vertex *tgt_vertex,
|
||||
RiseFallBoth *tgt_rf,
|
||||
ClockSet *clks,
|
||||
const RiseFallBoth *tgt_rf,
|
||||
ConstClockSet &clk_set,
|
||||
const Corner *corner,
|
||||
const SetupHold *setup_hold,
|
||||
ClkSkewMap &skews);
|
||||
VertexSet findFanout(Vertex *from);
|
||||
void reportClkSkew(ClkSkew &clk_skew,
|
||||
int digits);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
#include "Sta.hh"
|
||||
#include "VisitPathEnds.hh"
|
||||
#include "ArcDelayCalc.hh"
|
||||
#include "ClkSkew.hh"
|
||||
#include "ClkLatency.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
|
|
@ -156,7 +156,22 @@ void
|
|||
MakeTimingModel::makeCell()
|
||||
{
|
||||
cell_ = lib_builder_->makeCell(library_, cell_name_, filename_);
|
||||
cell_->setInterfaceTiming(true);
|
||||
cell_->setIsMacro(true);
|
||||
cell_->setArea(findArea());
|
||||
}
|
||||
|
||||
float
|
||||
MakeTimingModel::findArea()
|
||||
{
|
||||
float area = 0.0;
|
||||
LeafInstanceIterator *leaf_iter = network_->leafInstanceIterator();
|
||||
while (leaf_iter->hasNext()) {
|
||||
const Instance *inst = leaf_iter->next();
|
||||
const LibertyCell *cell = network_->libertyCell(inst);
|
||||
area += cell->area();
|
||||
}
|
||||
delete leaf_iter;
|
||||
return area;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -527,17 +542,17 @@ MakeTimingModel::findClkInsertionDelays()
|
|||
size_t clk_count = clks->size();
|
||||
if (clk_count == 1) {
|
||||
for (const Clock *clk : *clks) {
|
||||
ClkDelays delays;
|
||||
sta_->findClkDelays(clk, delays);
|
||||
ClkDelays delays = sta_->findClkDelays(clk);
|
||||
for (const MinMax *min_max : MinMax::range()) {
|
||||
TimingArcAttrsPtr attrs = nullptr;
|
||||
for (const RiseFall *clk_rf : RiseFall::range()) {
|
||||
int clk_rf_index = clk_rf->index();
|
||||
float delay = min_max->initValue();
|
||||
for (const int end_rf_index : RiseFall::rangeIndex()) {
|
||||
Delay delay1;
|
||||
for (const RiseFall *end_rf : RiseFall::range()) {
|
||||
PathVertex clk_path;
|
||||
float insertion, delay1, lib_clk_delay, latency;
|
||||
bool exists;
|
||||
delays[clk_rf_index][end_rf_index].value(min_max, delay1, exists);
|
||||
delays.delay(clk_rf, end_rf, min_max, insertion, delay1,
|
||||
lib_clk_delay, latency, clk_path, exists);
|
||||
if (exists)
|
||||
delay = min_max->minMax(delay, delayAsFloat(delay1));
|
||||
}
|
||||
|
|
@ -551,7 +566,8 @@ MakeTimingModel::findClkInsertionDelays()
|
|||
TimingRole *role = (min_max == MinMax::min())
|
||||
? TimingRole::clockTreePathMin()
|
||||
: TimingRole::clockTreePathMax();
|
||||
lib_builder_->makeClockTreePathArcs(cell_, lib_port, role, attrs);
|
||||
lib_builder_->makeClockTreePathArcs(cell_, lib_port, role,
|
||||
min_max, attrs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ public:
|
|||
private:
|
||||
void makeLibrary();
|
||||
void makeCell();
|
||||
float findArea();
|
||||
void makePorts();
|
||||
void checkClock(Clock *clk);
|
||||
void findTimingFromInputs();
|
||||
|
|
|
|||
162
search/Sim.cc
162
search/Sim.cc
|
|
@ -60,14 +60,13 @@ Sim::Sim(StaState *sta) :
|
|||
invalid_load_pins_(network_),
|
||||
instances_with_const_pins_(network_),
|
||||
instances_to_annotate_(network_),
|
||||
cudd_mgr_(Cudd_Init(0, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0))
|
||||
bdd_(sta)
|
||||
{
|
||||
}
|
||||
|
||||
Sim::~Sim()
|
||||
{
|
||||
delete observer_;
|
||||
Cudd_Quit(cudd_mgr_);
|
||||
}
|
||||
|
||||
#if CUDD
|
||||
|
|
@ -82,17 +81,18 @@ Sim::functionSense(const FuncExpr *expr,
|
|||
expr->asString());
|
||||
bool increasing, decreasing;
|
||||
{
|
||||
UniqueLock lock(cudd_lock_);
|
||||
DdNode *bdd = funcBdd(expr, inst);
|
||||
UniqueLock lock(bdd_lock_);
|
||||
DdNode *bdd = funcBddSim(expr, inst);
|
||||
DdManager *cudd_mgr = bdd_.cuddMgr();
|
||||
LibertyPort *input_port = network_->libertyPort(input_pin);
|
||||
DdNode *input_node = ensureNode(input_port);
|
||||
DdNode *input_node = bdd_.ensureNode(input_port);
|
||||
unsigned int input_index = Cudd_NodeReadIndex(input_node);
|
||||
increasing = (Cudd_Increasing(cudd_mgr_, bdd, input_index)
|
||||
== Cudd_ReadOne(cudd_mgr_));
|
||||
decreasing = (Cudd_Decreasing(cudd_mgr_, bdd, input_index)
|
||||
== Cudd_ReadOne(cudd_mgr_));
|
||||
Cudd_RecursiveDeref(cudd_mgr_, bdd);
|
||||
clearSymtab();
|
||||
increasing = (Cudd_Increasing(cudd_mgr, bdd, input_index)
|
||||
== Cudd_ReadOne(cudd_mgr));
|
||||
decreasing = (Cudd_Decreasing(cudd_mgr, bdd, input_index)
|
||||
== Cudd_ReadOne(cudd_mgr));
|
||||
Cudd_RecursiveDeref(cudd_mgr, bdd);
|
||||
bdd_.clearVarMap();
|
||||
}
|
||||
TimingSense sense;
|
||||
if (increasing && decreasing)
|
||||
|
|
@ -107,127 +107,57 @@ Sim::functionSense(const FuncExpr *expr,
|
|||
return sense;
|
||||
}
|
||||
|
||||
void
|
||||
Sim::clearSymtab() const
|
||||
{
|
||||
for (auto name_node : symtab_) {
|
||||
DdNode *sym_node = name_node.second;
|
||||
Cudd_RecursiveDeref(cudd_mgr_, sym_node);
|
||||
}
|
||||
symtab_.clear();
|
||||
}
|
||||
|
||||
LogicValue
|
||||
Sim::evalExpr(const FuncExpr *expr,
|
||||
const Instance *inst) const
|
||||
const Instance *inst)
|
||||
{
|
||||
UniqueLock lock(cudd_lock_);
|
||||
DdNode *bdd = funcBdd(expr, inst);
|
||||
UniqueLock lock(bdd_lock_);
|
||||
DdNode *bdd = funcBddSim(expr, inst);
|
||||
LogicValue value = LogicValue::unknown;
|
||||
if (bdd == Cudd_ReadLogicZero(cudd_mgr_))
|
||||
DdManager *cudd_mgr = bdd_.cuddMgr();
|
||||
if (bdd == Cudd_ReadLogicZero(cudd_mgr))
|
||||
value = LogicValue::zero;
|
||||
else if (bdd == Cudd_ReadOne(cudd_mgr_))
|
||||
else if (bdd == Cudd_ReadOne(cudd_mgr))
|
||||
value = LogicValue::one;
|
||||
|
||||
if (bdd) {
|
||||
Cudd_RecursiveDeref(cudd_mgr_, bdd);
|
||||
clearSymtab();
|
||||
Cudd_RecursiveDeref(bdd_.cuddMgr(), bdd);
|
||||
bdd_.clearVarMap();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// Returns nullptr if the expression simply references an internal port.
|
||||
// BDD with instance pin values substituted.
|
||||
DdNode *
|
||||
Sim::funcBdd(const FuncExpr *expr,
|
||||
const Instance *inst) const
|
||||
Sim::funcBddSim(const FuncExpr *expr,
|
||||
const Instance *inst)
|
||||
{
|
||||
DdNode *left = nullptr;
|
||||
DdNode *right = nullptr;
|
||||
DdNode *result = nullptr;
|
||||
switch (expr->op()) {
|
||||
case FuncExpr::op_port: {
|
||||
LibertyPort *port = expr->port();
|
||||
Pin *pin = network_->findPin(inst, port);
|
||||
// Internal ports don't have instance pins.
|
||||
if (pin) {
|
||||
DdNode *bdd = bdd_.funcBdd(expr);
|
||||
DdManager *cudd_mgr = bdd_.cuddMgr();
|
||||
InstancePinIterator *pin_iter = network_->pinIterator(inst);
|
||||
while (pin_iter->hasNext()) {
|
||||
const Pin *pin = pin_iter->next();
|
||||
const LibertyPort *port = network_->libertyPort(pin);
|
||||
DdNode *port_node = bdd_.findNode(port);
|
||||
if (port_node) {
|
||||
LogicValue value = logicValue(pin);
|
||||
int var_index = Cudd_NodeReadIndex(port_node);
|
||||
//printf("%s %d %c\n", port->name(), var_index, logicValueString(value));
|
||||
switch (value) {
|
||||
case LogicValue::zero:
|
||||
result = Cudd_ReadLogicZero(cudd_mgr_);
|
||||
break;
|
||||
bdd = Cudd_bddCompose(cudd_mgr, bdd, Cudd_ReadLogicZero(cudd_mgr), var_index);
|
||||
Cudd_Ref(bdd);
|
||||
break;
|
||||
case LogicValue::one:
|
||||
result = Cudd_ReadOne(cudd_mgr_);
|
||||
break;
|
||||
bdd = Cudd_bddCompose(cudd_mgr, bdd, Cudd_ReadOne(cudd_mgr), var_index);
|
||||
Cudd_Ref(bdd);
|
||||
break;
|
||||
default:
|
||||
result = ensureNode(port);
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FuncExpr::op_not:
|
||||
left = funcBdd(expr->left(), inst);
|
||||
if (left)
|
||||
result = Cudd_Not(left);
|
||||
break;
|
||||
case FuncExpr::op_or:
|
||||
left = funcBdd(expr->left(), inst);
|
||||
right = funcBdd(expr->right(), inst);
|
||||
if (left && right)
|
||||
result = Cudd_bddOr(cudd_mgr_, left, right);
|
||||
else if (left)
|
||||
result = left;
|
||||
else if (right)
|
||||
result = right;
|
||||
break;
|
||||
case FuncExpr::op_and:
|
||||
left = funcBdd(expr->left(), inst);
|
||||
right = funcBdd(expr->right(), inst);
|
||||
if (left && right)
|
||||
result = Cudd_bddAnd(cudd_mgr_, left, right);
|
||||
else if (left)
|
||||
result = left;
|
||||
else if (right)
|
||||
result = right;
|
||||
break;
|
||||
case FuncExpr::op_xor:
|
||||
left = funcBdd(expr->left(), inst);
|
||||
right = funcBdd(expr->right(), inst);
|
||||
if (left && right)
|
||||
result = Cudd_bddXor(cudd_mgr_, left, right);
|
||||
else if (left)
|
||||
result = left;
|
||||
else if (right)
|
||||
result = right;
|
||||
break;
|
||||
case FuncExpr::op_one:
|
||||
result = Cudd_ReadOne(cudd_mgr_);
|
||||
break;
|
||||
case FuncExpr::op_zero:
|
||||
result = Cudd_ReadLogicZero(cudd_mgr_);
|
||||
break;
|
||||
default:
|
||||
report_->critical(1520, "unknown function operator");
|
||||
}
|
||||
if (result)
|
||||
Cudd_Ref(result);
|
||||
if (left)
|
||||
Cudd_RecursiveDeref(cudd_mgr_, left);
|
||||
if (right)
|
||||
Cudd_RecursiveDeref(cudd_mgr_, right);
|
||||
return result;
|
||||
}
|
||||
|
||||
DdNode *
|
||||
Sim::ensureNode(LibertyPort *port) const
|
||||
{
|
||||
const char *port_name = port->name();
|
||||
DdNode *node = symtab_.findKey(port_name);
|
||||
if (node == nullptr) {
|
||||
node = Cudd_bddNewVar(cudd_mgr_);
|
||||
symtab_[port_name] = node;
|
||||
Cudd_Ref(node);
|
||||
}
|
||||
return node;
|
||||
return bdd;
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
@ -468,7 +398,7 @@ Sim::functionSense(const FuncExpr *expr,
|
|||
|
||||
LogicValue
|
||||
Sim::evalExpr(const FuncExpr *expr,
|
||||
const Instance *inst) const
|
||||
const Instance *inst)
|
||||
{
|
||||
switch (expr->op()) {
|
||||
case FuncExpr::op_port: {
|
||||
|
|
@ -1199,7 +1129,7 @@ isCondDisabled(Edge *edge,
|
|||
const Pin *from_pin,
|
||||
const Pin *to_pin,
|
||||
const Network *network,
|
||||
const Sim *sim)
|
||||
Sim *sim)
|
||||
{
|
||||
bool is_disabled;
|
||||
FuncExpr *disable_cond;
|
||||
|
|
@ -1214,7 +1144,7 @@ isCondDisabled(Edge *edge,
|
|||
const Pin *from_pin,
|
||||
const Pin *to_pin,
|
||||
const Network *network,
|
||||
const Sim *sim,
|
||||
Sim *sim,
|
||||
bool &is_disabled,
|
||||
FuncExpr *&disable_cond)
|
||||
{
|
||||
|
|
@ -1248,7 +1178,7 @@ bool
|
|||
isModeDisabled(Edge *edge,
|
||||
const Instance *inst,
|
||||
const Network *network,
|
||||
const Sim *sim)
|
||||
Sim *sim)
|
||||
{
|
||||
bool is_disabled;
|
||||
FuncExpr *disable_cond;
|
||||
|
|
@ -1261,7 +1191,7 @@ void
|
|||
isModeDisabled(Edge *edge,
|
||||
const Instance *inst,
|
||||
const Network *network,
|
||||
const Sim *sim,
|
||||
Sim *sim,
|
||||
bool &is_disabled,
|
||||
FuncExpr *&disable_cond)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,9 +25,7 @@
|
|||
#include "GraphClass.hh"
|
||||
#include "SdcClass.hh"
|
||||
#include "StaState.hh"
|
||||
|
||||
struct DdNode;
|
||||
struct DdManager;
|
||||
#include "Bdd.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
|
|
@ -35,7 +33,6 @@ class SimObserver;
|
|||
|
||||
typedef Map<const Pin*, LogicValue> PinValueMap;
|
||||
typedef std::queue<const Instance*> EvalQueue;
|
||||
typedef Map<const char*, DdNode*, CharPtrLess> BddSymbolTable;
|
||||
|
||||
// Propagate constants from constraints and netlist tie high/low
|
||||
// connections thru gates.
|
||||
|
|
@ -50,7 +47,7 @@ public:
|
|||
void ensureConstantsPropagated();
|
||||
void constantsInvalid();
|
||||
LogicValue evalExpr(const FuncExpr *expr,
|
||||
const Instance *inst) const;
|
||||
const Instance *inst);
|
||||
LogicValue logicValue(const Pin *pin) const;
|
||||
bool logicZeroOne(const Pin *pin) const;
|
||||
bool logicZeroOne(const Vertex *vertex) const;
|
||||
|
|
@ -110,6 +107,8 @@ protected:
|
|||
const Pin *load_pin);
|
||||
void setSimValue(Vertex *vertex,
|
||||
LogicValue value);
|
||||
DdNode *funcBddSim(const FuncExpr *expr,
|
||||
const Instance *inst);
|
||||
|
||||
SimObserver *observer_;
|
||||
bool valid_;
|
||||
|
|
@ -128,15 +127,8 @@ protected:
|
|||
// Instances with constant pin values for annotateVertexEdges.
|
||||
InstanceSet instances_with_const_pins_;
|
||||
InstanceSet instances_to_annotate_;
|
||||
|
||||
DdNode *funcBdd(const FuncExpr *expr,
|
||||
const Instance *inst) const;
|
||||
DdNode *ensureNode(LibertyPort *port) const;
|
||||
void clearSymtab() const;
|
||||
|
||||
DdManager *cudd_mgr_;
|
||||
mutable BddSymbolTable symtab_;
|
||||
mutable std::mutex cudd_lock_;
|
||||
Bdd bdd_;
|
||||
mutable std::mutex bdd_lock_;
|
||||
};
|
||||
|
||||
// Abstract base class for Sim value change observer.
|
||||
|
|
@ -160,7 +152,7 @@ isCondDisabled(Edge *edge,
|
|||
const Pin *from_pin,
|
||||
const Pin *to_pin,
|
||||
const Network *network,
|
||||
const Sim *sim);
|
||||
Sim *sim);
|
||||
|
||||
// isCondDisabled but also return the cond expression that causes
|
||||
// the disable. This can differ from the edge cond expression
|
||||
|
|
@ -172,7 +164,7 @@ isCondDisabled(Edge *edge,
|
|||
const Pin *from_pin,
|
||||
const Pin *to_pin,
|
||||
const Network *network,
|
||||
const Sim *sim,
|
||||
Sim *sim,
|
||||
bool &is_disabled,
|
||||
FuncExpr *&disable_cond);
|
||||
|
||||
|
|
@ -182,12 +174,12 @@ bool
|
|||
isModeDisabled(Edge *edge,
|
||||
const Instance *inst,
|
||||
const Network *network,
|
||||
const Sim *sim);
|
||||
Sim *sim);
|
||||
void
|
||||
isModeDisabled(Edge *edge,
|
||||
const Instance *inst,
|
||||
const Network *network,
|
||||
const Sim *sim,
|
||||
Sim *sim,
|
||||
bool &is_disabled,
|
||||
FuncExpr *&disable_cond);
|
||||
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@
|
|||
#include "CheckMinPeriods.hh"
|
||||
#include "CheckMaxSkews.hh"
|
||||
#include "ClkSkew.hh"
|
||||
#include "ClkLatency.hh"
|
||||
#include "FindRegister.hh"
|
||||
#include "ReportPath.hh"
|
||||
#include "VisitPathGroupVertices.hh"
|
||||
|
|
@ -2600,7 +2601,7 @@ Sta::updateTiming(bool full)
|
|||
////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
Sta::reportClkSkew(ClockSet *clks,
|
||||
Sta::reportClkSkew(ConstClockSeq clks,
|
||||
const Corner *corner,
|
||||
const SetupHold *setup_hold,
|
||||
int digits)
|
||||
|
|
@ -2616,15 +2617,6 @@ Sta::findWorstClkSkew(const SetupHold *setup_hold)
|
|||
return clk_skews_->findWorstClkSkew(cmd_corner_, setup_hold);
|
||||
}
|
||||
|
||||
void
|
||||
Sta::findClkDelays(const Clock *clk,
|
||||
// Return values.
|
||||
ClkDelays &delays)
|
||||
{
|
||||
clkSkewPreamble();
|
||||
clk_skews_->findClkDelays(clk, delays);
|
||||
}
|
||||
|
||||
void
|
||||
Sta::clkSkewPreamble()
|
||||
{
|
||||
|
|
@ -2635,6 +2627,26 @@ Sta::clkSkewPreamble()
|
|||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
Sta::reportClkLatency(ConstClockSeq clks,
|
||||
const Corner *corner,
|
||||
int digits)
|
||||
{
|
||||
ensureClkArrivals();
|
||||
ClkLatency clk_latency(this);
|
||||
clk_latency.reportClkLatency(clks, corner, digits);
|
||||
}
|
||||
|
||||
ClkDelays
|
||||
Sta::findClkDelays(const Clock *clk)
|
||||
{
|
||||
ensureClkArrivals();
|
||||
ClkLatency clk_latency(this);
|
||||
return clk_latency.findClkDelays(clk, nullptr);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
Sta::delaysInvalid()
|
||||
{
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,191 @@
|
|||
// 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 <fstream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "StaState.hh"
|
||||
#include "StringSet.hh"
|
||||
#include "Liberty.hh"
|
||||
#include "GraphClass.hh"
|
||||
#include "Parasitics.hh"
|
||||
#include "Bdd.hh"
|
||||
#include "CircuitSim.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
using std::string;
|
||||
using std::ofstream;
|
||||
|
||||
typedef std::map<const ParasiticNode*, int> ParasiticNodeMap;
|
||||
typedef Map<string, StringVector> CellSpicePortNames;
|
||||
typedef Map<const LibertyPort*, LogicValue> LibertyPortLogicValues;
|
||||
typedef std::vector<string> StdStringSeq;
|
||||
|
||||
// Utilities for writing a spice deck.
|
||||
class WriteSpice : public StaState
|
||||
{
|
||||
public:
|
||||
WriteSpice(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 StaState *sta);
|
||||
|
||||
protected:
|
||||
void initPowerGnd(const DcalcAnalysisPt *dcalc_ap);
|
||||
void writeHeader(string &title,
|
||||
float max_time,
|
||||
float time_step);
|
||||
void writePrintStmt(StdStringSeq &node_names);
|
||||
void writeGnuplotFile(StdStringSeq &node_nanes);
|
||||
void writeSubckts(StdStringSet &cell_names);
|
||||
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,
|
||||
LibertyPortLogicValues &port_values,
|
||||
const Clock *clk,
|
||||
DcalcAPIndex dcalc_ap_index);
|
||||
float pgPortVoltage(LibertyPgPort *pg_port);
|
||||
void writeVoltageSource(const char *inst_name,
|
||||
const char *port_name,
|
||||
float voltage);
|
||||
void writeVoltageSource(LibertyCell *cell,
|
||||
const char *inst_name,
|
||||
const char *subckt_port_name,
|
||||
const char *pg_port_name,
|
||||
float voltage);
|
||||
void writeClkedStepSource(const Pin *pin,
|
||||
const RiseFall *rf,
|
||||
const Clock *clk,
|
||||
DcalcAPIndex dcalc_ap_index);
|
||||
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 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);
|
||||
void writeRampVoltSource(const Pin *pin,
|
||||
const RiseFall *rf,
|
||||
float time,
|
||||
float slew);
|
||||
void writeWaveformVoltSource(const Pin *pin,
|
||||
DriverWaveform *drvr_waveform,
|
||||
const RiseFall *rf,
|
||||
float delay,
|
||||
float slew);
|
||||
void writeWaveformEdge(const RiseFall *rf,
|
||||
float time,
|
||||
float slew);
|
||||
float railToRailSlew(float slew,
|
||||
const RiseFall *rf);
|
||||
void seqPortValues(Sequential *seq,
|
||||
const RiseFall *rf,
|
||||
// Return values.
|
||||
LibertyPortLogicValues &port_values);
|
||||
LibertyPort *onePort(FuncExpr *expr);
|
||||
void writeMeasureDelayStmt(const Pin *from_pin,
|
||||
const RiseFall *from_rf,
|
||||
const Pin *to_pin,
|
||||
const RiseFall *to_rf,
|
||||
string prefix);
|
||||
void writeMeasureSlewStmt(const Pin *pin,
|
||||
const RiseFall *rf,
|
||||
string prefix);
|
||||
const char *spiceTrans(const RiseFall *rf);
|
||||
float findSlew(Vertex *vertex,
|
||||
const RiseFall *rf,
|
||||
TimingArc *next_arc,
|
||||
DcalcAPIndex dcalc_ap_index);
|
||||
float slewAxisMinValue(TimingArc *arc);
|
||||
float clkWaveformTimeOffset(const Clock *clk);
|
||||
|
||||
void gatePortValues(const Pin *input_pin,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Edge *gate_edge,
|
||||
// Return values.
|
||||
LibertyPortLogicValues &port_values,
|
||||
bool &is_clked);
|
||||
void regPortValues(const Pin *input_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const LibertyPort *drvr_port,
|
||||
const FuncExpr *drvr_func,
|
||||
// Return values.
|
||||
LibertyPortLogicValues &port_values,
|
||||
bool &is_clked);
|
||||
void gatePortValues(const Instance *inst,
|
||||
const FuncExpr *expr,
|
||||
const LibertyPort *input_port,
|
||||
// Return values.
|
||||
LibertyPortLogicValues &port_values);
|
||||
void writeSubcktInstLoads(const Pin *drvr_pin,
|
||||
const Pin *exclude);
|
||||
PinSeq drvrLoads(const Pin *drvr_pin);
|
||||
void writeSubcktInstVoltSrcs();
|
||||
string replaceFileExt(string filename,
|
||||
const char *ext);
|
||||
|
||||
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_;
|
||||
|
||||
ofstream spice_stream_;
|
||||
LibertyLibrary *default_library_;
|
||||
float power_voltage_;
|
||||
float gnd_voltage_;
|
||||
float max_time_;
|
||||
// Resistance to use to simulate a short circuit between spice nodes.
|
||||
float short_ckt_resistance_;
|
||||
// Input clock waveform cycles.
|
||||
// Sequential device numbers.
|
||||
int cap_index_;
|
||||
int res_index_;
|
||||
int volt_index_;
|
||||
ParasiticNodeMap node_map_;
|
||||
int next_node_index_;
|
||||
CellSpicePortNames cell_spice_port_names_;
|
||||
Bdd bdd_;
|
||||
};
|
||||
|
||||
void
|
||||
streamPrint(ofstream &stream,
|
||||
const char *fmt,
|
||||
...) __attribute__((format (printf, 2, 3)));
|
||||
|
||||
} // namespace
|
||||
|
|
@ -477,6 +477,16 @@ proc parse_rise_fall_flags { flags_var } {
|
|||
}
|
||||
}
|
||||
|
||||
proc parse_rise_fall_arg { arg } {
|
||||
if { $arg eq "r" || $arg eq "^" || $arg eq "rise" } {
|
||||
return "rise"
|
||||
} elseif { $arg eq "f" || $arg eq "v" || $arg eq "fall" } {
|
||||
return "fall"
|
||||
} else {
|
||||
error "unknown rise/fall transition name."
|
||||
}
|
||||
}
|
||||
|
||||
proc parse_min_max_flags { flags_var } {
|
||||
upvar 1 $flags_var flags
|
||||
if { [info exists flags(-min)] && [info exists flags(-max)] } {
|
||||
|
|
|
|||
12
tcl/Sdc.tcl
12
tcl/Sdc.tcl
|
|
@ -2943,6 +2943,10 @@ proc set_input_transition { args } {
|
|||
|
||||
################################################################
|
||||
|
||||
# set_load -wire_load port external wire load
|
||||
# set_load -pin_load port external pin load
|
||||
# set_load port same as -pin_load
|
||||
# set_load net overrides parasitics
|
||||
define_cmd_args "set_load" \
|
||||
{[-corner corner] [-rise] [-fall] [-max] [-min] [-subtract_pin_load]\
|
||||
[-pin_load] [-wire_load] capacitance objects}
|
||||
|
|
@ -2958,7 +2962,7 @@ proc set_load { args } {
|
|||
set subtract_pin_load [info exists flags(-subtract_pin_load)]
|
||||
set corner [parse_corner_or_all keys]
|
||||
set min_max [parse_min_max_all_check_flags flags]
|
||||
set tr [parse_rise_fall_flags flags]
|
||||
set rf [parse_rise_fall_flags flags]
|
||||
|
||||
set cap [lindex $args 0]
|
||||
check_positive_float "capacitance" $cap
|
||||
|
|
@ -2969,11 +2973,11 @@ proc set_load { args } {
|
|||
# -pin_load is the default.
|
||||
if { $pin_load || (!$pin_load && !$wire_load) } {
|
||||
foreach port $ports {
|
||||
set_port_ext_pin_cap $port $tr $corner $min_max $cap
|
||||
set_port_ext_pin_cap $port $rf $corner $min_max $cap
|
||||
}
|
||||
} elseif { $wire_load } {
|
||||
foreach port $ports {
|
||||
set_port_ext_wire_cap $port $subtract_pin_load $tr $corner $min_max $cap
|
||||
set_port_ext_wire_cap $port $subtract_pin_load $rf $corner $min_max $cap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2984,7 +2988,7 @@ proc set_load { args } {
|
|||
if { $wire_load } {
|
||||
sta_warn 465 "-wire_load not allowed for net objects."
|
||||
}
|
||||
if { $tr != "rise_fall" } {
|
||||
if { $rf != "rise_fall" } {
|
||||
sta_warn 466 "-rise/-fall not allowed for net objects."
|
||||
}
|
||||
foreach net $nets {
|
||||
|
|
|
|||
|
|
@ -312,7 +312,7 @@ proc delays_are_inf { delays } {
|
|||
|
||||
define_cmd_args "report_clock_skew" {[-setup|-hold]\
|
||||
[-clock clocks]\
|
||||
[-corner corner]]\
|
||||
[-corner corner]\
|
||||
[-digits digits]}
|
||||
|
||||
proc_redirect report_clock_skew {
|
||||
|
|
@ -351,6 +351,36 @@ proc_redirect report_clock_skew {
|
|||
|
||||
################################################################
|
||||
|
||||
define_cmd_args "report_clock_latency" {[-clock clocks]\
|
||||
[-corner corner]\
|
||||
[-digits digits]}
|
||||
|
||||
proc_redirect report_clock_latency {
|
||||
global sta_report_default_digits
|
||||
|
||||
parse_key_args "report_clock_" args \
|
||||
keys {-clock -corner -digits} flags {}
|
||||
check_argc_eq0 "report_clock_latency" $args
|
||||
|
||||
if [info exists keys(-clock)] {
|
||||
set clks [get_clocks_warn "-clocks" $keys(-clock)]
|
||||
} else {
|
||||
set clks [all_clocks]
|
||||
}
|
||||
set corner [parse_corner_or_all keys]
|
||||
if [info exists keys(-digits)] {
|
||||
set digits $keys(-digits)
|
||||
check_positive_integer "-digits" $digits
|
||||
} else {
|
||||
set digits $sta_report_default_digits
|
||||
}
|
||||
if { $clks != {} } {
|
||||
report_clk_latency $clks $corner $digits
|
||||
}
|
||||
}
|
||||
|
||||
################################################################
|
||||
|
||||
define_cmd_args "report_checks" \
|
||||
{[-from from_list|-rise_from from_list|-fall_from from_list]\
|
||||
[-through through_list|-rise_through through_list|-fall_through through_list]\
|
||||
|
|
@ -648,15 +678,6 @@ proc report_capacitance_limits { net corner min_max violators verbose nosplit }
|
|||
|
||||
################################################################
|
||||
|
||||
define_cmd_args "report_dcalc" \
|
||||
{[-from from_pin] [-to to_pin] [-corner corner] [-min] [-max] [-digits digits]}
|
||||
|
||||
proc_redirect report_dcalc {
|
||||
report_dcalc_cmd "report_dcalc" $args "-digits"
|
||||
}
|
||||
|
||||
################################################################
|
||||
|
||||
define_cmd_args "report_disabled_edges" {}
|
||||
|
||||
################################################################
|
||||
|
|
@ -777,7 +798,7 @@ proc_redirect report_path {
|
|||
check_argc_eq2 "report_path" $args
|
||||
|
||||
set pin_arg [lindex $args 0]
|
||||
set tr [parse_rise_fall_arg [lindex $args 1]]
|
||||
set rf [parse_rise_fall_arg [lindex $args 1]]
|
||||
|
||||
set pin [get_port_pin_error "pin" $pin_arg]
|
||||
if { [$pin is_hierarchical] } {
|
||||
|
|
@ -787,7 +808,7 @@ proc_redirect report_path {
|
|||
if { $vertex != "NULL" } {
|
||||
if { $report_all } {
|
||||
set first 1
|
||||
set path_iter [$vertex path_iterator $tr $min_max]
|
||||
set path_iter [$vertex path_iterator $rf $min_max]
|
||||
while {[$path_iter has_next]} {
|
||||
set path [$path_iter next]
|
||||
if { $first } {
|
||||
|
|
@ -804,7 +825,7 @@ proc_redirect report_path {
|
|||
}
|
||||
$path_iter finish
|
||||
} else {
|
||||
set worst_path [vertex_worst_arrival_path_rf $vertex $tr $min_max]
|
||||
set worst_path [vertex_worst_arrival_path_rf $vertex $rf $min_max]
|
||||
if { $worst_path != "NULL" } {
|
||||
if { $report_tags } {
|
||||
report_line "Tag: [$worst_path tag]"
|
||||
|
|
@ -818,16 +839,6 @@ proc_redirect report_path {
|
|||
}
|
||||
}
|
||||
|
||||
proc parse_rise_fall_arg { arg } {
|
||||
if { $arg eq "r" || $arg eq "^" || $arg eq "rise" } {
|
||||
return "rise"
|
||||
} elseif { $arg eq "f" || $arg eq "v" || $arg eq "fall" } {
|
||||
return "fall"
|
||||
} else {
|
||||
error "unknown rise/fall transition name."
|
||||
}
|
||||
}
|
||||
|
||||
proc parse_report_path_options { cmd args_var default_format
|
||||
unknown_key_is_error } {
|
||||
variable path_options
|
||||
|
|
|
|||
20
tcl/StaTcl.i
20
tcl/StaTcl.i
|
|
@ -3035,14 +3035,22 @@ report_path_cmd(PathRef *path)
|
|||
}
|
||||
|
||||
void
|
||||
report_clk_skew(ClockSet *clks,
|
||||
report_clk_skew(ConstClockSeq clks,
|
||||
const Corner *corner,
|
||||
const SetupHold *setup_hold,
|
||||
int digits)
|
||||
{
|
||||
cmdLinkedNetwork();
|
||||
Sta::sta()->reportClkSkew(clks, corner, setup_hold, digits);
|
||||
delete clks;
|
||||
}
|
||||
|
||||
void
|
||||
report_clk_latency(ConstClockSeq clks,
|
||||
const Corner *corner,
|
||||
int digits)
|
||||
{
|
||||
cmdLinkedNetwork();
|
||||
Sta::sta()->reportClkLatency(clks, corner, digits);
|
||||
}
|
||||
|
||||
float
|
||||
|
|
@ -3534,16 +3542,14 @@ write_path_spice_cmd(PathRef *path,
|
|||
const char *subckt_filename,
|
||||
const char *lib_subckt_filename,
|
||||
const char *model_filename,
|
||||
StdStringSet *off_path_pins,
|
||||
const char *power_name,
|
||||
const char *gnd_name,
|
||||
bool measure_stmts)
|
||||
CircuitSim ckt_sim)
|
||||
{
|
||||
Sta *sta = Sta::sta();
|
||||
writePathSpice(path, spice_filename, subckt_filename,
|
||||
lib_subckt_filename, model_filename, off_path_pins,
|
||||
power_name, gnd_name, measure_stmts, sta);
|
||||
delete off_path_pins;
|
||||
lib_subckt_filename, model_filename,
|
||||
power_name, gnd_name, ckt_sim, sta);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
// Swig TCL input/output type parsers.
|
||||
%{
|
||||
|
||||
#include "Machine.hh"
|
||||
|
|
@ -22,6 +22,8 @@
|
|||
#include "search/Tag.hh"
|
||||
#include "PathEnd.hh"
|
||||
#include "SearchClass.hh"
|
||||
#include "CircuitSim.hh"
|
||||
#include "ArcDelayCalc.hh"
|
||||
#include "Sta.hh"
|
||||
|
||||
namespace sta {
|
||||
|
|
@ -38,9 +40,9 @@ cmdGraph();
|
|||
|
||||
template <class TYPE>
|
||||
Vector<TYPE> *
|
||||
tclListSeq(Tcl_Obj *const source,
|
||||
swig_type_info *swig_type,
|
||||
Tcl_Interp *interp)
|
||||
tclListSeqPtr(Tcl_Obj *const source,
|
||||
swig_type_info *swig_type,
|
||||
Tcl_Interp *interp)
|
||||
{
|
||||
int argc;
|
||||
Tcl_Obj **argv;
|
||||
|
|
@ -60,11 +62,33 @@ tclListSeq(Tcl_Obj *const source,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
template <class TYPE>
|
||||
std::vector<TYPE>
|
||||
tclListSeq(Tcl_Obj *const source,
|
||||
swig_type_info *swig_type,
|
||||
Tcl_Interp *interp)
|
||||
{
|
||||
int argc;
|
||||
Tcl_Obj **argv;
|
||||
|
||||
std::vector<TYPE> seq;
|
||||
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
|
||||
&& argc > 0) {
|
||||
for (int i = 0; i < argc; i++) {
|
||||
void *obj;
|
||||
// Ignore returned TCL_ERROR because can't get swig_type_info.
|
||||
SWIG_ConvertPtr(argv[i], &obj, swig_type, false);
|
||||
seq.push_back(reinterpret_cast<TYPE>(obj));
|
||||
}
|
||||
}
|
||||
return seq;
|
||||
}
|
||||
|
||||
template <class SET_TYPE, class OBJECT_TYPE>
|
||||
SET_TYPE *
|
||||
tclListSet(Tcl_Obj *const source,
|
||||
swig_type_info *swig_type,
|
||||
Tcl_Interp *interp)
|
||||
tclListSetPtr(Tcl_Obj *const source,
|
||||
swig_type_info *swig_type,
|
||||
Tcl_Interp *interp)
|
||||
{
|
||||
int argc;
|
||||
Tcl_Obj **argv;
|
||||
|
|
@ -83,6 +107,29 @@ tclListSet(Tcl_Obj *const source,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
template <class SET_TYPE, class OBJECT_TYPE>
|
||||
SET_TYPE
|
||||
tclListSet(Tcl_Obj *const source,
|
||||
swig_type_info *swig_type,
|
||||
Tcl_Interp *interp)
|
||||
{
|
||||
int argc;
|
||||
Tcl_Obj **argv;
|
||||
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
|
||||
&& argc > 0) {
|
||||
SET_TYPE set;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
void *obj;
|
||||
// Ignore returned TCL_ERROR because can't get swig_type_info.
|
||||
SWIG_ConvertPtr(argv[i], &obj, swig_type, false);
|
||||
set.insert(reinterpret_cast<OBJECT_TYPE>(obj));
|
||||
}
|
||||
return set;
|
||||
}
|
||||
else
|
||||
return SET_TYPE();
|
||||
}
|
||||
|
||||
template <class SET_TYPE, class OBJECT_TYPE>
|
||||
SET_TYPE *
|
||||
tclListNetworkSet(Tcl_Obj *const source,
|
||||
|
|
@ -361,7 +408,7 @@ using namespace sta;
|
|||
}
|
||||
|
||||
%typemap(in) CellSeq* {
|
||||
$1 = tclListSeq<Cell*>($input, SWIGTYPE_p_Cell, interp);
|
||||
$1 = tclListSeqPtr<Cell*>($input, SWIGTYPE_p_Cell, interp);
|
||||
}
|
||||
|
||||
%typemap(out) CellSeq {
|
||||
|
|
@ -396,7 +443,7 @@ using namespace sta;
|
|||
}
|
||||
|
||||
%typemap(in) PortSeq* {
|
||||
$1 = tclListSeq<const Port*>($input, SWIGTYPE_p_Port, interp);
|
||||
$1 = tclListSeqPtr<const Port*>($input, SWIGTYPE_p_Port, interp);
|
||||
}
|
||||
|
||||
%typemap(out) PortSeq {
|
||||
|
|
@ -567,7 +614,7 @@ using namespace sta;
|
|||
}
|
||||
|
||||
%typemap(in) InstanceSeq* {
|
||||
$1 = tclListSeq<const Instance*>($input, SWIGTYPE_p_Instance, interp);
|
||||
$1 = tclListSeqPtr<const Instance*>($input, SWIGTYPE_p_Instance, interp);
|
||||
}
|
||||
|
||||
%typemap(out) InstanceSeq {
|
||||
|
|
@ -617,6 +664,10 @@ using namespace sta;
|
|||
seqPtrTclList<NetSeq, Net>($1, SWIGTYPE_p_Net, interp);
|
||||
}
|
||||
|
||||
%typemap(in) ConstNetSeq {
|
||||
$1 = tclListSeq<const Net*>($input, SWIGTYPE_p_Net, interp);
|
||||
}
|
||||
|
||||
%typemap(out) NetSeq {
|
||||
seqTclList<NetSeq, Net>($1, SWIGTYPE_p_Net, interp);
|
||||
}
|
||||
|
|
@ -646,6 +697,10 @@ using namespace sta;
|
|||
Tcl_SetObjResult(interp, obj);
|
||||
}
|
||||
|
||||
%typemap(in) ConstClockSeq {
|
||||
$1 = tclListSeq<const Clock*>($input, SWIGTYPE_p_Clock, interp);
|
||||
}
|
||||
|
||||
%typemap(out) ClockSeq* {
|
||||
seqPtrTclList<ClockSeq, Clock>($1, SWIGTYPE_p_Clock, interp);
|
||||
}
|
||||
|
|
@ -660,7 +715,7 @@ using namespace sta;
|
|||
}
|
||||
|
||||
%typemap(in) PinSeq* {
|
||||
$1 = tclListSeq<const Pin*>($input, SWIGTYPE_p_Pin, interp);
|
||||
$1 = tclListSeqPtr<const Pin*>($input, SWIGTYPE_p_Pin, interp);
|
||||
}
|
||||
|
||||
%typemap(in) PinSet* {
|
||||
|
|
@ -687,8 +742,12 @@ using namespace sta;
|
|||
Tcl_SetObjResult(interp, list);
|
||||
}
|
||||
|
||||
%typemap(in) ConstClockSet {
|
||||
$1 = tclListSet<ConstClockSet, Clock>($input, SWIGTYPE_p_Clock, interp);
|
||||
}
|
||||
|
||||
%typemap(in) ClockSet* {
|
||||
$1 = tclListSet<ClockSet, Clock>($input, SWIGTYPE_p_Clock, interp);
|
||||
$1 = tclListSetPtr<ClockSet, Clock>($input, SWIGTYPE_p_Clock, interp);
|
||||
}
|
||||
|
||||
%typemap(out) ClockSet* {
|
||||
|
|
@ -1011,7 +1070,7 @@ using namespace sta;
|
|||
}
|
||||
|
||||
%typemap(in) ExceptionThruSeq* {
|
||||
$1 = tclListSeq<ExceptionThru*>($input, SWIGTYPE_p_ExceptionThru, interp);
|
||||
$1 = tclListSeqPtr<ExceptionThru*>($input, SWIGTYPE_p_ExceptionThru, interp);
|
||||
}
|
||||
|
||||
%typemap(out) Vertex* {
|
||||
|
|
@ -1037,7 +1096,7 @@ using namespace sta;
|
|||
}
|
||||
|
||||
%typemap(in) EdgeSeq* {
|
||||
$1 = tclListSeq<Edge*>($input, SWIGTYPE_p_Edge, interp);
|
||||
$1 = tclListSeqPtr<Edge*>($input, SWIGTYPE_p_Edge, interp);
|
||||
}
|
||||
|
||||
%typemap(out) EdgeSeq {
|
||||
|
|
@ -1348,3 +1407,22 @@ using namespace sta;
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
%typemap(in) CircuitSim {
|
||||
int length;
|
||||
char *arg = Tcl_GetStringFromObj($input, &length);
|
||||
if (stringEq(arg, "hspice"))
|
||||
$1 = CircuitSim::hspice;
|
||||
else if (stringEq(arg, "ngspice"))
|
||||
$1 = CircuitSim::ngspice;
|
||||
else if (stringEq(arg, "xyce"))
|
||||
$1 = CircuitSim::xyce;
|
||||
else {
|
||||
tclArgError(interp, "unknown circuit simulator %s.", arg);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
%typemap(in) ArcDcalcArgPtrSeq {
|
||||
$1 = tclListSeq<ArcDcalcArg*>($input, SWIGTYPE_p_ArcDcalcArg, interp);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,13 +22,13 @@ define_cmd_args "write_path_spice" { -path_args path_args\
|
|||
-model_file model_file\
|
||||
-power power\
|
||||
-ground ground\
|
||||
[-measure_stmts]}
|
||||
[-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} \
|
||||
flags {-measure_stmts}
|
||||
-power -ground -path_args -simulator} \
|
||||
flags {}
|
||||
|
||||
if { [info exists keys(-spice_directory)] } {
|
||||
set spice_dir [file nativename $keys(-spice_directory)]
|
||||
|
|
@ -75,7 +75,7 @@ proc write_path_spice { args } {
|
|||
sta_error 609 "No -ground specified."
|
||||
}
|
||||
|
||||
set measure_stmts [info exists keys(-measure_stmts)]
|
||||
set ckt_sim [parse_ckt_sim_key keys]
|
||||
|
||||
if { ![info exists keys(-path_args)] } {
|
||||
sta_error 610 "No -path_args specified."
|
||||
|
|
@ -92,11 +92,27 @@ proc write_path_spice { args } {
|
|||
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 $measure_stmts
|
||||
$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 1710 "Unknown circuit simulator"
|
||||
}
|
||||
}
|
||||
return $ckt_sim
|
||||
}
|
||||
|
||||
# sta namespace end.
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue