CycleAccting use UnorderedSet

Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
James Cherry 2021-12-20 10:50:44 -07:00
parent fd01c54d8d
commit ad61c71ce1
7 changed files with 131 additions and 535 deletions

View File

@ -16,7 +16,7 @@
#pragma once
#include "DisallowCopyAssign.hh"
#include "UnorderedSet.hh"
#include "MinMax.hh"
#include "TimingRole.hh"
#include "StaState.hh"
@ -24,6 +24,45 @@
namespace sta {
class CycleAcctingHash
{
public:
size_t operator()(const CycleAccting *acct) const;
};
class CycleAcctingEqual
{
public:
bool operator()(const CycleAccting *acct1,
const CycleAccting *acct2) const;
};
class CycleAcctingLess
{
public:
bool operator()(const CycleAccting *acct1,
const CycleAccting *acct2) const;
};
typedef UnorderedSet<CycleAccting*, CycleAcctingHash, CycleAcctingEqual> CycleAcctingSet;
class CycleAcctings
{
public:
CycleAcctings(Sdc *sdc);
~CycleAcctings();
void clear();
// Find the cycle accounting info for paths that start at src clock
// edge and end at target clock edge.
CycleAccting *cycleAccting(const ClockEdge *src,
const ClockEdge *tgt);
void reportClkToClkMaxCycleWarnings(Report *report);
private:
Sdc *sdc_;
CycleAcctingSet cycle_acctings_;
};
class CycleAccting
{
public:
@ -81,24 +120,4 @@ private:
bool max_cycles_exceeded_;
};
class CycleAcctingLess
{
public:
bool operator()(const CycleAccting *acct1,
const CycleAccting *acct2) const;
};
class CycleAcctingHash
{
public:
size_t operator()(const CycleAccting *acct) const;
};
class CycleAcctingEqual
{
public:
bool operator()(const CycleAccting *acct1,
const CycleAccting *acct2) const;
};
} // namespace

View File

