Update from upstream

Signed-off-by: Matt Liberty <mliberty@precisioninno.com>
This commit is contained in:
Matt Liberty 2024-03-01 17:12:34 -08:00
parent 2e59563717
commit 6c969fd8c6
61 changed files with 3985 additions and 1908 deletions

View File

@ -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
###########################################################

View File

@ -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)
{

View File

@ -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();

678
dcalc/CcsCeffDelayCalc.cc Normal file
View File

@ -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

157
dcalc/CcsCeffDelayCalc.hh Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

Binary file not shown.

Binary file not shown.

View File

@ -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 *

View File

@ -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.

57
include/sta/Bdd.hh Normal file
View File

@ -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

23
include/sta/CircuitSim.hh Normal file
View File

@ -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

View File

@ -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_;

View File

@ -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;

View File

@ -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 *&parasitic) 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 *&parasitic) const;
void parasiticLoad(const Pin *drvr_pin,
const RiseFall *rf,
const DcalcAnalysisPt *dcalc_ap,

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -18,6 +18,8 @@
namespace sta {
class Power;
enum class PwrActivityOrigin
{
global,

View File

@ -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;

View File

@ -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,

View File

@ -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

View File

@ -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(); }

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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:

View File

@ -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);

View File

@ -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");

View File

@ -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_;

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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
{

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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;

179
search/Bdd.cc Normal file
View File

@ -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

70
search/ClkDelays.hh Normal file
View File

@ -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

299
search/ClkLatency.cc Normal file
View File

@ -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

52
search/ClkLatency.hh Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -57,6 +57,7 @@ public:
private:
void makeLibrary();
void makeCell();
float findArea();
void makePorts();
void checkClock(Clock *clk);
void findTimingFromInputs();

View File

@ -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)
{

View File

@ -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);

View File

@ -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

1267
search/WriteSpice.cc Normal file

File diff suppressed because it is too large Load Diff

191
search/WriteSpice.hh Normal file
View File

@ -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

View File

@ -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)] } {

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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.
}