rapidus liberty latch D->Q/EN->Q matching

Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
James Cherry 2026-03-05 18:41:25 -07:00
parent 8ed837d74b
commit f1e5587fef
6 changed files with 98 additions and 61 deletions

View File

@ -176,6 +176,7 @@ public:
// other conditional timing arcs between the same pins. // other conditional timing arcs between the same pins.
bool isCondDefault() const { return is_cond_default_; } bool isCondDefault() const { return is_cond_default_; }
void setIsCondDefault(bool is_default); void setIsCondDefault(bool is_default);
const FuncExpr *when() const { return attrs_->cond(); }
// SDF IOPATHs match sdfCond. // SDF IOPATHs match sdfCond.
// sdfCond (IOPATH) reuses sdfCondStart (timing check) variable. // sdfCond (IOPATH) reuses sdfCondStart (timing check) variable.
const std::string &sdfCond() const { return attrs_->sdfCondStart(); } const std::string &sdfCond() const { return attrs_->sdfCondStart(); }

View File

@ -1679,45 +1679,58 @@ LibertyCell::makeLatchEnables(Report *report,
{ {
if (hasSequentials() if (hasSequentials()
|| hasInferedRegTimingArcs()) { || hasInferedRegTimingArcs()) {
for (auto en_to_q : timing_arc_sets_) { for (TimingArcSet *d_to_q : timing_arc_sets_) {
if (en_to_q->role() == TimingRole::latchEnToQ()) { if (d_to_q->role() == TimingRole::latchDtoQ()) {
LibertyPort *en = en_to_q->from(); LibertyPort *d = d_to_q->from();
LibertyPort *q = en_to_q->to(); LibertyPort *q = d_to_q->to();
for (TimingArcSet *d_to_q : timingArcSetsTo(q)) { TimingArcSet *en_to_q = nullptr;
if (d_to_q->role() == TimingRole::latchDtoQ() TimingArcSet *en_to_q_when = nullptr;
&& condMatch(en_to_q, d_to_q)) { // Prefer en_to_q with matching when.
LibertyPort *d = d_to_q->from(); for (TimingArcSet *arc_to_q : timingArcSetsTo(q)) {
const RiseFall *en_rf = en_to_q->isRisingFallingEdge(); if (arc_to_q->role() == TimingRole::latchEnToQ()) {
if (en_rf) { if (condMatch(arc_to_q, d_to_q))
TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, d_to_q, en_to_q_when = arc_to_q;
report); else
LatchEnable *latch_enable = makeLatchEnable(d, en, en_rf, q, d_to_q, en_to_q = arc_to_q;
en_to_q, }
setup_check, }
debug); if (en_to_q_when)
FuncExpr *en_func = latch_enable->enableFunc(); en_to_q = en_to_q_when;
if (en_func) { if (en_to_q) {
TimingSense en_sense = en_func->portTimingSense(en); LibertyPort *en = en_to_q->from();
if (en_sense == TimingSense::positive_unate const RiseFall *en_rf = en_to_q->isRisingFallingEdge();
&& en_rf != RiseFall::rise()) if (en_rf) {
report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.", TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, d_to_q, report);
library_->name(), LatchEnable *latch_enable = makeLatchEnable(d, en, en_rf, q, d_to_q,
name(), en_to_q, setup_check, debug);
en->name(), FuncExpr *en_func = latch_enable->enableFunc();
q->name(), if (en_func) {
en_rf == RiseFall::rise()?"rising":"falling"); TimingSense en_sense = en_func->portTimingSense(en);
else if (en_sense == TimingSense::negative_unate if (en_sense == TimingSense::positive_unate
&& en_rf != RiseFall::fall()) && en_rf != RiseFall::rise())
report->warn(1115, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense.", report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.",
library_->name(), library_->name(),
name(), name(),
en->name(), en->name(),
q->name(), q->name(),
en_rf == RiseFall::rise()?"rising":"falling"); 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) Debug *debug)
{ {
FuncExpr *en_func = findLatchEnableFunc(d, en, en_rf); 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, latch_enables_.emplace_back(d, en, en_rf, en_func, q, d_to_q, en_to_q, setup_check);
setup_check);
size_t idx = latch_enables_.size() - 1; size_t idx = latch_enables_.size() - 1;
latch_d_to_q_map_[d_to_q] = idx; latch_d_to_q_map_[d_to_q] = idx;
latch_check_map_[setup_check] = idx; latch_check_map_[setup_check] = idx;

View File

@ -379,6 +379,16 @@ full_name()
to); to);
} }
const std::string
when()
{
const FuncExpr *when = self->when();
if (when)
return when->to_string();
else
return "";
}
TimingArcSeq & TimingArcSeq &
timing_arcs() { return self->arcs(); } timing_arcs() { return self->arcs(); }

View File

@ -126,7 +126,15 @@ proc report_timing_arcs { cell } {
puts "" puts ""
puts "Timing arcs" puts "Timing arcs"
foreach timing_arc $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]"
}
} }
} }
} }

View File

@ -2059,7 +2059,7 @@ LibertyReader::makeTimingModels(LibertyCell *cell,
const LibertyGroup *timing_group, const LibertyGroup *timing_group,
TimingArcAttrsPtr timing_attrs) TimingArcAttrsPtr timing_attrs)
{ {
switch (cell->libertyLibrary()->delayModelType()) { switch (cell->libertyLibrary()->delayModelType()) {
case DelayModelType::cmos_linear: case DelayModelType::cmos_linear:
makeLinearModels(cell, timing_group, timing_attrs); makeLinearModels(cell, timing_group, timing_attrs);
break; break;
@ -2115,6 +2115,7 @@ LibertyReader::makeTableModels(LibertyCell *cell,
const LibertyGroup *timing_group, const LibertyGroup *timing_group,
TimingArcAttrsPtr timing_attrs) TimingArcAttrsPtr timing_attrs)
{ {
bool found_model = false;
for (const RiseFall *rf : RiseFall::range()) { for (const RiseFall *rf : RiseFall::range()) {
std::string delay_attr_name = "cell_" + rf->to_string_long(); std::string delay_attr_name = "cell_" + rf->to_string_long();
TableModel *delay = readGateTableModel(timing_group, delay_attr_name.c_str(), rf, TableModel *delay = readGateTableModel(timing_group, delay_attr_name.c_str(), rf,
@ -2160,30 +2161,36 @@ LibertyReader::makeTableModels(LibertyCell *cell,
if (delay == nullptr) if (delay == nullptr)
libWarn(1211, timing_group, "missing cell_%s.", rf->name()); libWarn(1211, timing_group, "missing cell_%s.", rf->name());
} }
found_model = true;
} }
else {
std::string constraint_attr_name = rf->to_string_long() + "_constraint"; std::string constraint_attr_name = rf->to_string_long() + "_constraint";
ScaleFactorType scale_factor_type = ScaleFactorType scale_factor_type =
timingTypeScaleFactorType(timing_attrs->timingType()); timingTypeScaleFactorType(timing_attrs->timingType());
TableModel *constraint = readCheckTableModel(timing_group, TableModel *constraint = readCheckTableModel(timing_group,
constraint_attr_name.c_str(), constraint_attr_name.c_str(),
rf, TableTemplateType::delay, rf, TableTemplateType::delay,
time_scale_, scale_factor_type); time_scale_, scale_factor_type);
if (constraint) { if (constraint) {
std::string constraint_sigma_attr_name = "ocv_sigma_" + rf->to_string_long() std::string constraint_sigma_attr_name = "ocv_sigma_" + rf->to_string_long()
+ "_constraint"; + "_constraint";
TableModelsEarlyLate constraint_sigmas = TableModelsEarlyLate constraint_sigmas =
readEarlyLateTableModels(timing_group, readEarlyLateTableModels(timing_group,
constraint_sigma_attr_name.c_str(), constraint_sigma_attr_name.c_str(),
rf, TableTemplateType::delay, rf, TableTemplateType::delay,
time_scale_, time_scale_,
ScaleFactorType::unknown); ScaleFactorType::unknown);
timing_attrs->setModel(rf, new CheckTableModel(cell, constraint, timing_attrs->setModel(rf, new CheckTableModel(cell, constraint,
std::move(constraint_sigmas))); std::move(constraint_sigmas)));
found_model = true;
}
} }
} }
if (!found_model)
libWarn(1311, timing_group, "no table models found in timing group.");
} }
bool bool
LibertyReader::isGateTimingType(TimingType timing_type) LibertyReader::isGateTimingType(TimingType timing_type)
{ {

View File

@ -207,7 +207,6 @@ TimingArcSet::to_string()
std::string str = from_->name(); std::string str = from_->name();
str += " -> "; str += " -> ";
str += to_->name(); str += to_->name();
str += " " + role()->to_string();
return str; return str;
} }
@ -333,7 +332,7 @@ TimingArcSet::isRisingFallingEdge() const
if (from_rf1 == from_rf2) if (from_rf1 == from_rf2)
return from_rf1; return from_rf1;
} }
if (arcs_.size() == 1) if (arc_count == 1)
return arcs_[0]->fromEdge()->asRiseFall(); return arcs_[0]->fromEdge()->asRiseFall();
else else
return nullptr; return nullptr;