support equiv cells across libraries

This commit is contained in:
James Cherry 2019-06-20 21:41:49 -07:00
parent e26da7c37e
commit 5f23536b17
9 changed files with 179 additions and 165 deletions

View File

@ -15,7 +15,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Machine.hh"
#include "UnorderedMap.hh"
#include "PortDirection.hh"
#include "Transition.hh"
#include "MinMax.hh"
@ -31,17 +30,6 @@ namespace sta {
using std::max;
typedef UnorderedMap<unsigned, LibertyCellSeq*> LibertyCellHashMap;
typedef Set<LibertyCell*> LibertyCellSet;
static void
findEquivCells1(const LibertyLibrary *library,
LibertyCellSet &cell_equivs);
static void
sortCellEquivs(LibertyCellSet &cell_equivs);
static float
cellDriveResistance(const LibertyCell *cell);
static unsigned
hashCell(const LibertyCell *cell);
static unsigned
@ -55,91 +43,110 @@ hashPort(const LibertyPort *port);
static unsigned
hashString(const char *str);
static bool
equivCellSequentials(const LibertyCell *cell1,
const LibertyCell *cell2);
void
findEquivCells(const LibertyLibrary *library)
class CellDriveResistanceLess
{
LibertyCellSet cell_equivs;
findEquivCells1(library, cell_equivs);
// Sort by drive strength.
sortCellEquivs(cell_equivs);
public:
bool operator()(const LibertyCell *cell1,
const LibertyCell *cell2) const
{
return cell1->driveResistance() > cell2->driveResistance();
}
};
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, CellDriveResistanceLess());
}
if (map_libs) {
for (auto lib : *map_libs)
mapEquivCells(lib, hash_matches);
}
hash_matches.deleteContents();
}
static void
findEquivCells1(const LibertyLibrary *library,
LibertyCellSet &cell_equivs)
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)
{
LibertyCellHashMap cell_hash;
LibertyCellIterator cell_iter(library);
while (cell_iter.hasNext()) {
LibertyCell *cell = cell_iter.next();
if (!cell->dontUse()) {
if (!cell->dontUse()
&& !stringBeginEqual(cell->name(), "DLY")) {
unsigned hash = hashCell(cell);
// Use a comprehensive hash on cell properties to segregate
// cells into groups of potential matches.
LibertyCellSeq *matches = cell_hash[hash];
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 = match->equivCellsRaw();
LibertyCellSeq *equivs = equiv_cells_.findKey(match);
if (equivs == nullptr) {
equivs = new LibertyCellSeq;
equivs->push_back(match);
match->setEquivCells(equivs);
cell_equivs.insert(match);
unique_equiv_cells_.push_back(match);
equiv_cells_[match] = equivs;
}
equivs->push_back(cell);
cell->setEquivCells(equivs);
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;
}
}
}
else {
matches = new LibertyCellSeq;
cell_hash[hash] = matches;
}
matches->push_back(cell);
}
}
cell_hash.deleteContents();
}
struct CellDriveResistanceLess
{
bool operator()(const LibertyCell *cell1,
const LibertyCell *cell2) const
{
return cellDriveResistance(cell1) > cellDriveResistance(cell2);
}
};
static float
cellDriveResistance(const LibertyCell *cell)
{
return max(cell->driveResistance(TransRiseFall::rise()),
cell->driveResistance(TransRiseFall::fall()));
}
static void
sortCellEquivs(LibertyCellSet &cell_equivs)
{
for (auto equiv : cell_equivs) {
LibertyCellSeq *equivs = equiv->equivCells();
sort(equivs, CellDriveResistanceLess());
LibertyCell *lower = nullptr;
LibertyCellSeq::Iterator cell_iter(equivs);
while (cell_iter.hasNext()) {
LibertyCell *cell = cell_iter.next();
if (lower) {
lower->setHigherDrive(cell);
cell->setLowerDrive(lower);
}
lower = cell;
}
}
}
@ -273,7 +280,7 @@ equivCellPorts(const LibertyCell *cell1,
}
}
static bool
bool
equivCellSequentials(const LibertyCell *cell1,
const LibertyCell *cell2)
{

View File

@ -17,15 +17,37 @@
#ifndef STA_EQUIV_CELLS_H
#define STA_EQUIV_CELLS_H
#include "Vector.hh"
#include "Map.hh"
#include "UnorderedMap.hh"
#include "LibertyClass.hh"
namespace sta {
// Find equivalent cells, sort by drive strength and
// and set cell->equivCells/higherDrive/lowerDrive.
void
findEquivCells(const LibertyLibrary *library);
typedef Map<LibertyCell*, LibertyCellSeq*> EquivCellMap;
typedef UnorderedMap<unsigned, LibertyCellSeq*> LibertyCellHashMap;
class EquivCells
{
public:
// Find equivalent cells in equiv_libs.
// Optionally add mappings for cells in map_libs.
EquivCells(LibertyLibrarySeq *equiv_libs,
LibertyLibrarySeq *map_libs);
~EquivCells();
// Find equivalents for cell (member of from_libs) in to_libs.
LibertyCellSeq *equivs(LibertyCell *cell);
protected:
void findEquivCells(const LibertyLibrary *library,
LibertyCellHashMap &hash_matches);
void mapEquivCells(const LibertyLibrary *library,
LibertyCellHashMap &hash_matches);
EquivCellMap equiv_cells_;
// Unique cell for each equiv cell group.
LibertyCellSeq unique_equiv_cells_;
};
// Predicate that is true when the ports, functions, sequentials and
// timing arcs match.
@ -48,5 +70,9 @@ bool
equivCellTimingArcSets(const LibertyCell *cell1,
const LibertyCell *cell2);
bool
equivCellSequentials(const LibertyCell *cell1,
const LibertyCell *cell2);
} // namespace
#endif

View File

@ -84,7 +84,6 @@ LibertyLibrary::LibertyLibrary(const char *name,
default_operating_conditions_(nullptr),
ocv_arc_depth_(0.0),
default_ocv_derate_(nullptr),
found_equiv_cells_(false),
buffers_(nullptr)
{
// Scalar templates are builtin.
@ -153,15 +152,6 @@ LibertyLibrary::findLibertyCellsMatching(PatternMatch *pattern,
}
}
void
LibertyLibrary::ensureEquivCells()
{
if (!found_equiv_cells_) {
findEquivCells(this);
found_equiv_cells_ = true;
}
}
LibertyCellSeq *
LibertyLibrary::buffers()
{
@ -866,10 +856,7 @@ LibertyCell::LibertyCell(LibertyLibrary *library,
ocv_derate_(nullptr),
is_disabled_constraint_(false),
leakage_power_(0.0),
leakage_power_exists_(false),
equiv_cells_(nullptr),
higher_drive_(nullptr),
lower_drive_(nullptr)
leakage_power_exists_(false)
{
liberty_cell_ = this;
}
@ -885,16 +872,6 @@ LibertyCell::~LibertyCell()
timing_arc_set_from_map_.deleteContents();
timing_arc_set_to_map_.deleteContents();
if (equiv_cells_) {
// Carefull because loop below nulls equiv_cells_.
auto equiv_cells = equiv_cells_;
// equiv_cells_ is shared by all of the equivalent cells, so
// delete it once for all of them and null the others.
for (auto equiv : *equiv_cells_)
equiv->setEquivCells(nullptr);
delete equiv_cells;
}
deleteInternalPowerAttrs();
internal_powers_.deleteContents();
leakage_powers_.deleteContents();
@ -1463,20 +1440,6 @@ LibertyCell::setCornerCell(LibertyCell *corner_cell,
////////////////////////////////////////////////////////////////
LibertyCellSeq *
LibertyCell::equivCells()
{
if (equiv_cells_ == nullptr)
liberty_library_->ensureEquivCells();
return equiv_cells_;
}
void
LibertyCell::setEquivCells(LibertyCellSeq *equiv_cells)
{
equiv_cells_ = equiv_cells;
}
// Use the worst "drive" for all the timing arcs in the cell.
float
LibertyCell::driveResistance(const TransRiseFall *tr) const
@ -1489,7 +1452,8 @@ LibertyCell::driveResistance(const TransRiseFall *tr) const
TimingArcSetArcIterator arc_iter(set);
while (arc_iter.hasNext()) {
TimingArc *arc = arc_iter.next();
if (arc->toTrans()->asRiseFall() == tr) {
if (tr == nullptr
|| arc->toTrans()->asRiseFall() == tr) {
GateTimingModel *model = dynamic_cast<GateTimingModel*>(arc->model());
if (model) {
float drive = model->driveResistance(this, nullptr);
@ -1503,30 +1467,10 @@ LibertyCell::driveResistance(const TransRiseFall *tr) const
return max_drive;
}
LibertyCell *
LibertyCell::higherDrive() const
float
LibertyCell::driveResistance() const
{
liberty_library_->ensureEquivCells();
return higher_drive_;
}
LibertyCell *
LibertyCell::lowerDrive() const
{
liberty_library_->ensureEquivCells();
return lower_drive_;
}
void
LibertyCell::setHigherDrive(LibertyCell *cell)
{
higher_drive_ = cell;
}
void
LibertyCell::setLowerDrive(LibertyCell *cell)
{
lower_drive_ = cell;
return driveResistance(nullptr);
}
////////////////////////////////////////////////////////////////

View File

@ -289,7 +289,6 @@ protected:
const TableModel *model,
float in_slew,
float wire_delay) const;
void ensureEquivCells();
Units *units_;
DelayModelType delay_model_type_;
@ -330,7 +329,6 @@ protected:
OcvDerate *default_ocv_derate_;
OcvDerateMap ocv_derate_map_;
SupplyVoltageMap supply_voltage_map_;
bool found_equiv_cells_;
LibertyCellSeq *buffers_;
// Set if any library has rise/fall capacitances.
@ -492,13 +490,9 @@ public:
virtual void finish(bool infer_latches,
Report *report,
Debug *debug);
LibertyCellSeq *equivCells();
// Internal.
LibertyCellSeq *equivCellsRaw() { return equiv_cells_; }
void setEquivCells(LibertyCellSeq *equiv_cells);
float driveResistance(const TransRiseFall *tr) const;
void setHigherDrive(LibertyCell *cell);
void setLowerDrive(LibertyCell *cell);
// Max of rise/fall.
float driveResistance() const;
bool isBuffer() const;
// Only valid when isBuffer() returns true.
void bufferPorts(// Return values.
@ -569,10 +563,6 @@ protected:
float leakage_power_;
bool leakage_power_exists_;
LibertyPgPortMap pg_port_map_;
LibertyCellSeq *equiv_cells_;
// Next higher/lower drive equivalent cells.
LibertyCell *higher_drive_;
LibertyCell *lower_drive_;
private:
DISALLOW_COPY_AND_ASSIGN(LibertyCell);

View File

@ -57,6 +57,7 @@ class TransRiseFall;
class TransRiseFallBoth;
class LibertyCellSequentialIterator;
typedef Vector<LibertyLibrary*> LibertyLibrarySeq;
typedef Vector<LibertyCell*> LibertyCellSeq;
typedef Vector<Sequential*> SequentialSeq;
typedef Map<LibertyCell*, LibertyCellSeq*> LibertyCellEquivMap;

View File

@ -531,10 +531,6 @@ getProperty(const LibertyCell *cell,
return PropertyValue(cell->driveResistance(TransRiseFall::rise()));
else if (stringEqual(property, "drive_resistance_fall"))
return PropertyValue(cell->driveResistance(TransRiseFall::fall()));
else if (stringEqual(property, "higher_drive"))
return PropertyValue(cell->higherDrive());
else if (stringEqual(property, "lower_drive"))
return PropertyValue(cell->lowerDrive());
else if (stringEqual(property, "is_buffer"))
return PropertyValue(cell->isBuffer());
else if (stringEqual(property, "dont_use"))

View File

@ -276,7 +276,8 @@ Sta::Sta() :
report_path_(nullptr),
power_(nullptr),
link_make_black_boxes_(true),
update_genclks_(false)
update_genclks_(false),
equiv_cells_(nullptr)
{
}
@ -517,6 +518,7 @@ Sta::~Sta()
delete units_;
delete report_;
delete power_;
delete equiv_cells_;
}
void
@ -3644,7 +3646,7 @@ Sta::replaceCell(Instance *inst,
{
NetworkEdit *network = networkCmdEdit();
LibertyCell *from_lib_cell = network->libertyCell(inst);
if (equivCells(from_lib_cell, to_lib_cell)) {
if (sta::equivCells(from_lib_cell, to_lib_cell)) {
replaceEquivCellBefore(inst, to_lib_cell);
network->replaceCell(inst, to_cell);
replaceEquivCellAfter(inst);
@ -4966,6 +4968,24 @@ Sta::reportCheck(MaxSkewCheck *check,
////////////////////////////////////////////////////////////////
void
Sta::makeEquivCells(LibertyLibrarySeq *equiv_libs,
LibertyLibrarySeq *map_libs)
{
delete equiv_cells_;
equiv_cells_ = new EquivCells(equiv_libs, map_libs);
}
LibertyCellSeq *
Sta::equivCells(LibertyCell *cell)
{
if (equiv_cells_)
return equiv_cells_->equivs(cell);
else
return nullptr;
}
////////////////////////////////////////////////////////////////
void
Sta::powerPreamble()
{

View File

@ -58,6 +58,7 @@ class ReportField;
class Power;
class PowerResult;
class ClockIterator;
class EquivCells;
typedef InstanceSeq::Iterator SlowDrvrIterator;
typedef Vector<const char*> CheckError;
@ -1182,6 +1183,12 @@ public:
// Return values.
PowerResult &result);
// Find equivalent cells in equiv_libs.
// Optionally add mappings for cells in map_libs.
void makeEquivCells(LibertyLibrarySeq *equiv_libs,
LibertyLibrarySeq *map_libs);
LibertyCellSeq *equivCells(LibertyCell *cell);
protected:
// Default constructors that are called by makeComponents in the Sta
// constructor. These can be redefined by a derived class to
@ -1311,6 +1318,7 @@ protected:
Tcl_Interp *tcl_interp_;
bool link_make_black_boxes_;
bool update_genclks_;
EquivCells *equiv_cells_;
// Singleton sta used by tcl command interpreter.
static Sta *sta_;

View File

@ -50,8 +50,8 @@
#include "Transition.hh"
#include "TimingRole.hh"
#include "TimingArc.hh"
#include "EquivCells.hh"
#include "Liberty.hh"
#include "EquivCells.hh"
#include "Network.hh"
#include "Clock.hh"
#include "PortDelay.hh"
@ -2279,19 +2279,41 @@ find_library_buffers(LibertyLibrary *library)
return library->buffers();
}
LibertyCellSeq *
equiv_cells(LibertyCell *cell)
void
make_equiv_cells(LibertyLibrary *lib)
{
return cell->equivCells();
LibertyLibrarySeq libs;
libs.push_back(lib);
Sta::sta()->makeEquivCells(&libs, nullptr);
}
LibertyCellSeq *
find_equiv_cells(LibertyCell *cell)
{
return Sta::sta()->equivCells(cell);
}
bool
equiv_cells(LibertyCell *cell1,
LibertyCell *cell2)
{
return sta::equivCells(cell1, cell2);
}
bool
equiv_cell_ports(LibertyCell *cell1,
LibertyCell *cell2)
LibertyCell *cell2)
{
return equivCellPorts(cell1, cell2);
}
bool
equiv_cell_timing_arcs(LibertyCell *cell1,
LibertyCell *cell2)
{
return equivCellTimingArcSets(cell1, cell2);
}
void
set_cmd_namespace_cmd(const char *namespc)
{