diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ab8e49f..961cc8e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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} ) diff --git a/README.md b/README.md index 79405a52..db16c0f0 100644 --- a/README.md +++ b/README.md @@ -85,26 +85,27 @@ The build dependency versions are show below. Other versions may work, but these are the versions used for development. ``` - from Ubuntu Xcode - 22.04.2 11.3 -cmake 3.10.2 3.24.2 3.16.2 -clang 9.1.0 14.0.3 -gcc 3.3.2 11.3.0 + from Ubuntu Macos + 22.04.2 14.4.1 +cmake 3.10.2 3.24.2 3.29.2 +clang 9.1.0 15.0.0 +gcc 3.3.2 11.4.0 tcl 8.4 8.6 8.6.6 -swig 1.3.28 4.1.0 4.0.1 -bison 1.35 3.0.2 3.8.2 +swig 1.3.28 4.1.0 4.2.1 +bison 1.35 3.8.2 3.8.2 flex 2.5.4 2.6.4 2.6.4 ``` Note that flex versions before 2.6.4 contain 'register' declarations that are illegal in c++17. -These packages are **optional**: - +External library dependencies: ``` -tclreadline 2.3.8 -libz 1.1.4 1.2.5 1.2.8 -cudd 2.4.1 3.0.0 + from Ubuntu Macos +eigen 3.4 .0 3.4.0 required +tclreadline 2.3.8 optional +libz 1.1.4 1.2.5 1.2.8 optional +cudd 2.4.1 3.0.0 optional ``` The [TCL readline library](https://tclreadline.sourceforge.net/tclreadline.html) diff --git a/dcalc/ArcDelayCalc.cc b/dcalc/ArcDelayCalc.cc index bb7ea5c1..15f5a581 100644 --- a/dcalc/ArcDelayCalc.cc +++ b/dcalc/ArcDelayCalc.cc @@ -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) { diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index 5cb2cf86..20f8d9e5 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -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)); diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index 7c11eec7..9e12ecf5 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -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(); diff --git a/dcalc/CcsSimDelayCalc.cc b/dcalc/CcsSimDelayCalc.cc new file mode 100644 index 00000000..7c4a6224 --- /dev/null +++ b/dcalc/CcsSimDelayCalc.cc @@ -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 . + +#include "CcsSimDelayCalc.hh" + +#include // 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_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(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(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 &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 &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/CcsSimDelayCalc.hh b/dcalc/CcsSimDelayCalc.hh new file mode 100644 index 00000000..5f000472 --- /dev/null +++ b/dcalc/CcsSimDelayCalc.hh @@ -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 . + +#pragma once + +#include +#include +#include + +#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 PinNodeMap; +typedef Map NodeIndexMap; +typedef Map PortIndexMap; +typedef SparseMatrix 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 &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_; + + // 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 output_waveforms_; + vector ref_time_; + double drive_resistance_; + double resistance_sum_; + + vector node_capacitances_; + bool includes_pin_caps_; + float coupling_cap_multiplier_; + + // Indexed by driver index. + vector ceff_; + vector 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 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 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/dcalc/DelayCalc.cc b/dcalc/DelayCalc.cc index 2fe5dcd5..61b3f007 100644 --- a/dcalc/DelayCalc.cc +++ b/dcalc/DelayCalc.cc @@ -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 diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 9d8f8435..8a96730f 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -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. diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt index 2682fccf..44c18ac5 100644 Binary files a/doc/OpenSTA.odt and b/doc/OpenSTA.odt differ diff --git a/doc/OpenSTA.pdf b/doc/OpenSTA.pdf index d0e5f76e..b09c49c2 100644 Binary files a/doc/OpenSTA.pdf and b/doc/OpenSTA.pdf differ diff --git a/graph/Graph.cc b/graph/Graph.cc index 3ce7aec6..591a2271 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -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(); } diff --git a/include/sta/ArcDelayCalc.hh b/include/sta/ArcDelayCalc.hh index b1dabe55..7336c394 100644 --- a/include/sta/ArcDelayCalc.hh +++ b/include/sta/ArcDelayCalc.hh @@ -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); diff --git a/include/sta/Graph.hh b/include/sta/Graph.hh index 593e0f2f..8bf525bc 100644 --- a/include/sta/Graph.hh +++ b/include/sta/Graph.hh @@ -46,7 +46,6 @@ typedef ArrayTable RequiredsTable; typedef ArrayTable PrevPathsTable; typedef Map PinVertexMap; typedef Iterator VertexEdgeIterator; -typedef Map WidthCheckAnnotations; typedef Map PeriodCheckAnnotations; typedef Vector 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. diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index bf69f979..5e581065 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -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, diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 1d6e16c6..943f43ff 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -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); diff --git a/include/sta/PathEnd.hh b/include/sta/PathEnd.hh index 8b75ccda..ec1cca01 100644 --- a/include/sta/PathEnd.hh +++ b/include/sta/PathEnd.hh @@ -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, diff --git a/include/sta/Sdc.hh b/include/sta/Sdc.hh index a8cb921e..2c730191 100644 --- a/include/sta/Sdc.hh +++ b/include/sta/Sdc.hh @@ -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); diff --git a/include/sta/TimingRole.hh b/include/sta/TimingRole.hh index 51cbf6b7..9dc6951b 100644 --- a/include/sta/TimingRole.hh +++ b/include/sta/TimingRole.hh @@ -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; diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index e486915c..4208f3ee 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -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(); diff --git a/liberty/LibertyBuilder.cc b/liberty/LibertyBuilder.cc index 39a1f1b8..1b353ebf 100644 --- a/liberty/LibertyBuilder.cc +++ b/liberty/LibertyBuilder.cc @@ -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, diff --git a/liberty/LibertyBuilder.hh b/liberty/LibertyBuilder.hh index 820112c0..7aed7212 100644 --- a/liberty/LibertyBuilder.hh +++ b/liberty/LibertyBuilder.hh @@ -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, diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 5d394ea4..ae7cc4b9 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -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(); + 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(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; diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index 6a088619..6ef9365f 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -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); diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc index 2a53babf..7d01985f 100644 --- a/liberty/LibertyWriter.cc +++ b/liberty/LibertyWriter.cc @@ -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(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(), diff --git a/liberty/TimingRole.cc b/liberty/TimingRole.cc index 221f7305..deb4f762 100644 --- a/liberty/TimingRole.cc +++ b/liberty/TimingRole.cc @@ -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) diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index aa728ac5..c78edd4d 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -668,17 +668,9 @@ Sdc::moveDeratingFactors(Sdc *from, from->derating_factors_ = nullptr; } - to->net_derating_factors_.deleteContents(); - to->net_derating_factors_ = from->net_derating_factors_; - from->net_derating_factors_.clear(); - - to->inst_derating_factors_.deleteContents(); - to->inst_derating_factors_ = from->inst_derating_factors_; - from->inst_derating_factors_.clear(); - - to->cell_derating_factors_.deleteContents(); - to->cell_derating_factors_ = from->cell_derating_factors_; - from->cell_derating_factors_.clear(); + to->net_derating_factors_ = std::move(from->net_derating_factors_); + to->inst_derating_factors_ = std::move(from->inst_derating_factors_); + to->cell_derating_factors_ = std::move(from->cell_derating_factors_); } void @@ -1740,6 +1732,13 @@ Sdc::removeClockInsertion(const Clock *clk, deleteClockInsertion(insertion); } +void +Sdc::moveClockInsertions(Sdc *from, + Sdc *to) +{ + to->clk_insertions_ = std::move(from->clk_insertions_); +} + void Sdc::deleteClockInsertion(ClockInsertion *insertion) { @@ -2746,46 +2745,18 @@ void Sdc::movePortDelays(Sdc *from, Sdc *to) { - to->input_delays_.deleteContents(); - to->input_delays_ = from->input_delays_; - from->input_delays_.clear(); - - to->input_delay_pin_map_.deleteContents(); - to->input_delay_pin_map_ = from->input_delay_pin_map_; - from->input_delay_pin_map_.clear(); - - to->input_delay_ref_pin_map_.deleteContents(); - to->input_delay_ref_pin_map_ = from->input_delay_ref_pin_map_; - from->input_delay_ref_pin_map_.clear(); - - to->input_delay_leaf_pin_map_.deleteContents(); - to->input_delay_leaf_pin_map_ = from->input_delay_leaf_pin_map_; - from->input_delay_leaf_pin_map_.clear(); - - to->input_delay_internal_pin_map_.deleteContents(); - to->input_delay_internal_pin_map_ = from->input_delay_internal_pin_map_; - from->input_delay_internal_pin_map_.clear(); - + to->input_delays_ = std::move(from->input_delays_); + to->input_delay_pin_map_ = std::move(from->input_delay_pin_map_); + to->input_delay_ref_pin_map_ = std::move(from->input_delay_ref_pin_map_); + to->input_delay_leaf_pin_map_ = std::move(from->input_delay_leaf_pin_map_); + to->input_delay_internal_pin_map_ = std::move(from->input_delay_internal_pin_map_); to->input_delay_index_ = from->input_delay_index_; from->input_delay_index_ = 0; - //////////////// - - to->output_delays_.deleteContents(); - to->output_delays_ = from->output_delays_; - from->output_delays_.clear(); - - to->output_delay_pin_map_.deleteContents(); - to->output_delay_pin_map_ = from->output_delay_pin_map_; - from->output_delay_pin_map_.clear(); - - to->output_delay_ref_pin_map_.deleteContents(); - to->output_delay_ref_pin_map_ = from->output_delay_ref_pin_map_; - from->output_delay_ref_pin_map_.clear(); - - to->output_delay_leaf_pin_map_.deleteContents(); - to->output_delay_leaf_pin_map_ = from->output_delay_leaf_pin_map_; - from->output_delay_leaf_pin_map_.clear(); + to->output_delays_ = std::move(from->output_delays_); + to->output_delay_pin_map_ = std::move(from->output_delay_pin_map_); + to->output_delay_ref_pin_map_ = std::move(from->output_delay_ref_pin_map_); + to->output_delay_leaf_pin_map_ = std::move(from->output_delay_leaf_pin_map_); } //////////////////////////////////////////////////////////////// @@ -3355,11 +3326,11 @@ Sdc::movePortExtCaps(Sdc *from, Sdc *to) { for (int corner_index = 0; corner_index < from->corners()->count(); corner_index++) { - to->port_ext_cap_maps_[corner_index] = from->port_ext_cap_maps_[corner_index]; - from->port_ext_cap_maps_[corner_index].clear(); + to->port_ext_cap_maps_[corner_index] = + std::move(from->port_ext_cap_maps_[corner_index]); - to->net_wire_cap_maps_[corner_index] = from->net_wire_cap_maps_[corner_index]; - from->net_wire_cap_maps_[corner_index].clear(); + to->net_wire_cap_maps_[corner_index] = + std::move(from->net_wire_cap_maps_[corner_index]); } } @@ -3953,7 +3924,7 @@ Sdc::recordPathDelayInternalEndpoints(ExceptionPath *exception) if (to && to->hasPins()) { for (const Pin *pin : *to->pins()) { - if (!(hasLibertyChecks(pin) + if (!(hasLibertyCheckTo(pin) || network_->isTopLevelPort(pin))) { path_delay_internal_endpoints_.insert(pin); } @@ -3969,7 +3940,7 @@ Sdc::unrecordPathDelayInternalEndpoints(ExceptionPath *exception) && to->hasPins() && !path_delay_internal_endpoints_.empty()) { for (const Pin *pin : *to->pins()) { - if (!(hasLibertyChecks(pin) + if (!(hasLibertyCheckTo(pin) || network_->isTopLevelPort(pin)) && !pathDelayTo(pin)) path_delay_internal_endpoints_.erase(pin); @@ -3978,7 +3949,7 @@ Sdc::unrecordPathDelayInternalEndpoints(ExceptionPath *exception) } bool -Sdc::hasLibertyChecks(const Pin *pin) +Sdc::hasLibertyCheckTo(const Pin *pin) { const Instance *inst = network_->instance(pin); LibertyCell *cell = network_->libertyCell(inst); @@ -3986,7 +3957,7 @@ Sdc::hasLibertyChecks(const Pin *pin) LibertyPort *port = network_->libertyPort(pin); if (port) { for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, port)) { - if (arc_set->role()->isTimingCheck()) + if (arc_set->role()->isTimingCheckBetween()) return true; } } diff --git a/sdf/ReportAnnotation.cc b/sdf/ReportAnnotation.cc index f51858ba..1bfdc077 100644 --- a/sdf/ReportAnnotation.cc +++ b/sdf/ReportAnnotation.cc @@ -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++; - } - } } } diff --git a/sdf/SdfReader.cc b/sdf/SdfReader.cc index 7dff5edd..195f51be 100644 --- a/sdf/SdfReader.cc +++ b/sdf/SdfReader.cc @@ -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); } } } diff --git a/sdf/SdfWriter.cc b/sdf/SdfWriter.cc index 1fa32bab..22d2e24e 100644 --- a/sdf/SdfWriter.cc +++ b/sdf/SdfWriter.cc @@ -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); } diff --git a/search/Bfs.cc b/search/Bfs.cc index 7971b975..bc77bb4e 100644 --- a/search/Bfs.cc +++ b/search/Bfs.cc @@ -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); diff --git a/search/CheckMinPulseWidths.cc b/search/CheckMinPulseWidths.cc index ece0222f..6c1e35a4 100644 --- a/search/CheckMinPulseWidths.cc +++ b/search/CheckMinPulseWidths.cc @@ -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; + } } } diff --git a/search/ClkDelays.hh b/search/ClkDelays.hh index 003b705c..729deb66 100644 --- a/search/ClkDelays.hh +++ b/search/ClkDelays.hh @@ -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]; }; diff --git a/search/ClkLatency.cc b/search/ClkLatency.cc index 8c9c90ce..818251a2 100644 --- a/search/ClkLatency.cc +++ b/search/ClkLatency.cc @@ -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); } diff --git a/search/ClkSkew.cc b/search/ClkSkew.cc index b3fd5160..6a515d78 100644 --- a/search/ClkSkew.cc +++ b/search/ClkSkew.cc @@ -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); } diff --git a/search/FindRegister.cc b/search/FindRegister.cc index 547474a4..90973487 100644 --- a/search/FindRegister.cc +++ b/search/FindRegister.cc @@ -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; diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index bacdaeea..10867949 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -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); diff --git a/search/PathEnd.cc b/search/PathEnd.cc index 02098a63..42b27305 100644 --- a/search/PathEnd.cc +++ b/search/PathEnd.cc @@ -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; diff --git a/search/Sim.cc b/search/Sim.cc index 2e37b334..6c46dabe 100644 --- a/search/Sim.cc +++ b/search/Sim.cc @@ -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); diff --git a/search/Sta.cc b/search/Sta.cc index f0c6500c..01a6766d 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -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; diff --git a/tcl/Sdc.tcl b/tcl/Sdc.tcl index 322d1e62..a916b6a8 100644 --- a/tcl/Sdc.tcl +++ b/tcl/Sdc.tcl @@ -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 diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index 750a4989..1d45e6e1 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -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(self->targetClkEndTrans(Sta::sta())); } -float clk_skew() { return self->clkSkew(Sta::sta()); } +Delay clk_skew() { return self->clkSkew(Sta::sta()); } } diff --git a/test/asap7_invbuf.lib.gz b/test/asap7_invbuf.lib.gz new file mode 100644 index 00000000..91e03475 Binary files /dev/null and b/test/asap7_invbuf.lib.gz differ diff --git a/test/ccs_sim1.ok b/test/ccs_sim1.ok new file mode 100644 index 00000000..01adfab6 --- /dev/null +++ b/test/ccs_sim1.ok @@ -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) + + diff --git a/test/ccs_sim1.spef b/test/ccs_sim1.spef new file mode 100644 index 00000000..f4b4594f --- /dev/null +++ b/test/ccs_sim1.spef @@ -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 diff --git a/test/ccs_sim1.tcl b/test/ccs_sim1.tcl new file mode 100644 index 00000000..b3b14ea4 --- /dev/null +++ b/test/ccs_sim1.tcl @@ -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 diff --git a/test/ccs_sim1.v b/test/ccs_sim1.v new file mode 100644 index 00000000..9fc0ae6b --- /dev/null +++ b/test/ccs_sim1.v @@ -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 diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index 444cab71..a109a348 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -122,6 +122,7 @@ record_example_tests { } record_sta_tests { + ccs_sim1 verilog_attribute }