rm ccs_sim dcalc

Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
James Cherry 2024-07-01 12:30:35 -07:00
parent 80db28bb8f
commit 93b13c0533
9 changed files with 0 additions and 1124 deletions

View File

@ -69,7 +69,6 @@ set(STA_SOURCE
dcalc/ArnoldiDelayCalc.cc
dcalc/ArnoldiReduce.cc
dcalc/CcsCeffDelayCalc.cc
dcalc/CcsSimDelayCalc.cc
dcalc/DcalcAnalysisPt.cc
dcalc/DelayCalc.cc
dcalc/DelayCalcBase.cc

View File

@ -1,832 +0,0 @@
// 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 "DcalcAnalysisPt.hh"
#include "Network.hh"
#include "Corner.hh"
#include "Graph.hh"
#include "GraphDelayCalc.hh"
#include "DmpDelayCalc.hh"
// Lawrence Pillage - “Electronic Circuit & System Simulation Methods” 1998
// McGraw-Hill, Inc. New York, NY.
namespace sta {
using std::abs;
using std::make_shared;
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_),
node_index_map_(ParasiticNodeLess(parasitics_, network_)),
watch_pin_values_(network_),
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 (const auto [load_pin, load_idx] : load_pin_index_map) {
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)
{
ArcDcalcArgSeq dcalc_args;
dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, load_cap, parasitic);
ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_pin_index_map, dcalc_ap);
return dcalc_results[0];
}
ArcDcalcResultSeq
CcsSimDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
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();
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_);
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 = dcalc_arg.arc()->gateTableModel(dcalc_ap);
if (table_model && dcalc_arg.parasitic()) {
OutputWaveforms *output_waveforms = table_model->outputWaveforms();
float in_slew = dcalc_arg.inSlewFlt();
if (output_waveforms
// Bounds check because extrapolating waveforms does not work for shit.
&& output_waveforms->slewAxis()->inBounds(in_slew)
&& output_waveforms->capAxis()->inBounds(dcalc_arg.loadCap())) {
output_waveforms_[drvr_idx] = output_waveforms;
ref_time_[drvr_idx] = output_waveforms->referenceTime(in_slew);
debugPrint(debug_, "ccs_dcalc", 1, "%s %s",
network_->libertyPort(dcalc_arg.drvrPin())->libertyCell()->name(),
drvr_rf_->asString());
LibertyCell *drvr_cell = dcalc_arg.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());
drvr_cell->ensureVoltageWaveforms(dcalc_ap);
if (drvr_idx == 0) {
vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_;
vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_;
vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_;
}
}
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_pin_index_map, dcalc_ap);
}
else {
simulate(dcalc_args);
ArcDcalcArg &drvr_arg = dcalc_args[0];
const LibertyLibrary *drvr_library = drvr_arg.drvrLibrary();
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 (const auto [load_pin, load_idx] : load_pin_index_map) {
size_t load_node = pin_node_map_[load_pin];
ThresholdTimes &wire_times = threshold_times_[load_node];
ThresholdTimes &drvr_times = threshold_times_[drvr_node];
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] = dcalc_arg.loadCap();
// voltageTime is always for a rising waveform so 0.0v is initial voltage.
drvr_current_[drvr_idx] =
output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlewFlt(),
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();
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);
recordWaveformStep(time);
bool loads_finished = true;
for (const auto [load, load_node] : pin_node_map_) {
if ((drvr_rf_ == RiseFall::rise()
&& voltages_[load_node] < vh_ + (vdd_ - vh_) * .5)
|| (drvr_rf_ == RiseFall::fall()
&& (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_ * (*dcalc_args_)[0].loadCap() * .02;
}
double
CcsSimDelayCalc::maxTime()
{
return (*dcalc_args_)[0].inSlewFlt()
+ (drive_resistance_ + resistance_sum_) * (*dcalc_args_)[0].loadCap() * 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();
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 %zu",
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 (const auto [pin, node] : pin_node_map_)
measureThresholds(time, 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 %zu cross %.2f %s",
n,
th,
delayAsString(t_cross, this));
threshold_times_[n][m] = t_cross;
}
}
}
void
CcsSimDelayCalc::recordWaveformStep(double time)
{
if (!watch_pin_values_.empty()) {
times_.push_back(time);
for (auto& [pin, waveform] : watch_pin_values_) {
size_t node = pin_node_map_[pin];
double pin_v = voltages_[node];
waveform.push_back(pin_v);
}
}
}
////////////////////////////////////////////////////////////////
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)
{
GateTableModel *model = arc->gateTableModel(dcalc_ap);
if (model) {
float in_slew1 = delayAsFloat(in_slew);
return model->reportGateDelay(pinPvt(drvr_pin, dcalc_ap), in_slew1, load_cap,
false, digits);
}
return "";
}
////////////////////////////////////////////////////////////////
void
CcsSimDelayCalc::watchPin(const Pin *pin)
{
watch_pin_values_[pin] = FloatSeq();
}
void
CcsSimDelayCalc::clearWatchPins()
{
watch_pin_values_.clear();
}
PinSeq
CcsSimDelayCalc::watchPins() const
{
PinSeq pins;
for (const auto& [pin, values] : watch_pin_values_)
pins.push_back(pin);
return pins;
}
Waveform
CcsSimDelayCalc::watchWaveform(const Pin *pin)
{
for (ArcDcalcArg &dcalc_arg : *dcalc_args_) {
if (dcalc_arg.inPin() == pin)
return inputWaveform(dcalc_arg, dcalc_ap_, this);
}
FloatSeq &voltages = watch_pin_values_[pin];
TableAxisPtr time_axis = make_shared<TableAxis>(TableAxisVariable::time,
new FloatSeq(times_));
Table1 waveform(new FloatSeq(voltages), time_axis);
return waveform;
}
////////////////////////////////////////////////////////////////
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

View File

@ -1,219 +0,0 @@
// 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 "Parasitics.hh"
#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, ParasiticNodeLess> NodeIndexMap;
typedef Map<const Pin*, size_t> PortIndexMap;
typedef SparseMatrix<double> MatrixSd;
typedef map<const Pin*, FloatSeq, PinIdLess> WatchPinValuesMap;
ArcDelayCalc *
makeCcsSimDelayCalc(StaState *sta);
class CcsSimDelayCalc : public DelayCalcBase,
public ArcDcalcWaveforms
{
public:
CcsSimDelayCalc(StaState *sta);
~CcsSimDelayCalc();
ArcDelayCalc *copy() override;
const char *name() const override { return "ccs_sim"; }
Parasitic *findParasitic(const Pin *drvr_pin,
const RiseFall *rf,
const DcalcAnalysisPt *dcalc_ap) override;
bool reduceSupported() const override { return false; }
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,
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;
// Record waveform for drvr/load pin.
void watchPin(const Pin *pin) override;
void clearWatchPins() override;
PinSeq watchPins() const override;
Waveform watchWaveform(const Pin *pin) override;
protected:
void simulate(ArcDcalcArgSeq &dcalc_args);
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 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_;
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.
WatchPinValuesMap watch_pin_values_;
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,7 +23,6 @@
#include "DmpDelayCalc.hh"
#include "ArnoldiDelayCalc.hh"
#include "CcsCeffDelayCalc.hh"
#include "CcsSimDelayCalc.hh"
#include "PrimaDelayCalc.hh"
namespace sta {
@ -41,7 +40,6 @@ registerDelayCalcs()
registerDelayCalc("dmp_ceff_two_pole", makeDmpCeffTwoPoleDelayCalc);
registerDelayCalc("arnoldi", makeArnoldiDelayCalc);
registerDelayCalc("ccs_ceff", makeCcsCeffDelayCalc);
registerDelayCalc("ccs_sim", makeCcsSimDelayCalc);
registerDelayCalc("prima", makePrimaDelayCalc);
}

View File

@ -1,17 +0,0 @@
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)

View File

@ -1,37 +0,0 @@
*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

View File

@ -1,8 +0,0 @@
# 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

View File

@ -1,7 +0,0 @@
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,7 +122,6 @@ record_example_tests {
}
record_sta_tests {
ccs_sim1
prima3
verilog_attribute
}