PrimaDelayCalc::reportGateDelay resolves #418

Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
James Cherry 2026-04-09 15:13:40 -07:00
parent c887b2e4b3
commit b136ba309a
2 changed files with 113 additions and 40 deletions

View File

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

View File

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