@ -1,453 +0,0 @@
// 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 <https://www.gnu.org/licenses/>.
#pragma once
#include <stddef.h> // size_t
#include "Hash.hh"
namespace sta {
template <class KEY> class HashSetBucket;
template <class KEY, class HASH, class EQUAL>
class HashSet
{
public:
HashSet();
explicit HashSet(size_t capacity,
bool auto_resize);
explicit HashSet(size_t capacity,
bool auto_resize,
HASH hash,
EQUAL equal);
~HashSet();
// Number of objects in the table.
size_t size() const { return size_; }
// Number of hash buckets.
size_t capacity() const { return capacity_; }
// Multi-threaded findKey is safe during resize.
void reserve(size_t capacity);
void insert(KEY key);
KEY findKey(KEY key);
KEY findKey(KEY key) const;
bool hasKey(KEY key);
void erase(KEY key);
bool empty() const;
void clear();
void deleteContentsClear();
int longestBucketLength() const;
size_t longestBucketHash() const;
int bucketLength(size_t hash) const;
void
deleteContents()
{
Iterator iter(this);
while (iter.hasNext())
delete iter.next();
}
// Java style container itererator
// Map::Iterator<string *, Value, stringLess> iter(map);
// while (iter.hasNext()) {
// Value *v = iter.next();
// }
class Iterator
{
public:
Iterator() : container_(nullptr) {}
explicit Iterator(HashSet<KEY, HASH, EQUAL> *container)
{ init(container); }
explicit Iterator(HashSet<KEY, HASH, EQUAL> &container)
{ init(container); }
void init(HashSet<KEY, HASH, EQUAL> *container)
{ container_ = container;
hash_= 0;
next_ = nullptr;
if (container_)
findNext();
}
void init(HashSet<KEY, HASH, EQUAL> &container)
{ container_ = &container;
hash_= 0;
next_ = nullptr;
if (container_)
findNext();
}
bool hasNext() { return container_ && next_ != nullptr; }
KEY next() {
HashSetBucket<KEY> *next = next_;
findNext();
return next->key();
}
HashSet<KEY, HASH, EQUAL> *container() { return container_; }
private:
void
findNext()
{
if (next_)
next_ = next_->next();
while (next_ == nullptr
&& hash_ < container_->capacity())
next_ = container_->table_[hash_++];
}
HashSet<KEY, HASH, EQUAL> *container_;
size_t hash_;
HashSetBucket<KEY> *next_;
};
class ConstIterator
{
public:
ConstIterator() : container_(nullptr) {}
explicit ConstIterator(const HashSet<KEY, HASH, EQUAL> *container)
{ init(container); }
explicit ConstIterator(const HashSet<KEY, HASH, EQUAL> &container)
{ init(container); }
void init(const HashSet<KEY, HASH, EQUAL> *container)
{ container_ = container; hash_= 0; next_ = nullptr; findNext(); }
void init(HashSet<KEY, HASH, EQUAL> &container)
{ container_ = &container; hash_= 0; next_ = nullptr; findNext(); }
bool hasNext() { return container_ && next_ != nullptr; }
KEY next() {
HashSetBucket<KEY> *next = next_;
findNext();
return next->key();
}
HashSet<KEY, HASH, EQUAL> *container() { return container_; }
private:
void
findNext()
{
if (next_)
next_ = next_->next();
while (next_ == nullptr
&& hash_ < container_->capacity())
next_ = container_->table_[hash_++];
}
const HashSet<KEY, HASH, EQUAL> *container_;
size_t hash_;
HashSetBucket<KEY> *next_;
};
protected:
void initTable();
static const int default_capacity = (2 << 6) - 1;
// Table size.
size_t capacity_;
bool auto_resize_;
HASH hash_;
EQUAL equal_;
size_t size_;
HashSetBucket<KEY> **table_;
HashSet *tmp_;
};
template <class KEY>
class HashSetBucket
{
public:
HashSetBucket(KEY key, HashSetBucket *next);
KEY key() const { return key_; }
void setKey(KEY key) { key_ = key; }
HashSetBucket *next() const { return next_; }
void setNext(HashSetBucket *next) { next_ = next; }
private:
KEY key_;
HashSetBucket *next_;
};
template <class KEY>
HashSetBucket<KEY>::HashSetBucket(KEY key,
HashSetBucket *next) :
key_(key),
next_(next)
{
}
////////////////////////////////////////////////////////////////
template <class KEY, class HASH, class EQUAL>
HashSet<KEY, HASH, EQUAL>::HashSet() :
capacity_(default_capacity),
auto_resize_(true),
hash_(HASH()),
equal_(EQUAL())
{
initTable();
}
template <class KEY, class HASH, class EQUAL>
HashSet<KEY, HASH, EQUAL>::HashSet(size_t capacity,
bool auto_resize) :
capacity_(capacity),
auto_resize_(auto_resize),
hash_(HASH()),
equal_(EQUAL())
{
initTable();
}
template <class KEY, class HASH, class EQUAL>
HashSet<KEY, HASH, EQUAL>::HashSet(size_t capacity,
bool auto_resize,
HASH hash,
EQUAL equal) :
capacity_(capacity),
auto_resize_(auto_resize),
hash_(hash),
equal_(equal)
{
initTable();
}
template <class KEY, class HASH, class EQUAL>
void
HashSet<KEY, HASH, EQUAL>::initTable()
{
size_ = 0;
tmp_ = nullptr;
table_ = new HashSetBucket<KEY>*[capacity_];
for (size_t i = 0; i < capacity_; i++)
table_[i] = nullptr;
}
template <class KEY, class HASH, class EQUAL>
HashSet<KEY, HASH, EQUAL>::~HashSet()
{
for (size_t hash = 0; hash < capacity_; hash++) {
HashSetBucket<KEY> *next;
for (HashSetBucket<KEY> *bucket = table_[hash];
bucket;
bucket = next) {
next = bucket->next();
delete bucket;
}
}
delete [] table_;
delete tmp_;
}
template <class KEY, class HASH, class EQUAL>
KEY
HashSet<KEY, HASH, EQUAL>::findKey(KEY key) const
{
size_t hash = hash_(key) % capacity_;
HashSetBucket<KEY> *head = table_[hash];
for (HashSetBucket<KEY> *bucket = head; bucket; bucket = bucket->next()) {
KEY bucket_key = bucket->key();
if (equal_(bucket_key, key)) {
return bucket_key;
}
}
return nullptr;
}
template <class KEY, class HASH, class EQUAL>
KEY
HashSet<KEY, HASH, EQUAL>::findKey(KEY key)
{
size_t hash = hash_(key) % capacity_;
HashSetBucket<KEY> *head = table_[hash];
for (HashSetBucket<KEY> *bucket = head; bucket; bucket = bucket->next()) {
KEY bucket_key = bucket->key();
if (equal_(bucket_key, key)) {
return bucket_key;
}
}
return nullptr;
}
template <class KEY, class HASH, class EQUAL>
bool
HashSet<KEY, HASH, EQUAL>::hasKey(KEY key)
{
size_t hash = hash_(key) % capacity_;
HashSetBucket<KEY> *head = table_[hash];
for (HashSetBucket<KEY> *bucket = head; bucket; bucket = bucket->next()) {
KEY bucket_key = bucket->key();
if (equal_(bucket_key, key)) {
return true;
}
}
return false;
}
template <class KEY, class HASH, class EQUAL>
void
HashSet<KEY, HASH, EQUAL>::insert(KEY key)
{
size_t hash = hash_(key) % capacity_;
HashSetBucket<KEY> *head = table_[hash];
for (HashSetBucket<KEY> *bucket = head; bucket; bucket = bucket->next()) {
if (equal_(bucket->key(), key)) {
bucket->setKey(key);
return;
}
}
HashSetBucket<KEY> *bucket = new HashSetBucket<KEY>(key, head);
table_[hash] = bucket;
size_++;
if (size_ > capacity_
&& auto_resize_)
reserve(nextMersenne(capacity_));
}
template <class KEY, class HASH, class EQUAL>
void
HashSet<KEY, HASH, EQUAL>::reserve(size_t capacity)
{
if (capacity != capacity_) {
if (size_ == 0) {
// Table is empty.
capacity_ = capacity;
delete [] table_;
delete tmp_;
initTable();
}
else {
delete tmp_;
// Copy entries to tmp.
tmp_ = new HashSet<KEY, HASH, EQUAL>(capacity, auto_resize_,
hash_, equal_);
for (size_t hash = 0; hash < capacity_; hash++) {
for (HashSetBucket<KEY> *bucket = table_[hash];
bucket;
bucket = bucket->next())
tmp_->insert(bucket->key());
}
size_t prev_capacity = capacity_;
HashSetBucket<KEY> **prev_table = table_;
// Switch over.
table_ = tmp_->table_;
capacity_ = capacity;
tmp_->capacity_ = prev_capacity;
tmp_->table_ = prev_table;
}
}
}
template <class KEY, class HASH, class EQUAL>
void
HashSet<KEY, HASH, EQUAL>::erase(KEY key)
{
size_t hash = hash_(key) % capacity_;
HashSetBucket<KEY> *head = table_[hash];
HashSetBucket<KEY> *prev = nullptr;
for (HashSetBucket<KEY> *bucket = head; bucket; bucket = bucket->next()) {
if (equal_(bucket->key(), key)) {
if (prev)
prev->setNext(bucket->next());
else
table_[hash] = bucket->next();
delete bucket;
size_--;
break;
}
prev = bucket;
}
}
template <class KEY, class HASH, class EQUAL>
void
HashSet<KEY, HASH, EQUAL>::deleteContentsClear()
{
if (size_ > 0) {
for (size_t hash = 0; hash < capacity_; hash++) {
HashSetBucket<KEY> *next;
for (HashSetBucket<KEY> *bucket = table_[hash];
bucket;
bucket = next) {
delete bucket->key();
next = bucket->next();
delete bucket;
}
table_[hash] = nullptr;
}
size_ = 0;
}
}
template <class KEY, class HASH, class EQUAL>
void
HashSet<KEY, HASH, EQUAL>::clear()
{
if (size_ > 0) {
for (size_t hash = 0; hash < capacity_; hash++) {
HashSetBucket<KEY> *next;
for (HashSetBucket<KEY> *bucket = table_[hash];
bucket;
bucket = next) {
next = bucket->next();
delete bucket;
}
table_[hash] = nullptr;
}
size_ = 0;
}
}
template <class KEY, class HASH, class EQUAL>
bool
HashSet<KEY, HASH, EQUAL>::empty() const
{
return size_ == 0;
}
template <class KEY, class HASH, class EQUAL>
int
HashSet<KEY, HASH, EQUAL>::longestBucketLength() const
{
return bucketLength(longestBucketHash());
}
template <class KEY, class HASH, class EQUAL>
size_t
HashSet<KEY, HASH, EQUAL>::longestBucketHash() const
{
int longest = 0;
size_t longest_hash = 0;
for (size_t hash = 0; hash < capacity_; hash++) {
int length = bucketLength(hash);
if (length > longest) {
longest = length;
longest_hash = hash;
}
}
return longest_hash;
}
template <class KEY, class HASH, class EQUAL>
int
HashSet<KEY, HASH, EQUAL>::bucketLength(size_t hash) const
{
int length = 0;
for (HashSetBucket<KEY> *bucket = table_[hash];
bucket;
bucket = bucket->next())
length++;
return length;
}
} // namespace

