561 lines
15 KiB
C++
561 lines
15 KiB
C++
// OpenSTA, Static Timing Analyzer
|
|
// Copyright (c) 2025, Parallax Software, Inc.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
//
|
|
// The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software.
|
|
//
|
|
// Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
//
|
|
// This notice may not be removed or altered from any source distribution.
|
|
|
|
#include "EquivCells.hh"
|
|
|
|
#include "Hash.hh"
|
|
#include "MinMax.hh"
|
|
#include "PortDirection.hh"
|
|
#include "Transition.hh"
|
|
#include "TimingRole.hh"
|
|
#include "FuncExpr.hh"
|
|
#include "TimingArc.hh"
|
|
#include "Liberty.hh"
|
|
#include "TableModel.hh"
|
|
#include "Sequential.hh"
|
|
|
|
namespace sta {
|
|
|
|
using std::max;
|
|
|
|
static unsigned
|
|
hashCell(const LibertyCell *cell);
|
|
static unsigned
|
|
hashCellPorts(const LibertyCell *cell);
|
|
static unsigned
|
|
hashCellSequentials(const LibertyCell *cell);
|
|
static unsigned
|
|
hashSequential(const Sequential *seq);
|
|
bool
|
|
equivCellStatetables(const LibertyCell *cell1,
|
|
const LibertyCell *cell2);
|
|
static bool
|
|
equivCellPortSeq(const LibertyPortSeq &ports1,
|
|
const LibertyPortSeq &ports2);
|
|
static bool
|
|
equivStatetableRows(const StatetableRows &table1,
|
|
const StatetableRows &table2);
|
|
static bool
|
|
equivStatetableRow(const StatetableRow &row1,
|
|
const StatetableRow &row2);
|
|
static unsigned
|
|
hashStatetable(const Statetable *statetable);
|
|
static unsigned
|
|
hashStatetableRow(const StatetableRow &row);
|
|
static unsigned
|
|
hashFuncExpr(const FuncExpr *expr);
|
|
static unsigned
|
|
hashPort(const LibertyPort *port);
|
|
static unsigned
|
|
hashCellPgPorts(const LibertyCell *cell);
|
|
static unsigned
|
|
hashPgPort(const LibertyPgPort *port);
|
|
static bool
|
|
cellHasFuncs(const LibertyCell *cell);
|
|
|
|
static bool
|
|
equivCellPgPorts(const LibertyCell *cell1,
|
|
const LibertyCell *cell2);
|
|
|
|
static float
|
|
cellDriveResistance(const LibertyCell *cell)
|
|
{
|
|
LibertyCellPortBitIterator port_iter(cell);
|
|
while (port_iter.hasNext()) {
|
|
auto port = port_iter.next();
|
|
if (port->direction()->isOutput())
|
|
return port->driveResistance();
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
class CellDriveResistanceGreater
|
|
{
|
|
public:
|
|
bool operator()(const LibertyCell *cell1,
|
|
const LibertyCell *cell2) const
|
|
{
|
|
return cellDriveResistance(cell1) > cellDriveResistance(cell2);
|
|
}
|
|
};
|
|
|
|
EquivCells::EquivCells(LibertyLibrarySeq *equiv_libs,
|
|
LibertyLibrarySeq *map_libs)
|
|
{
|
|
LibertyCellHashMap hash_matches;
|
|
for (auto lib : *equiv_libs)
|
|
findEquivCells(lib, hash_matches);
|
|
// Sort the equiv sets by drive resistance.
|
|
for (auto cell : unique_equiv_cells_) {
|
|
auto equivs = equiv_cells_.findKey(cell);
|
|
sort(equivs, CellDriveResistanceGreater());
|
|
}
|
|
if (map_libs) {
|
|
for (auto lib : *map_libs)
|
|
mapEquivCells(lib, hash_matches);
|
|
}
|
|
hash_matches.deleteContents();
|
|
}
|
|
|
|
EquivCells::~EquivCells()
|
|
{
|
|
for (auto cell : unique_equiv_cells_)
|
|
delete equiv_cells_.findKey(cell);
|
|
}
|
|
|
|
LibertyCellSeq *
|
|
EquivCells::equivs(LibertyCell *cell)
|
|
{
|
|
return equiv_cells_.findKey(cell);
|
|
}
|
|
|
|
// Use a comprehensive hash on cell properties to segregate
|
|
// cells into groups of potential matches.
|
|
void
|
|
EquivCells::findEquivCells(const LibertyLibrary *library,
|
|
LibertyCellHashMap &hash_matches)
|
|
{
|
|
LibertyCellIterator cell_iter(library);
|
|
while (cell_iter.hasNext()) {
|
|
LibertyCell *cell = cell_iter.next();
|
|
if (!cell->dontUse()) {
|
|
unsigned hash = hashCell(cell);
|
|
LibertyCellSeq *matches = hash_matches.findKey(hash);
|
|
if (matches) {
|
|
LibertyCellSeq::Iterator match_iter(matches);
|
|
while (match_iter.hasNext()) {
|
|
LibertyCell *match = match_iter.next();
|
|
if (equivCells(match, cell)) {
|
|
LibertyCellSeq *equivs = equiv_cells_.findKey(match);
|
|
if (equivs == nullptr) {
|
|
equivs = new LibertyCellSeq;
|
|
equivs->push_back(match);
|
|
unique_equiv_cells_.push_back(match);
|
|
equiv_cells_[match] = equivs;
|
|
}
|
|
equivs->push_back(cell);
|
|
equiv_cells_[cell] = equivs;
|
|
break;
|
|
}
|
|
}
|
|
matches->push_back(cell);
|
|
}
|
|
else {
|
|
matches = new LibertyCellSeq;
|
|
hash_matches[hash] = matches;
|
|
matches->push_back(cell);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Map library cells to equiv cells.
|
|
void
|
|
EquivCells::mapEquivCells(const LibertyLibrary *library,
|
|
LibertyCellHashMap &hash_matches)
|
|
{
|
|
|
|
LibertyCellIterator cell_iter(library);
|
|
while (cell_iter.hasNext()) {
|
|
LibertyCell *cell = cell_iter.next();
|
|
if (!cell->dontUse()) {
|
|
unsigned hash = hashCell(cell);
|
|
LibertyCellSeq *matches = hash_matches.findKey(hash);
|
|
if (matches) {
|
|
LibertyCellSeq::Iterator match_iter(matches);
|
|
while (match_iter.hasNext()) {
|
|
LibertyCell *match = match_iter.next();
|
|
if (equivCells(match, cell)) {
|
|
LibertyCellSeq *equivs = equiv_cells_.findKey(match);
|
|
equiv_cells_[cell] = equivs;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static unsigned
|
|
hashCell(const LibertyCell *cell)
|
|
{
|
|
return hashCellPorts(cell)
|
|
+ hashCellPgPorts(cell)
|
|
+ hashCellSequentials(cell);
|
|
}
|
|
|
|
static unsigned
|
|
hashCellPorts(const LibertyCell *cell)
|
|
{
|
|
unsigned hash = 0;
|
|
LibertyCellPortIterator port_iter(cell);
|
|
while (port_iter.hasNext()) {
|
|
LibertyPort *port = port_iter.next();
|
|
hash += hashPort(port);
|
|
hash += hashFuncExpr(port->function()) * 3;
|
|
hash += hashFuncExpr(port->tristateEnable()) * 5;
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
static unsigned
|
|
hashPort(const LibertyPort *port)
|
|
{
|
|
return hashString(port->name()) * 3
|
|
+ port->direction()->index() * 5;
|
|
}
|
|
|
|
static unsigned
|
|
hashCellPgPorts(const LibertyCell *cell)
|
|
{
|
|
unsigned hash = 0;
|
|
LibertyCellPgPortIterator port_iter(cell);
|
|
while (port_iter.hasNext()) {
|
|
LibertyPgPort *port = port_iter.next();
|
|
hash += hashPgPort(port);
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
static unsigned
|
|
hashPgPort(const LibertyPgPort *port)
|
|
{
|
|
return hashString(port->name()) * 3
|
|
+ static_cast<int>(port->pgType()) * 5;
|
|
}
|
|
|
|
static unsigned
|
|
hashCellSequentials(const LibertyCell *cell)
|
|
{
|
|
unsigned hash = 0;
|
|
for (const Sequential *seq : cell->sequentials())
|
|
hash += hashSequential(seq);
|
|
const Statetable *statetable = cell->statetable();
|
|
if (statetable)
|
|
hash += hashStatetable(statetable);
|
|
return hash;
|
|
}
|
|
|
|
static unsigned
|
|
hashSequential(const Sequential *seq)
|
|
{
|
|
unsigned hash = 0;
|
|
hash += seq->isRegister() * 3;
|
|
hash += hashFuncExpr(seq->clock()) * 5;
|
|
hash += hashFuncExpr(seq->data()) * 7;
|
|
hash += hashPort(seq->output()) * 9;
|
|
hash += hashPort(seq->outputInv()) * 11;
|
|
hash += hashFuncExpr(seq->clear()) * 13;
|
|
hash += hashFuncExpr(seq->preset()) * 17;
|
|
hash += int(seq->clearPresetOutput()) * 19;
|
|
hash += int(seq->clearPresetOutputInv()) * 23;
|
|
return hash;
|
|
}
|
|
|
|
static unsigned
|
|
hashStatetable(const Statetable *statetable)
|
|
{
|
|
unsigned hash = 0;
|
|
unsigned hash_ports = 0;
|
|
for (LibertyPort *input_port : statetable->inputPorts())
|
|
hash_ports += hashPort(input_port);
|
|
hash += hash_ports * 3;
|
|
|
|
hash_ports = 0;
|
|
for (LibertyPort *internal_port : statetable->internalPorts())
|
|
hash_ports += hashPort(internal_port);
|
|
hash += hash_ports * 5;
|
|
|
|
unsigned hash_rows = 0;
|
|
for (const StatetableRow &row : statetable->table())
|
|
hash_rows += hashStatetableRow(row);
|
|
hash += hash_rows * 7;
|
|
return hash;
|
|
}
|
|
|
|
static unsigned
|
|
hashStatetableRow(const StatetableRow &row)
|
|
{
|
|
unsigned hash = 0;
|
|
for (StateInputValue input_value : row.inputValues())
|
|
hash += static_cast<int>(input_value) * 9;
|
|
for (StateInternalValue current_value : row.currentValues())
|
|
hash += static_cast<int>(current_value) * 11;
|
|
for (StateInternalValue next_value : row.nextValues())
|
|
hash += static_cast<int>(next_value) * 13;
|
|
return hash;
|
|
}
|
|
|
|
static unsigned
|
|
hashFuncExpr(const FuncExpr *expr)
|
|
{
|
|
if (expr == nullptr)
|
|
return 0;
|
|
else {
|
|
switch (expr->op()) {
|
|
case FuncExpr::op_port:
|
|
return hashPort(expr->port()) * 17;
|
|
break;
|
|
case FuncExpr::op_not:
|
|
return hashFuncExpr(expr->left()) * 31;
|
|
break;
|
|
default:
|
|
return (hashFuncExpr(expr->left()) + hashFuncExpr(expr->right()))
|
|
* ((1 << expr->op()) - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
equivCells(const LibertyCell *cell1,
|
|
const LibertyCell *cell2)
|
|
{
|
|
return equivCellPorts(cell1, cell2)
|
|
&& equivCellFuncs(cell1, cell2)
|
|
&& equivCellPgPorts(cell1, cell2)
|
|
&& equivCellSequentials(cell1, cell2)
|
|
&& equivCellStatetables(cell1, cell2)
|
|
// Reqwuire timing arc equivalence if there are no functions.
|
|
&& (cellHasFuncs(cell1)
|
|
|| equivCellTimingArcSets(cell1, cell2));
|
|
}
|
|
|
|
bool
|
|
equivCellsArcs(const LibertyCell *cell1,
|
|
const LibertyCell *cell2)
|
|
{
|
|
return equivCellPorts(cell1, cell2)
|
|
&& equivCellFuncs(cell1, cell2)
|
|
&& equivCellPgPorts(cell1, cell2)
|
|
&& equivCellSequentials(cell1, cell2)
|
|
&& equivCellStatetables(cell1, cell2)
|
|
// Reqwuire timing arc equivalence if there are no functions.
|
|
&& equivCellTimingArcSets(cell1, cell2);
|
|
}
|
|
|
|
static bool
|
|
cellHasFuncs(const LibertyCell *cell)
|
|
{
|
|
LibertyCellPortIterator port_iter(cell);
|
|
while (port_iter.hasNext()) {
|
|
LibertyPort *port = port_iter.next();
|
|
if (port->function())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
equivCellFuncs(const LibertyCell *cell1,
|
|
const LibertyCell *cell2)
|
|
{
|
|
LibertyCellPortIterator port_iter1(cell1);
|
|
while (port_iter1.hasNext()) {
|
|
LibertyPort *port1 = port_iter1.next();
|
|
const char *name = port1->name();
|
|
LibertyPort *port2 = cell2->findLibertyPort(name);
|
|
if (!(port2
|
|
&& FuncExpr::equiv(port1->function(), port2->function())
|
|
&& FuncExpr::equiv(port1->tristateEnable(),
|
|
port2->tristateEnable())))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
equivCellPorts(const LibertyCell *cell1,
|
|
const LibertyCell *cell2)
|
|
{
|
|
if (cell1->portCount() != cell2->portCount())
|
|
return false;
|
|
else {
|
|
LibertyCellPortIterator port_iter1(cell1);
|
|
while (port_iter1.hasNext()) {
|
|
LibertyPort *port1 = port_iter1.next();
|
|
const char* name = port1->name();
|
|
LibertyPort *port2 = cell2->findLibertyPort(name);
|
|
if (!(port2 && LibertyPort::equiv(port1, port2)))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static bool
|
|
equivCellPgPorts(const LibertyCell *cell1,
|
|
const LibertyCell *cell2)
|
|
{
|
|
if (cell1->pgPortCount() != cell2->pgPortCount())
|
|
return false;
|
|
else {
|
|
LibertyCellPgPortIterator port_iter1(cell1);
|
|
while (port_iter1.hasNext()) {
|
|
LibertyPgPort *port1 = port_iter1.next();
|
|
const char* name = port1->name();
|
|
LibertyPgPort *port2 = cell2->findPgPort(name);
|
|
if (!(port2 && LibertyPgPort::equiv(port1, port2)))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool
|
|
equivCellSequentials(const LibertyCell *cell1,
|
|
const LibertyCell *cell2)
|
|
{
|
|
const SequentialSeq &seqs1 = cell1->sequentials();
|
|
const SequentialSeq &seqs2 = cell2->sequentials();
|
|
auto seq_itr1 = seqs1.begin(), seq_itr2 = seqs2.begin();
|
|
for (;
|
|
seq_itr1 != seqs1.end() && seq_itr2 != seqs2.end();
|
|
seq_itr1++, seq_itr2++) {
|
|
const Sequential *seq1 = *seq_itr1;
|
|
const Sequential *seq2 = *seq_itr2;
|
|
if (!(FuncExpr::equiv(seq1->clock(), seq2->clock())
|
|
&& FuncExpr::equiv(seq1->data(), seq2->data())
|
|
&& LibertyPort::equiv(seq1->output(), seq2->output())
|
|
&& LibertyPort::equiv(seq1->outputInv(), seq2->outputInv())
|
|
&& FuncExpr::equiv(seq1->clear(), seq2->clear())
|
|
&& FuncExpr::equiv(seq1->preset(), seq2->preset())))
|
|
return false;
|
|
}
|
|
return seq_itr1 == seqs1.end() && seq_itr2 == seqs2.end();
|
|
}
|
|
|
|
bool
|
|
equivCellStatetables(const LibertyCell *cell1,
|
|
const LibertyCell *cell2)
|
|
|
|
{
|
|
const Statetable *statetable1 = cell1->statetable();
|
|
const Statetable *statetable2 = cell2->statetable();
|
|
return (statetable1 == nullptr && statetable2 == nullptr)
|
|
|| (statetable1 && statetable2
|
|
&& equivCellPortSeq(statetable1->inputPorts(), statetable2->inputPorts())
|
|
&& equivCellPortSeq(statetable1->internalPorts(), statetable2->internalPorts())
|
|
&& equivStatetableRows(statetable1->table(), statetable2->table()));
|
|
}
|
|
|
|
static bool
|
|
equivCellPortSeq(const LibertyPortSeq &ports1,
|
|
const LibertyPortSeq &ports2)
|
|
{
|
|
if (ports1.size() != ports2.size())
|
|
return false;
|
|
|
|
auto port_itr1 = ports1.begin();
|
|
auto port_itr2 = ports2.begin();
|
|
for (;
|
|
port_itr1 != ports1.end() && port_itr2 != ports2.end();
|
|
port_itr1++, port_itr2++) {
|
|
const LibertyPort *port1 = *port_itr1;
|
|
const LibertyPort *port2 = *port_itr2;
|
|
if (!LibertyPort::equiv(port1, port2))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
equivStatetableRows(const StatetableRows &table1,
|
|
const StatetableRows &table2)
|
|
{
|
|
if (table1.size() != table2.size())
|
|
return false;
|
|
|
|
auto row_itr1 = table1.begin();
|
|
auto row_itr2 = table2.begin();
|
|
for (;
|
|
row_itr1 != table1.end() && row_itr2 != table2.end();
|
|
row_itr1++, row_itr2++) {
|
|
const StatetableRow &row1 = *row_itr1;
|
|
const StatetableRow &row2 = *row_itr2;
|
|
if (!equivStatetableRow(row1, row2))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
equivStatetableRow(const StatetableRow &row1,
|
|
const StatetableRow &row2)
|
|
{
|
|
const StateInputValues &input_values1 = row1.inputValues();
|
|
const StateInputValues &input_values2 = row2.inputValues();
|
|
if (input_values1.size() != input_values2.size())
|
|
return false;
|
|
for (auto input_itr1 = input_values1.begin(),
|
|
input_itr2 = input_values2.begin();
|
|
input_itr1 != input_values1.end() && input_itr2 != input_values2.end();
|
|
input_itr1++, input_itr2++) {
|
|
if (*input_itr1 != *input_itr2)
|
|
return false;
|
|
}
|
|
|
|
const StateInternalValues ¤t_values1 = row1.currentValues();
|
|
const StateInternalValues ¤t_values2 = row2.currentValues();
|
|
if (current_values1.size() != current_values2.size())
|
|
return false;
|
|
for (auto current_itr1 = current_values1.begin(),
|
|
current_itr2 = current_values2.begin();
|
|
current_itr1 != current_values1.end() && current_itr2 != current_values2.end();
|
|
current_itr1++, current_itr2++) {
|
|
if (*current_itr1 != *current_itr2)
|
|
return false;
|
|
}
|
|
|
|
const StateInternalValues &next_values1 = row1.nextValues();
|
|
const StateInternalValues &next_values2 = row2.nextValues();
|
|
if (next_values1.size() != next_values2.size())
|
|
return false;
|
|
for (auto next_itr1 = next_values1.begin(),
|
|
next_itr2 = next_values2.begin();
|
|
next_itr1 != next_values1.end() && next_itr2 != next_values2.end();
|
|
next_itr1++, next_itr2++) {
|
|
if (*next_itr1 != *next_itr2)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
equivCellTimingArcSets(const LibertyCell *cell1,
|
|
const LibertyCell *cell2)
|
|
{
|
|
if (cell1->timingArcSetCount() != cell2->timingArcSetCount())
|
|
return false;
|
|
else {
|
|
for (TimingArcSet *arc_set1 : cell1->timingArcSets()) {
|
|
TimingArcSet *arc_set2 = cell2->findTimingArcSet(arc_set1);
|
|
if (!(arc_set2 && TimingArcSet::equiv(arc_set1, arc_set2)))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
} // namespace
|