diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 741ce875..19177c68 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -1489,7 +1489,8 @@ LibertyCell::outputPortSequential(LibertyPort *port) bool LibertyCell::hasSequentials() const { - return !sequentials_.empty(); + return !sequentials_.empty() + || statetable_ != nullptr; } void @@ -2477,10 +2478,8 @@ bool LibertyPort::less(const LibertyPort *port1, const LibertyPort *port2) { - if (port1 == nullptr && port2 != nullptr) - return true; - if (port1 != nullptr && port2 == nullptr) - return false; + if (port1 == nullptr || port2 == nullptr) + return port1 == nullptr && port2 != nullptr; const std::string &name1 = port1->name(); const std::string &name2 = port2->name(); if (name1 == name2) { diff --git a/liberty/test/cpp/TestLibertyStaCallbacks.cc b/liberty/test/cpp/TestLibertyStaCallbacks.cc index 70f3502f..e6e67e0c 100644 --- a/liberty/test/cpp/TestLibertyStaCallbacks.cc +++ b/liberty/test/cpp/TestLibertyStaCallbacks.cc @@ -4308,4 +4308,52 @@ library(test_r11_ccs) { }() )); } +// Regression: hasSequentials must return true for cells that define +// sequential behavior via a statetable group (no ff/latch). +// Multi-bit flip-flops (MBFFs) and clock-gated cells commonly use +// statetable instead of ff/latch groups. Without the statetable_ +// check in hasSequentials(), these cells are misclassified as +// combinational — breaking power categorization, resizer guards, +// and dbSta cell-type mapping. +TEST_F(StaLibertyTest, HasSequentialsStatetableMBFF) { + const char *content = R"( +library(test_mbff_statetable) { + delay_model : table_lookup ; + time_unit : "1ns" ; + voltage_unit : "1V" ; + current_unit : "1mA" ; + capacitive_load_unit(1, ff) ; + cell(MBFF2) { + area : 6.0 ; + pin(D0) { direction : input ; capacitance : 0.01 ; } + pin(D1) { direction : input ; capacitance : 0.01 ; } + pin(CLK) { direction : input ; capacitance : 0.01 ; clock : true ; } + pin(Q0) { direction : output ; function : "IQ0" ; } + pin(Q1) { direction : output ; function : "IQ1" ; } + statetable("D0 D1 CLK", "IQ0 IQ1") { + table : "- - ~R : - - : N N ,\ + H - R : - - : H N ,\ + L - R : - - : L N ,\ + - H R : - - : N H ,\ + - L R : - - : N L" ; + } + } +} +)"; + LibertyLibrary *lib = writeAndReadLibReturn(sta_, content); + ASSERT_NE(lib, nullptr); + LibertyCell *mbff = lib->findLibertyCell("MBFF2"); + ASSERT_NE(mbff, nullptr); + + // The cell has no ff/latch group, so sequentials_ is empty. + EXPECT_TRUE(mbff->sequentials().empty()); + // But it has a statetable, so it IS sequential. + EXPECT_NE(mbff->statetable(), nullptr); + // hasSequentials() must return true for statetable-only cells. + EXPECT_TRUE(mbff->hasSequentials()) + << "MBFF2 uses statetable (no ff/latch) but hasSequentials() " + "returned false — statetable cells are misclassified as " + "combinational"; +} + } // namespace sta