From f1e5587fef8115ccdb32a57d0196d22be348e11c Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 5 Mar 2026 18:41:25 -0700 Subject: [PATCH] rapidus liberty latch D->Q/EN->Q matching Signed-off-by: James Cherry --- include/sta/TimingArc.hh | 1 + liberty/Liberty.cc | 88 +++++++++++++++++++++++----------------- liberty/Liberty.i | 10 +++++ liberty/Liberty.tcl | 10 ++++- liberty/LibertyReader.cc | 47 ++++++++++++--------- liberty/TimingArc.cc | 3 +- 6 files changed, 98 insertions(+), 61 deletions(-) diff --git a/include/sta/TimingArc.hh b/include/sta/TimingArc.hh index 63c04d49..257e446d 100644 --- a/include/sta/TimingArc.hh +++ b/include/sta/TimingArc.hh @@ -176,6 +176,7 @@ public: // other conditional timing arcs between the same pins. bool isCondDefault() const { return is_cond_default_; } void setIsCondDefault(bool is_default); + const FuncExpr *when() const { return attrs_->cond(); } // SDF IOPATHs match sdfCond. // sdfCond (IOPATH) reuses sdfCondStart (timing check) variable. const std::string &sdfCond() const { return attrs_->sdfCondStart(); } diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 130af802..181b0e42 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -1679,45 +1679,58 @@ LibertyCell::makeLatchEnables(Report *report, { if (hasSequentials() || hasInferedRegTimingArcs()) { - for (auto en_to_q : timing_arc_sets_) { - if (en_to_q->role() == TimingRole::latchEnToQ()) { - LibertyPort *en = en_to_q->from(); - LibertyPort *q = en_to_q->to(); - for (TimingArcSet *d_to_q : timingArcSetsTo(q)) { - if (d_to_q->role() == TimingRole::latchDtoQ() - && condMatch(en_to_q, d_to_q)) { - LibertyPort *d = d_to_q->from(); - const RiseFall *en_rf = en_to_q->isRisingFallingEdge(); - if (en_rf) { - TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, d_to_q, - report); - LatchEnable *latch_enable = makeLatchEnable(d, en, en_rf, q, d_to_q, - en_to_q, - setup_check, - debug); - FuncExpr *en_func = latch_enable->enableFunc(); - if (en_func) { - TimingSense en_sense = en_func->portTimingSense(en); - if (en_sense == TimingSense::positive_unate - && en_rf != RiseFall::rise()) - report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.", - library_->name(), - name(), - en->name(), - q->name(), - en_rf == RiseFall::rise()?"rising":"falling"); - else if (en_sense == TimingSense::negative_unate - && en_rf != RiseFall::fall()) - report->warn(1115, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense.", - library_->name(), - name(), - en->name(), - q->name(), - en_rf == RiseFall::rise()?"rising":"falling"); - } + for (TimingArcSet *d_to_q : timing_arc_sets_) { + if (d_to_q->role() == TimingRole::latchDtoQ()) { + LibertyPort *d = d_to_q->from(); + LibertyPort *q = d_to_q->to(); + TimingArcSet *en_to_q = nullptr; + TimingArcSet *en_to_q_when = nullptr; + // Prefer en_to_q with matching when. + for (TimingArcSet *arc_to_q : timingArcSetsTo(q)) { + if (arc_to_q->role() == TimingRole::latchEnToQ()) { + if (condMatch(arc_to_q, d_to_q)) + en_to_q_when = arc_to_q; + else + en_to_q = arc_to_q; + } + } + if (en_to_q_when) + en_to_q = en_to_q_when; + if (en_to_q) { + LibertyPort *en = en_to_q->from(); + const RiseFall *en_rf = en_to_q->isRisingFallingEdge(); + if (en_rf) { + TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, d_to_q, report); + LatchEnable *latch_enable = makeLatchEnable(d, en, en_rf, q, d_to_q, + en_to_q, setup_check, debug); + FuncExpr *en_func = latch_enable->enableFunc(); + if (en_func) { + TimingSense en_sense = en_func->portTimingSense(en); + if (en_sense == TimingSense::positive_unate + && en_rf != RiseFall::rise()) + report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.", + library_->name(), + name(), + en->name(), + q->name(), + en_rf == RiseFall::rise()?"rising":"falling"); + else if (en_sense == TimingSense::negative_unate + && en_rf != RiseFall::fall()) + report->warn(1115, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense.", + library_->name(), + name(), + en->name(), + q->name(), + en_rf == RiseFall::rise()?"rising":"falling"); } } } + else + report->warn(1121, "cell %s/%s no latch enable found for %s -> %s.", + library_->name(), + name(), + d->name(), + q->name()); } } } @@ -1810,8 +1823,7 @@ LibertyCell::makeLatchEnable(LibertyPort *d, Debug *debug) { FuncExpr *en_func = findLatchEnableFunc(d, en, en_rf); - latch_enables_.emplace_back(d, en, en_rf, en_func, q, d_to_q, en_to_q, - setup_check); + latch_enables_.emplace_back(d, en, en_rf, en_func, q, d_to_q, en_to_q, setup_check); size_t idx = latch_enables_.size() - 1; latch_d_to_q_map_[d_to_q] = idx; latch_check_map_[setup_check] = idx; diff --git a/liberty/Liberty.i b/liberty/Liberty.i index 2257b2eb..d2856e68 100644 --- a/liberty/Liberty.i +++ b/liberty/Liberty.i @@ -379,6 +379,16 @@ full_name() to); } +const std::string +when() +{ + const FuncExpr *when = self->when(); + if (when) + return when->to_string(); + else + return ""; +} + TimingArcSeq & timing_arcs() { return self->arcs(); } diff --git a/liberty/Liberty.tcl b/liberty/Liberty.tcl index fd75a8bf..6d93601d 100644 --- a/liberty/Liberty.tcl +++ b/liberty/Liberty.tcl @@ -126,7 +126,15 @@ proc report_timing_arcs { cell } { puts "" puts "Timing arcs" foreach timing_arc $timing_arcs { - puts [$timing_arc to_string] + puts " [$timing_arc to_string]" + puts " [$timing_arc role]" + set when [$timing_arc when] + if { $when != "" } { + puts " when $when" + } + foreach arc [$timing_arc timing_arcs] { + puts " [$arc from_edge] -> [$arc to_edge]" + } } } } diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index c070e3dd..6851b501 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -2059,7 +2059,7 @@ LibertyReader::makeTimingModels(LibertyCell *cell, const LibertyGroup *timing_group, TimingArcAttrsPtr timing_attrs) { - switch (cell->libertyLibrary()->delayModelType()) { + switch (cell->libertyLibrary()->delayModelType()) { case DelayModelType::cmos_linear: makeLinearModels(cell, timing_group, timing_attrs); break; @@ -2115,6 +2115,7 @@ LibertyReader::makeTableModels(LibertyCell *cell, const LibertyGroup *timing_group, TimingArcAttrsPtr timing_attrs) { + bool found_model = false; for (const RiseFall *rf : RiseFall::range()) { std::string delay_attr_name = "cell_" + rf->to_string_long(); TableModel *delay = readGateTableModel(timing_group, delay_attr_name.c_str(), rf, @@ -2160,30 +2161,36 @@ LibertyReader::makeTableModels(LibertyCell *cell, if (delay == nullptr) libWarn(1211, timing_group, "missing cell_%s.", rf->name()); } + found_model = true; } - - std::string constraint_attr_name = rf->to_string_long() + "_constraint"; - ScaleFactorType scale_factor_type = - timingTypeScaleFactorType(timing_attrs->timingType()); - TableModel *constraint = readCheckTableModel(timing_group, - constraint_attr_name.c_str(), - rf, TableTemplateType::delay, - time_scale_, scale_factor_type); - if (constraint) { - std::string constraint_sigma_attr_name = "ocv_sigma_" + rf->to_string_long() - + "_constraint"; - TableModelsEarlyLate constraint_sigmas = - readEarlyLateTableModels(timing_group, - constraint_sigma_attr_name.c_str(), - rf, TableTemplateType::delay, - time_scale_, - ScaleFactorType::unknown); - timing_attrs->setModel(rf, new CheckTableModel(cell, constraint, - std::move(constraint_sigmas))); + else { + std::string constraint_attr_name = rf->to_string_long() + "_constraint"; + ScaleFactorType scale_factor_type = + timingTypeScaleFactorType(timing_attrs->timingType()); + TableModel *constraint = readCheckTableModel(timing_group, + constraint_attr_name.c_str(), + rf, TableTemplateType::delay, + time_scale_, scale_factor_type); + if (constraint) { + std::string constraint_sigma_attr_name = "ocv_sigma_" + rf->to_string_long() + + "_constraint"; + TableModelsEarlyLate constraint_sigmas = + readEarlyLateTableModels(timing_group, + constraint_sigma_attr_name.c_str(), + rf, TableTemplateType::delay, + time_scale_, + ScaleFactorType::unknown); + timing_attrs->setModel(rf, new CheckTableModel(cell, constraint, + std::move(constraint_sigmas))); + found_model = true; + } } } + if (!found_model) + libWarn(1311, timing_group, "no table models found in timing group."); } + bool LibertyReader::isGateTimingType(TimingType timing_type) { diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc index 7bc324a6..ba5f086b 100644 --- a/liberty/TimingArc.cc +++ b/liberty/TimingArc.cc @@ -207,7 +207,6 @@ TimingArcSet::to_string() std::string str = from_->name(); str += " -> "; str += to_->name(); - str += " " + role()->to_string(); return str; } @@ -333,7 +332,7 @@ TimingArcSet::isRisingFallingEdge() const if (from_rf1 == from_rf2) return from_rf1; } - if (arcs_.size() == 1) + if (arc_count == 1) return arcs_[0]->fromEdge()->asRiseFall(); else return nullptr;