diff --git a/CMakeLists.txt b/CMakeLists.txt index 961cc8e0..de2a90c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,7 @@ set(STA_SOURCE dcalc/LumpedCapDelayCalc.cc dcalc/NetCaps.cc dcalc/ParallelDelayCalc.cc + dcalc/PrimaDelayCalc.cc dcalc/UnitDelayCalc.cc graph/DelayFloat.cc diff --git a/dcalc/DelayCalc.cc b/dcalc/DelayCalc.cc index 61b3f007..b6ce0b50 100644 --- a/dcalc/DelayCalc.cc +++ b/dcalc/DelayCalc.cc @@ -24,6 +24,7 @@ #include "ArnoldiDelayCalc.hh" #include "CcsCeffDelayCalc.hh" #include "CcsSimDelayCalc.hh" +#include "PrimaDelayCalc.hh" namespace sta { @@ -41,6 +42,7 @@ registerDelayCalcs() registerDelayCalc("arnoldi", makeArnoldiDelayCalc); registerDelayCalc("ccs_ceff", makeCcsCeffDelayCalc); registerDelayCalc("ccs_sim", makeCcsSimDelayCalc); + registerDelayCalc("prima", makePrimaDelayCalc); } void diff --git a/dcalc/DelayCalc.i b/dcalc/DelayCalc.i index d94565d4..efd55ae0 100644 --- a/dcalc/DelayCalc.i +++ b/dcalc/DelayCalc.i @@ -21,6 +21,7 @@ #include "Sta.hh" #include "ArcDelayCalc.hh" #include "dcalc/ArcDcalcWaveforms.hh" +#include "dcalc/PrimaDelayCalc.hh" %} @@ -133,4 +134,16 @@ ccs_load_waveform(const Pin *in_pin, return Table1(); } +void +set_prima_reduce_order(size_t order) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + PrimaDelayCalc *dcalc = dynamic_cast(sta->arcDelayCalc()); + if (dcalc) { + dcalc->setPrimaReduceOrder(order); + sta->delaysInvalid(); + } +} + %} // inline diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc new file mode 100644 index 00000000..b1d580a8 --- /dev/null +++ b/dcalc/PrimaDelayCalc.cc @@ -0,0 +1,1131 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2024, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "PrimaDelayCalc.hh" + +#include // abs + +#include "Debug.hh" +#include "Units.hh" +#include "TimingArc.hh" +#include "Liberty.hh" +#include "PortDirection.hh" +#include "Network.hh" +#include "Sdc.hh" +#include "DcalcAnalysisPt.hh" +#include "Corner.hh" +#include "Graph.hh" +#include "Parasitics.hh" +#include "GraphDelayCalc.hh" +#include "DmpDelayCalc.hh" + +#include +#include + +namespace sta { + +using std::abs; +using std::make_shared; +using Eigen::SparseLU; +using Eigen::HouseholderQR; +using Eigen::ColPivHouseholderQR; + +// Lawrence Pillage - “Electronic Circuit & System Simulation Methods” 1998 +// McGraw-Hill, Inc. New York, NY. + +ArcDelayCalc * +makePrimaDelayCalc(StaState *sta) +{ + return new PrimaDelayCalc(sta); +} + +PrimaDelayCalc::PrimaDelayCalc(StaState *sta) : + DelayCalcBase(sta), + dcalc_args_(nullptr), + load_pin_index_map_(nullptr), + pin_node_map_(network_), + prima_order_(3), + make_waveforms_(false), + waveform_drvr_pin_(nullptr), + waveform_load_pin_(nullptr), + watch_pin_values_(network_), + table_dcalc_(makeDmpCeffElmoreDelayCalc(sta)) +{ +} + +PrimaDelayCalc::PrimaDelayCalc(const PrimaDelayCalc &dcalc) : + DelayCalcBase(dcalc), + dcalc_args_(nullptr), + load_pin_index_map_(nullptr), + pin_node_map_(network_), + prima_order_(dcalc.prima_order_), + make_waveforms_(false), + waveform_drvr_pin_(nullptr), + waveform_load_pin_(nullptr), + watch_pin_values_(network_), + table_dcalc_(makeDmpCeffElmoreDelayCalc(this)) +{ +} + +PrimaDelayCalc::~PrimaDelayCalc() +{ + delete table_dcalc_; +} + +ArcDelayCalc * +PrimaDelayCalc::copy() +{ + return new PrimaDelayCalc(*this); +} + +// Notify algorithm components. +void +PrimaDelayCalc::copyState(const StaState *sta) +{ + StaState::copyState(sta); + table_dcalc_->copyState(sta); +} + +Parasitic * +PrimaDelayCalc::findParasitic(const Pin *drvr_pin, + const RiseFall *rf, + const DcalcAnalysisPt *dcalc_ap) +{ + const Corner *corner = dcalc_ap->corner(); + const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); + // set_load net has precidence over parasitics. + if (sdc_->drvrPinHasWireCap(drvr_pin, corner) + || network_->direction(drvr_pin)->isInternal()) + return nullptr; + Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + if (parasitic) + return parasitic; + const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); + Wireload *wireload = sdc_->wireload(cnst_min_max); + if (wireload) { + float pin_cap, wire_cap, fanout; + bool has_wire_cap; + graph_delay_calc_->netCaps(drvr_pin, rf, dcalc_ap, pin_cap, wire_cap, + fanout, has_wire_cap); + parasitic = parasitics_->makeWireloadNetwork(drvr_pin, wireload, + fanout, cnst_min_max, + parasitic_ap); + } + return parasitic; +} + +Parasitic * +PrimaDelayCalc::reduceParasitic(const Parasitic *, + const Pin *, + const RiseFall *, + const DcalcAnalysisPt *) +{ + return nullptr; +} + +ArcDcalcResult +PrimaDelayCalc::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(); + pi_elmore = parasitics_->reduceToPiElmore(parasitic, drvr_pin, rf, + dcalc_ap->corner(), + dcalc_ap->constraintMinMax(), 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 +PrimaDelayCalc::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, parasitic); + ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_cap, + load_pin_index_map, dcalc_ap); + return dcalc_results[0]; +} + +ArcDcalcResultSeq +PrimaDelayCalc::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(); + parasitic_network_ = dcalc_args[0].parasitic(); + + bool failed = false; + output_waveforms_.resize(drvr_count_); + for (size_t drvr_idx = 0; drvr_idx < drvr_count_; 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(); + Slew in_slew = 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; + debugPrint(debug_, "ccs_dcalc", 1, "%s %s", + dcalc_arg.drvrCell()->name(), + drvr_rf_->asString()); + LibertyCell *drvr_cell = dcalc_arg.drvrCell(); + 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(); + 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 + failed = true; + } + else + failed = true; + } + + if (failed) + return tableDcalcResults(load_cap); + else { + simulate(); + return dcalcResults(); + } +} + +ArcDcalcResultSeq +PrimaDelayCalc::tableDcalcResults(float load_cap) +{ + for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) { + ArcDcalcArg &dcalc_arg = (*dcalc_args_)[drvr_idx]; + const Pin *drvr_pin = dcalc_arg.drvrPin(); + const RiseFall *rf = dcalc_arg.drvrEdge(); + const Parasitic *parasitic = table_dcalc_->findParasitic(drvr_pin, rf, dcalc_ap_); + dcalc_arg.setParasitic(parasitic); + } + return table_dcalc_->gateDelays(*dcalc_args_, load_cap, *load_pin_index_map_, + dcalc_ap_); +} + +void +PrimaDelayCalc::simulate() +{ + initSim(); + stampEqns(); + setXinit(); + + if (prima_order_ > 0 + && node_count_ > prima_order_) { + primaReduce(); + simulate1(Gq_, Cq_, Bq_, xq_init_, Vq_, prima_order_); + } + else { + MatrixXd x_to_v = MatrixXd::Identity(order_, order_); + simulate1(G_, C_, B_, x_init_, x_to_v, order_); + } +} + +void +PrimaDelayCalc::simulate1(const MatrixSd &G, + const MatrixSd &C, + const MatrixXd &B, + const VectorXd &x_init, + const MatrixXd &x_to_v, + const size_t order) +{ + VectorXd x(order); + VectorXd x_prev(order); + VectorXd x_prev2(order); + + v_.resize(order); + v_prev_.resize(order); + + initCeffIdrvr(); + x = x_prev = x_prev2 = x_init; + v_ = v_prev_ = x_to_v * x_init; + + time_step_ = time_step_prev_ = timeStep(); + debugPrint(debug_, "ccs_dcalc", 1, "time step %s", delayAsString(time_step_, this)); + + MatrixSd A(order, order); + A = G + (2.0 / time_step_) * C; + A.makeCompressed(); + SparseLU A_solver; + A_solver.compute(A); + + // Initial time depends on ceff which impact delay, so use a sim step + // to find an initial ceff. + setPortCurrents(); + VectorXd rhs(order); + rhs = B * u_ + (1.0 / time_step_) * C * (3.0 * x_prev - x_prev2); + x = A_solver.solve(rhs); + v_ = x_to_v * x; + + updateCeffIdrvr(); + x = x_prev = x_prev2 = x_init; + v_ = v_prev_ = x_to_v * x_init; + + // voltageTime is always for a rising waveform so 0.0v is initial voltage. + double time_begin = output_waveforms_[0]->voltageTime((*dcalc_args_)[0].inSlew(), + 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_) { + setPortCurrents(); + rhs = B * u_ + (1.0 / time_step_) * C * (3.0 * x_prev - x_prev2); + x = A_solver.solve(rhs); + v_ = x_to_v * x; + + const ArcDcalcArg &dcalc_arg = (*dcalc_args_)[0]; + debugPrint(debug_, "ccs_dcalc", 3, "%s ceff %s VDrvr %.4f Idrvr %s", + delayAsString(time, this), + units_->capacitanceUnit()->asString(ceff_[0]), + voltage(dcalc_arg.drvrPin()), + units_->currentUnit()->asString(drvr_current_[0], 4)); + + updateCeffIdrvr(); + + measureThresholds(time); + if (make_waveforms_) + recordWaveformStep(time); + + if (loadWaveformsFinished()) + break; + + time_step_prev_ = time_step_; + x_prev2.swap(x_prev); + x_prev.swap(x); + v_prev_.swap(v_); + } +} + +double +PrimaDelayCalc::timeStep() +{ + // Needs to use LTE for time step dynamic control. + return driverResistance() * load_cap_ * .02; +} + +double +PrimaDelayCalc::maxTime() +{ + return (*dcalc_args_)[0].inSlew() + + (driverResistance() + resistance_sum_) * load_cap_ * 4; +} + +float +PrimaDelayCalc::driverResistance() +{ + const Pin *drvr_pin = (*dcalc_args_)[0].drvrPin(); + LibertyPort *drvr_port = network_->libertyPort(drvr_pin); + const MinMax *min_max = dcalc_ap_->delayMinMax(); + return drvr_port->driveResistance(drvr_rf_, min_max); +} + +void +PrimaDelayCalc::initSim() +{ + ceff_.resize(drvr_count_); + drvr_current_.resize(drvr_count_); + + findNodeCount(); + setOrder(); + + // Reset waveform recording. + times_.clear(); + drvr_voltages_.clear(); + load_voltages_.clear(); + + measure_thresholds_ = {vl_, vth_, vh_}; +} + +void +PrimaDelayCalc::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 (node1 + && !parasitics_->isExternal(node1)) { + size_t node_idx = node_index_map_[node1]; + node_capacitances_[node_idx] += cap; + } + ParasiticNode *node2 = parasitics_->node2(capacitor); + if (node2 + && !parasitics_->isExternal(node2)) { + size_t node_idx = node_index_map_[node2]; + node_capacitances_[node_idx] += cap; + } + } + node_count_ = node_index_map_.size(); +} + +float +PrimaDelayCalc::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 +PrimaDelayCalc::setOrder() +{ + port_count_ = drvr_count_; + order_ = node_count_ + port_count_; + + // Matrix resize also zeros. + G_.resize(order_, order_); + C_.resize(order_, order_); + B_.resize(order_, port_count_); + u_.resize(port_count_); + threshold_times_.resize(node_count_); +} + +void +PrimaDelayCalc::initCeffIdrvr() +{ + for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) { + const ArcDcalcArg &dcalc_arg = (*dcalc_args_)[drvr_idx]; + 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(dcalc_arg.inSlew(), + ceff_[drvr_idx], 0.0); + } +} + +void +PrimaDelayCalc::setXinit() +{ + x_init_.resize(order_); + double drvr_init_volt = (drvr_rf_ == RiseFall::rise()) ? 0.0 : vdd_; + // Init node voltages. + for (size_t n = 0; n < node_count_ + port_count_; n++) + x_init_[n] = drvr_init_volt; + // Init port voltages. + for (size_t p = 0; p < port_count_; p++) + x_init_[node_count_ + p] = drvr_init_volt; +} + +void +PrimaDelayCalc::stampEqns() +{ + G_.setZero(); + C_.setZero(); + B_.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; + } + } + + for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) { + const ArcDcalcArg &dcalc_arg = (*dcalc_args_)[drvr_idx]; + size_t drvr_node = pin_node_map_[dcalc_arg.drvrPin()]; + G_.coeffRef(node_count_ + drvr_idx, drvr_node) = 1.0; + G_.coeffRef(node_count_ + drvr_idx, node_count_ + drvr_idx) = -1.0; + // special sauce + G_.coeffRef(drvr_node, drvr_node) += 1e-6; + B_.coeffRef(drvr_node, drvr_idx) = 1.0; + } + + if (debug_->check("ccs_dcalc", 3)) { + reportMatrix("G", G_); + reportMatrix("C", C_); + reportMatrix("B", B_); + } +} + +// Grounded resistor. +void +PrimaDelayCalc::stampConductance(size_t n1, + double g) +{ + G_.coeffRef(n1, n1) += g; +} + +// Floating resistor. +void +PrimaDelayCalc::stampConductance(size_t n1, + size_t n2, + double g) +{ + G_.coeffRef(n1, n1) += g; + G_.coeffRef(n2, n2) += g; + G_.coeffRef(n1, n2) -= g; + G_.coeffRef(n2, n1) -= g; +} + +// Grounded capacitance. +void +PrimaDelayCalc::stampCapacitance(size_t n1, + double cap) +{ + C_.coeffRef(n1, n1) += cap; +} + +// Floating capacitance. +void +PrimaDelayCalc::stampCapacitance(size_t n1, + size_t n2, + double cap) +{ + C_.coeffRef(n1, n1) += cap; + C_.coeffRef(n2, n2) += cap; + C_.coeffRef(n1, n2) -= cap; + C_.coeffRef(n2, n1) -= cap; +} + +//////////////////////////////////////////////////////////////// + +void +PrimaDelayCalc::setPortCurrents() +{ + for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) + u_[drvr_idx] = drvr_current_[drvr_idx]; +} + +void +PrimaDelayCalc::updateCeffIdrvr() +{ + for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) { + const ArcDcalcArg &dcalc_arg = (*dcalc_args_)[drvr_idx]; + const Pin *drvr_pin = dcalc_arg.drvrPin(); + size_t node_idx = pin_node_map_[drvr_pin]; + double drvr_current = drvr_current_[drvr_idx]; + double v1 = voltage(node_idx); + double v2 = voltagePrev(node_idx); + double dv = v1 - v2; + if (drvr_rf_ == RiseFall::rise()) { + if (drvr_current != 0.0 + && dv > 0.0) { + double ceff = drvr_current * time_step_ / dv; + if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff)) + ceff_[drvr_idx] = ceff; + } + if (v1 > (vdd_ - .01)) + // Whoa partner. Head'n for the weeds. + drvr_current_[drvr_idx] = 0.0; + else + drvr_current_[drvr_idx] = + output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlew(), + ceff_[drvr_idx], v1); + } + else { + if (drvr_current != 0.0 + && dv < 0.0) { + double ceff = drvr_current * time_step_ / dv; + if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff)) + ceff_[drvr_idx] = ceff; + } + if (v1 < 0.01) { + // Whoa partner. Head'n for the weeds. + drvr_current_[drvr_idx] = 0.0; + } + else + drvr_current_[drvr_idx] = + output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlew(), + ceff_[drvr_idx], + vdd_ - v1); + } + } +} + +bool +PrimaDelayCalc::loadWaveformsFinished() +{ + for (auto pin_node : pin_node_map_) { + size_t node_idx = pin_node.second; + double v = voltage(node_idx); + if ((drvr_rf_ == RiseFall::rise() + && v < vh_ + (vdd_ - vh_) * .5) + || (drvr_rf_ == RiseFall::fall() + && (v > vl_ * .5))) { + return false; + } + } + return true; +} + +//////////////////////////////////////////////////////////////// + +void +PrimaDelayCalc::measureThresholds(double time) +{ + for (auto pin_node1 : pin_node_map_) { + size_t node_idx = pin_node1.second; + double v = voltage(node_idx); + double v_prev = voltagePrev(node_idx); + 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", + node_idx, + th, + delayAsString(t_cross, this)); + threshold_times_[node_idx][m] = t_cross; + } + } + } +} + +double +PrimaDelayCalc::voltage(const Pin *pin) +{ + size_t node_idx = pin_node_map_[pin]; + return v_[node_idx]; +} + +double +PrimaDelayCalc::voltage(size_t node_idx) +{ + return v_[node_idx]; +} + +double +PrimaDelayCalc::voltagePrev(size_t node_idx) +{ + return v_prev_[node_idx]; +} + +ArcDcalcResultSeq +PrimaDelayCalc::dcalcResults() +{ + ArcDcalcResultSeq dcalc_results(drvr_count_); + for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) { + ArcDcalcArg &dcalc_arg = (*dcalc_args_)[drvr_idx]; + ArcDcalcResult &dcalc_result = dcalc_results[drvr_idx]; + const Pin *drvr_pin = dcalc_arg.drvrPin(); + const LibertyLibrary *drvr_library = dcalc_arg.drvrLibrary(); + size_t drvr_node = pin_node_map_[drvr_pin]; + ThresholdTimes &drvr_times = threshold_times_[drvr_node]; + float ref_time = output_waveforms_[drvr_idx]->referenceTime(dcalc_arg.inSlew()); + ArcDelay gate_delay = drvr_times[threshold_vth] - ref_time; + 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 +PrimaDelayCalc::setPrimaReduceOrder(size_t order) +{ + prima_order_ = order; +} + +// This version fills in one column of the orthonomal matrix +// at a time as in the Gram-Schmidt wikipedia algorithm. +void +PrimaDelayCalc::primaReduce() +{ + G_.makeCompressed(); + // Step 3: solve G*R = B for R + SparseLU G_solver(G_); + if (G_solver.info() != Eigen::Success) + report_->error(1752, "G matrix is singular."); + MatrixXd R(order_, port_count_); + R = G_solver.solve(B_); + + // Step 4 + HouseholderQR R_solver(R); + MatrixXd Q = R_solver.householderQ(); + + // Vq is "X" in the prima paper (too many "x" variables in the paper). + Vq_.resize(order_, prima_order_); + // Vq = first port_count columns of Q. + Vq_.block(0, 0, order_, port_count_) = Q.block(0, 0, order_, port_count_); + + // Step 6 - Arnolid iteration + for (size_t k = 1; k < prima_order_; k++) { + VectorXd V = C_ * Vq_.col(k - 1); + Vq_.col(k) = G_solver.solve(V); + + // Modified Gram-Schmidt orthonormalization + for (size_t j = 0; j < k; j++) { + double H = Vq_.col(j).transpose() * Vq_.col(k); + Vq_.col(k) = Vq_.col(k) - H * Vq_.col(j); + } + VectorXd Vq_k = Vq_.col(k); + HouseholderQR Vq_k_solver(Vq_k); + MatrixXd VqQ = Vq_k_solver.householderQ(); + Vq_.col(k) = VqQ.col(0); + } + + // Step 8 - Matrix projection + MatrixSd Vqs = Vq_.sparseView(); + Cq_ = Vqs.transpose() * C_ * Vqs; + Gq_ = Vqs.transpose() * G_ * Vqs; + Bq_ = Vqs.transpose() * B_; + + // x = Vq * x~ + // solve x_init = Vq * x~_init for x~_init + xq_init_ = Vq_.colPivHouseholderQr().solve(x_init_); + + if (debug_->check("ccs_dcalc", 3)) { + reportMatrix("Vq", Vq_); + reportMatrix("G~", Gq_); + reportMatrix("C~", Cq_); + reportMatrix("B~", Bq_); + } +} + +// This version fills in port_count columns of the orthonomal matrix +// at a time as shown in the prima algorithm figure 4. +void +PrimaDelayCalc::primaReduce2() +{ + G_.makeCompressed(); + // Step 3: solve G*R = B for R + SparseLU G_solver(G_); + MatrixXd R(order_, port_count_); + R = G_solver.solve(B_); + + // Step 4 + HouseholderQR R_solver(R); + MatrixXd Q = R_solver.householderQ(); + + // Vq is "X" in the prima paper (too many "x" variables in the paper). + size_t n = ceil(prima_order_ / static_cast(port_count_)); + MatrixXd Vq(order_, n * port_count_); + // // Vq = first port_count columns of Q. + Vq.block(0, 0, order_, port_count_) = Q.block(0, 0, order_, port_count_); + + // Step 6 - Arnolid iteration + for (size_t k = 1; k < n; k++) { + MatrixXd V = C_ * Vq.block(0, (k - 1) * port_count_, order_, port_count_); + MatrixXd GV = G_solver.solve(V); + Vq.block(0, k * port_count_, order_, port_count_) = GV; + + // Modified Gram-Schmidt orthonormalization + for (size_t j = 0; j < k; j++) { + MatrixXd H = Vq.block(0, j * port_count_, order_, port_count_).transpose() + * Vq.block(0, k * port_count_, order_, port_count_); + Vq.block(0, k * port_count_, order_, port_count_) = + Vq.block(0, k * port_count_, order_, port_count_) - Vq.block(0, j * port_count_, order_, port_count_) * H; + } + MatrixXd Vq_k = Vq.block(0, k * port_count_, order_, port_count_); + HouseholderQR Vq_k_solver(Vq_k); + MatrixXd VqQ = Vq_k_solver.householderQ(); + Vq.block(0, k * port_count_, order_, port_count_) = + VqQ.block(0, 0, order_, port_count_); + } + Vq_.resize(order_, prima_order_); + Vq_ = Vq.block(0, 0, order_, prima_order_); + + // Step 8 - Matrix projection + MatrixSd Vqs = Vq_.sparseView(); + Cq_ = Vqs.transpose() * C_ * Vqs; + Gq_ = Vqs.transpose() * G_ * Vqs; + Bq_ = Vqs.transpose() * B_; + + // x = Vq * x~ + // solve x_init = Vq * x~_init for x~_init + xq_init_ = Vq_.colPivHouseholderQr().solve(x_init_); + + if (debug_->check("ccs_dcalc", 3)) { + reportMatrix("Vq", Vq_); + reportMatrix("G~", Gq_); + reportMatrix("C~", Cq_); + reportMatrix("B~", Bq_); + } +} + +//////////////////////////////////////////////////////////////// + +void +PrimaDelayCalc::recordWaveformStep(double time) +{ + times_.push_back(time); + if (waveform_drvr_pin_) { + double drvr_v = voltage(waveform_drvr_pin_); + drvr_voltages_.push_back(drvr_v); + } + if (waveform_load_pin_) { + double load_v = voltage(waveform_load_pin_); + load_voltages_.push_back(load_v); + } + for (auto &pin_wave : watch_pin_values_) { + const Pin *pin = pin_wave.first; + FloatSeq &waveform = pin_wave.second; + double pin_v = voltage(pin); + waveform.push_back(pin_v); + } +} + +//////////////////////////////////////////////////////////////// + +string +PrimaDelayCalc::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 ""; +} + +//////////////////////////////////////////////////////////////// + +void +PrimaDelayCalc::watchPin(const Pin *pin) +{ + watch_pin_values_[pin] = FloatSeq(); + make_waveforms_ = true; +} + +void +PrimaDelayCalc::clearWatchPins() +{ + watch_pin_values_.clear(); + make_waveforms_ = false; +} + +PinSeq +PrimaDelayCalc::watchPins() const +{ + PinSeq pins; + for (auto pin_values : watch_pin_values_) { + const Pin *pin = pin_values.first; + pins.push_back(pin); + } + return pins; +} + +Waveform +PrimaDelayCalc::watchWaveform(const Pin *pin) +{ + FloatSeq &voltages = watch_pin_values_[pin]; + TableAxisPtr time_axis = make_shared(TableAxisVariable::time, + new FloatSeq(times_)); + Table1 waveform(new FloatSeq(voltages), time_axis); + return waveform; +} + +//////////////////////////////////////////////////////////////// + +// Waveform accessors for swig/tcl. +Table1 +PrimaDelayCalc::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(TableAxisVariable::time, + new FloatSeq(times_)); + Table1 waveform(new FloatSeq(drvr_voltages_), time_axis); + return waveform; +} + +Table1 +PrimaDelayCalc::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(TableAxisVariable::time, + new FloatSeq(times_)); + Table1 waveform(new FloatSeq(load_voltages_), time_axis); + return waveform; +} + +Table1 +PrimaDelayCalc::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); + Slew in_slew = 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(1751, "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 +PrimaDelayCalc::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 +PrimaDelayCalc::reportMatrix(const char *name, + MatrixSd &matrix) +{ + report_->reportLine("%s", name); + reportMatrix(matrix); +} + +void +PrimaDelayCalc::reportMatrix(const char *name, + MatrixXd &matrix) +{ + report_->reportLine("%s", name); + reportMatrix(matrix); +} + +void +PrimaDelayCalc::reportMatrix(const char *name, + VectorXd &matrix) +{ + report_->reportLine("%s", name); + reportMatrix(matrix); +} + +void +PrimaDelayCalc::reportVector(const char *name, + vector &matrix) +{ + report_->reportLine("%s", name); + reportVector(matrix); +} + +void +PrimaDelayCalc::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 +PrimaDelayCalc::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 +PrimaDelayCalc::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 +PrimaDelayCalc::reportVector(vector &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 diff --git a/dcalc/PrimaDelayCalc.hh b/dcalc/PrimaDelayCalc.hh new file mode 100644 index 00000000..3cf2a779 --- /dev/null +++ b/dcalc/PrimaDelayCalc.hh @@ -0,0 +1,263 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2024, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include +#include +#include +#include + +#include "Map.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 std::map; + +typedef Map PinNodeMap; +typedef Map NodeIndexMap; +typedef Map PortIndexMap; +typedef SparseMatrix MatrixSd; +typedef Map PinLMap; +typedef map WatchPinValuesMap; + +typedef Table1 Waveform; + +ArcDelayCalc * +makePrimaDelayCalc(StaState *sta); + +class PrimaDelayCalc : public DelayCalcBase, + public ArcDcalcWaveforms +{ +public: + PrimaDelayCalc(StaState *sta); + PrimaDelayCalc(const PrimaDelayCalc &dcalc); + ~PrimaDelayCalc(); + ArcDelayCalc *copy() override; + void copyState(const StaState *sta) override; + void setPrimaReduceOrder(size_t order); + 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; + + // Record waveform for drvr/load pin. + void watchPin(const Pin *pin); + void clearWatchPins(); + PinSeq watchPins() const; + Waveform watchWaveform(const Pin *pin); + + Waveform inputWaveform(const Pin *in_pin, + const RiseFall *in_rf, + const Corner *corner, + const MinMax *min_max) override; + Waveform 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; + Waveform 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: + ArcDcalcResultSeq tableDcalcResults(float load_cap); + void simulate(); + void simulate1(const MatrixSd &G, + const MatrixSd &C, + const MatrixXd &B, + const VectorXd &x_init, + const MatrixXd &x_to_v, + const size_t order); + double maxTime(); + double timeStep(); + float driverResistance(); + void updateCeffIdrvr(); + void initSim(); + void findLoads(); + void findNodeCount(); + void setOrder(); + void initCeffIdrvr(); + void setXinit(); + void stampEqns(); + 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); + void setPortCurrents(); + void measureThresholds(double time); + double voltage(const Pin *pin); + double voltage(size_t node_idx); + double voltagePrev(size_t node_idx); + bool loadWaveformsFinished(); + ArcDcalcResultSeq dcalcResults(); + + 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 primaReduce(); + void primaReduce2(); + + 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 &matrix); + void reportMatrix(MatrixSd &matrix); + void reportMatrix(MatrixXd &matrix); + void reportMatrix(VectorXd &matrix); + void reportVector(vector &matrix); + + ArcDcalcArgSeq *dcalc_args_; + size_t drvr_count_; + float load_cap_; + const DcalcAnalysisPt *dcalc_ap_; + const Parasitic *parasitic_network_; + const RiseFall *drvr_rf_; + const LoadPinIndexMap *load_pin_index_map_; + + PinNodeMap pin_node_map_; // Parasitic pin -> array index + NodeIndexMap node_index_map_; // Parasitic node -> array index + vector output_waveforms_; + double resistance_sum_; + + vector node_capacitances_; + bool includes_pin_caps_; + float coupling_cap_multiplier_; + + size_t node_count_; // Parasitic network node count + size_t port_count_; // aka drvr_count_ + size_t order_; // node_count_ + port_count_ + + // MNA node eqns + // G*x(t) + C*x'(t) = B*u(t) + MatrixSd G_; + MatrixSd C_; + MatrixXd B_; + VectorXd x_init_; + VectorXd u_; + + // Prima reduced MNA eqns + size_t prima_order_; + MatrixXd Vq_; + MatrixSd Gq_; + MatrixSd Cq_; + MatrixXd Bq_; + VectorXd xq_init_; + + // Node voltages. + VectorXd v_; // voltage[node_idx] + VectorXd v_prev_; + + // Indexed by driver index. + vector ceff_; + vector drvr_current_; + + double time_step_; + double time_step_prev_; + + // Waveform recording. + bool make_waveforms_; + const Pin *waveform_drvr_pin_; + const Pin *waveform_load_pin_; + FloatSeq drvr_voltages_; + FloatSeq load_voltages_; + WatchPinValuesMap watch_pin_values_; + FloatSeq times_; + + 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 ThresholdTimes; + // Vl Vth Vh + ThresholdTimes measure_thresholds_; + // Indexed by node number. + vector threshold_times_; + + // Delay calculator to use when ccs waveforms are missing from liberty. + ArcDelayCalc *table_dcalc_; + + using ArcDelayCalc::reduceParasitic; +}; + +} // namespacet diff --git a/include/sta/Parasitics.hh b/include/sta/Parasitics.hh index 23e589a7..52c6caa3 100644 --- a/include/sta/Parasitics.hh +++ b/include/sta/Parasitics.hh @@ -269,6 +269,7 @@ public: protected: void makeWireloadNetworkWorst(Parasitic *parasitic, const Pin *drvr_pin, + const Net *net, float wireload_cap, float wireload_res, float fanout); diff --git a/parasitics/Parasitics.cc b/parasitics/Parasitics.cc index 5d8a10c5..0ab184bb 100644 --- a/parasitics/Parasitics.cc +++ b/parasitics/Parasitics.cc @@ -203,7 +203,7 @@ Parasitics::makeWireloadNetwork(const Pin *drvr_pin, const MinMax *min_max, const ParasiticAnalysisPt *ap) { - Net *net = network_->net(drvr_pin); + const Net *net = findParasiticNet(drvr_pin); Parasitic *parasitic = makeParasiticNetwork(net, false, ap); const OperatingConditions *op_cond = sdc_->operatingConditions(min_max); float wireload_cap, wireload_res; @@ -214,7 +214,7 @@ Parasitics::makeWireloadNetwork(const Pin *drvr_pin, tree = op_cond->wireloadTree(); switch (tree) { case WireloadTree::worst_case: - makeWireloadNetworkWorst(parasitic, drvr_pin, wireload_cap, + makeWireloadNetworkWorst(parasitic, drvr_pin, net, wireload_cap, wireload_res, fanout); break; case WireloadTree::balanced: @@ -235,12 +235,12 @@ Parasitics::makeWireloadNetwork(const Pin *drvr_pin, void Parasitics::makeWireloadNetworkWorst(Parasitic *parasitic, const Pin *drvr_pin, + const Net *net, float wireload_cap, float wireload_res, float /* fanout */) { ParasiticNode *drvr_node = ensureParasiticNode(parasitic, drvr_pin, network_); - Net *net = network_->net(drvr_pin); size_t resistor_index = 1; ParasiticNode *load_node = ensureParasiticNode(parasitic, net, 0, network_); makeResistor(parasitic, resistor_index++, wireload_res, drvr_node, load_node); diff --git a/test/asap7_seq.lib.gz b/test/asap7_seq.lib.gz new file mode 100644 index 00000000..0f03f277 Binary files /dev/null and b/test/asap7_seq.lib.gz differ diff --git a/test/asap7_simple.lib.gz b/test/asap7_simple.lib.gz new file mode 100644 index 00000000..b401a565 Binary files /dev/null and b/test/asap7_simple.lib.gz differ diff --git a/test/prima3.ok b/test/prima3.ok new file mode 100644 index 00000000..cb6d66b9 --- /dev/null +++ b/test/prima3.ok @@ -0,0 +1,51 @@ +Warning: asap7_simple.lib.gz line 71510, when attribute inside table model. +Warning: asap7_simple.lib.gz line 71986, when attribute inside table model. +Warning: asap7_simple.lib.gz line 72462, when attribute inside table model. +Warning: asap7_simple.lib.gz line 72938, when attribute inside table model. +Warning: asap7_simple.lib.gz line 73414, when attribute inside table model. +Warning: asap7_simple.lib.gz line 74830, when attribute inside table model. +Warning: asap7_simple.lib.gz line 71029, timing group from output port. +Warning: asap7_simple.lib.gz line 71505, timing group from output port. +Warning: asap7_simple.lib.gz line 71981, timing group from output port. +Warning: asap7_simple.lib.gz line 72457, timing group from output port. +Warning: asap7_simple.lib.gz line 72933, timing group from output port. +Warning: asap7_simple.lib.gz line 73409, timing group from output port. +Warning: asap7_simple.lib.gz line 73885, timing group from output port. +Warning: asap7_simple.lib.gz line 82276, when attribute inside table model. +Warning: asap7_simple.lib.gz line 83692, when attribute inside table model. +Warning: asap7_simple.lib.gz line 81795, timing group from output port. +Warning: asap7_simple.lib.gz line 82271, timing group from output port. +Warning: asap7_simple.lib.gz line 82747, timing group from output port. +Startpoint: r2 (rising edge-triggered flip-flop clocked by clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by clk) +Path Group: clk +Path Type: max + + Slew Delay Time Description +---------------------------------------------------------------- + 0.00 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock source latency + 10.00 0.00 0.00 ^ clk2 (in) + 48.15 12.04 12.04 ^ r2/CLK (DFFHQx4_ASAP7_75t_R) + 38.97 90.82 102.86 ^ r2/Q (DFFHQx4_ASAP7_75t_R) + 59.28 16.50 119.36 ^ u1/A (BUFx2_ASAP7_75t_R) + 70.25 51.69 171.05 ^ u1/Y (BUFx2_ASAP7_75t_R) + 83.74 18.32 189.37 ^ u2/B (AND2x2_ASAP7_75t_R) + 72.19 60.76 250.13 ^ u2/Y (AND2x2_ASAP7_75t_R) + 85.61 18.34 268.46 ^ r3/D (DFFHQx4_ASAP7_75t_R) + 268.46 data arrival time + + 0.00 500.00 500.00 clock clk (rise edge) + 0.00 500.00 clock source latency + 10.00 0.00 500.00 ^ clk3 (in) + 47.52 11.84 511.84 ^ r3/CLK (DFFHQx4_ASAP7_75t_R) + 0.00 511.84 clock reconvergence pessimism + -14.89 496.95 library setup time + 496.95 data required time +---------------------------------------------------------------- + 496.95 data required time + -268.46 data arrival time +---------------------------------------------------------------- + 228.48 slack (MET) + + diff --git a/test/prima3.tcl b/test/prima3.tcl new file mode 100644 index 00000000..c04dc722 --- /dev/null +++ b/test/prima3.tcl @@ -0,0 +1,13 @@ +# prima reg1 asap7 +read_liberty asap7_invbuf.lib.gz +read_liberty asap7_seq.lib.gz +read_liberty asap7_simple.lib.gz +read_verilog reg1_asap7.v +link_design top +create_clock -name clk -period 500 {clk1 clk2 clk3} +set_input_delay -clock clk 1 {in1 in2} +set_input_transition 10 {in1 in2 clk1 clk2 clk3} +set_propagated_clock {clk1 clk2 clk3} +read_spef reg1_asap7.spef +sta::set_delay_calculator prima +report_checks -fields {input_pins slew} -format full_clock diff --git a/test/reg1_asap7.spef b/test/reg1_asap7.spef new file mode 100644 index 00000000..14bc4d6b --- /dev/null +++ b/test/reg1_asap7.spef @@ -0,0 +1,135 @@ +*SPEF "IEEE 1481-1998" +*DESIGN "reg1" +*DATE "Fri Nov 20 13:23:00 2002" +*VENDOR "Parallax Software, Inc" +*PROGRAM "Handjob" +*VERSION "1.0.1c" +*DESIGN_FLOW "MISSING_NETS" +*DIVIDER / +*DELIMITER : +*BUS_DELIMITER [ ] +*T_UNIT 1.0 PS +*C_UNIT 1.0 FF +*R_UNIT 1.0 KOHM +*L_UNIT 1.0 UH + +*POWER_NETS VDD +*GROUND_NETS VSS + +*PORTS +in1 I +in2 I +clk1 I +clk2 I +clk3 I +out O + +*D_NET in1 13.4 +*CONN +*P in1 I +*I r1:D I *L .0036 +*CAP +1 in1 6.7 +2 r1:D 6.7 +*RES +3 in1 r1:D 2.42 +*END + +*D_NET in2 13.4 +*CONN +*P in2 I +*I r2:D I *L .0036 +*CAP +1 in2 6.7 +2 r2:D 6.7 +*RES +3 in2 r2:D 2.42 +*END + +*D_NET clk1 13.4 +*CONN +*P clk1 I +*I r1:CLK I *L .0036 +*CAP +1 clk1 6.7 +2 r1:CLK 6.7 +*RES +3 clk1 r1:CLK 2.42 +*END + +*D_NET clk2 13.4 +*CONN +*P clk2 I +*I r2:CLK I *L .0036 +*CAP +1 clk2 6.7 +2 r2:CLK 6.7 +*RES +3 clk2 r2:CLK 2.42 +*END + +*D_NET clk3 13.4 +*CONN +*P clk3 I +*I r3:CLK I *L .0036 +*CAP +1 clk3 6.7 +2 r3:CLK 6.7 +*RES +3 clk3 r3:CLK 2.42 +*END + +*D_NET r1q 13.4 +*CONN +*I r1:Q O +*I u2:A I *L .0086 +*CAP +1 r1:Q 6.7 +2 u2:A 6.7 +*RES +3 r1:Q u2:A 2.42 +*END + +*D_NET r2q 13.4 +*CONN +*I r2:Q O +*I u1:A I *L .0086 +*CAP +1 r2:Q 6.7 +2 u1:A 6.7 +*RES +3 r2:Q u1:A 2.42 +*END + +*D_NET u1z 13.4 +*CONN +*I u1:Y O +*I u2:B I *L .0086 +*CAP +1 u1:Y 6.7 +2 u2:B 6.7 +*RES +3 u1:Y u2:B 2.42 +*END + +*D_NET u2z 13.4 +*CONN +*I u2:Y O +*I r3:D I *L .0086 +*CAP +1 u2:Y 6.7 +2 r3:D 6.7 +*RES +3 u2:Y r3:D 2.42 +*END + +*D_NET out 13.4 +*CONN +*I r3:Q O +*P out O +*CAP +1 r3:Q 6.7 +2 out 6.7 +*RES +3 r3:Q out 2.42 +*END diff --git a/test/reg1_asap7.v b/test/reg1_asap7.v new file mode 100644 index 00000000..5eb10b46 --- /dev/null +++ b/test/reg1_asap7.v @@ -0,0 +1,11 @@ +module top (in1, in2, clk1, clk2, clk3, out); + input in1, in2, clk1, clk2, clk3; + output out; + wire r1q, r2q, u1z, u2z; + + DFFHQx4_ASAP7_75t_R r1 (.D(in1), .CLK(clk1), .Q(r1q)); + DFFHQx4_ASAP7_75t_R r2 (.D(in2), .CLK(clk2), .Q(r2q)); + BUFx2_ASAP7_75t_R u1 (.A(r2q), .Y(u1z)); + AND2x2_ASAP7_75t_R u2 (.A(r1q), .B(u1z), .Y(u2z)); + DFFHQx4_ASAP7_75t_R r3 (.D(u2z), .CLK(clk3), .Q(out)); +endmodule // top diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index a109a348..55482d62 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -123,6 +123,7 @@ record_example_tests { record_sta_tests { ccs_sim1 + prima3 verilog_attribute }