2018-09-28 17:54:21 +02:00
|
|
|
// OpenSTA, Static Timing Analyzer
|
2019-01-01 21:26:11 +01:00
|
|
|
// Copyright (c) 2019, Parallax Software, Inc.
|
2018-09-28 17:54:21 +02:00
|
|
|
//
|
|
|
|
|
// 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/>.
|
|
|
|
|
|
|
|
|
|
#include "Machine.hh"
|
|
|
|
|
#include "PortDirection.hh"
|
|
|
|
|
#include "Transition.hh"
|
|
|
|
|
#include "MinMax.hh"
|
|
|
|
|
#include "TimingRole.hh"
|
|
|
|
|
#include "FuncExpr.hh"
|
|
|
|
|
#include "TimingArc.hh"
|
|
|
|
|
#include "Liberty.hh"
|
|
|
|
|
#include "TableModel.hh"
|
|
|
|
|
#include "Sequential.hh"
|
|
|
|
|
#include "EquivCells.hh"
|
|
|
|
|
|
|
|
|
|
namespace sta {
|
|
|
|
|
|
|
|
|
|
typedef Map<unsigned,LibertyCellSeq*, std::less<unsigned> > LibertyCellHashMap;
|
|
|
|
|
typedef Set<LibertyCellSeq*> LibertyCellSeqSet;
|
|
|
|
|
|
|
|
|
|
static LibertyCellEquivMap *
|
|
|
|
|
findEquivCells1(const LibertyLibrary *library);
|
|
|
|
|
static void
|
|
|
|
|
sortCellEquivs(LibertyCellEquivMap *cell_equivs);
|
|
|
|
|
float
|
|
|
|
|
cellDriveResistance(const LibertyCell *cell);
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
hashString(const char *str);
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
equivCellSequentials(const LibertyCell *cell1,
|
|
|
|
|
const LibertyCell *cell2);
|
|
|
|
|
|
|
|
|
|
LibertyCellEquivMap *
|
|
|
|
|
makeEquivCellMap(const LibertyLibrary *library)
|
|
|
|
|
{
|
|
|
|
|
LibertyCellEquivMap *cell_equivs = findEquivCells1(library);
|
|
|
|
|
sortCellEquivs(cell_equivs);
|
|
|
|
|
return cell_equivs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
deleteEquivCellMap(LibertyCellEquivMap *equiv_map)
|
|
|
|
|
{
|
|
|
|
|
// Multiple cells can point to the same cell sequence, so collect
|
|
|
|
|
// them into a set so the are only deleted once.
|
|
|
|
|
LibertyCellSeqSet cells_seqs;
|
|
|
|
|
LibertyCellEquivMap::Iterator equiv_iter(equiv_map);
|
|
|
|
|
while (equiv_iter.hasNext()) {
|
|
|
|
|
LibertyCellSeq *cells = equiv_iter.next();
|
|
|
|
|
cells_seqs.insert(cells);
|
|
|
|
|
}
|
|
|
|
|
LibertyCellSeqSet::Iterator cells_iter(cells_seqs);
|
|
|
|
|
while (cells_iter.hasNext()) {
|
|
|
|
|
LibertyCellSeq *cells = cells_iter.next();
|
|
|
|
|
delete cells;
|
|
|
|
|
}
|
|
|
|
|
delete equiv_map;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static LibertyCellEquivMap *
|
|
|
|
|
findEquivCells1(const LibertyLibrary *library)
|
|
|
|
|
{
|
|
|
|
|
LibertyCellHashMap cell_hash;
|
|
|
|
|
LibertyCellEquivMap *cell_equivs = new LibertyCellEquivMap;
|
2018-12-05 23:18:41 +01:00
|
|
|
LibertyCellIterator cell_iter(library);
|
|
|
|
|
while (cell_iter.hasNext()) {
|
|
|
|
|
LibertyCell *cell = cell_iter.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
if (!cell->dontUse()) {
|
|
|
|
|
bool found_equiv = false;
|
|
|
|
|
unsigned hash = hashCell(cell);
|
|
|
|
|
// Use a comprehensive hash on cell properties to segregate
|
|
|
|
|
// cells into groups of potential matches.
|
|
|
|
|
LibertyCellSeq *matches = cell_hash[hash];
|
|
|
|
|
if (matches) {
|
|
|
|
|
LibertyCellSeq::Iterator match_iter(matches);
|
|
|
|
|
while (match_iter.hasNext()) {
|
|
|
|
|
LibertyCell *match = match_iter.next();
|
|
|
|
|
if (equivCells(match, cell)) {
|
|
|
|
|
LibertyCellSeq *equivs = cell_equivs->findKey(match);
|
|
|
|
|
equivs->push_back(cell);
|
|
|
|
|
(*cell_equivs)[cell] = equivs;
|
|
|
|
|
found_equiv = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
matches = new LibertyCellSeq;
|
|
|
|
|
cell_hash[hash] = matches;
|
|
|
|
|
}
|
|
|
|
|
matches->push_back(cell);
|
|
|
|
|
if (!found_equiv) {
|
|
|
|
|
LibertyCellSeq *equivs = new LibertyCellSeq;
|
|
|
|
|
equivs->push_back(cell);
|
|
|
|
|
(*cell_equivs)[cell] = equivs;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LibertyCellHashMap::Iterator hash_iter(cell_hash);
|
|
|
|
|
while (hash_iter.hasNext()) {
|
|
|
|
|
LibertyCellSeq *cells = hash_iter.next();
|
|
|
|
|
delete cells;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return cell_equivs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct CellDriveResistanceLess
|
|
|
|
|
{
|
|
|
|
|
bool operator()(const LibertyCell *cell1,
|
|
|
|
|
const LibertyCell *cell2) const
|
|
|
|
|
{
|
|
|
|
|
return cellDriveResistance(cell1) > cellDriveResistance(cell2);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
sortCellEquivs(LibertyCellEquivMap *cell_equivs)
|
|
|
|
|
{
|
|
|
|
|
LibertyCellEquivMap::Iterator equivs_iter(cell_equivs);
|
|
|
|
|
while (equivs_iter.hasNext()) {
|
|
|
|
|
LibertyCellSeq *equivs = equivs_iter.next();
|
|
|
|
|
sort(equivs, CellDriveResistanceLess());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Use the worst "drive" for all the timing arcs in the cell.
|
|
|
|
|
// Note that this function can NOT be static for sun's compiler to
|
|
|
|
|
// be happy with using it in a sort predicate (presumably because the
|
|
|
|
|
// template functions are compiled outside the scope of this file).
|
|
|
|
|
float
|
|
|
|
|
cellDriveResistance(const LibertyCell *cell)
|
|
|
|
|
{
|
|
|
|
|
float max_drive = 0.0;
|
2018-12-05 23:18:41 +01:00
|
|
|
LibertyCellTimingArcSetIterator set_iter(cell);
|
|
|
|
|
while (set_iter.hasNext()) {
|
|
|
|
|
TimingArcSet *set = set_iter.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
if (!set->role()->isTimingCheck()) {
|
2018-12-05 23:18:41 +01:00
|
|
|
TimingArcSetArcIterator arc_iter(set);
|
|
|
|
|
while (arc_iter.hasNext()) {
|
|
|
|
|
TimingArc *arc = arc_iter.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
GateTimingModel *model = dynamic_cast<GateTimingModel*>(arc->model());
|
|
|
|
|
if (model) {
|
2019-03-13 01:25:53 +01:00
|
|
|
float drive = model->driveResistance(cell, nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (drive > max_drive)
|
|
|
|
|
max_drive = drive;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return max_drive;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
|
hashCell(const LibertyCell *cell)
|
|
|
|
|
{
|
|
|
|
|
return hashCellPorts(cell) + hashCellSequentials(cell);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
|
hashCellPorts(const LibertyCell *cell)
|
|
|
|
|
{
|
|
|
|
|
unsigned hash = 0;
|
2018-12-05 23:18:41 +01:00
|
|
|
LibertyCellPortIterator port_iter(cell);
|
|
|
|
|
while (port_iter.hasNext()) {
|
|
|
|
|
LibertyPort *port = port_iter.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
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
|
|
|
|
|
hashCellSequentials(const LibertyCell *cell)
|
|
|
|
|
{
|
|
|
|
|
unsigned hash = 0;
|
2018-12-05 23:18:41 +01:00
|
|
|
LibertyCellSequentialIterator seq_iter(cell);
|
|
|
|
|
while (seq_iter.hasNext()) {
|
|
|
|
|
Sequential *seq = seq_iter.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
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;
|
2019-03-13 01:25:53 +01:00
|
|
|
hash += int(seq->clearPresetOutput()) * 17;
|
|
|
|
|
hash += int(seq->clearPresetOutputInv()) * 19;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
return hash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
|
hashFuncExpr(const FuncExpr *expr)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
if (expr == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
|
hashString(const char *str)
|
|
|
|
|
{
|
|
|
|
|
unsigned hash = 0;
|
|
|
|
|
size_t length = strlen(str);
|
|
|
|
|
for (size_t i = 0; i < length; i++) {
|
|
|
|
|
hash = str[i] + (hash << 2);
|
|
|
|
|
}
|
|
|
|
|
return hash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
equivCells(const LibertyCell *cell1,
|
|
|
|
|
const LibertyCell *cell2)
|
|
|
|
|
{
|
|
|
|
|
return equivCellPortsAndFuncs(cell1, cell2)
|
|
|
|
|
&& equivCellSequentials(cell1, cell2)
|
|
|
|
|
&& equivCellTimingArcSets(cell1, cell2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
equivCellPortsAndFuncs(const LibertyCell *cell1,
|
|
|
|
|
const LibertyCell *cell2)
|
|
|
|
|
{
|
|
|
|
|
if (cell1->portCount() != cell2->portCount())
|
|
|
|
|
return false;
|
|
|
|
|
else {
|
2018-12-05 23:18:41 +01:00
|
|
|
LibertyCellPortIterator port_iter1(cell1);
|
|
|
|
|
while (port_iter1.hasNext()) {
|
|
|
|
|
LibertyPort *port1 = port_iter1.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
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 {
|
2018-12-05 23:18:41 +01:00
|
|
|
LibertyCellPortIterator port_iter1(cell1);
|
|
|
|
|
while (port_iter1.hasNext()) {
|
|
|
|
|
LibertyPort *port1 = port_iter1.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
const char* name = port1->name();
|
|
|
|
|
LibertyPort *port2 = cell2->findLibertyPort(name);
|
2018-12-05 23:18:41 +01:00
|
|
|
if (!(port2 && LibertyPort::equiv(port1, port2)))
|
2018-09-28 17:54:21 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
equivCellSequentials(const LibertyCell *cell1,
|
|
|
|
|
const LibertyCell *cell2)
|
|
|
|
|
{
|
2018-12-05 23:18:41 +01:00
|
|
|
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();
|
2018-09-28 17:54:21 +02:00
|
|
|
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())
|
2018-12-05 23:18:41 +01:00
|
|
|
&& FuncExpr::equiv(seq1->preset(), seq2->preset())))
|
|
|
|
|
return false;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
2018-12-05 23:18:41 +01:00
|
|
|
return !seq_iter1.hasNext() && !seq_iter2.hasNext();
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
equivCellTimingArcSets(const LibertyCell *cell1,
|
|
|
|
|
const LibertyCell *cell2)
|
|
|
|
|
{
|
|
|
|
|
if (cell1->timingArcSetCount() != cell2->timingArcSetCount())
|
|
|
|
|
return false;
|
|
|
|
|
else {
|
2018-12-05 23:18:41 +01:00
|
|
|
LibertyCellTimingArcSetIterator set_iter1(cell1);
|
|
|
|
|
while (set_iter1.hasNext()) {
|
|
|
|
|
TimingArcSet *set1 = set_iter1.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
TimingArcSet *set2 = cell2->findTimingArcSet(set1);
|
2018-12-05 23:18:41 +01:00
|
|
|
if (!(set2 && TimingArcSet::equiv(set1, set2)))
|
2018-09-28 17:54:21 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|