View File

@ -22,7 +22,6 @@
#include "StringUtil.hh"
#include "StringSet.hh"
#include "Map.hh"
#include "HashSet.hh"
#include "UnorderedMap.hh"
#include "MinMax.hh"
#include "StaState.hh"
@ -107,8 +106,6 @@ typedef Set<InputDelay*> InputDelaySet;
typedef Map<const Pin*,InputDelaySet*> InputDelaysPinMap;
typedef Set<OutputDelay*> OutputDelaySet;
typedef Map<const Pin*,OutputDelaySet*> OutputDelaysPinMap;
// Use HashSet so no read lock is required.
typedef HashSet<CycleAccting*, CycleAcctingHash, CycleAcctingEqual> CycleAcctingSet;
typedef Set<Instance*> InstanceSet;
typedef UnorderedMap<const Pin*,ExceptionPathSet*> PinExceptionsMap;
typedef Map<const Clock*,ExceptionPathSet*> ClockExceptionsMap;
@ -1286,7 +1283,7 @@ protected:
ClockGatingCheckMap clk_gating_check_map_;
InstanceClockGatingCheckMap inst_clk_gating_check_map_;
PinClockGatingCheckMap pin_clk_gating_check_map_;
CycleAcctingSet cycle_acctings_;
CycleAcctings cycle_acctings_;
std::mutex cycle_acctings_lock_;
DataChecksMap data_checks_from_map_;
DataChecksMap data_checks_to_map_;

