Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions liberty/Liberty.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1489,7 +1489,8 @@ LibertyCell::outputPortSequential(LibertyPort *port)
bool
LibertyCell::hasSequentials() const
{
return !sequentials_.empty();
return !sequentials_.empty()
|| statetable_ != nullptr;
}

void
Expand Down Expand Up @@ -2479,7 +2480,7 @@ LibertyPort::less(const LibertyPort *port1,
{
if (port1 == nullptr && port2 != nullptr)
return true;
if (port1 != nullptr && port2 == nullptr)
if (port1 == nullptr || port2 == nullptr)
return false;
Comment thread
maliberty marked this conversation as resolved.
Outdated
const std::string &name1 = port1->name();
const std::string &name2 = port2->name();
Expand Down
48 changes: 48 additions & 0 deletions liberty/test/cpp/TestLibertyStaCallbacks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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