Merge remote-tracking branch 'parallax/master'
Get the CCS update Signed-off-by: Matt Liberty <mliberty@precisioninno.com>
This commit is contained in:
commit
f8a9e1771b
|
|
@ -69,6 +69,7 @@ set(STA_SOURCE
|
|||
dcalc/ArnoldiDelayCalc.cc
|
||||
dcalc/ArnoldiReduce.cc
|
||||
dcalc/CcsCeffDelayCalc.cc
|
||||
dcalc/CcsSimDelayCalc.cc
|
||||
dcalc/DcalcAnalysisPt.cc
|
||||
dcalc/DelayCalc.cc
|
||||
dcalc/DelayCalcBase.cc
|
||||
|
|
@ -363,6 +364,8 @@ include(FindZLIB)
|
|||
|
||||
find_package(Threads)
|
||||
|
||||
find_package(Eigen3 REQUIRED)
|
||||
|
||||
################################################################
|
||||
#
|
||||
# Locate CUDD bdd package.
|
||||
|
|
@ -508,6 +511,7 @@ target_sources(OpenSTA
|
|||
)
|
||||
|
||||
target_link_libraries(OpenSTA
|
||||
Eigen3::Eigen
|
||||
${TCL_LIBRARY}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
|
|
|||
25
README.md
25
README.md
|
|
@ -85,26 +85,27 @@ The build dependency versions are show below. Other versions may
|
|||
work, but these are the versions used for development.
|
||||
|
||||
```
|
||||
from Ubuntu Xcode
|
||||
22.04.2 11.3
|
||||
cmake 3.10.2 3.24.2 3.16.2
|
||||
clang 9.1.0 14.0.3
|
||||
gcc 3.3.2 11.3.0
|
||||
from Ubuntu Macos
|
||||
22.04.2 14.4.1
|
||||
cmake 3.10.2 3.24.2 3.29.2
|
||||
clang 9.1.0 15.0.0
|
||||
gcc 3.3.2 11.4.0
|
||||
tcl 8.4 8.6 8.6.6
|
||||
swig 1.3.28 4.1.0 4.0.1
|
||||
bison 1.35 3.0.2 3.8.2
|
||||
swig 1.3.28 4.1.0 4.2.1
|
||||
bison 1.35 3.8.2 3.8.2
|
||||
flex 2.5.4 2.6.4 2.6.4
|
||||
```
|
||||
|
||||
Note that flex versions before 2.6.4 contain 'register' declarations that
|
||||
are illegal in c++17.
|
||||
|
||||
These packages are **optional**:
|
||||
|
||||
External library dependencies:
|
||||
```
|
||||
tclreadline 2.3.8
|
||||
libz 1.1.4 1.2.5 1.2.8
|
||||
cudd 2.4.1 3.0.0
|
||||
from Ubuntu Macos
|
||||
eigen 3.4 .0 3.4.0 required
|
||||
tclreadline 2.3.8 optional
|
||||
libz 1.1.4 1.2.5 1.2.8 optional
|
||||
cudd 2.4.1 3.0.0 optional
|
||||
```
|
||||
|
||||
The [TCL readline library](https://tclreadline.sourceforge.net/tclreadline.html)
|
||||
|
|
|
|||
|
|
@ -130,6 +130,12 @@ ArcDcalcArg::drvrNet(const Network *network) const
|
|||
return network->net(drvr_pin_);
|
||||
}
|
||||
|
||||
float
|
||||
ArcDcalcArg::inSlewFlt() const
|
||||
{
|
||||
return delayAsFloat(in_slew_);
|
||||
}
|
||||
|
||||
void
|
||||
ArcDcalcArg::setInSlew(Slew in_slew)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1334,7 +1334,6 @@ ArnoldiDelayCalc::ra_get_s(delay_work *D,
|
|||
tlohi = slew_derate*delayAsFloat(s1);
|
||||
smin = r*c*c_smin; // c_smin = ra_hinv((1-vhi)/vhi-log(vhi)) + log(vhi);
|
||||
if (c_log*r*c >= tlohi) {
|
||||
// printf("hit smin\n");
|
||||
s = smin;
|
||||
} else {
|
||||
s = smin+0.3*tlohi;
|
||||
|
|
@ -1410,7 +1409,6 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D,
|
|||
if (debug_->check("arnoldi", 1)) {
|
||||
double p = 1.0/(r*ctot);
|
||||
double thix,tlox;
|
||||
if (bad) printf("bad\n");
|
||||
debugPrint(debug_, "arnoldi", 1, "at r=%s s=%s",
|
||||
units_->resistanceUnit()->asString(r),
|
||||
units_->timeUnit()->asString(s));
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
|
|||
vth_ = drvr_library->outputThreshold(rf) * vdd_;
|
||||
vl_ = drvr_library->slewLowerThreshold(rf) * vdd_;
|
||||
vh_ = drvr_library->slewUpperThreshold(rf) * vdd_;
|
||||
in_slew_ = in_slew;
|
||||
in_slew_ = delayAsFloat(in_slew);
|
||||
load_cap_ = load_cap;
|
||||
parasitic_ = parasitic;
|
||||
drvr_cell->ensureVoltageWaveforms();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,901 @@
|
|||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2023, 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 "CcsSimDelayCalc.hh"
|
||||
|
||||
#include <cmath> // abs
|
||||
|
||||
#include "Debug.hh"
|
||||
#include "Units.hh"
|
||||
#include "TimingArc.hh"
|
||||
#include "Liberty.hh"
|
||||
#include "Sdc.hh"
|
||||
#include "Parasitics.hh"
|
||||
#include "DcalcAnalysisPt.hh"
|
||||
#include "Network.hh"
|
||||
#include "Corner.hh"
|
||||
#include "Graph.hh"
|
||||
#include "GraphDelayCalc.hh"
|
||||
#include "DmpDelayCalc.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
using std::abs;
|
||||
using std::make_shared;
|
||||
|
||||
// Lawrence Pillage - “Electronic Circuit & System Simulation Methods” 1998
|
||||
// McGraw-Hill, Inc. New York, NY.
|
||||
|
||||
ArcDelayCalc *
|
||||
makeCcsSimDelayCalc(StaState *sta)
|
||||
{
|
||||
return new CcsSimDelayCalc(sta);
|
||||
}
|
||||
|
||||
CcsSimDelayCalc::CcsSimDelayCalc(StaState *sta) :
|
||||
DelayCalcBase(sta),
|
||||
dcalc_args_(nullptr),
|
||||
load_pin_index_map_(network_),
|
||||
dcalc_failed_(false),
|
||||
pin_node_map_(network_),
|
||||
make_waveforms_(false),
|
||||
waveform_drvr_pin_(nullptr),
|
||||
waveform_load_pin_(nullptr),
|
||||
table_dcalc_(makeDmpCeffElmoreDelayCalc(sta))
|
||||
{
|
||||
}
|
||||
|
||||
CcsSimDelayCalc::~CcsSimDelayCalc()
|
||||
{
|
||||
delete table_dcalc_;
|
||||
}
|
||||
|
||||
ArcDelayCalc *
|
||||
CcsSimDelayCalc::copy()
|
||||
{
|
||||
return new CcsSimDelayCalc(this);
|
||||
}
|
||||
|
||||
Parasitic *
|
||||
CcsSimDelayCalc::findParasitic(const Pin *drvr_pin,
|
||||
const RiseFall *,
|
||||
const DcalcAnalysisPt *dcalc_ap)
|
||||
{
|
||||
const Corner *corner = dcalc_ap->corner();
|
||||
Parasitic *parasitic = nullptr;
|
||||
// set_load net has precidence over parasitics.
|
||||
if (!sdc_->drvrPinHasWireCap(drvr_pin, corner)) {
|
||||
const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt();
|
||||
if (parasitics_->haveParasitics())
|
||||
parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap);
|
||||
}
|
||||
return parasitic;
|
||||
}
|
||||
|
||||
Parasitic *
|
||||
CcsSimDelayCalc::reduceParasitic(const Parasitic *parasitic_network,
|
||||
const Pin *,
|
||||
const RiseFall *,
|
||||
const DcalcAnalysisPt *)
|
||||
{
|
||||
return const_cast<Parasitic *>(parasitic_network);
|
||||
}
|
||||
|
||||
ArcDcalcResult
|
||||
CcsSimDelayCalc::inputPortDelay(const Pin *drvr_pin,
|
||||
float in_slew,
|
||||
const RiseFall *rf,
|
||||
const Parasitic *parasitic,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap)
|
||||
{
|
||||
ArcDcalcResult dcalc_result(load_pin_index_map.size());
|
||||
LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
|
||||
|
||||
const Parasitic *pi_elmore = nullptr;
|
||||
if (parasitic && parasitics_->isParasiticNetwork(parasitic)) {
|
||||
const ParasiticAnalysisPt *ap = dcalc_ap->parasiticAnalysisPt();
|
||||
parasitics_->reduceToPiElmore(parasitic, drvr_pin, rf,
|
||||
dcalc_ap->corner(),
|
||||
dcalc_ap->constraintMinMax(), ap);
|
||||
pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap);
|
||||
}
|
||||
|
||||
for (auto load_pin_index : load_pin_index_map) {
|
||||
const Pin *load_pin = load_pin_index.first;
|
||||
size_t load_idx = load_pin_index.second;
|
||||
ArcDelay wire_delay = 0.0;
|
||||
Slew load_slew = in_slew;
|
||||
bool elmore_exists = false;
|
||||
float elmore = 0.0;
|
||||
if (pi_elmore)
|
||||
parasitics_->findElmore(pi_elmore, load_pin, elmore, elmore_exists);
|
||||
if (elmore_exists)
|
||||
// Input port with no external driver.
|
||||
dspfWireDelaySlew(load_pin, rf, in_slew, elmore, wire_delay, load_slew);
|
||||
thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew);
|
||||
dcalc_result.setWireDelay(load_idx, wire_delay);
|
||||
dcalc_result.setLoadSlew(load_idx, load_slew);
|
||||
}
|
||||
return dcalc_result;
|
||||
}
|
||||
|
||||
ArcDcalcResult
|
||||
CcsSimDelayCalc::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)
|
||||
{
|
||||
Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin);
|
||||
load_pin_index_map_ = graph_delay_calc_->makeLoadPinIndexMap(drvr_vertex);
|
||||
ArcDcalcArgSeq dcalc_args;
|
||||
dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, parasitic);
|
||||
ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_cap,
|
||||
load_pin_index_map, dcalc_ap);
|
||||
return dcalc_results[0];
|
||||
}
|
||||
|
||||
ArcDcalcResultSeq
|
||||
CcsSimDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
|
||||
float load_cap,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap)
|
||||
{
|
||||
dcalc_args_ = &dcalc_args;
|
||||
load_pin_index_map_ = load_pin_index_map;
|
||||
drvr_count_ = dcalc_args.size();
|
||||
load_cap_ = load_cap;
|
||||
dcalc_ap_ = dcalc_ap;
|
||||
drvr_rf_ = dcalc_args[0].arc()->toEdge()->asRiseFall();
|
||||
dcalc_failed_ = false;
|
||||
parasitic_network_ = dcalc_args[0].parasitic();
|
||||
ArcDcalcResultSeq dcalc_results(drvr_count_);
|
||||
|
||||
// Assume drivers are in the same library.
|
||||
LibertyCell *drvr_cell = dcalc_args[0].arc()->to()->libertyCell();
|
||||
const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary();
|
||||
bool vdd_exists;
|
||||
drvr_library->supplyVoltage("VDD", vdd_, vdd_exists);
|
||||
if (!vdd_exists)
|
||||
report_->error(1720, "VDD not defined in library %s", drvr_library->name());
|
||||
vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_;
|
||||
vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_;
|
||||
vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_;
|
||||
drvr_cell->ensureVoltageWaveforms();
|
||||
|
||||
size_t drvr_count = dcalc_args.size();
|
||||
output_waveforms_.resize(drvr_count);
|
||||
ref_time_.resize(drvr_count);
|
||||
|
||||
for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) {
|
||||
ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx];
|
||||
GateTableModel *table_model = gateTableModel(dcalc_arg.arc(), dcalc_ap);
|
||||
if (table_model && dcalc_arg.parasitic()) {
|
||||
OutputWaveforms *output_waveforms = table_model->outputWaveforms();
|
||||
float in_slew = delayAsFloat(dcalc_arg.inSlew());
|
||||
if (output_waveforms
|
||||
// Bounds check because extrapolating waveforms does not work for shit.
|
||||
&& output_waveforms->slewAxis()->inBounds(in_slew)
|
||||
&& output_waveforms->capAxis()->inBounds(load_cap)) {
|
||||
output_waveforms_[drvr_idx] = output_waveforms;
|
||||
ref_time_[drvr_idx] = output_waveforms->referenceTime(in_slew);
|
||||
debugPrint(debug_, "ccs_dcalc", 1, "%s %s",
|
||||
network_->libertyPort(dcalc_arg.drvrPin())->libertyCell()->name(),
|
||||
drvr_rf_->asString());
|
||||
}
|
||||
else
|
||||
dcalc_failed_ = true;
|
||||
}
|
||||
else
|
||||
dcalc_failed_ = true;
|
||||
}
|
||||
|
||||
if (dcalc_failed_) {
|
||||
const Parasitic *parasitic_network = dcalc_args[0].parasitic();
|
||||
for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) {
|
||||
ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx];
|
||||
Parasitic *pi_elmore = nullptr;
|
||||
const Pin *drvr_pin = dcalc_arg.drvrPin();
|
||||
if (parasitic_network) {
|
||||
const ParasiticAnalysisPt *ap = dcalc_ap_->parasiticAnalysisPt();
|
||||
parasitics_->reduceToPiElmore(parasitic_network, drvr_pin, drvr_rf_,
|
||||
dcalc_ap_->corner(),
|
||||
dcalc_ap_->constraintMinMax(), ap);
|
||||
pi_elmore = parasitics_->findPiElmore(drvr_pin, drvr_rf_, ap);
|
||||
dcalc_arg.setParasitic(pi_elmore);
|
||||
}
|
||||
}
|
||||
dcalc_results = table_dcalc_->gateDelays(dcalc_args, load_cap,
|
||||
load_pin_index_map, dcalc_ap);
|
||||
}
|
||||
else {
|
||||
simulate(dcalc_args);
|
||||
|
||||
for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) {
|
||||
ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx];
|
||||
ArcDcalcResult &dcalc_result = dcalc_results[drvr_idx];
|
||||
const Pin *drvr_pin = dcalc_arg.drvrPin();
|
||||
size_t drvr_node = pin_node_map_[drvr_pin];
|
||||
ThresholdTimes &drvr_times = threshold_times_[drvr_node];
|
||||
ArcDelay gate_delay = drvr_times[threshold_vth] - ref_time_[drvr_idx];
|
||||
Slew drvr_slew = abs(drvr_times[threshold_vh] - drvr_times[threshold_vl]);
|
||||
dcalc_result.setGateDelay(gate_delay);
|
||||
dcalc_result.setDrvrSlew(drvr_slew);
|
||||
debugPrint(debug_, "ccs_dcalc", 2,
|
||||
"%s gate delay %s slew %s",
|
||||
network_->pathName(drvr_pin),
|
||||
delayAsString(gate_delay, this),
|
||||
delayAsString(drvr_slew, this));
|
||||
|
||||
dcalc_result.setLoadCount(load_pin_index_map.size());
|
||||
for (auto load_pin_index : load_pin_index_map) {
|
||||
const Pin *load_pin = load_pin_index.first;
|
||||
size_t load_idx = load_pin_index.second;
|
||||
size_t load_node = pin_node_map_[load_pin];
|
||||
ThresholdTimes &wire_times = threshold_times_[load_node];
|
||||
ThresholdTimes &drvr_times = threshold_times_[drvr_node];
|
||||
ArcDelay wire_delay = wire_times[threshold_vth] - drvr_times[threshold_vth];
|
||||
Slew load_slew = abs(wire_times[threshold_vh] - wire_times[threshold_vl]);
|
||||
debugPrint(debug_, "ccs_dcalc", 2,
|
||||
"load %s %s delay %s slew %s",
|
||||
network_->pathName(load_pin),
|
||||
drvr_rf_->asString(),
|
||||
delayAsString(wire_delay, this),
|
||||
delayAsString(load_slew, this));
|
||||
|
||||
thresholdAdjust(load_pin, drvr_library, drvr_rf_, wire_delay, load_slew);
|
||||
dcalc_result.setWireDelay(load_idx, wire_delay);
|
||||
dcalc_result.setLoadSlew(load_idx, load_slew);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dcalc_results;
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::simulate(ArcDcalcArgSeq &dcalc_args)
|
||||
{
|
||||
const Pin *drvr_pin = dcalc_args[0].drvrPin();
|
||||
LibertyPort *drvr_port = network_->libertyPort(drvr_pin);
|
||||
const MinMax *min_max = dcalc_ap_->delayMinMax();
|
||||
drive_resistance_ = drvr_port->driveResistance(drvr_rf_, min_max);
|
||||
|
||||
initSim();
|
||||
stampConductances();
|
||||
|
||||
// The conductance matrix does not change as long as the time step is constant.
|
||||
// Factor stamping and LU decomposition of the conductance matrix
|
||||
// outside of the simulation loop.
|
||||
// Prevent copying of matrix.
|
||||
conductances_.makeCompressed();
|
||||
// LU factor conductances.
|
||||
solver_.compute(conductances_);
|
||||
|
||||
for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) {
|
||||
ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx];
|
||||
// Find initial ceff.
|
||||
ceff_[drvr_idx] = load_cap_;
|
||||
// voltageTime is always for a rising waveform so 0.0v is initial voltage.
|
||||
drvr_current_[drvr_idx] =
|
||||
output_waveforms_[drvr_idx]->voltageCurrent(delayAsFloat(dcalc_arg.inSlew()),
|
||||
ceff_[drvr_idx], 0.0);
|
||||
}
|
||||
// Initial time depends on ceff which impact delay, so use a sim step
|
||||
// to find an initial ceff.
|
||||
setCurrents();
|
||||
voltages_ = solver_.solve(currents_);
|
||||
updateCeffIdrvr();
|
||||
initNodeVoltages();
|
||||
|
||||
// voltageTime is always for a rising waveform so 0.0v is initial voltage.
|
||||
double time_begin = output_waveforms_[0]->voltageTime(dcalc_args[0].inSlewFlt(),
|
||||
ceff_[0], 0.0);
|
||||
// Limit in case load voltage waveforms don't get to final value.
|
||||
double time_end = time_begin + maxTime();
|
||||
|
||||
if (make_waveforms_)
|
||||
recordWaveformStep(time_begin);
|
||||
|
||||
for (double time = time_begin; time <= time_end; time += time_step_) {
|
||||
stampConductances();
|
||||
conductances_.makeCompressed();
|
||||
solver_.compute(conductances_);
|
||||
setCurrents();
|
||||
voltages_ = solver_.solve(currents_);
|
||||
|
||||
debugPrint(debug_, "ccs_dcalc", 3, "%s ceff %s VDrvr %.4f Idrvr %s",
|
||||
delayAsString(time, this),
|
||||
units_->capacitanceUnit()->asString(ceff_[0]),
|
||||
voltages_[pin_node_map_[dcalc_args[0].drvrPin()]],
|
||||
units_->currentUnit()->asString(drvr_current_[0], 4));
|
||||
|
||||
updateCeffIdrvr();
|
||||
measureThresholds(time);
|
||||
if (make_waveforms_)
|
||||
recordWaveformStep(time);
|
||||
|
||||
bool loads_finished = true;
|
||||
for (auto load_node1 : pin_node_map_) {
|
||||
size_t load_node = load_node1.second;
|
||||
if ((drvr_rf_ == RiseFall::rise()
|
||||
&& voltages_[load_node] < vh_ + (vdd_ - vh_) * .5)
|
||||
|| (drvr_rf_ == RiseFall::fall()
|
||||
&& (voltages_[load_node] > vl_ * .5))) {
|
||||
loads_finished = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (loads_finished)
|
||||
break;
|
||||
|
||||
time_step_prev_ = time_step_;
|
||||
// swap faster than copying with '='.
|
||||
voltages_prev2_.swap(voltages_prev1_);
|
||||
voltages_prev1_.swap(voltages_);
|
||||
}
|
||||
}
|
||||
|
||||
double
|
||||
CcsSimDelayCalc::timeStep()
|
||||
{
|
||||
// Needs to use LTE for time step dynamic control.
|
||||
return drive_resistance_ * load_cap_ * .02;
|
||||
}
|
||||
|
||||
double
|
||||
CcsSimDelayCalc::maxTime()
|
||||
{
|
||||
return (*dcalc_args_)[0].inSlewFlt()
|
||||
+ (drive_resistance_ + resistance_sum_) * load_cap_ * 2;
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::initSim()
|
||||
{
|
||||
ceff_.resize(drvr_count_);
|
||||
drvr_current_.resize(drvr_count_);
|
||||
|
||||
findNodeCount();
|
||||
setOrder();
|
||||
|
||||
initNodeVoltages();
|
||||
|
||||
// time step required by initCapacitanceCurrents
|
||||
time_step_ = time_step_prev_ = timeStep();
|
||||
debugPrint(debug_, "ccs_dcalc", 1, "time step %s", delayAsString(time_step_, this));
|
||||
|
||||
// Reset waveform recording.
|
||||
times_.clear();
|
||||
drvr_voltages_.clear();
|
||||
load_voltages_.clear();
|
||||
|
||||
measure_thresholds_ = {vl_, vth_, vh_};
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::findNodeCount()
|
||||
{
|
||||
includes_pin_caps_ = parasitics_->includesPinCaps(parasitic_network_);
|
||||
coupling_cap_multiplier_ = 1.0;
|
||||
|
||||
node_capacitances_.clear();
|
||||
pin_node_map_.clear();
|
||||
node_index_map_.clear();
|
||||
|
||||
for (ParasiticNode *node : parasitics_->nodes(parasitic_network_)) {
|
||||
if (!parasitics_->isExternal(node)) {
|
||||
size_t node_idx = node_index_map_.size();
|
||||
node_index_map_[node] = node_idx;
|
||||
const Pin *pin = parasitics_->pin(node);
|
||||
if (pin) {
|
||||
pin_node_map_[pin] = node_idx;
|
||||
debugPrint(debug_, "ccs_dcalc", 1, "pin %s node %lu",
|
||||
network_->pathName(pin),
|
||||
node_idx);
|
||||
}
|
||||
double cap = parasitics_->nodeGndCap(node) + pinCapacitance(node);
|
||||
node_capacitances_.push_back(cap);
|
||||
}
|
||||
}
|
||||
|
||||
for (ParasiticCapacitor *capacitor : parasitics_->capacitors(parasitic_network_)) {
|
||||
float cap = parasitics_->value(capacitor) * coupling_cap_multiplier_;
|
||||
ParasiticNode *node1 = parasitics_->node1(capacitor);
|
||||
if (!parasitics_->isExternal(node1)) {
|
||||
size_t node_idx = node_index_map_[node1];
|
||||
node_capacitances_[node_idx] += cap;
|
||||
}
|
||||
ParasiticNode *node2 = parasitics_->node2(capacitor);
|
||||
if (!parasitics_->isExternal(node2)) {
|
||||
size_t node_idx = node_index_map_[node2];
|
||||
node_capacitances_[node_idx] += cap;
|
||||
}
|
||||
}
|
||||
node_count_ = node_index_map_.size();
|
||||
}
|
||||
|
||||
float
|
||||
CcsSimDelayCalc::pinCapacitance(ParasiticNode *node)
|
||||
{
|
||||
const Pin *pin = parasitics_->pin(node);
|
||||
float pin_cap = 0.0;
|
||||
if (pin) {
|
||||
Port *port = network_->port(pin);
|
||||
LibertyPort *lib_port = network_->libertyPort(port);
|
||||
const Corner *corner = dcalc_ap_->corner();
|
||||
const MinMax *cnst_min_max = dcalc_ap_->constraintMinMax();
|
||||
if (lib_port) {
|
||||
if (!includes_pin_caps_)
|
||||
pin_cap = sdc_->pinCapacitance(pin, drvr_rf_, corner, cnst_min_max);
|
||||
}
|
||||
else if (network_->isTopLevelPort(pin))
|
||||
pin_cap = sdc_->portExtCap(port, drvr_rf_, corner, cnst_min_max);
|
||||
}
|
||||
return pin_cap;
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::setOrder()
|
||||
{
|
||||
currents_.resize(node_count_);
|
||||
voltages_.resize(node_count_);
|
||||
voltages_prev1_.resize(node_count_);
|
||||
voltages_prev2_.resize(node_count_);
|
||||
// Matrix resize also zeros.
|
||||
conductances_.resize(node_count_, node_count_);
|
||||
threshold_times_.resize(node_count_);
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::initNodeVoltages()
|
||||
{
|
||||
double drvr_init_volt = (drvr_rf_ == RiseFall::rise()) ? 0.0 : vdd_;
|
||||
for (size_t i = 0; i < node_count_; i++) {
|
||||
voltages_[i] = drvr_init_volt;
|
||||
voltages_prev1_[i] = drvr_init_volt;
|
||||
voltages_prev2_[i] = drvr_init_volt;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::simulateStep()
|
||||
{
|
||||
setCurrents();
|
||||
voltages_ = solver_.solve(currents_);
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::stampConductances()
|
||||
{
|
||||
conductances_.setZero();
|
||||
for (size_t node_idx = 0; node_idx < node_count_; node_idx++)
|
||||
stampCapacitance(node_idx, node_capacitances_[node_idx]);
|
||||
|
||||
resistance_sum_ = 0.0;
|
||||
for (ParasiticResistor *resistor : parasitics_->resistors(parasitic_network_)) {
|
||||
ParasiticNode *node1 = parasitics_->node1(resistor);
|
||||
ParasiticNode *node2 = parasitics_->node2(resistor);
|
||||
// One commercial extractor creates resistors with identical from/to nodes.
|
||||
if (node1 != node2) {
|
||||
size_t node_idx1 = node_index_map_[node1];
|
||||
size_t node_idx2 = node_index_map_[node2];
|
||||
float resistance = parasitics_->value(resistor);
|
||||
stampConductance(node_idx1, node_idx2, 1.0 / resistance);
|
||||
resistance_sum_ += resistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Grounded resistor.
|
||||
void
|
||||
CcsSimDelayCalc::stampConductance(size_t n1,
|
||||
double g)
|
||||
{
|
||||
conductances_.coeffRef(n1, n1) += g;
|
||||
}
|
||||
|
||||
// Floating resistor.
|
||||
void
|
||||
CcsSimDelayCalc::stampConductance(size_t n1,
|
||||
size_t n2,
|
||||
double g)
|
||||
{
|
||||
conductances_.coeffRef(n1, n1) += g;
|
||||
conductances_.coeffRef(n2, n2) += g;
|
||||
conductances_.coeffRef(n1, n2) -= g;
|
||||
conductances_.coeffRef(n2, n1) -= g;
|
||||
}
|
||||
|
||||
// Grounded capacitance.
|
||||
void
|
||||
CcsSimDelayCalc::stampCapacitance(size_t n1,
|
||||
double cap)
|
||||
{
|
||||
double g = cap * 2.0 / time_step_;
|
||||
stampConductance(n1, g);
|
||||
}
|
||||
|
||||
// Floating capacitance.
|
||||
void
|
||||
CcsSimDelayCalc::stampCapacitance(size_t n1,
|
||||
size_t n2,
|
||||
double cap)
|
||||
{
|
||||
double g = cap * 2.0 / time_step_;
|
||||
stampConductance(n1, n2, g);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::setCurrents()
|
||||
{
|
||||
currents_.setZero(node_count_);
|
||||
for (size_t i = 0; i < drvr_count_; i++) {
|
||||
size_t drvr_node = pin_node_map_[(*dcalc_args_)[i].drvrPin()];
|
||||
insertCurrentSrc(drvr_node, drvr_current_[i]);
|
||||
}
|
||||
|
||||
for (size_t node_idx = 0; node_idx < node_count_; node_idx++)
|
||||
insertCapCurrentSrc(node_idx, node_capacitances_[node_idx]);
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::insertCapCurrentSrc(size_t n1,
|
||||
double cap)
|
||||
{
|
||||
// Direct implementation of figure 4.11 in
|
||||
// “Electronic Circuit & System Simulation Methods” allowing for time
|
||||
// step changes.
|
||||
// double g0 = 2.0 * cap / time_step_;
|
||||
// double g1 = 2.0 * cap / time_step_prev_;
|
||||
// double dv = voltages_prev2_[n1] - voltages_prev1_[n1];
|
||||
// double ieq_prev = cap * dv / time_step_ + g0 * voltages_prev1_[n1];
|
||||
// double i_cap = (g0 + g1) * voltages_prev1_[n1] - ieq_prev;
|
||||
|
||||
// Above simplified.
|
||||
// double i_cap
|
||||
// = cap / time_step_ * voltages_prev1_[n1]
|
||||
// + 2.0 * cap / time_step_prev_ * voltages_prev1_[n1]
|
||||
// - cap / time_step_ * voltages_prev2_[n1];
|
||||
|
||||
// Simplified for constant time step.
|
||||
double i_cap
|
||||
= 3.0 * cap / time_step_ * voltages_prev1_[n1]
|
||||
- cap / time_step_ * voltages_prev2_[n1];
|
||||
insertCurrentSrc(n1, i_cap);
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::insertCapaCurrentSrc(size_t n1,
|
||||
size_t n2,
|
||||
double cap)
|
||||
{
|
||||
double g0 = 2.0 * cap / time_step_;
|
||||
double g1 = 2.0 * cap / time_step_prev_;
|
||||
double dv = (voltages_prev2_[n1] - voltages_prev2_[n2])
|
||||
- (voltages_prev1_[n1] - voltages_prev1_[n2]);
|
||||
double ieq_prev = cap * dv / time_step_ + g0*(voltages_prev1_[n1]-voltages_prev1_[n2]);
|
||||
double i_cap = (g0 + g1) * (voltages_prev1_[n1] - voltages_prev1_[n2]) - ieq_prev;
|
||||
insertCurrentSrc(n1, n2, i_cap);
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::insertCurrentSrc(size_t n1,
|
||||
double current)
|
||||
{
|
||||
currents_.coeffRef(n1) += current;
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::insertCurrentSrc(size_t n1,
|
||||
size_t n2,
|
||||
double current)
|
||||
{
|
||||
currents_.coeffRef(n1) += current;
|
||||
currents_.coeffRef(n2) -= current;
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::updateCeffIdrvr()
|
||||
{
|
||||
for (size_t i = 0; i < drvr_count_; i++) {
|
||||
size_t drvr_node = pin_node_map_[(*dcalc_args_)[i].drvrPin()];
|
||||
double dv = voltages_[drvr_node] - voltages_prev1_[drvr_node];
|
||||
if (drvr_rf_ == RiseFall::rise()) {
|
||||
if (drvr_current_[i] != 0.0
|
||||
&& dv > 0.0) {
|
||||
double ceff = drvr_current_[i] * time_step_ / dv;
|
||||
if (output_waveforms_[i]->capAxis()->inBounds(ceff))
|
||||
ceff_[i] = ceff;
|
||||
}
|
||||
|
||||
double v = voltages_[drvr_node];
|
||||
if (voltages_[drvr_node] > (vdd_ - .01))
|
||||
// Whoa partner. Head'n for the weeds.
|
||||
drvr_current_[i] = 0.0;
|
||||
else
|
||||
drvr_current_[i] =
|
||||
output_waveforms_[i]->voltageCurrent((*dcalc_args_)[i].inSlewFlt(),
|
||||
ceff_[i], v);
|
||||
}
|
||||
else {
|
||||
if (drvr_current_[i] != 0.0
|
||||
&& dv < 0.0) {
|
||||
double ceff = drvr_current_[i] * time_step_ / dv;
|
||||
if (output_waveforms_[i]->capAxis()->inBounds(ceff))
|
||||
ceff_[i] = ceff;
|
||||
}
|
||||
double v = vdd_ - voltages_[drvr_node];
|
||||
if (voltages_[drvr_node] < 0.01)
|
||||
// Whoa partner. Head'n for the weeds.
|
||||
drvr_current_[i] = 0.0;
|
||||
else
|
||||
drvr_current_[i] =
|
||||
output_waveforms_[i]->voltageCurrent((*dcalc_args_)[i].inSlewFlt(),
|
||||
ceff_[i], v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::measureThresholds(double time)
|
||||
{
|
||||
for (auto pin_node1 : pin_node_map_) {
|
||||
size_t pin_node = pin_node1.second;
|
||||
measureThresholds(time, pin_node);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::measureThresholds(double time,
|
||||
size_t n)
|
||||
{
|
||||
double v = voltages_[n];
|
||||
double v_prev = voltages_prev1_[n];
|
||||
for (size_t m = 0; m < measure_threshold_count_; m++) {
|
||||
double th = measure_thresholds_[m];
|
||||
if ((v_prev < th && th <= v)
|
||||
|| (v_prev > th && th >= v)) {
|
||||
double t_cross = time - time_step_ + (th - v_prev) * time_step_ / (v - v_prev);
|
||||
debugPrint(debug_, "ccs_measure", 1, "node %lu cross %.2f %s",
|
||||
n,
|
||||
th,
|
||||
delayAsString(t_cross, this));
|
||||
threshold_times_[n][m] = t_cross;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::recordWaveformStep(double time)
|
||||
{
|
||||
times_.push_back(time);
|
||||
size_t drvr_node = pin_node_map_[waveform_drvr_pin_];
|
||||
drvr_voltages_.push_back(voltages_[drvr_node]);
|
||||
if (waveform_load_pin_) {
|
||||
size_t load_node = pin_node_map_[waveform_load_pin_];
|
||||
load_voltages_.push_back(voltages_[load_node]);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
string
|
||||
CcsSimDelayCalc::reportGateDelay(const Pin *drvr_pin,
|
||||
const TimingArc *arc,
|
||||
const Slew &in_slew,
|
||||
float load_cap,
|
||||
const Parasitic *,
|
||||
const LoadPinIndexMap &,
|
||||
const DcalcAnalysisPt *dcalc_ap,
|
||||
int digits)
|
||||
{
|
||||
GateTimingModel *model = gateModel(arc, dcalc_ap);
|
||||
if (model) {
|
||||
float in_slew1 = delayAsFloat(in_slew);
|
||||
return model->reportGateDelay(pinPvt(drvr_pin, dcalc_ap), in_slew1, load_cap,
|
||||
false, digits);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
// Waveform accessors for swig/tcl.
|
||||
Table1
|
||||
CcsSimDelayCalc::drvrWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max)
|
||||
{
|
||||
makeWaveforms(in_pin, in_rf, drvr_pin, drvr_rf, nullptr, corner, min_max);
|
||||
TableAxisPtr time_axis = make_shared<TableAxis>(TableAxisVariable::time,
|
||||
new FloatSeq(times_));
|
||||
Table1 waveform(new FloatSeq(drvr_voltages_), time_axis);
|
||||
return waveform;
|
||||
}
|
||||
|
||||
Table1
|
||||
CcsSimDelayCalc::loadWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Pin *load_pin,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max)
|
||||
{
|
||||
makeWaveforms(in_pin, in_rf, drvr_pin, drvr_rf, load_pin, corner, min_max);
|
||||
TableAxisPtr time_axis = make_shared<TableAxis>(TableAxisVariable::time,
|
||||
new FloatSeq(times_));
|
||||
Table1 waveform(new FloatSeq(load_voltages_), time_axis);
|
||||
return waveform;
|
||||
}
|
||||
|
||||
Table1
|
||||
CcsSimDelayCalc::inputWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max)
|
||||
{
|
||||
LibertyPort *port = network_->libertyPort(in_pin);
|
||||
if (port) {
|
||||
DriverWaveform *driver_waveform = port->driverWaveform(in_rf);
|
||||
const Vertex *in_vertex = graph_->pinLoadVertex(in_pin);
|
||||
DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
|
||||
float in_slew = delayAsFloat(graph_->slew(in_vertex, in_rf, dcalc_ap->index()));
|
||||
LibertyLibrary *library = port->libertyLibrary();
|
||||
float vdd;
|
||||
bool vdd_exists;
|
||||
library->supplyVoltage("VDD", vdd, vdd_exists);
|
||||
if (!vdd_exists)
|
||||
report_->error(1721, "VDD not defined in library %s", library->name());
|
||||
Table1 in_waveform = driver_waveform->waveform(in_slew);
|
||||
// Scale the waveform from 0:vdd.
|
||||
FloatSeq *scaled_values = new FloatSeq;
|
||||
for (float value : *in_waveform.values())
|
||||
scaled_values->push_back(value * vdd);
|
||||
return Table1(scaled_values, in_waveform.axis1ptr());
|
||||
}
|
||||
return Table1();
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::makeWaveforms(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Pin *load_pin,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max)
|
||||
{
|
||||
Edge *edge;
|
||||
const TimingArc *arc;
|
||||
graph_->gateEdgeArc(in_pin, in_rf, drvr_pin, drvr_rf, edge, arc);
|
||||
if (arc) {
|
||||
DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
|
||||
const Parasitic *parasitic = findParasitic(drvr_pin, drvr_rf, dcalc_ap);
|
||||
if (parasitic) {
|
||||
make_waveforms_ = true;
|
||||
waveform_drvr_pin_ = drvr_pin;
|
||||
waveform_load_pin_ = load_pin;
|
||||
Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin);
|
||||
graph_delay_calc_->findDriverArcDelays(drvr_vertex, edge, arc, dcalc_ap, this);
|
||||
make_waveforms_ = false;
|
||||
waveform_drvr_pin_ = nullptr;
|
||||
waveform_load_pin_ = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::reportMatrix(const char *name,
|
||||
MatrixSd &matrix)
|
||||
{
|
||||
report_->reportLine("%s", name);
|
||||
reportMatrix(matrix);
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::reportMatrix(const char *name,
|
||||
MatrixXd &matrix)
|
||||
{
|
||||
report_->reportLine("%s", name);
|
||||
reportMatrix(matrix);
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::reportMatrix(const char *name,
|
||||
VectorXd &matrix)
|
||||
{
|
||||
report_->reportLine("%s", name);
|
||||
reportMatrix(matrix);
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::reportVector(const char *name,
|
||||
vector<double> &matrix)
|
||||
{
|
||||
report_->reportLine("%s", name);
|
||||
reportVector(matrix);
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::reportMatrix(MatrixSd &matrix)
|
||||
{
|
||||
for (Index i = 0; i < matrix.rows(); i++) {
|
||||
string line = "| ";
|
||||
for (Index j = 0; j < matrix.cols(); j++) {
|
||||
string entry = stdstrPrint("%10.3e", matrix.coeff(i, j));
|
||||
line += entry;
|
||||
line += " ";
|
||||
}
|
||||
line += "|";
|
||||
report_->reportLineString(line);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::reportMatrix(MatrixXd &matrix)
|
||||
{
|
||||
for (Index i = 0; i < matrix.rows(); i++) {
|
||||
string line = "| ";
|
||||
for (Index j = 0; j < matrix.cols(); j++) {
|
||||
string entry = stdstrPrint("%10.3e", matrix.coeff(i, j));
|
||||
line += entry;
|
||||
line += " ";
|
||||
}
|
||||
line += "|";
|
||||
report_->reportLineString(line);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::reportMatrix(VectorXd &matrix)
|
||||
{
|
||||
string line = "| ";
|
||||
for (Index i = 0; i < matrix.rows(); i++) {
|
||||
string entry = stdstrPrint("%10.3e", matrix.coeff(i));
|
||||
line += entry;
|
||||
line += " ";
|
||||
}
|
||||
line += "|";
|
||||
report_->reportLineString(line);
|
||||
}
|
||||
|
||||
void
|
||||
CcsSimDelayCalc::reportVector(vector<double> &matrix)
|
||||
{
|
||||
string line = "| ";
|
||||
for (size_t i = 0; i < matrix.size(); i++) {
|
||||
string entry = stdstrPrint("%10.3e", matrix[i]);
|
||||
line += entry;
|
||||
line += " ";
|
||||
}
|
||||
line += "|";
|
||||
report_->reportLineString(line);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2023, 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 <vector>
|
||||
#include <Eigen/SparseCore>
|
||||
#include <Eigen/SparseLU>
|
||||
|
||||
#include "LumpedCapDelayCalc.hh"
|
||||
#include "ArcDcalcWaveforms.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
class ArcDelayCalc;
|
||||
class StaState;
|
||||
class Corner;
|
||||
|
||||
using std::vector;
|
||||
using std::array;
|
||||
using Eigen::MatrixXd;
|
||||
using Eigen::MatrixXcd;
|
||||
using Eigen::VectorXd;
|
||||
using Eigen::SparseMatrix;
|
||||
using Eigen::Index;
|
||||
using Eigen::SparseLU;
|
||||
|
||||
typedef Map<const Pin*, size_t, PinIdLess> PinNodeMap;
|
||||
typedef Map<const ParasiticNode*, size_t> NodeIndexMap;
|
||||
typedef Map<const Pin*, size_t> PortIndexMap;
|
||||
typedef SparseMatrix<double> MatrixSd;
|
||||
|
||||
ArcDelayCalc *
|
||||
makeCcsSimDelayCalc(StaState *sta);
|
||||
|
||||
class CcsSimDelayCalc : public DelayCalcBase, public ArcDcalcWaveforms
|
||||
{
|
||||
public:
|
||||
CcsSimDelayCalc(StaState *sta);
|
||||
~CcsSimDelayCalc();
|
||||
ArcDelayCalc *copy() override;
|
||||
Parasitic *findParasitic(const Pin *drvr_pin,
|
||||
const RiseFall *rf,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
Parasitic *reduceParasitic(const Parasitic *parasitic_network,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *rf,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
ArcDcalcResult inputPortDelay(const Pin *drvr_pin,
|
||||
float in_slew,
|
||||
const RiseFall *rf,
|
||||
const Parasitic *parasitic,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap) 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;
|
||||
ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args,
|
||||
float load_cap,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
string reportGateDelay(const Pin *drvr_pin,
|
||||
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 inputWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max) override;
|
||||
Table1 drvrWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max) override;
|
||||
Table1 loadWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Pin *load_pin,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max) override;
|
||||
|
||||
protected:
|
||||
void simulate(ArcDcalcArgSeq &dcalc_args);
|
||||
virtual double maxTime();
|
||||
virtual double timeStep();
|
||||
void updateCeffIdrvr();
|
||||
void initSim();
|
||||
void findLoads();
|
||||
virtual void findNodeCount();
|
||||
void setOrder();
|
||||
void initNodeVoltages();
|
||||
void simulateStep();
|
||||
virtual void stampConductances();
|
||||
void stampConductance(size_t n1,
|
||||
double g);
|
||||
void stampConductance(size_t n1,
|
||||
size_t n2,
|
||||
double g);
|
||||
void stampCapacitance(size_t n1,
|
||||
double cap);
|
||||
void stampCapacitance(size_t n1,
|
||||
size_t n2,
|
||||
double cap);
|
||||
float pinCapacitance(ParasiticNode *node);
|
||||
virtual void setCurrents();
|
||||
void insertCapCurrentSrc(size_t n1,
|
||||
double cap);
|
||||
void insertCapaCurrentSrc(size_t n1,
|
||||
size_t n2,
|
||||
double cap);
|
||||
void insertCurrentSrc(size_t n1,
|
||||
double current);
|
||||
void insertCurrentSrc(size_t n1,
|
||||
size_t n2,
|
||||
double current);
|
||||
void measureThresholds(double time);
|
||||
void measureThresholds(double time,
|
||||
size_t i);
|
||||
void loadDelaySlew(const Pin *load_pin,
|
||||
// Return values.
|
||||
ArcDelay &delay,
|
||||
Slew &slew);
|
||||
void recordWaveformStep(double time);
|
||||
void makeWaveforms(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Pin *load_pin,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max);
|
||||
void reportMatrix(const char *name,
|
||||
MatrixSd &matrix);
|
||||
void reportMatrix(const char *name,
|
||||
MatrixXd &matrix);
|
||||
void reportMatrix(const char *name,
|
||||
VectorXd &matrix);
|
||||
void reportVector(const char *name,
|
||||
vector<double> &matrix);
|
||||
void reportMatrix(MatrixSd &matrix);
|
||||
void reportMatrix(MatrixXd &matrix);
|
||||
void reportMatrix(VectorXd &matrix);
|
||||
void reportVector(vector<double> &matrix);
|
||||
|
||||
ArcDcalcArgSeq *dcalc_args_;
|
||||
size_t drvr_count_;
|
||||
float load_cap_;
|
||||
const DcalcAnalysisPt *dcalc_ap_;
|
||||
const Parasitic *parasitic_network_;
|
||||
const RiseFall *drvr_rf_;
|
||||
|
||||
// Tmp for gateDelay/loadDelay api.
|
||||
ArcDcalcResult dcalc_result_;
|
||||
LoadPinIndexMap load_pin_index_map_;
|
||||
|
||||
bool dcalc_failed_;
|
||||
size_t node_count_; // Parasitic network node count
|
||||
PinNodeMap pin_node_map_; // Parasitic pin -> array index
|
||||
NodeIndexMap node_index_map_; // Parasitic node -> array index
|
||||
vector<OutputWaveforms*> output_waveforms_;
|
||||
vector<float> ref_time_;
|
||||
double drive_resistance_;
|
||||
double resistance_sum_;
|
||||
|
||||
vector<double> node_capacitances_;
|
||||
bool includes_pin_caps_;
|
||||
float coupling_cap_multiplier_;
|
||||
|
||||
// Indexed by driver index.
|
||||
vector<double> ceff_;
|
||||
vector<double> drvr_current_;
|
||||
|
||||
double time_step_;
|
||||
double time_step_prev_;
|
||||
// I = GV
|
||||
// currents_ = conductances_ * voltages_
|
||||
VectorXd currents_;
|
||||
MatrixSd conductances_;
|
||||
VectorXd voltages_;
|
||||
VectorXd voltages_prev1_;
|
||||
VectorXd voltages_prev2_;
|
||||
SparseLU<MatrixSd> solver_;
|
||||
|
||||
// Waveform recording.
|
||||
bool make_waveforms_;
|
||||
const Pin *waveform_drvr_pin_;
|
||||
const Pin *waveform_load_pin_;
|
||||
FloatSeq drvr_voltages_;
|
||||
FloatSeq load_voltages_;
|
||||
FloatSeq times_;
|
||||
|
||||
size_t drvr_idx_;
|
||||
|
||||
float vdd_;
|
||||
float vth_;
|
||||
float vl_;
|
||||
float vh_;
|
||||
|
||||
static constexpr size_t threshold_vl = 0;
|
||||
static constexpr size_t threshold_vth = 1;
|
||||
static constexpr size_t threshold_vh = 2;
|
||||
static constexpr size_t measure_threshold_count_ = 3;
|
||||
typedef array<double, measure_threshold_count_> ThresholdTimes;
|
||||
// Vl Vth Vh
|
||||
ThresholdTimes measure_thresholds_;
|
||||
// Indexed by node number.
|
||||
vector<ThresholdTimes> threshold_times_;
|
||||
|
||||
// Delay calculator to use when ccs waveforms are missing from liberty.
|
||||
ArcDelayCalc *table_dcalc_;
|
||||
|
||||
using ArcDelayCalc::reduceParasitic;
|
||||
};
|
||||
|
||||
} // namespacet
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
#include "DmpDelayCalc.hh"
|
||||
#include "ArnoldiDelayCalc.hh"
|
||||
#include "CcsCeffDelayCalc.hh"
|
||||
#include "CcsSimDelayCalc.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
|
|
@ -39,6 +40,7 @@ registerDelayCalcs()
|
|||
registerDelayCalc("dmp_ceff_two_pole", makeDmpCeffTwoPoleDelayCalc);
|
||||
registerDelayCalc("arnoldi", makeArnoldiDelayCalc);
|
||||
registerDelayCalc("ccs_ceff", makeCcsCeffDelayCalc);
|
||||
registerDelayCalc("ccs_sim", makeCcsSimDelayCalc);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -1413,12 +1413,14 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge,
|
|||
int slew_index = dcalc_ap->checkDataSlewIndex();
|
||||
const Slew &to_slew = graph_->slew(to_vertex, to_rf, slew_index);
|
||||
debugPrint(debug_, "delay_calc", 3,
|
||||
" %s %s -> %s %s (%s)",
|
||||
" %s %s -> %s %s (%s) corner:%s/%s",
|
||||
arc_set->from()->name(),
|
||||
arc->fromEdge()->asString(),
|
||||
arc_set->to()->name(),
|
||||
arc->toEdge()->asString(),
|
||||
arc_set->role()->asString());
|
||||
arc_set->role()->asString(),
|
||||
dcalc_ap->corner()->name(),
|
||||
dcalc_ap->delayMinMax()->asString());
|
||||
debugPrint(debug_, "delay_calc", 3,
|
||||
" from_slew = %s to_slew = %s",
|
||||
delayAsString(from_slew, this),
|
||||
|
|
@ -1426,7 +1428,7 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge,
|
|||
float related_out_cap = 0.0;
|
||||
if (related_out_pin)
|
||||
related_out_cap = loadCap(related_out_pin, to_rf,dcalc_ap,arc_delay_calc);
|
||||
ArcDelay check_delay = arc_delay_calc->checkDelay(to_pin, arc, from_slew,
|
||||
ArcDelay check_delay = arc_delay_calc->checkDelay(to_pin, arc, from_slew,
|
||||
to_slew, related_out_cap,
|
||||
dcalc_ap);
|
||||
debugPrint(debug_, "delay_calc", 3,
|
||||
|
|
@ -1512,30 +1514,6 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge,
|
|||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
GraphDelayCalc::minPulseWidth(const Pin *pin,
|
||||
const RiseFall *hi_low,
|
||||
DcalcAPIndex ap_index,
|
||||
const MinMax *min_max,
|
||||
// Return values.
|
||||
float &min_width,
|
||||
bool &exists)
|
||||
{
|
||||
// Sdf annotation.
|
||||
graph_->widthCheckAnnotation(pin, hi_low, ap_index,
|
||||
min_width, exists);
|
||||
if (!exists) {
|
||||
// Liberty library.
|
||||
LibertyPort *port = network_->libertyPort(pin);
|
||||
if (port) {
|
||||
Instance *inst = network_->instance(pin);
|
||||
const Pvt *pvt = inst ? sdc_->pvt(inst, min_max) : nullptr;
|
||||
OperatingConditions *op_cond=sdc_->operatingConditions(min_max);
|
||||
port->minPulseWidth(hi_low, op_cond, pvt, min_width, exists);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GraphDelayCalc::minPeriod(const Pin *pin,
|
||||
// Return values.
|
||||
|
|
|
|||
BIN
doc/OpenSTA.odt
BIN
doc/OpenSTA.odt
Binary file not shown.
BIN
doc/OpenSTA.pdf
BIN
doc/OpenSTA.pdf
Binary file not shown.
|
|
@ -47,7 +47,6 @@ Graph::Graph(StaState *sta,
|
|||
slew_rf_count_(slew_rf_count),
|
||||
have_arc_delays_(have_arc_delays),
|
||||
ap_count_(ap_count),
|
||||
width_check_annotations_(nullptr),
|
||||
period_check_annotations_(nullptr),
|
||||
reg_clk_vertices_(new VertexSet(graph_))
|
||||
{
|
||||
|
|
@ -62,7 +61,6 @@ Graph::~Graph()
|
|||
delete reg_clk_vertices_;
|
||||
deleteSlewTables();
|
||||
deleteArcDelayTables();
|
||||
removeWidthCheckAnnotations();
|
||||
removePeriodCheckAnnotations();
|
||||
}
|
||||
|
||||
|
|
@ -205,9 +203,9 @@ Graph::makePortInstanceEdges(const Instance *inst,
|
|||
// Vertices can be missing from the graph if the pins
|
||||
// are power or ground.
|
||||
if (from_vertex) {
|
||||
bool is_check = arc_set->role()->isTimingCheck();
|
||||
if (to_bidirect_drvr_vertex &&
|
||||
!is_check)
|
||||
TimingRole *role = arc_set->role();
|
||||
bool is_check = role->isTimingCheckBetween();
|
||||
if (to_bidirect_drvr_vertex && !is_check)
|
||||
makeEdge(from_vertex, to_bidirect_drvr_vertex, arc_set);
|
||||
else if (to_vertex) {
|
||||
makeEdge(from_vertex, to_vertex, arc_set);
|
||||
|
|
@ -856,7 +854,7 @@ Graph::arcDelayAnnotated(const Edge *edge,
|
|||
const TimingArc *arc,
|
||||
DcalcAPIndex ap_index) const
|
||||
{
|
||||
if (arc_delay_annotated_.size()) {
|
||||
if (!arc_delay_annotated_.empty()) {
|
||||
size_t index = (edge->arcDelays() + arc->index()) * ap_count_ + ap_index;
|
||||
if (index >= arc_delay_annotated_.size())
|
||||
report_->critical(1080, "arc_delay_annotated array bounds exceeded");
|
||||
|
|
@ -912,7 +910,6 @@ Graph::setDelayCount(DcalcAPIndex ap_count)
|
|||
// Discard any existing delays.
|
||||
deleteSlewTables();
|
||||
deleteArcDelayTables();
|
||||
removeWidthCheckAnnotations();
|
||||
removePeriodCheckAnnotations();
|
||||
makeSlewTables(ap_count);
|
||||
makeArcDelayTables(ap_count);
|
||||
|
|
@ -955,11 +952,11 @@ Graph::delayAnnotated(Edge *edge)
|
|||
TimingArcSet *arc_set = edge->timingArcSet();
|
||||
for (TimingArc *arc : arc_set->arcs()) {
|
||||
for (DcalcAPIndex ap_index = 0; ap_index < ap_count_; ap_index++) {
|
||||
if (arcDelayAnnotated(edge, arc, ap_index))
|
||||
return true;
|
||||
if (!arcDelayAnnotated(edge, arc, ap_index))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -994,58 +991,28 @@ Graph::makeVertexSlews(Vertex *vertex)
|
|||
////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
Graph::widthCheckAnnotation(const Pin *pin,
|
||||
const RiseFall *rf,
|
||||
DcalcAPIndex ap_index,
|
||||
// Return values.
|
||||
float &width,
|
||||
bool &exists)
|
||||
Graph::minPulseWidthArc(Vertex *vertex,
|
||||
// high = rise, low = fall
|
||||
const RiseFall *hi_low,
|
||||
// Return values.
|
||||
Edge *&edge,
|
||||
TimingArc *&arc)
|
||||
{
|
||||
exists = false;
|
||||
if (width_check_annotations_) {
|
||||
float *widths = width_check_annotations_->findKey(pin);
|
||||
if (widths) {
|
||||
int index = ap_index * RiseFall::index_count + rf->index();
|
||||
width = widths[index];
|
||||
if (width >= 0.0)
|
||||
exists = true;
|
||||
VertexOutEdgeIterator edge_iter(vertex, this);
|
||||
while (edge_iter.hasNext()) {
|
||||
edge = edge_iter.next();
|
||||
TimingArcSet *arc_set = edge->timingArcSet();
|
||||
if (arc_set->role() == TimingRole::width()) {
|
||||
for (TimingArc *arc1 : arc_set->arcs()) {
|
||||
if (arc1->fromEdge()->asRiseFall() == hi_low) {
|
||||
arc = arc1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Graph::setWidthCheckAnnotation(const Pin *pin,
|
||||
const RiseFall *rf,
|
||||
DcalcAPIndex ap_index,
|
||||
float width)
|
||||
{
|
||||
if (width_check_annotations_ == nullptr)
|
||||
width_check_annotations_ = new WidthCheckAnnotations;
|
||||
float *widths = width_check_annotations_->findKey(pin);
|
||||
if (widths == nullptr) {
|
||||
int width_count = RiseFall::index_count * ap_count_;
|
||||
widths = new float[width_count];
|
||||
// Use negative (illegal) width values to indicate unannotated checks.
|
||||
for (int i = 0; i < width_count; i++)
|
||||
widths[i] = -1;
|
||||
(*width_check_annotations_)[pin] = widths;
|
||||
}
|
||||
int index = ap_index * RiseFall::index_count + rf->index();
|
||||
widths[index] = width;
|
||||
}
|
||||
|
||||
void
|
||||
Graph::removeWidthCheckAnnotations()
|
||||
{
|
||||
if (width_check_annotations_) {
|
||||
WidthCheckAnnotations::Iterator check_iter(width_check_annotations_);
|
||||
while (check_iter.hasNext()) {
|
||||
float *widths = check_iter.next();
|
||||
delete [] widths;
|
||||
}
|
||||
delete width_check_annotations_;
|
||||
width_check_annotations_ = nullptr;
|
||||
}
|
||||
edge = nullptr;
|
||||
arc = nullptr;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
|
@ -1112,7 +1079,6 @@ Graph::removeDelaySlewAnnotations()
|
|||
}
|
||||
vertex->removeSlewAnnotated();
|
||||
}
|
||||
removeWidthCheckAnnotations();
|
||||
removePeriodCheckAnnotations();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ public:
|
|||
Edge *edge() const { return edge_; }
|
||||
const TimingArc *arc() const { return arc_; }
|
||||
Slew inSlew() const { return in_slew_; }
|
||||
float inSlewFlt() const;
|
||||
void setInSlew(Slew in_slew);
|
||||
const Parasitic *parasitic() { return parasitic_; }
|
||||
void setParasitic(const Parasitic *parasitic);
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ typedef ArrayTable<Required> RequiredsTable;
|
|||
typedef ArrayTable<PathVertexRep> PrevPathsTable;
|
||||
typedef Map<const Pin*, Vertex*> PinVertexMap;
|
||||
typedef Iterator<Edge*> VertexEdgeIterator;
|
||||
typedef Map<const Pin*, float*> WidthCheckAnnotations;
|
||||
typedef Map<const Pin*, float*> PeriodCheckAnnotations;
|
||||
typedef Vector<DelayTable*> DelayTableSeq;
|
||||
typedef ObjectId EdgeId;
|
||||
|
|
@ -184,18 +183,11 @@ public:
|
|||
int edgeCount() { return edges_->size(); }
|
||||
virtual int arcCount() { return arc_count_; }
|
||||
|
||||
// Sdf width check annotation.
|
||||
void widthCheckAnnotation(const Pin *pin,
|
||||
const RiseFall *rf,
|
||||
DcalcAPIndex ap_index,
|
||||
// Return values.
|
||||
float &width,
|
||||
bool &exists);
|
||||
void setWidthCheckAnnotation(const Pin *pin,
|
||||
const RiseFall *rf,
|
||||
DcalcAPIndex ap_index,
|
||||
float width);
|
||||
|
||||
void minPulseWidthArc(Vertex *vertex,
|
||||
const RiseFall *hi_low,
|
||||
// Return values.
|
||||
Edge *&edge,
|
||||
TimingArc *&arc);
|
||||
// Sdf period check annotation.
|
||||
void periodCheckAnnotation(const Pin *pin,
|
||||
DcalcAPIndex ap_index,
|
||||
|
|
@ -229,7 +221,6 @@ protected:
|
|||
virtual void makePortInstanceEdges(const Instance *inst,
|
||||
LibertyCell *cell,
|
||||
LibertyPort *from_to_port);
|
||||
void removeWidthCheckAnnotations();
|
||||
void removePeriodCheckAnnotations();
|
||||
void makeSlewTables(DcalcAPIndex count);
|
||||
void deleteSlewTables();
|
||||
|
|
@ -264,8 +255,6 @@ protected:
|
|||
DelayTableSeq slew_tables_; // [ap_index][tr_index][vertex_id]
|
||||
VertexId slew_count_;
|
||||
DelayTableSeq arc_delays_; // [ap_index][edge_arc_index]
|
||||
// Sdf width check annotations.
|
||||
WidthCheckAnnotations *width_check_annotations_;
|
||||
// Sdf period check annotations.
|
||||
PeriodCheckAnnotations *period_check_annotations_;
|
||||
// Register/latch clock vertices to search from.
|
||||
|
|
|
|||
|
|
@ -109,17 +109,6 @@ public:
|
|||
// Precedence:
|
||||
// SDF annotation
|
||||
// Liberty library
|
||||
// (ignores set_min_pulse_width constraint)
|
||||
void minPulseWidth(const Pin *pin,
|
||||
const RiseFall *hi_low,
|
||||
DcalcAPIndex ap_index,
|
||||
const MinMax *min_max,
|
||||
// Return values.
|
||||
float &min_width,
|
||||
bool &exists);
|
||||
// Precedence:
|
||||
// SDF annotation
|
||||
// Liberty library
|
||||
void minPeriod(const Pin *pin,
|
||||
// Return values.
|
||||
float &min_period,
|
||||
|
|
|
|||
|
|
@ -728,17 +728,12 @@ public:
|
|||
void minPeriod(float &min_period,
|
||||
bool &exists) const;
|
||||
void setMinPeriod(float min_period);
|
||||
// This corresponds to the min_pulse_width_high/low port attribute.
|
||||
// high = rise, low = fall
|
||||
void minPulseWidth(const RiseFall *hi_low,
|
||||
const OperatingConditions *op_cond,
|
||||
const Pvt *pvt,
|
||||
float &min_width,
|
||||
bool &exists) const;
|
||||
// Unscaled value.
|
||||
void minPulseWidth(const RiseFall *hi_low,
|
||||
float &min_width,
|
||||
bool &exists) const;
|
||||
void setMinPulseWidth(RiseFall *hi_low,
|
||||
void setMinPulseWidth(const RiseFall *hi_low,
|
||||
float min_width);
|
||||
bool isClock() const;
|
||||
void setIsClock(bool is_clk);
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ public:
|
|||
// PathEndDataCheck data clock path.
|
||||
virtual const PathVertex *dataClkPath() const { return nullptr; }
|
||||
virtual int setupDefaultCycles() const { return 1; }
|
||||
virtual float clkSkew(const StaState *sta);
|
||||
virtual Delay clkSkew(const StaState *sta);
|
||||
|
||||
static bool less(const PathEnd *path_end1,
|
||||
const PathEnd *path_end2,
|
||||
|
|
@ -328,7 +328,7 @@ public:
|
|||
virtual int exceptPathCmp(const PathEnd *path_end,
|
||||
const StaState *sta) const;
|
||||
virtual Delay sourceClkDelay(const StaState *sta) const;
|
||||
virtual float clkSkew(const StaState *sta);
|
||||
virtual Delay clkSkew(const StaState *sta);
|
||||
|
||||
protected:
|
||||
PathEndCheck(Path *path,
|
||||
|
|
|
|||
|
|
@ -445,6 +445,8 @@ public:
|
|||
float delay);
|
||||
void removeClockInsertion(const Clock *clk,
|
||||
const Pin *pin);
|
||||
static void moveClockInsertions(Sdc *from,
|
||||
Sdc *to);
|
||||
bool hasClockInsertion(const Pin *pin) const;
|
||||
float clockInsertion(const Clock *clk,
|
||||
const RiseFall *rf,
|
||||
|
|
@ -1066,7 +1068,7 @@ protected:
|
|||
virtual void recordPathDelayInternalEndpoints(ExceptionPath *exception);
|
||||
virtual void unrecordPathDelayInternalEndpoints(ExceptionPath *exception);
|
||||
bool pathDelayTo(const Pin *pin);
|
||||
bool hasLibertyChecks(const Pin *pin);
|
||||
bool hasLibertyCheckTo(const Pin *pin);
|
||||
void deleteMatchingExceptions(ExceptionPath *exception);
|
||||
void findMatchingExceptions(ExceptionPath *exception,
|
||||
ExceptionPathSet &matches);
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ public:
|
|||
int index() const { return index_; }
|
||||
bool isWire() const;
|
||||
bool isTimingCheck() const { return is_timing_check_; }
|
||||
// TIming check but not width or period.
|
||||
bool isTimingCheckBetween() const;
|
||||
bool isAsyncTimingCheck() const;
|
||||
bool isNonSeqTimingCheck() const { return is_non_seq_check_; }
|
||||
bool isDataCheck() const;
|
||||
|
|
|
|||
|
|
@ -1896,7 +1896,7 @@ void
|
|||
LibertyCell::ensureVoltageWaveforms()
|
||||
{
|
||||
if (!have_voltage_waveforms_) {
|
||||
float vdd = 0;
|
||||
float vdd = 0.0; // shutup gcc
|
||||
bool vdd_exists;
|
||||
liberty_library_->supplyVoltage("VDD", vdd, vdd_exists);
|
||||
if (!vdd_exists || vdd == 0.0)
|
||||
|
|
@ -2330,28 +2330,6 @@ LibertyPort::setMinPeriod(float min_period)
|
|||
min_period_exists_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
LibertyPort::minPulseWidth(const RiseFall *hi_low,
|
||||
const OperatingConditions *op_cond,
|
||||
const Pvt *pvt,
|
||||
float &min_width,
|
||||
bool &exists) const
|
||||
{
|
||||
if (scaled_ports_) {
|
||||
LibertyPort *scaled_port = (*scaled_ports_)[op_cond];
|
||||
if (scaled_port) {
|
||||
scaled_port->minPulseWidth(hi_low, min_width, exists);
|
||||
return;
|
||||
}
|
||||
}
|
||||
int hi_low_index = hi_low->index();
|
||||
LibertyLibrary *lib = liberty_cell_->libertyLibrary();
|
||||
min_width = min_pulse_width_[hi_low_index]
|
||||
* lib->scaleFactor(ScaleFactorType::min_pulse_width, hi_low_index,
|
||||
liberty_cell_, pvt);
|
||||
exists = min_pulse_width_exists_ & (1 << hi_low_index);
|
||||
}
|
||||
|
||||
void
|
||||
LibertyPort::minPulseWidth(const RiseFall *hi_low,
|
||||
float &min_width,
|
||||
|
|
@ -2363,7 +2341,7 @@ LibertyPort::minPulseWidth(const RiseFall *hi_low,
|
|||
}
|
||||
|
||||
void
|
||||
LibertyPort::setMinPulseWidth(RiseFall *hi_low,
|
||||
LibertyPort::setMinPulseWidth(const RiseFall *hi_low,
|
||||
float min_width)
|
||||
{
|
||||
int hi_low_index = hi_low->index();
|
||||
|
|
|
|||
|
|
@ -276,6 +276,8 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell,
|
|||
return makeClockTreePathArcs(cell, to_port, TimingRole::clockTreePathMax(),
|
||||
MinMax::max(), attrs);
|
||||
case TimingType::min_pulse_width:
|
||||
return makeMinPulseWidthArcs(cell, from_port, to_port, related_out,
|
||||
TimingRole::width(), attrs);
|
||||
case TimingType::minimum_period:
|
||||
case TimingType::nochange_high_high:
|
||||
case TimingType::nochange_high_low:
|
||||
|
|
@ -668,6 +670,26 @@ LibertyBuilder::makeClockTreePathArcs(LibertyCell *cell,
|
|||
return arc_set;
|
||||
}
|
||||
|
||||
TimingArcSet *
|
||||
LibertyBuilder::makeMinPulseWidthArcs(LibertyCell *cell,
|
||||
LibertyPort *from_port,
|
||||
LibertyPort *to_port,
|
||||
LibertyPort *related_out,
|
||||
TimingRole *role,
|
||||
TimingArcAttrsPtr attrs)
|
||||
{
|
||||
TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, related_out,
|
||||
role, attrs);
|
||||
for (auto to_rf : RiseFall::range()) {
|
||||
TimingModel *model = attrs->model(to_rf);
|
||||
if (model)
|
||||
makeTimingArc(arc_set, to_rf->opposite(), to_rf, model);
|
||||
}
|
||||
return arc_set;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
TimingArcSet *
|
||||
LibertyBuilder::makeTimingArcSet(LibertyCell *cell,
|
||||
LibertyPort *from,
|
||||
|
|
|
|||
|
|
@ -84,6 +84,12 @@ public:
|
|||
TimingRole *role,
|
||||
const MinMax *min_max,
|
||||
TimingArcAttrsPtr attrs);
|
||||
TimingArcSet *makeMinPulseWidthArcs(LibertyCell *cell,
|
||||
LibertyPort *from_port,
|
||||
LibertyPort *to_port,
|
||||
LibertyPort *related_out,
|
||||
TimingRole *role,
|
||||
TimingArcAttrsPtr attrs);
|
||||
|
||||
protected:
|
||||
ConcretePort *makeBusPort(const char *name,
|
||||
|
|
|
|||
|
|
@ -1922,8 +1922,10 @@ LibertyReader::finishPortGroups()
|
|||
{
|
||||
for (PortGroup *port_group : cell_port_groups_) {
|
||||
int line = port_group->line();
|
||||
for (LibertyPort *port : *port_group->ports())
|
||||
for (LibertyPort *port : *port_group->ports()) {
|
||||
checkPort(port, line);
|
||||
makeMinPulseWidthArcs(port, line);
|
||||
}
|
||||
makeTimingArcs(port_group);
|
||||
makeInternalPowers(port_group);
|
||||
delete port_group;
|
||||
|
|
@ -1947,6 +1949,47 @@ LibertyReader::checkPort(LibertyPort *port,
|
|||
port->setDirection(PortDirection::tristate());
|
||||
}
|
||||
|
||||
// Make timing arcs for the port min_pulse_width_low/high attributes.
|
||||
// This is redundant but makes sdf annotation consistent.
|
||||
void
|
||||
LibertyReader::makeMinPulseWidthArcs(LibertyPort *port,
|
||||
int line)
|
||||
{
|
||||
TimingArcAttrsPtr attrs = nullptr;
|
||||
for (auto hi_low : RiseFall::range()) {
|
||||
float min_width;
|
||||
bool exists;
|
||||
port->minPulseWidth(hi_low, min_width, exists);
|
||||
if (exists) {
|
||||
if (attrs == nullptr) {
|
||||
attrs = make_shared<TimingArcAttrs>();
|
||||
attrs->setTimingType(TimingType::min_pulse_width);
|
||||
}
|
||||
// rise/fall_constraint model is on the trailing edge of the pulse.
|
||||
const RiseFall *model_rf = hi_low->opposite();
|
||||
TimingModel *check_model =
|
||||
makeScalarCheckModel(min_width, ScaleFactorType::min_pulse_width, model_rf);
|
||||
attrs->setModel(model_rf, check_model);
|
||||
}
|
||||
}
|
||||
if (attrs)
|
||||
builder_.makeTimingArcs(cell_, port, port, nullptr, attrs, line);
|
||||
}
|
||||
|
||||
TimingModel *
|
||||
LibertyReader::makeScalarCheckModel(float value,
|
||||
ScaleFactorType scale_factor_type,
|
||||
const RiseFall *rf)
|
||||
{
|
||||
TablePtr table = make_shared<Table0>(value);
|
||||
TableTemplate *tbl_template =
|
||||
library_->findTableTemplate("scalar", TableTemplateType::delay);
|
||||
TableModel *table_model = new TableModel(table, tbl_template,
|
||||
scale_factor_type, rf);
|
||||
CheckTableModel *check_model = new CheckTableModel(cell_, table_model, nullptr);
|
||||
return check_model;
|
||||
}
|
||||
|
||||
void
|
||||
LibertyReader::makeTimingArcs(PortGroup *port_group)
|
||||
{
|
||||
|
|
@ -3530,7 +3573,7 @@ LibertyReader::visitMinPulseWidthHigh(LibertyAttr *attr)
|
|||
|
||||
void
|
||||
LibertyReader::visitMinPulseWidth(LibertyAttr *attr,
|
||||
RiseFall *rf)
|
||||
const RiseFall *rf)
|
||||
{
|
||||
if (cell_) {
|
||||
float value;
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ public:
|
|||
virtual void visitMinPulseWidthLow(LibertyAttr *attr);
|
||||
virtual void visitMinPulseWidthHigh(LibertyAttr *attr);
|
||||
virtual void visitMinPulseWidth(LibertyAttr *attr,
|
||||
RiseFall *rf);
|
||||
const RiseFall *rf);
|
||||
virtual void visitPulseClock(LibertyAttr *attr);
|
||||
virtual void visitClockGateClockPin(LibertyAttr *attr);
|
||||
virtual void visitClockGateEnablePin(LibertyAttr *attr);
|
||||
|
|
@ -479,6 +479,11 @@ public:
|
|||
virtual void visitAttr9(LibertyAttr *) {}
|
||||
|
||||
protected:
|
||||
TimingModel *makeScalarCheckModel(float value,
|
||||
ScaleFactorType scale_factor_type,
|
||||
const RiseFall *rf);
|
||||
void makeMinPulseWidthArcs(LibertyPort *port,
|
||||
int line);
|
||||
void setEnergyScale();
|
||||
void defineVisitors();
|
||||
virtual void begin(LibertyGroup *group);
|
||||
|
|
|
|||
|
|
@ -55,17 +55,21 @@ protected:
|
|||
void writePortAttrs(const LibertyPort *port);
|
||||
void writeTimingArcSet(const TimingArcSet *arc_set);
|
||||
void writeTimingModels(const TimingArc *arc,
|
||||
RiseFall *rf);
|
||||
const RiseFall *rf);
|
||||
void writeTableModel(const TableModel *model);
|
||||
void writeTableModel0(const TableModel *model);
|
||||
void writeTableModel1(const TableModel *model);
|
||||
void writeTableModel2(const TableModel *model);
|
||||
void writeTableAxis(const TableAxis *axis,
|
||||
int index);
|
||||
void writeTableAxis4(const TableAxis *axis,
|
||||
int index);
|
||||
void writeTableAxis10(const TableAxis *axis,
|
||||
int index);
|
||||
|
||||
const char *asString(bool value);
|
||||
const char *asString(const PortDirection *dir);
|
||||
const char *timingTypeString(const TimingArcSet *arc_set);
|
||||
bool isAutoWidthArc(const LibertyPort *port,
|
||||
const TimingArcSet *arc_set);
|
||||
|
||||
const LibertyLibrary *library_;
|
||||
const char *filename_;
|
||||
|
|
@ -214,20 +218,21 @@ LibertyWriter::writeTableTemplate(const TableTemplate *tbl_template)
|
|||
fprintf(stream_, " variable_3 : %s;\n",
|
||||
tableVariableString(axis3->variable()));
|
||||
if (axis1)
|
||||
writeTableAxis(axis1, 1);
|
||||
writeTableAxis4(axis1, 1);
|
||||
if (axis2)
|
||||
writeTableAxis(axis2, 2);
|
||||
writeTableAxis4(axis2, 2);
|
||||
if (axis3)
|
||||
writeTableAxis(axis3, 3);
|
||||
writeTableAxis4(axis3, 3);
|
||||
fprintf(stream_, " }\n");
|
||||
}
|
||||
}
|
||||
|
||||
// indent 4
|
||||
void
|
||||
LibertyWriter::writeTableAxis(const TableAxis *axis,
|
||||
int index)
|
||||
LibertyWriter::writeTableAxis4(const TableAxis *axis,
|
||||
int index)
|
||||
{
|
||||
fprintf(stream_, " index_%d (\"", index);
|
||||
fprintf(stream_, " index_%d(\"", index);
|
||||
const Unit *unit = tableVariableUnit(axis->variable(), library_->units());
|
||||
bool first = true;
|
||||
for (size_t i = 0; i < axis->size(); i++) {
|
||||
|
|
@ -239,6 +244,15 @@ LibertyWriter::writeTableAxis(const TableAxis *axis,
|
|||
fprintf(stream_, "\");\n");
|
||||
}
|
||||
|
||||
// indent 10
|
||||
void
|
||||
LibertyWriter::writeTableAxis10(const TableAxis *axis,
|
||||
int index)
|
||||
{
|
||||
fprintf(stream_, " ");
|
||||
writeTableAxis4(axis, index);
|
||||
}
|
||||
|
||||
void
|
||||
LibertyWriter::writeBusDcls()
|
||||
{
|
||||
|
|
@ -356,8 +370,25 @@ LibertyWriter::writePortAttrs(const LibertyPort *port)
|
|||
fprintf(stream_, " max_capacitance : %s;\n",
|
||||
cap_unit_->asString(limit, 3));
|
||||
|
||||
for (TimingArcSet *arc_set : port->libertyCell()->timingArcSets(nullptr,port))
|
||||
writeTimingArcSet(arc_set);
|
||||
for (TimingArcSet *arc_set : port->libertyCell()->timingArcSets(nullptr,port)) {
|
||||
if (!isAutoWidthArc(port, arc_set))
|
||||
writeTimingArcSet(arc_set);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if arc is added for port min_pulse_width_high/low attribute.
|
||||
bool
|
||||
LibertyWriter::isAutoWidthArc(const LibertyPort *port,
|
||||
const TimingArcSet *arc_set)
|
||||
{
|
||||
if (arc_set->role() == TimingRole::width()) {
|
||||
float min_width;
|
||||
bool exists1, exists2;
|
||||
port->minPulseWidth(RiseFall::rise(), min_width, exists1);
|
||||
port->minPulseWidth(RiseFall::fall(), min_width, exists2);
|
||||
return exists1 || exists2;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -386,7 +417,7 @@ LibertyWriter::writeTimingArcSet(const TimingArcSet *arc_set)
|
|||
|
||||
void
|
||||
LibertyWriter::writeTimingModels(const TimingArc *arc,
|
||||
RiseFall *rf)
|
||||
const RiseFall *rf)
|
||||
{
|
||||
TimingModel *model = arc->model();
|
||||
const GateTableModel *gate_model = dynamic_cast<GateTableModel*>(model);
|
||||
|
|
@ -450,6 +481,7 @@ LibertyWriter::writeTableModel0(const TableModel *model)
|
|||
void
|
||||
LibertyWriter::writeTableModel1(const TableModel *model)
|
||||
{
|
||||
writeTableAxis10(model->axis1(), 1);
|
||||
fprintf(stream_, " values(\"");
|
||||
bool first_col = true;
|
||||
for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) {
|
||||
|
|
@ -465,6 +497,8 @@ LibertyWriter::writeTableModel1(const TableModel *model)
|
|||
void
|
||||
LibertyWriter::writeTableModel2(const TableModel *model)
|
||||
{
|
||||
writeTableAxis10(model->axis1(), 1);
|
||||
writeTableAxis10(model->axis2(), 2);
|
||||
fprintf(stream_, " values(\"");
|
||||
bool first_row = true;
|
||||
for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) {
|
||||
|
|
@ -577,6 +611,8 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set)
|
|||
return "min_clock_tree_path";
|
||||
else if (role == TimingRole::clockTreePathMax())
|
||||
return "max_clock_tree_path";
|
||||
else if (role == TimingRole::width())
|
||||
return "min_pulse_width";
|
||||
else {
|
||||
report_->error(1333, "%s/%s/%s timing arc type %s not supported.",
|
||||
library_->name(),
|
||||
|
|
|
|||
|
|
@ -258,6 +258,14 @@ TimingRole::isLatchDtoQ() const
|
|||
return this == latch_d_q_;
|
||||
}
|
||||
|
||||
bool
|
||||
TimingRole::isTimingCheckBetween() const
|
||||
{
|
||||
return is_timing_check_
|
||||
&& this != width_
|
||||
&& this != period_;
|
||||
}
|
||||
|
||||
bool
|
||||
TimingRole::less(const TimingRole *role1,
|
||||
const TimingRole *role2)
|
||||
|
|
|
|||
83
sdc/Sdc.cc
83
sdc/Sdc.cc
|
|
@ -668,17 +668,9 @@ Sdc::moveDeratingFactors(Sdc *from,
|
|||
from->derating_factors_ = nullptr;
|
||||
}
|
||||
|
||||
to->net_derating_factors_.deleteContents();
|
||||
to->net_derating_factors_ = from->net_derating_factors_;
|
||||
from->net_derating_factors_.clear();
|
||||
|
||||
to->inst_derating_factors_.deleteContents();
|
||||
to->inst_derating_factors_ = from->inst_derating_factors_;
|
||||
from->inst_derating_factors_.clear();
|
||||
|
||||
to->cell_derating_factors_.deleteContents();
|
||||
to->cell_derating_factors_ = from->cell_derating_factors_;
|
||||
from->cell_derating_factors_.clear();
|
||||
to->net_derating_factors_ = std::move(from->net_derating_factors_);
|
||||
to->inst_derating_factors_ = std::move(from->inst_derating_factors_);
|
||||
to->cell_derating_factors_ = std::move(from->cell_derating_factors_);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1740,6 +1732,13 @@ Sdc::removeClockInsertion(const Clock *clk,
|
|||
deleteClockInsertion(insertion);
|
||||
}
|
||||
|
||||
void
|
||||
Sdc::moveClockInsertions(Sdc *from,
|
||||
Sdc *to)
|
||||
{
|
||||
to->clk_insertions_ = std::move(from->clk_insertions_);
|
||||
}
|
||||
|
||||
void
|
||||
Sdc::deleteClockInsertion(ClockInsertion *insertion)
|
||||
{
|
||||
|
|
@ -2746,46 +2745,18 @@ void
|
|||
Sdc::movePortDelays(Sdc *from,
|
||||
Sdc *to)
|
||||
{
|
||||
to->input_delays_.deleteContents();
|
||||
to->input_delays_ = from->input_delays_;
|
||||
from->input_delays_.clear();
|
||||
|
||||
to->input_delay_pin_map_.deleteContents();
|
||||
to->input_delay_pin_map_ = from->input_delay_pin_map_;
|
||||
from->input_delay_pin_map_.clear();
|
||||
|
||||
to->input_delay_ref_pin_map_.deleteContents();
|
||||
to->input_delay_ref_pin_map_ = from->input_delay_ref_pin_map_;
|
||||
from->input_delay_ref_pin_map_.clear();
|
||||
|
||||
to->input_delay_leaf_pin_map_.deleteContents();
|
||||
to->input_delay_leaf_pin_map_ = from->input_delay_leaf_pin_map_;
|
||||
from->input_delay_leaf_pin_map_.clear();
|
||||
|
||||
to->input_delay_internal_pin_map_.deleteContents();
|
||||
to->input_delay_internal_pin_map_ = from->input_delay_internal_pin_map_;
|
||||
from->input_delay_internal_pin_map_.clear();
|
||||
|
||||
to->input_delays_ = std::move(from->input_delays_);
|
||||
to->input_delay_pin_map_ = std::move(from->input_delay_pin_map_);
|
||||
to->input_delay_ref_pin_map_ = std::move(from->input_delay_ref_pin_map_);
|
||||
to->input_delay_leaf_pin_map_ = std::move(from->input_delay_leaf_pin_map_);
|
||||
to->input_delay_internal_pin_map_ = std::move(from->input_delay_internal_pin_map_);
|
||||
to->input_delay_index_ = from->input_delay_index_;
|
||||
from->input_delay_index_ = 0;
|
||||
|
||||
////////////////
|
||||
|
||||
to->output_delays_.deleteContents();
|
||||
to->output_delays_ = from->output_delays_;
|
||||
from->output_delays_.clear();
|
||||
|
||||
to->output_delay_pin_map_.deleteContents();
|
||||
to->output_delay_pin_map_ = from->output_delay_pin_map_;
|
||||
from->output_delay_pin_map_.clear();
|
||||
|
||||
to->output_delay_ref_pin_map_.deleteContents();
|
||||
to->output_delay_ref_pin_map_ = from->output_delay_ref_pin_map_;
|
||||
from->output_delay_ref_pin_map_.clear();
|
||||
|
||||
to->output_delay_leaf_pin_map_.deleteContents();
|
||||
to->output_delay_leaf_pin_map_ = from->output_delay_leaf_pin_map_;
|
||||
from->output_delay_leaf_pin_map_.clear();
|
||||
to->output_delays_ = std::move(from->output_delays_);
|
||||
to->output_delay_pin_map_ = std::move(from->output_delay_pin_map_);
|
||||
to->output_delay_ref_pin_map_ = std::move(from->output_delay_ref_pin_map_);
|
||||
to->output_delay_leaf_pin_map_ = std::move(from->output_delay_leaf_pin_map_);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
|
@ -3355,11 +3326,11 @@ Sdc::movePortExtCaps(Sdc *from,
|
|||
Sdc *to)
|
||||
{
|
||||
for (int corner_index = 0; corner_index < from->corners()->count(); corner_index++) {
|
||||
to->port_ext_cap_maps_[corner_index] = from->port_ext_cap_maps_[corner_index];
|
||||
from->port_ext_cap_maps_[corner_index].clear();
|
||||
to->port_ext_cap_maps_[corner_index] =
|
||||
std::move(from->port_ext_cap_maps_[corner_index]);
|
||||
|
||||
to->net_wire_cap_maps_[corner_index] = from->net_wire_cap_maps_[corner_index];
|
||||
from->net_wire_cap_maps_[corner_index].clear();
|
||||
to->net_wire_cap_maps_[corner_index] =
|
||||
std::move(from->net_wire_cap_maps_[corner_index]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3953,7 +3924,7 @@ Sdc::recordPathDelayInternalEndpoints(ExceptionPath *exception)
|
|||
if (to
|
||||
&& to->hasPins()) {
|
||||
for (const Pin *pin : *to->pins()) {
|
||||
if (!(hasLibertyChecks(pin)
|
||||
if (!(hasLibertyCheckTo(pin)
|
||||
|| network_->isTopLevelPort(pin))) {
|
||||
path_delay_internal_endpoints_.insert(pin);
|
||||
}
|
||||
|
|
@ -3969,7 +3940,7 @@ Sdc::unrecordPathDelayInternalEndpoints(ExceptionPath *exception)
|
|||
&& to->hasPins()
|
||||
&& !path_delay_internal_endpoints_.empty()) {
|
||||
for (const Pin *pin : *to->pins()) {
|
||||
if (!(hasLibertyChecks(pin)
|
||||
if (!(hasLibertyCheckTo(pin)
|
||||
|| network_->isTopLevelPort(pin))
|
||||
&& !pathDelayTo(pin))
|
||||
path_delay_internal_endpoints_.erase(pin);
|
||||
|
|
@ -3978,7 +3949,7 @@ Sdc::unrecordPathDelayInternalEndpoints(ExceptionPath *exception)
|
|||
}
|
||||
|
||||
bool
|
||||
Sdc::hasLibertyChecks(const Pin *pin)
|
||||
Sdc::hasLibertyCheckTo(const Pin *pin)
|
||||
{
|
||||
const Instance *inst = network_->instance(pin);
|
||||
LibertyCell *cell = network_->libertyCell(inst);
|
||||
|
|
@ -3986,7 +3957,7 @@ Sdc::hasLibertyChecks(const Pin *pin)
|
|||
LibertyPort *port = network_->libertyPort(pin);
|
||||
if (port) {
|
||||
for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, port)) {
|
||||
if (arc_set->role()->isTimingCheck())
|
||||
if (arc_set->role()->isTimingCheckBetween())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ protected:
|
|||
|
||||
void init();
|
||||
void findCounts();
|
||||
void findWidthPeriodCount(Pin *pin);
|
||||
void findPeriodCount(Pin *pin);
|
||||
void reportDelayCounts();
|
||||
void reportCheckCounts();
|
||||
void reportArcs();
|
||||
|
|
@ -78,9 +78,9 @@ protected:
|
|||
void reportArcs(Vertex *vertex,
|
||||
bool report_annotated,
|
||||
int &i);
|
||||
void reportWidthPeriodArcs(const Pin *pin,
|
||||
bool report_annotated,
|
||||
int &i);
|
||||
void reportPeriodArcs(const Pin *pin,
|
||||
bool report_annotated,
|
||||
int &i);
|
||||
void reportCount(const char *title,
|
||||
int index,
|
||||
int &total,
|
||||
|
|
@ -282,9 +282,8 @@ ReportAnnotated::reportCheckCount(TimingRole *role,
|
|||
{
|
||||
int index = role->index();
|
||||
if (edge_count_[index] > 0) {
|
||||
const char *role_name = role->asString();
|
||||
string title;
|
||||
stringPrint(title, "cell %s arcs", role_name);
|
||||
stringPrint(title, "cell %s arcs", role->asString());
|
||||
reportCount(title.c_str(), index, total, annotated_total);
|
||||
}
|
||||
}
|
||||
|
|
@ -345,7 +344,7 @@ ReportAnnotated::findCounts()
|
|||
}
|
||||
}
|
||||
}
|
||||
findWidthPeriodCount(from_pin);
|
||||
findPeriodCount(from_pin);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -376,7 +375,7 @@ ReportAnnotated::roleIndex(const TimingRole *role,
|
|||
// Width and period checks are not edges in the graph so
|
||||
// they require special handling.
|
||||
void
|
||||
ReportAnnotated::findWidthPeriodCount(Pin *pin)
|
||||
ReportAnnotated::findPeriodCount(Pin *pin)
|
||||
{
|
||||
LibertyPort *port = network_->libertyPort(pin);
|
||||
if (port) {
|
||||
|
|
@ -400,27 +399,6 @@ ReportAnnotated::findWidthPeriodCount(Pin *pin)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
int width_index = TimingRole::width()->index();
|
||||
if (report_role_[width_index]) {
|
||||
for (auto hi_low : RiseFall::range()) {
|
||||
port->minPulseWidth(hi_low, value, exists);
|
||||
if (exists) {
|
||||
edge_count_[width_index]++;
|
||||
graph_->widthCheckAnnotation(pin, hi_low, ap_index,
|
||||
value, annotated);
|
||||
if (annotated) {
|
||||
edge_annotated_count_[width_index]++;
|
||||
if (list_annotated_)
|
||||
annotated_pins_.insert(pin);
|
||||
}
|
||||
else {
|
||||
if (list_unannotated_)
|
||||
unannotated_pins_.insert(pin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -476,7 +454,7 @@ ReportAnnotated::reportArcs(const char *header,
|
|||
reportArcs(vertex, report_annotated, i);
|
||||
if (bidirect_drvr_vertex)
|
||||
reportArcs(bidirect_drvr_vertex, report_annotated, i);
|
||||
reportWidthPeriodArcs(pin, report_annotated, i);
|
||||
reportPeriodArcs(pin, report_annotated, i);
|
||||
if (max_lines_ != 0 && i > max_lines_)
|
||||
break;
|
||||
}
|
||||
|
|
@ -521,15 +499,13 @@ ReportAnnotated::reportArcs(Vertex *vertex,
|
|||
}
|
||||
|
||||
void
|
||||
ReportAnnotated::reportWidthPeriodArcs(const Pin *pin,
|
||||
bool report_annotated,
|
||||
int &i)
|
||||
ReportAnnotated::reportPeriodArcs(const Pin *pin,
|
||||
bool report_annotated,
|
||||
int &i)
|
||||
{
|
||||
LibertyPort *port = network_->libertyPort(pin);
|
||||
if (port) {
|
||||
DcalcAPIndex ap_index = 0;
|
||||
float value;
|
||||
bool exists, annotated;
|
||||
int period_index = TimingRole::period()->index();
|
||||
if (report_role_[period_index]
|
||||
&& (max_lines_ == 0 || i < max_lines_)) {
|
||||
|
|
@ -547,27 +523,6 @@ ReportAnnotated::reportWidthPeriodArcs(const Pin *pin,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
int width_index = TimingRole::width()->index();
|
||||
if (report_role_[width_index]
|
||||
&& (max_lines_ == 0 || i < max_lines_)) {
|
||||
bool report = false;
|
||||
for (auto hi_low : RiseFall::range()) {
|
||||
port->minPulseWidth(hi_low, value, exists);
|
||||
if (exists) {
|
||||
edge_count_[width_index]++;
|
||||
graph_->widthCheckAnnotation(pin, hi_low, ap_index,
|
||||
value, annotated);
|
||||
report |= (annotated == report_annotated);
|
||||
}
|
||||
}
|
||||
if (report) {
|
||||
report_->reportLine(" %-18s %s",
|
||||
"min width",
|
||||
network_->pathName(pin));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -550,22 +550,12 @@ SdfReader::timingCheckWidth(SdfPortSpec *edge,
|
|||
if (port) {
|
||||
Pin *pin = network_->findPin(instance_, port_name);
|
||||
if (pin) {
|
||||
const RiseFall *rf = edge->transition()->asRiseFall();
|
||||
float **values = triple->values();
|
||||
float *value_ptr = values[triple_min_index_];
|
||||
if (value_ptr) {
|
||||
float value = *value_ptr;
|
||||
graph_->setWidthCheckAnnotation(pin, rf, arc_delay_min_index_,
|
||||
value);
|
||||
}
|
||||
if (triple_max_index_ != null_index_) {
|
||||
value_ptr = values[triple_max_index_];
|
||||
if (value_ptr) {
|
||||
float value = *value_ptr;
|
||||
graph_->setWidthCheckAnnotation(pin, rf, arc_delay_max_index_,
|
||||
value);
|
||||
}
|
||||
}
|
||||
const RiseFall *rf = edge->transition()->asRiseFall();
|
||||
Edge *edge;
|
||||
TimingArc *arc;
|
||||
graph_->minPulseWidthArc(graph_->pinLoadVertex(pin), rf, edge, arc);
|
||||
if (edge)
|
||||
setEdgeArcDelays(edge, arc, triple);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -522,14 +522,12 @@ SdfWriter::writeTimingChecks(const Instance *inst,
|
|||
}
|
||||
for (auto hi_low : RiseFall::range()) {
|
||||
float min_width, max_width;
|
||||
bool exists;
|
||||
graph_delay_calc_->minPulseWidth(pin, hi_low, arc_delay_min_index_,
|
||||
MinMax::min(),
|
||||
min_width, exists);
|
||||
graph_delay_calc_->minPulseWidth(pin, hi_low, arc_delay_max_index_,
|
||||
MinMax::max(),
|
||||
max_width, exists);
|
||||
if (exists) {
|
||||
Edge *edge;
|
||||
TimingArc *arc;
|
||||
graph_->minPulseWidthArc(vertex, hi_low, edge, arc);
|
||||
if (edge) {
|
||||
min_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_min_index_));
|
||||
max_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_max_index_));
|
||||
ensureTimingCheckheaders(check_header, inst, inst_header);
|
||||
writeWidthCheck(pin, hi_low, min_width, max_width);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,10 +87,10 @@ BfsIterator::reportEntries(const Network *network)
|
|||
while (levelLessOrEqual(level, last_level_)) {
|
||||
VertexSeq &level_vertices = queue_[level];
|
||||
if (!level_vertices.empty()) {
|
||||
printf("Level %d\n", level);
|
||||
report_->reportLine("Level %d", level);
|
||||
for (auto vertex : level_vertices) {
|
||||
if (vertex)
|
||||
printf(" %s\n", vertex->name(network));
|
||||
report_->reportLine(" %s", vertex->name(network));
|
||||
}
|
||||
}
|
||||
incrLevel(level);
|
||||
|
|
|
|||
|
|
@ -424,9 +424,11 @@ MinPulseWidthCheck::minWidth(const StaState *sta) const
|
|||
}
|
||||
|
||||
// Precedence:
|
||||
// set_min_pulse_width constraint
|
||||
// set_min_pulse_width SDC command
|
||||
// SDF annotation
|
||||
// Liberty library
|
||||
// port min_pulse_width_low/high
|
||||
// min_pulse_width timing group
|
||||
static void
|
||||
minPulseWidth(const Path *path,
|
||||
const StaState *sta,
|
||||
|
|
@ -441,12 +443,17 @@ minPulseWidth(const Path *path,
|
|||
// set_min_pulse_width command.
|
||||
sdc->minPulseWidth(pin, clk, rf, min_width, exists);
|
||||
if (!exists) {
|
||||
GraphDelayCalc *graph_dcalc = sta->graphDelayCalc();
|
||||
const MinMax *min_max = path->minMax(sta);
|
||||
const PathAnalysisPt *path_ap = path->pathAnalysisPt(sta);
|
||||
const DcalcAnalysisPt *dcalc_ap = path_ap->dcalcAnalysisPt();
|
||||
graph_dcalc->minPulseWidth(pin, rf, dcalc_ap->index(), min_max,
|
||||
min_width, exists);
|
||||
Vertex *vertex = path->vertex(sta);
|
||||
Graph *graph = sta->graph();
|
||||
Edge *edge;
|
||||
TimingArc *arc;
|
||||
graph->minPulseWidthArc(vertex, rf, edge, arc);
|
||||
if (edge) {
|
||||
min_width = delayAsFloat(graph->arcDelay(edge, arc, dcalc_ap->index()));
|
||||
exists = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,19 +31,19 @@ public:
|
|||
const RiseFall *end_rf,
|
||||
const MinMax *min_max,
|
||||
// Return values.
|
||||
float &insertion,
|
||||
float &delay,
|
||||
Delay &insertion,
|
||||
Delay &delay,
|
||||
float &lib_clk_delay,
|
||||
float &latency,
|
||||
Delay &latency,
|
||||
PathVertex &path,
|
||||
bool &exists) const;
|
||||
void latency(const RiseFall *src_rf,
|
||||
const RiseFall *end_rf,
|
||||
const MinMax *min_max,
|
||||
// Return values.
|
||||
float &delay,
|
||||
Delay &delay,
|
||||
bool &exists) const;
|
||||
static float latency(PathVertex *clk_path,
|
||||
static Delay latency(PathVertex *clk_path,
|
||||
StaState *sta);
|
||||
void setLatency(const RiseFall *src_rf,
|
||||
const RiseFall *end_rf,
|
||||
|
|
@ -59,10 +59,10 @@ private:
|
|||
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];
|
||||
Delay insertion_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count];
|
||||
Delay 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];
|
||||
Delay 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];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -78,19 +78,19 @@ ClkLatency::reportClkLatency(const Clock *clk,
|
|||
for (const RiseFall *src_rf : RiseFall::range()) {
|
||||
for (const RiseFall *end_rf : RiseFall::range()) {
|
||||
PathVertex path_min;
|
||||
float insertion_min;
|
||||
float delay_min;
|
||||
Delay insertion_min;
|
||||
Delay delay_min;
|
||||
float lib_clk_delay_min;
|
||||
float latency_min;
|
||||
Delay 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;
|
||||
Delay insertion_max;
|
||||
Delay delay_max;
|
||||
float lib_clk_delay_max;
|
||||
float latency_max;
|
||||
Delay latency_max;
|
||||
bool exists_max;
|
||||
clk_delays.delay(src_rf, end_rf, MinMax::max(), insertion_max,
|
||||
delay_max, lib_clk_delay_max, latency_max,
|
||||
|
|
@ -102,15 +102,15 @@ ClkLatency::reportClkLatency(const Clock *clk,
|
|||
report_->reportLine(" min max");
|
||||
|
||||
report_->reportLine("%7s %7s source latency",
|
||||
time_unit->asString(insertion_min, digits),
|
||||
time_unit->asString(insertion_max, digits));
|
||||
delayAsString(insertion_min, this, digits),
|
||||
delayAsString(insertion_max, this, digits));
|
||||
report_->reportLine("%7s %7s network latency %s",
|
||||
time_unit->asString(delay_min, digits),
|
||||
delayAsString(delay_min, this, digits),
|
||||
"",
|
||||
sdc_network_->pathName(path_min.pin(this)));
|
||||
report_->reportLine("%7s %7s network latency %s",
|
||||
"",
|
||||
time_unit->asString(delay_max, digits),
|
||||
delayAsString(delay_max, this, digits),
|
||||
sdc_network_->pathName(path_max.pin(this)));
|
||||
if (lib_clk_delay_min != 0.0
|
||||
|| lib_clk_delay_max != 0.0)
|
||||
|
|
@ -119,11 +119,11 @@ ClkLatency::reportClkLatency(const Clock *clk,
|
|||
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;
|
||||
delayAsString(latency_min, this, digits),
|
||||
delayAsString(latency_max, this, digits));
|
||||
Delay skew = latency_max - latency_min;
|
||||
report_->reportLine(" %7s skew",
|
||||
time_unit->asString(skew, digits));
|
||||
delayAsString(skew, this, digits));
|
||||
report_->reportBlankLine();
|
||||
}
|
||||
}
|
||||
|
|
@ -154,11 +154,11 @@ ClkLatency::findClkDelays(ConstClockSeq clks,
|
|||
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;
|
||||
Delay latency = ClkDelays::latency(path, this);
|
||||
Delay clk_latency;
|
||||
bool exists;
|
||||
clk_delays.latency(clk_rf, end_rf, min_max, clk_latency, exists);
|
||||
if (!exists || min_max->compare(latency, clk_latency))
|
||||
if (!exists || delayGreater(latency, clk_latency, min_max, this))
|
||||
clk_delays.setLatency(clk_rf, end_rf, min_max, path, this);
|
||||
}
|
||||
}
|
||||
|
|
@ -189,10 +189,10 @@ ClkDelays::delay(const RiseFall *src_rf,
|
|||
const RiseFall *end_rf,
|
||||
const MinMax *min_max,
|
||||
// Return values.
|
||||
float &insertion,
|
||||
float &delay,
|
||||
Delay &insertion,
|
||||
Delay &delay,
|
||||
float &lib_clk_delay,
|
||||
float &latency,
|
||||
Delay &latency,
|
||||
PathVertex &path,
|
||||
bool &exists) const
|
||||
{
|
||||
|
|
@ -212,7 +212,7 @@ ClkDelays::latency(const RiseFall *src_rf,
|
|||
const RiseFall *end_rf,
|
||||
const MinMax *min_max,
|
||||
// Return values.
|
||||
float &latency,
|
||||
Delay &latency,
|
||||
bool &exists) const
|
||||
{
|
||||
int src_rf_index = src_rf->index();
|
||||
|
|
@ -249,7 +249,7 @@ ClkDelays::setLatency(const RiseFall *src_rf,
|
|||
exists_[src_rf_index][end_rf_index][mm_index] = true;
|
||||
}
|
||||
|
||||
float
|
||||
Delay
|
||||
ClkDelays::latency(PathVertex *clk_path,
|
||||
StaState *sta)
|
||||
{
|
||||
|
|
@ -280,7 +280,8 @@ ClkDelays::insertionDelay(PathVertex *clk_path,
|
|||
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);
|
||||
return delayAsFloat(sta->search()->clockInsertion(clk, src_pin, clk_rf, min_max,
|
||||
min_max, path_ap));
|
||||
}
|
||||
|
||||
float
|
||||
|
|
@ -292,7 +293,7 @@ ClkDelays::clkTreeDelay(PathVertex *clk_path,
|
|||
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);
|
||||
float slew = delayAsFloat(clk_path->slew(sta));
|
||||
return port->clkTreeDelay(slew, rf, min_max);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ ClkSkew::clkTreeDelay(PathVertex &clk_path,
|
|||
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);
|
||||
float slew = delayAsFloat(clk_path.slew(sta));
|
||||
return port->clkTreeDelay(slew, rf, min_max);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -530,11 +530,13 @@ FindRegClkPins::FindRegClkPins(StaState *sta) :
|
|||
bool
|
||||
FindRegClkPins::matchPin(Pin *pin)
|
||||
{
|
||||
// Liberty port clock attribute is not present in latches (for nlc18 anyway).
|
||||
LibertyPort *port = network_->libertyPort(pin);
|
||||
LibertyCell *cell = port->libertyCell();
|
||||
for (TimingArcSet *arc_set : cell->timingArcSets(port, nullptr)) {
|
||||
TimingRole *role = arc_set->role();
|
||||
if (role->isTimingCheck())
|
||||
if (role == TimingRole::regClkToQ()
|
||||
|| role == TimingRole::latchEnToQ())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ MakeTimingModel::saveSdc()
|
|||
Sdc::movePortDelays(sdc_, sdc_backup_);
|
||||
Sdc::movePortExtCaps(sdc_, sdc_backup_);
|
||||
Sdc::moveDeratingFactors(sdc_, sdc_backup_);
|
||||
Sdc::moveClockInsertions(sdc_, sdc_backup_);
|
||||
sta_->delaysInvalid();
|
||||
}
|
||||
|
||||
|
|
@ -123,6 +124,7 @@ MakeTimingModel::restoreSdc()
|
|||
Sdc::movePortDelays(sdc_backup_, sdc_);
|
||||
Sdc::movePortExtCaps(sdc_backup_, sdc_);
|
||||
Sdc::moveDeratingFactors(sdc_backup_, sdc_);
|
||||
Sdc::moveClockInsertions(sdc_backup_, sdc_);
|
||||
delete sdc_backup_;
|
||||
sta_->delaysInvalid();
|
||||
}
|
||||
|
|
@ -568,7 +570,8 @@ MakeTimingModel::makeClkTreePaths(LibertyPort *lib_port,
|
|||
? clk_rf
|
||||
: clk_rf->opposite();
|
||||
PathVertex clk_path;
|
||||
float insertion, delay, lib_clk_delay, latency;
|
||||
Delay insertion, delay, latency;
|
||||
float lib_clk_delay;
|
||||
bool exists;
|
||||
delays.delay(clk_rf, end_rf, min_max, insertion, delay,
|
||||
lib_clk_delay, latency, clk_path, exists);
|
||||
|
|
|
|||
|
|
@ -1069,7 +1069,7 @@ PathEndCheck::sourceClkDelay(const StaState *sta) const
|
|||
// Propagated clock. Propagated arrival is seeded with insertion delay.
|
||||
Arrival clk_arrival = src_clk_path.arrival(sta);
|
||||
const ClockEdge *src_clk_edge = src_clk_info->clkEdge();
|
||||
float insertion = sourceClkInsertionDelay(sta);
|
||||
Delay insertion = sourceClkInsertionDelay(sta);
|
||||
return delayRemove(clk_arrival - src_clk_edge->time(), insertion);
|
||||
}
|
||||
else
|
||||
|
|
@ -1080,7 +1080,7 @@ PathEndCheck::sourceClkDelay(const StaState *sta) const
|
|||
return 0.0;
|
||||
}
|
||||
|
||||
float
|
||||
Delay
|
||||
PathEndCheck::clkSkew(const StaState *sta)
|
||||
{
|
||||
commonClkPessimism(sta);
|
||||
|
|
@ -1858,7 +1858,7 @@ PathEndPathDelay::sourceClkOffset(const StaState *sta) const
|
|||
return pathDelaySrcClkOffset(path_, path_delay_, src_clk_arrival_, sta);
|
||||
}
|
||||
|
||||
float
|
||||
Delay
|
||||
PathEnd::clkSkew(const StaState *)
|
||||
{
|
||||
return 0.0;
|
||||
|
|
|
|||
|
|
@ -142,7 +142,6 @@ Sim::funcBddSim(const FuncExpr *expr,
|
|||
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:
|
||||
bdd = Cudd_bddCompose(cudd_mgr, bdd, Cudd_ReadLogicZero(cudd_mgr), var_index);
|
||||
|
|
|
|||
|
|
@ -5167,7 +5167,9 @@ instMaxSlew(const Instance *inst,
|
|||
Pin *pin = pin_iter->next();
|
||||
if (network->isDriver(pin)) {
|
||||
Vertex *vertex = graph->pinDrvrVertex(pin);
|
||||
max_slew = max(max_slew, sta->vertexSlew(vertex, MinMax::max()));
|
||||
Slew slew = sta->vertexSlew(vertex, MinMax::max());
|
||||
if (delayGreater(slew, max_slew, sta))
|
||||
max_slew = slew;
|
||||
}
|
||||
}
|
||||
delete pin_iter;
|
||||
|
|
|
|||
|
|
@ -965,8 +965,8 @@ proc get_ports { args } {
|
|||
}
|
||||
|
||||
variable filter_regexp1 {@?([a-zA-Z_]+) *(==|!=|=~) *([0-9a-zA-Z_\*]+)}
|
||||
variable filter_or_regexp "($filter_regexp1) +\\|\\| +($filter_regexp1)"
|
||||
variable filter_and_regexp "($filter_regexp1) +&& +($filter_regexp1)"
|
||||
variable filter_or_regexp "($filter_regexp1) *\\|\\| *($filter_regexp1)"
|
||||
variable filter_and_regexp "($filter_regexp1) *&& *($filter_regexp1)"
|
||||
|
||||
proc filter_ports1 { filter objects } {
|
||||
variable filter_regexp1
|
||||
|
|
|
|||
|
|
@ -4940,7 +4940,7 @@ bool path_delay_margin_is_external()
|
|||
Crpr common_clk_pessimism() { return self->commonClkPessimism(Sta::sta()); }
|
||||
RiseFall *target_clk_end_trans()
|
||||
{ return const_cast<RiseFall*>(self->targetClkEndTrans(Sta::sta())); }
|
||||
float clk_skew() { return self->clkSkew(Sta::sta()); }
|
||||
Delay clk_skew() { return self->clkSkew(Sta::sta()); }
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -0,0 +1,17 @@
|
|||
Startpoint: in1 (input port)
|
||||
Endpoint: out1 (output port)
|
||||
Path Group: unconstrained
|
||||
Path Type: max
|
||||
|
||||
Slew Delay Time Description
|
||||
-------------------------------------------------------------------
|
||||
0.000 0.000 v input external delay
|
||||
20.000 0.000 0.000 v in1 (in)
|
||||
20.000 0.000 0.000 v u1/A (INVx8_ASAP7_75t_R)
|
||||
49.139 20.018 20.018 ^ u1/Y (INVx8_ASAP7_75t_R)
|
||||
139.668 44.168 64.186 ^ out1 (out)
|
||||
64.186 data arrival time
|
||||
-------------------------------------------------------------------
|
||||
(Path is unconstrained)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
*SPEF "IEEE 1481-1998"
|
||||
*DESIGN "top"
|
||||
*DATE "Tue Sep 25 11:51:50 2012"
|
||||
*VENDOR "handjob"
|
||||
*PROGRAM "handjob"
|
||||
*VERSION "0.0"
|
||||
*DESIGN_FLOW "NETLIST_TYPE_VERILOG"
|
||||
*DIVIDER /
|
||||
*DELIMITER :
|
||||
*BUS_DELIMITER [ ]
|
||||
*T_UNIT 1 PS
|
||||
*C_UNIT 1 FF
|
||||
*R_UNIT 1 KOHM
|
||||
*L_UNIT 1 UH
|
||||
|
||||
*POWER_NETS VDD
|
||||
*GROUND_NETS VSS
|
||||
|
||||
*PORTS
|
||||
in1 I
|
||||
out1 O
|
||||
|
||||
*D_NET out1 16.6
|
||||
*CONN
|
||||
*I u1:Y O
|
||||
*I u2:Y O
|
||||
*I out1 O
|
||||
*CAP
|
||||
1 u1:Y 10
|
||||
2 u2:Y 10
|
||||
3 out1:1 10
|
||||
4 out1 50
|
||||
*RES
|
||||
1 u1:Y out1:1 0.1
|
||||
2 u2:Y out1:1 0.1
|
||||
3 out1:1 out1 1.0
|
||||
*END
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# ccs_sim parallel inverters
|
||||
read_liberty asap7_invbuf.lib.gz
|
||||
read_verilog ccs_sim1.v
|
||||
link_design top
|
||||
read_spef ccs_sim1.spef
|
||||
set_input_transition 20 in1
|
||||
sta::set_delay_calculator ccs_sim
|
||||
report_checks -fields {input_pins slew} -unconstrained -digits 3
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
module top (in1, out1);
|
||||
input in1;
|
||||
output out1;
|
||||
|
||||
INVx8_ASAP7_75t_R u1 (.A(in1), .Y(out1));
|
||||
INVx8_ASAP7_75t_R u2 (.A(in1), .Y(out1));
|
||||
endmodule // top
|
||||
|
|
@ -122,6 +122,7 @@ record_example_tests {
|
|||
}
|
||||
|
||||
record_sta_tests {
|
||||
ccs_sim1
|
||||
verilog_attribute
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue