Merge remote-tracking branch 'parallax/master'

Get the CCS update

Signed-off-by: Matt Liberty <mliberty@precisioninno.com>
This commit is contained in:
Matt Liberty 2024-04-22 15:49:23 -07:00
commit f8a9e1771b
48 changed files with 1532 additions and 364 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

901
dcalc/CcsSimDelayCalc.cc Normal file
View File

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

238
dcalc/CcsSimDelayCalc.hh Normal file
View File

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

View File

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

View File

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

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

BIN
test/asap7_invbuf.lib.gz Normal file

Binary file not shown.

17
test/ccs_sim1.ok Normal file
View File

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

37
test/ccs_sim1.spef Normal file
View File

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

8
test/ccs_sim1.tcl Normal file
View File

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

7
test/ccs_sim1.v Normal file
View File

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

View File

@ -122,6 +122,7 @@ record_example_tests {
}
record_sta_tests {
ccs_sim1
verilog_attribute
}