diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc index 561b7953..93388a10 100644 --- a/dcalc/PrimaDelayCalc.cc +++ b/dcalc/PrimaDelayCalc.cc @@ -216,43 +216,8 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, parasitics_ = scene->parasitics(min_max); node_index_map_ = NodeIndexMap(ParasiticNodeLess(parasitics_, network_)); - 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 = dcalc_arg.arc()->gateTableModel(scene, min_max); - if (table_model && dcalc_arg.parasitic()) { - OutputWaveforms *output_waveforms = table_model->outputWaveforms(); - float in_slew = dcalc_arg.inSlewFlt(); - if (output_waveforms - // Bounds check because extrapolating waveforms does not work for shit. - && output_waveforms->slewAxis()->inBounds(in_slew) - && output_waveforms->capAxis()->inBounds(dcalc_arg.loadCap())) { - output_waveforms_[drvr_idx] = output_waveforms; - debugPrint(debug_, "ccs_dcalc", 1, "{} {}", dcalc_arg.drvrCell()->name(), - drvr_rf_->shortName()); - 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 {}", - drvr_library->name()); - drvr_cell->ensureVoltageWaveforms(scenes_); - 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) + bool arg_fail = checkArgs(dcalc_args, scene, min_max); + if (arg_fail) return tableDcalcResults(); else { simulate(); @@ -260,6 +225,100 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, } } +// Return true on failure. +// Use falureReason() to get failure string. +bool +PrimaDelayCalc::checkArgs(ArcDcalcArgSeq &dcalc_args, + const Scene *scene, + const MinMax *min_max) +{ + drvr_count_ = dcalc_args.size(); + output_waveforms_.resize(drvr_count_); + failure_reason_ = nullptr; + failure_arg_ = nullptr; + for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) { + ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx]; + GateTableModel *table_model = dcalc_arg.arc()->gateTableModel(scene, min_max); + if (table_model) { + if (dcalc_arg.parasitic()) { + OutputWaveforms *output_waveforms = table_model->outputWaveforms(); + float in_slew = dcalc_arg.inSlewFlt(); + if (output_waveforms) { + const LibertyLibrary *drvr_library = dcalc_arg.drvrLibrary(); + float vdd; + bool vdd_exists; + drvr_library->supplyVoltage("VDD", vdd, vdd_exists); + if (vdd_exists) { + if (drvr_idx == 0) { + // Assume drivers are in the same library. + const RiseFall *drvr_rf = dcalc_arg.drvrEdge(); + vdd_ = vdd; + vth_ = drvr_library->outputThreshold(drvr_rf) * vdd_; + vl_ = drvr_library->slewLowerThreshold(drvr_rf) * vdd_; + vh_ = drvr_library->slewUpperThreshold(drvr_rf) * vdd_; + } + } + else { + failure_reason_ = "vdd not defined"; + failure_arg_ = &dcalc_arg; + } + + // Bounds check because extrapolating waveforms does not work for shit. + if (output_waveforms->slewAxis()->inBounds(in_slew)) { + if (output_waveforms->capAxis()->inBounds(dcalc_arg.loadCap())) { + output_waveforms_[drvr_idx] = output_waveforms; + debugPrint(debug_, "prima", 1, "{} {}", + dcalc_arg.drvrCell()->name(), + dcalc_arg.drvrEdge()->to_string().c_str()); + LibertyCell *drvr_cell = dcalc_arg.drvrCell(); + drvr_cell->ensureVoltageWaveforms(scenes_); + } + else { + failure_reason_ = "load cap out of bounds"; + failure_arg_ = &dcalc_arg; + } + } + else { + failure_reason_ = "input slew out of bounds"; + failure_arg_ = &dcalc_arg; + } + } + else { + failure_reason_ = "no output waveforms"; + failure_arg_ = &dcalc_arg; + } + } + else { + failure_reason_ = "no parasitic"; + failure_arg_ = &dcalc_arg; + } + } + else { + failure_reason_ = "no table model"; + failure_arg_ = &dcalc_arg; + } + } + if (failure_reason_) { + std::string reason = failureReason(); + debugPrint(debug_,"prima", 1, "arg check failed {}.", reason.c_str()); + } + return failure_reason_ != nullptr; +} + +std::string +PrimaDelayCalc::failureReason() +{ + const Pin *drvr_pin = failure_arg_->drvrPin(); + const Instance *inst = network_->instance(drvr_pin); + LibertyPort *from = failure_arg_->arc()->from(); + LibertyPort *to = failure_arg_->arc()->to(); + return sta::format("{} {} -> {} {}", + sdc_network_->pathName(inst), + from->name(), + to->name(), + failure_reason_); +} + ArcDcalcResultSeq PrimaDelayCalc::tableDcalcResults() { @@ -938,8 +997,16 @@ PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin, const MinMax *min_max, int digits) { - GateTimingModel *model = arc->gateModel(scene, min_max); - if (model) { + ArcDcalcArgSeq dcalc_args; + dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, + load_cap, parasitic); + bool arg_fail = checkArgs(dcalc_args, scene, min_max); + if (arg_fail) + return table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap, + parasitic, load_pin_index_map, scene, + min_max, digits); + else { + GateTimingModel *model = arc->gateModel(scene, min_max); // Delay calc to find ceff. gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, load_pin_index_map, scene, min_max); @@ -949,7 +1016,6 @@ PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin, in_slew1, ceff, min_max, PocvMode::scalar, digits); } - return ""; } //////////////////////////////////////////////////////////////// diff --git a/dcalc/PrimaDelayCalc.hh b/dcalc/PrimaDelayCalc.hh index c0b39ba7..086e29ce 100644 --- a/dcalc/PrimaDelayCalc.hh +++ b/dcalc/PrimaDelayCalc.hh @@ -100,6 +100,10 @@ public: const Scene *scene, const MinMax *min_max, int digits) override; + bool checkArgs(ArcDcalcArgSeq &dcalc_args, + const Scene *scene, + const MinMax *min_max); + std::string failureReason(); // Record waveform for drvr/load pin. void watchPin(const Pin *pin) override; @@ -253,6 +257,9 @@ protected: // Delay calculator to use when ccs waveforms are missing from liberty. ArcDelayCalc *table_dcalc_; + const char *failure_reason_; + ArcDcalcArg *failure_arg_; + using ArcDelayCalc::reduceParasitic; };