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.
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(); }

View File

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

View File

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

View File

@ -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]"
}
}
}
}

View File

@ -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)
{

View File

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