View File

@ -30,7 +30,6 @@ class Sdc;
class Clock;
class ClockEdge;
class CycleAccting;
class CycleAcctingLess;
class InputDelay;
class OutputDelay;
class FalsePath;

View File

@ -24,9 +24,80 @@
#include "Units.hh"
#include "TimingRole.hh"
#include "Clock.hh"
#include "Sdc.hh"
namespace sta {
CycleAcctings::CycleAcctings(Sdc *sdc) :
sdc_(sdc)
{
}
CycleAcctings::~CycleAcctings()
{
clear();
}
void
CycleAcctings::clear()
{
cycle_acctings_.deleteContentsClear();
}
// Determine cycle accounting "on demand".
CycleAccting *
CycleAcctings::cycleAccting(const ClockEdge *src,
const ClockEdge *tgt)
{
if (src == nullptr)
src = tgt;
CycleAccting probe(src, tgt);
CycleAccting *acct = cycle_acctings_.findKey(&probe);
if (acct == nullptr) {
acct = new CycleAccting(src, tgt);
if (src == sdc_->defaultArrivalClockEdge())
acct->findDefaultArrivalSrcDelays();
else
acct->findDelays(sdc_);
cycle_acctings_.insert(acct);
}
return acct;
}
void
CycleAcctings::reportClkToClkMaxCycleWarnings(Report *report)
{
// Find cycle acctings that exceed max cycle count. Eliminate
// duplicate warnings between different src/tgt clk edges.
ClockPairSet clk_warnings;
for (Clock *src_clk : *sdc_->clocks()) {
for (RiseFall *src_rf : RiseFall::range()) {
ClockEdge *src = src_clk->edge(src_rf);
for (Clock *tgt_clk : *sdc_->clocks()) {
for (RiseFall *tgt_rf : RiseFall::range()) {
ClockEdge *tgt = tgt_clk->edge(tgt_rf);
CycleAccting probe(src, tgt);
CycleAccting *acct = cycle_acctings_.findKey(&probe);
if (acct && acct->maxCyclesExceeded()) {
// Canonicalize the warning wrt src/tgt.
ClockPair clk_pair1(src_clk, tgt_clk);
ClockPair clk_pair2(tgt_clk, src_clk);
if (!clk_warnings.hasKey(clk_pair1)
&& !clk_warnings.hasKey(clk_pair2)) {
report->warn(9, "No common period was found between clocks %s and %s.",
src_clk->name(),
tgt_clk->name());
clk_warnings.insert(clk_pair1);
}
}
}
}
}
}
}
////////////////////////////////////////////////////////////////
CycleAccting::CycleAccting(const ClockEdge *src,
const ClockEdge *tgt) :
src_(src),

View File

@ -92,6 +92,7 @@ Sdc::Sdc(StaState *sta) :
clk_group_same_(nullptr),
clk_sense_map_(network_),
clk_gating_check_(nullptr),
cycle_acctings_(this),
input_delay_index_(0),
port_cap_map_(nullptr),
net_wire_cap_map_(nullptr),
@ -2413,65 +2414,20 @@ CycleAccting *
Sdc::cycleAccting(const ClockEdge *src,
const ClockEdge *tgt)
{
if (src == nullptr)
src = tgt;
CycleAccting *acct;
CycleAccting probe(src, tgt);
acct = cycle_acctings_.findKey(&probe);
if (acct == nullptr) {
UniqueLock lock(cycle_acctings_lock_);
// Recheck with lock.
acct = cycle_acctings_.findKey(&probe);
if (acct == nullptr) {
acct = new CycleAccting(src, tgt);
if (src == defaultArrivalClockEdge())
acct->findDefaultArrivalSrcDelays();
else
acct->findDelays(this);
cycle_acctings_.insert(acct);
}
}
return acct;
UniqueLock lock(cycle_acctings_lock_);
return cycle_acctings_.cycleAccting(src, tgt);
}
void
Sdc::reportClkToClkMaxCycleWarnings()
{
// Find cycle acctings that exceed max cycle count. Eliminate
// duplicate warnings between different src/tgt clk edges.
ClockPairSet clk_warnings;
ClockPairSeq clk_warnings2;
CycleAcctingSet::Iterator acct_iter(cycle_acctings_);
while (acct_iter.hasNext()) {
CycleAccting *acct = acct_iter.next();
if (acct->maxCyclesExceeded()) {
Clock *src = acct->src()->clock();
Clock *tgt = acct->target()->clock();
// Canonicalize the warning wrt src/tgt.
if (src->index() > tgt->index())
std::swap(src, tgt);
ClockPair clk_pair(src, tgt);
if (!clk_warnings.hasKey(clk_pair)) {
clk_warnings.insert(clk_pair);
clk_warnings2.push_back(clk_pair);
}
}
}
// Sort clk pairs so that results are stable.
sort(clk_warnings2, ClockPairLess());
for (auto pair : clk_warnings2) {
report_->warn(9, "No common period was found between clocks %s and %s.",
pair.first->name(),
pair.second->name());
}
cycle_acctings_.reportClkToClkMaxCycleWarnings(report_);
}
void
Sdc::clearCycleAcctings()
{
cycle_acctings_.deleteContentsClear();
cycle_acctings_.clear();
}
////////////////////////////////////////////////////////////////

View File

@ -844,13 +844,12 @@ public:
const Corner *corner,
const MinMaxAll *min_max,
const StaState *sta);
MakeEndpointPathEnds(const MakeEndpointPathEnds &make_path_ends);
~MakeEndpointPathEnds();
virtual VertexVisitor *copy() const;
virtual void visit(Vertex *vertex);
private:
DISALLOW_COPY_AND_ASSIGN(MakeEndpointPathEnds);
VisitPathEnds *visit_path_ends_;
PathEndVisitor *path_end_visitor_;
const Corner *corner_;
@ -870,6 +869,15 @@ MakeEndpointPathEnds::MakeEndpointPathEnds(PathEndVisitor *path_end_visitor,
{
}
MakeEndpointPathEnds::MakeEndpointPathEnds(const MakeEndpointPathEnds &make_path_ends) :
visit_path_ends_(new VisitPathEnds(make_path_ends.sta_)),
path_end_visitor_(make_path_ends.path_end_visitor_->copy()),
corner_(make_path_ends.corner_),
min_max_(make_path_ends.min_max_),
sta_(make_path_ends.sta_)
{
}
MakeEndpointPathEnds::~MakeEndpointPathEnds()
{
delete visit_path_ends_;
@ -903,15 +911,14 @@ PathGroups::makeGroupPathEnds(VertexSet *endpoints,
end_visitor.visit(endpoint);
}
else {
Vector<MakeEndpointPathEnds*> visitors;
for (int i = 0; i < thread_count_; i++)
visitors.push_back(new MakeEndpointPathEnds(visitor, corner, min_max, this));
Vector<MakeEndpointPathEnds> visitors(thread_count_,
MakeEndpointPathEnds(visitor, corner,
min_max, this));
for (auto endpoint : *endpoints) {
dispatch_queue_->dispatch( [endpoint, &visitors](int i)
{ visitors[i]->visit(endpoint); } );
{ visitors[i].visit(endpoint); } );
}
dispatch_queue_->finishTasks();
visitors.deleteContents();
}
}