// OpenSTA, Static Timing Analyzer
// Copyright (c) 2020, 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 .
#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
hashFuncExpr(const FuncExpr *expr);
static unsigned
hashPort(const LibertyPort *port);
static unsigned
hashCellPgPorts(const LibertyCell *cell);
static unsigned
hashPgPort(const LibertyPgPort *port);
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(port->pgType()) * 5;
}
static unsigned
hashCellSequentials(const LibertyCell *cell)
{
unsigned hash = 0;
LibertyCellSequentialIterator seq_iter(cell);
while (seq_iter.hasNext()) {
Sequential *seq = seq_iter.next();
hash += hashFuncExpr(seq->clock()) * 3;
hash += hashFuncExpr(seq->data()) * 5;
hash += hashPort(seq->output()) * 7;
hash += hashPort(seq->outputInv()) * 9;
hash += hashFuncExpr(seq->clear()) * 11;
hash += hashFuncExpr(seq->preset()) * 13;
hash += int(seq->clearPresetOutput()) * 17;
hash += int(seq->clearPresetOutputInv()) * 19;
}
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 equivCellPortsAndFuncs(cell1, cell2)
&& equivCellPgPorts(cell1, cell2)
&& equivCellSequentials(cell1, cell2)
&& equivCellTimingArcSets(cell1, cell2);
}
bool
equivCellPortsAndFuncs(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)
&& 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)
{
LibertyCellSequentialIterator seq_iter1(cell1);
LibertyCellSequentialIterator seq_iter2(cell2);
while (seq_iter1.hasNext() && seq_iter2.hasNext()) {
Sequential *seq1 = seq_iter1.next();
Sequential *seq2 = seq_iter2.next();
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_iter1.hasNext() && !seq_iter2.hasNext();
}
bool
equivCellTimingArcSets(const LibertyCell *cell1,
const LibertyCell *cell2)
{
if (cell1->timingArcSetCount() != cell2->timingArcSetCount())
return false;
else {
LibertyCellTimingArcSetIterator set_iter1(cell1);
while (set_iter1.hasNext()) {
TimingArcSet *set1 = set_iter1.next();
TimingArcSet *set2 = cell2->findTimingArcSet(set1);
if (!(set2 && TimingArcSet::equiv(set1, set2)))
return false;
}
return true;
}
}
} // namespace