diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 6c821dab..8dbcfe47 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -395,7 +395,7 @@ GraphDelayCalc::seedRootSlew(Vertex *vertex, seedDrvrSlew(vertex, arc_delay_calc); else seedLoadSlew(vertex); - iter_->enqueueAdjacentVertices(vertex); + iter_->enqueueFanout(vertex); } void @@ -686,7 +686,11 @@ GraphDelayCalc::findVertexDelay(Vertex *vertex, debugPrint(debug_, "delay_calc", 2, "find delays {} ({})", vertex->to_string(this), network_->cellName(network_->instance(pin))); - if (vertex->isRoot()) + if (vertex->isRoot() + // Bidirect port drivers are enqueued by their load vertex in + // annotateLoadDelays. + || (vertex->isBidirectDriver() + && network_->isTopLevelPort(pin))) seedRootSlew(vertex, arc_delay_calc); else { if (network_->isLeaf(pin)) { @@ -703,7 +707,7 @@ GraphDelayCalc::findVertexDelay(Vertex *vertex, // change when non-incremental to stride past annotations. if (!incremental_ || loadSlewsChanged(load_slews_prev, load_pin_index_map)) - iter_->enqueueAdjacentVertices(vertex); + iter_->enqueueFanout(vertex); } } else { @@ -711,14 +715,9 @@ GraphDelayCalc::findVertexDelay(Vertex *vertex, enqueueTimingChecksEdges(vertex); // Enqueue driver vertices from this input load. if (propagate) - iter_->enqueueAdjacentVertices(vertex); + iter_->enqueueFanout(vertex); } } - // Bidirect port drivers are enqueued by their load vertex in - // annotateLoadDelays. - else if (vertex->isBidirectDriver() - && network_->isTopLevelPort(pin)) - seedRootSlew(vertex, arc_delay_calc); } } diff --git a/graph/Graph.cc b/graph/Graph.cc index 589e1fa0..b1f40d4c 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -418,6 +418,8 @@ Graph::makePinVertices(Pin *pin, Vertex *&vertex, Vertex *&bidir_drvr_vertex) { + vertex = nullptr; + bidir_drvr_vertex = nullptr; PortDirection *dir = network_->direction(pin); if (!dir->isPowerGround()) { bool is_reg_clk = network_->isRegClkPin(pin); @@ -427,8 +429,6 @@ Graph::makePinVertices(Pin *pin, bidir_drvr_vertex = makeVertex(pin, true, is_reg_clk); pin_bidirect_drvr_vertex_map_[pin] = bidir_drvr_vertex; } - else - bidir_drvr_vertex = nullptr; } } @@ -581,9 +581,9 @@ Graph::visitFanouts(Vertex *vertex, const VertexFn &fn) { if (pred->searchFrom(vertex)) { - for (Edge *edge = this->edge(vertex->out_edges_); - edge; - edge = this->edge(edge->vertex_out_next_)) { + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); Vertex *to_vertex = this->vertex(edge->to_); if (pred->searchThru(edge) && pred->searchTo(to_vertex)) @@ -598,9 +598,9 @@ Graph::visitFanoutEdges(Vertex *vertex, const EdgeFn &fn) { if (pred->searchFrom(vertex)) { - for (Edge *edge = this->edge(vertex->out_edges_); - edge; - edge = this->edge(edge->vertex_out_next_)) { + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); Vertex *to_vertex = this->vertex(edge->to_); if (pred->searchThru(edge) && pred->searchTo(to_vertex)) @@ -615,9 +615,9 @@ Graph::visitFanins(Vertex *vertex, const VertexFn &fn) { if (pred->searchFrom(vertex)) { - for (Edge *edge = this->edge(vertex->in_edges_); - edge; - edge = this->edge(edge->vertex_in_next_)) { + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); Vertex *from_vertex = this->vertex(edge->from_); if (pred->searchThru(edge) && pred->searchFrom(from_vertex)) @@ -632,9 +632,9 @@ Graph::visitFaninEdges(Vertex *vertex, const EdgeFn &fn) { if (pred->searchFrom(vertex)) { - for (Edge *edge = this->edge(vertex->in_edges_); - edge; - edge = this->edge(edge->vertex_in_next_)) { + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); Vertex *from_vertex = this->vertex(edge->from_); if (pred->searchThru(edge) && pred->searchFrom(from_vertex)) diff --git a/include/sta/Bfs.hh b/include/sta/Bfs.hh index 5c11ab1c..c0c24f1e 100644 --- a/include/sta/Bfs.hh +++ b/include/sta/Bfs.hh @@ -24,6 +24,7 @@ #pragma once +#include #include #include @@ -38,6 +39,8 @@ class SearchPred; class BfsFwdIterator; class BfsBkwdIterator; +using VertexFn = std::function; + // LevelQueue is a vector of vertex vectors indexed by logic level. using LevelQueue = std::vector; @@ -50,26 +53,23 @@ using LevelQueue = std::vector; // Vertices are marked as being in the queue by using a flag on // the vertex indexed by bfs_index. A unique flag is only needed // if the BFS in in use when other BFS's are simultaneously in use. -class BfsIterator : public StaState, - public Iterator +class BfsIterator : public StaState { public: // Make sure that the BFS queue is deep enough for the max logic level. void ensureSize(); // Reset to virgin state. void clear(); + // Apply fn to each vertex and clear. + void clear(const VertexFn &fn); [[nodiscard]] bool empty() const; // Enqueue a vertex to search from. void enqueue(Vertex *vertex); // Enqueue vertices adjacent to a vertex. - void enqueueAdjacentVertices(Vertex *vertex); - virtual void enqueueAdjacentVertices(Vertex *vertex, - const Mode *mode); + virtual void enqueueAdjacentVertices(Vertex *vertex) = 0; virtual void enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred, const Mode *mode) = 0; - virtual void enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred) = 0; + [[nodiscard]] bool inQueue(Vertex *vertex); void checkInQueue(Vertex *vertex); // Notify iterator that vertex will be deleted. @@ -77,10 +77,6 @@ public: void remove(Vertex *vertex); void reportEntries() const; - bool hasNext() override; - bool hasNext(Level to_level); - Vertex *next() override; - // Apply visitor to all vertices in the queue in level order. // Returns the number of vertices that are visited. virtual int visit(Level to_level, @@ -91,6 +87,10 @@ public: int visitParallel(Level to_level, VertexVisitor *visitor); + bool hasNext(); + bool hasNext(Level to_level); + Vertex *next(); + protected: BfsIterator(BfsIndex bfs_index, Level level_min, @@ -104,10 +104,10 @@ protected: virtual bool levelLessOrEqual(Level level1, Level level2) const = 0; virtual void incrLevel(Level &level) const = 0; - void findNext(Level to_level); void deleteEntries(); void checkLevel(Vertex *vertex, Level level); + void findNext(Level to_level); BfsIndex bfs_index_; Level level_min_; @@ -131,12 +131,13 @@ public: SearchPred *search_pred, StaState *sta); ~BfsFwdIterator() override; + void enqueueAdjacentVertices(Vertex *vertex) override; void enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred) override; - void enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred, const Mode *mode) override; using BfsIterator::enqueueAdjacentVertices; + void enqueueFanout(Vertex *vertex); + void enqueueFanout(Vertex *vertex, + const Mode *mode); protected: bool levelLessOrEqual(Level level1, @@ -153,14 +154,17 @@ public: SearchPred *search_pred, StaState *sta); ~BfsBkwdIterator() override; + void enqueueAdjacentVertices(Vertex *vertex) override; void enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred) override; - void enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred, const Mode *mode) override; using BfsIterator::enqueueAdjacentVertices; + void enqueueFanin(Vertex *vertex); + void enqueueFanin(Vertex *vertex, + const Mode *mode); protected: + void enqueueFanin(Vertex *vertex, + SearchPred *search_pred); bool levelLessOrEqual(Level level1, Level level2) const override; bool levelLess(Level level1, diff --git a/include/sta/FilterObjects.hh b/include/sta/FilterObjects.hh index 71bea6c0..791c6b10 100644 --- a/include/sta/FilterObjects.hh +++ b/include/sta/FilterObjects.hh @@ -30,6 +30,7 @@ #include "GraphClass.hh" #include "NetworkClass.hh" +#include "Scene.hh" #include "SdcClass.hh" #include "SearchClass.hh" #include "StringUtil.hh" @@ -64,6 +65,16 @@ filterClocks(std::string_view filter_expression, ClockSeq *clks, Sta *sta); +SceneSeq +filterScenes(std::string_view filter_expression, + SceneSeq *scenes, + Sta *sta); + +ModeSeq +filterModes(std::string_view filter_expression, + ModeSeq *modes, + Sta *sta); + LibertyCellSeq filterLibCells(std::string_view filter_expression, LibertyCellSeq *cells, diff --git a/include/sta/Property.hh b/include/sta/Property.hh index 57260cde..84ad5cfc 100644 --- a/include/sta/Property.hh +++ b/include/sta/Property.hh @@ -28,6 +28,7 @@ #include #include #include +#include #include "LibertyClass.hh" #include "NetworkClass.hh" @@ -61,6 +62,118 @@ private: std::map> registry_; }; +// Adding a new property type +// value union (string values use std::string* so the union stays trivial) +// enum Type +// constructor +// copy constructor switch clause +// move constructor switch clause +// operator= & switch clause +// operator= && switch clause +// StaTcl.i swig %typemap(out) PropertyValue switch clause + +class PropertyValue +{ +public: + enum class Type { none, string, float_, bool_, + library, cell, port, + liberty_library, liberty_cell, liberty_port, + instance, pin, pins, net, + clk, clks, paths, pwr_activity }; + PropertyValue(); + PropertyValue(std::string_view value); + PropertyValue(std::string value); + PropertyValue(float value, + const Unit *unit); + PropertyValue(bool value); + PropertyValue(const Library *value); + PropertyValue(const Cell *value); + PropertyValue(const Port *value); + PropertyValue(const LibertyLibrary *value); + PropertyValue(const LibertyCell *value); + PropertyValue(const LibertyPort *value); + PropertyValue(const Instance *value); + PropertyValue(const Pin *value); + PropertyValue(PinSeq *value); + PropertyValue(PinSet *value); + PropertyValue(const PinSet &value); + PropertyValue(const Net *value); + PropertyValue(const Clock *value); + PropertyValue(ClockSeq *value); + PropertyValue(ClockSet *value); + PropertyValue(ConstPathSeq *value); + PropertyValue(PwrActivity *value); + PropertyValue(const PropertyValue &value); + PropertyValue(PropertyValue &&value) noexcept; + ~PropertyValue(); + Type type() const { return type_; } + const Unit *unit() const { return unit_; } + + std::string to_string(const Network *network) const; + const std::string &stringValue() const; // valid for type string + float floatValue() const; // valid for type float + bool boolValue() const; // valid for type bool + const LibertyLibrary *libertyLibrary() const { return liberty_library_; } + const LibertyCell *libertyCell() const { return liberty_cell_; } + const LibertyPort *libertyPort() const { return liberty_port_; } + const Library *library() const { return library_; } + const Cell *cell() const { return cell_; } + const Port *port() const { return port_; } + const Instance *instance() const { return inst_; } + const Pin *pin() const { return pin_; } + PinSeq *pins() const { return pins_; } + const Net *net() const { return net_; } + const Clock *clock() const { return clk_; } + ClockSeq *clocks() const { return clks_; } + ConstPathSeq *paths() const { return paths_; } + PwrActivity pwrActivity() const { return pwr_activity_; } + + // Copy assignment. + PropertyValue &operator=(const PropertyValue &); + // Move assignment. + PropertyValue &operator=(PropertyValue &&) noexcept; + +private: + void destroyActive(); + + Type type_; + union { + // Use heap string to simplify initialization/destrucction. + std::string *string_; + float float_; + bool bool_; + const Library *library_; + const Cell *cell_; + const Port *port_; + const LibertyLibrary *liberty_library_; + const LibertyCell *liberty_cell_; + const LibertyPort *liberty_port_; + const Instance *inst_; + const Pin *pin_; + PinSeq *pins_; + const Net *net_; + const Clock *clk_; + ClockSeq *clks_; + ConstPathSeq *paths_; + PwrActivity pwr_activity_; + }; + const Unit *unit_; +}; + +// Key for user-defined property values: the object instance and the +// property name. +class PropertyKey +{ +public: + PropertyKey(const void *object, + std::string_view property); + bool operator<(const PropertyKey &key) const; + +private: + const void *object_; + std::string property_; +}; + class Properties { public: @@ -124,6 +237,25 @@ public: const PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, const PropertyRegistry::PropertyHandler &handler); + void defineProperty(std::string_view property, + const PropertyRegistry::PropertyHandler &handler); + void defineProperty(std::string_view property, + const PropertyRegistry::PropertyHandler &handler); + + // User-defined, per-object mutable properties. defineProperty registers + // a property of the given value type ("bool", "float" or "string"); + // setProperty sets the value on one object. Objects the property was never + // set on read back as an empty (none) value. The property is read through + // the same registry path as every other property so get_property / + // get_* -filter work unchanged. + template + void defineProperty(std::string_view object_type, + std::string_view property, + std::string_view value_type); + void setProperty(const void *object, + std::string_view object_type, + std::string_view property, + std::string_view value); protected: PropertyValue portSlew(const Port *port, @@ -149,6 +281,9 @@ protected: PropertyValue edgeDelay(Edge *edge, const RiseFall *rf, const MinMax *min_max); + PropertyValue::Type propertyType(std::string_view type); + PropertyValue coercePropertyValue(PropertyValue::Type type, + std::string_view value); PropertyRegistry registry_library_; PropertyRegistry registry_liberty_library_; @@ -160,106 +295,16 @@ protected: PropertyRegistry registry_pin_; PropertyRegistry registry_net_; PropertyRegistry registry_clock_; + PropertyRegistry registry_scene_; + PropertyRegistry registry_mode_; - Sta *sta_; -}; - -// Adding a new property type -// value union (string values use std::string* so the union stays trivial) -// enum Type -// constructor -// copy constructor switch clause -// move constructor switch clause -// operator= & switch clause -// operator= && switch clause -// StaTcl.i swig %typemap(out) PropertyValue switch clause + // Value types of user-defined properties keyed by object type name and + // property name. + std::map, PropertyValue::Type> prop_types_; + // User-defined property values. + std::map prop_values_; -class PropertyValue -{ -public: - enum class Type { none, string, float_, bool_, - library, cell, port, - liberty_library, liberty_cell, liberty_port, - instance, pin, pins, net, - clk, clks, paths, pwr_activity }; - PropertyValue(); - PropertyValue(std::string_view value); - PropertyValue(std::string value); - PropertyValue(float value, - const Unit *unit); - PropertyValue(bool value); - PropertyValue(const Library *value); - PropertyValue(const Cell *value); - PropertyValue(const Port *value); - PropertyValue(const LibertyLibrary *value); - PropertyValue(const LibertyCell *value); - PropertyValue(const LibertyPort *value); - PropertyValue(const Instance *value); - PropertyValue(const Pin *value); - PropertyValue(PinSeq *value); - PropertyValue(PinSet *value); - PropertyValue(const PinSet &value); - PropertyValue(const Net *value); - PropertyValue(const Clock *value); - PropertyValue(ClockSeq *value); - PropertyValue(ClockSet *value); - PropertyValue(ConstPathSeq *value); - PropertyValue(PwrActivity *value); - PropertyValue(const PropertyValue &value); - PropertyValue(PropertyValue &&value) noexcept; - ~PropertyValue(); - Type type() const { return type_; } - const Unit *unit() const { return unit_; } - - std::string to_string(const Network *network) const; - const std::string &stringValue() const; // valid for type string - float floatValue() const; // valid for type float - bool boolValue() const; // valid for type bool - const LibertyLibrary *libertyLibrary() const { return liberty_library_; } - const LibertyCell *libertyCell() const { return liberty_cell_; } - const LibertyPort *libertyPort() const { return liberty_port_; } - const Library *library() const { return library_; } - const Cell *cell() const { return cell_; } - const Port *port() const { return port_; } - const Instance *instance() const { return inst_; } - const Pin *pin() const { return pin_; } - PinSeq *pins() const { return pins_; } - const Net *net() const { return net_; } - const Clock *clock() const { return clk_; } - ClockSeq *clocks() const { return clks_; } - ConstPathSeq *paths() const { return paths_; } - PwrActivity pwrActivity() const { return pwr_activity_; } - - // Copy assignment. - PropertyValue &operator=(const PropertyValue &); - // Move assignment. - PropertyValue &operator=(PropertyValue &&) noexcept; - -private: - void destroyActive(); - - Type type_; - union { - // Use heap string to simplify initialization/destrucction. - std::string *string_; - float float_; - bool bool_; - const Library *library_; - const Cell *cell_; - const Port *port_; - const LibertyLibrary *liberty_library_; - const LibertyCell *liberty_cell_; - const LibertyPort *liberty_port_; - const Instance *inst_; - const Pin *pin_; - PinSeq *pins_; - const Net *net_; - const Clock *clk_; - ClockSeq *clks_; - ConstPathSeq *paths_; - PwrActivity pwr_activity_; - }; - const Unit *unit_; + Sta *sta_; }; } // namespace sta diff --git a/include/sta/Search.hh b/include/sta/Search.hh index 539c9895..5661a387 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -286,8 +286,6 @@ public: TagGroupBldr *tag_bldr); void postponeLatchDataOutputs(Vertex *vertex); void postponeArrivals(Vertex *vertex); - void enqueuePendingClkFanouts(); - void postponeClkFanouts(Vertex *vertex); void seedRequired(Vertex *vertex); void seedRequiredEnqueueFanin(Vertex *vertex); void seedInputDelayArrival(const Pin *pin, @@ -503,12 +501,12 @@ protected: bool is_segment_start, const MinMax *min_max, Scene *scene); - void seedClkVertexArrivals(); - void findClkArrivals1(); + void enqueueClkRoots(); + void enqueueInvalidClks(); - void findAllArrivals(bool thru_latches, - bool clks_only); + void findAllArrivals(bool thru_latches); void findArrivals1(Level level); + void findArrivals2(Level level); Tag *mutateTag(Tag *from_tag, const Pin *from_pin, const RiseFall *from_rf, @@ -646,12 +644,9 @@ protected: std::vector tag_group_free_indices_; std::mutex tag_group_lock_; - // Latches data outputs to queue on the next search pass. - VertexSet postponed_arrivals_; - std::mutex postponed_arrivals_lock_; - // Clock network endpoints where arrival search was suppended by findClkArrivals(). - VertexSet postponed_clk_endpoints_; - std::mutex postponed_clk_endpoints_lock_; + // Arrivals to queue on the next search pass. + VertexSet pending_arrivals_; + std::mutex pending_arrivals_lock_; VertexSet endpoints_; bool endpoints_initialized_{false}; diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 9f9f53ed..99bcba4f 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -947,6 +947,11 @@ public: // from/thrus/to are owned and deleted by Search. // PathEnds in the returned PathEndSeq are owned by Search PathGroups // and deleted on next call. + // + // IMPORTANT: THIS IS NOT THE DROID YOU ARE LOOKING FOR. + // This function is specifically designed to support the many options + // and results of timing reports. It is NOT a good option to find paths + // for optimization. PathEndSeq findPathEnds(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, diff --git a/sdc/FilterObjects.cc b/sdc/FilterObjects.cc index 509e48df..eeea29f4 100644 --- a/sdc/FilterObjects.cc +++ b/sdc/FilterObjects.cc @@ -42,6 +42,7 @@ #include "GraphCmp.hh" #include "Liberty.hh" #include "LibertyClass.hh" +#include "Mode.hh" #include "Network.hh" #include "NetworkClass.hh" #include "PathEnd.hh" @@ -497,6 +498,30 @@ filterClocks(std::string_view filter_expression, }, sta); } +SceneSeq +filterScenes(std::string_view filter_expression, + SceneSeq *scenes, + Sta *sta) +{ + return filterObjects(filter_expression, scenes, + [] (const Scene *scene1, + const Scene *scene2) { + return scene1->name() < scene2->name(); + }, sta); +} + +ModeSeq +filterModes(std::string_view filter_expression, + ModeSeq *modes, + Sta *sta) +{ + return filterObjects(filter_expression, modes, + [] (const Mode *mode1, + const Mode *mode2) { + return mode1->name() < mode2->name(); + }, sta); +} + LibertyCellSeq filterLibCells(std::string_view filter_expression, LibertyCellSeq *cells, diff --git a/sdc/Sdc.i b/sdc/Sdc.i index 4b3050b0..3ac13d81 100644 --- a/sdc/Sdc.i +++ b/sdc/Sdc.i @@ -1531,6 +1531,22 @@ filter_clocks(const char *filter_expression, return filterClocks(filter_expression, clocks, sta); } +SceneSeq +filter_scenes(const char *filter_expression, + SceneSeq *scenes) +{ + sta::Sta *sta = Sta::sta(); + return filterScenes(filter_expression, scenes, sta); +} + +ModeSeq +filter_modes(const char *filter_expression, + ModeSeq *modes) +{ + sta::Sta *sta = Sta::sta(); + return filterModes(filter_expression, modes, sta); +} + LibertyCellSeq filter_lib_cells(const char *filter_expression, LibertyCellSeq *cells) diff --git a/search/Bfs.cc b/search/Bfs.cc index 1768ae43..50c8252c 100644 --- a/search/Bfs.cc +++ b/search/Bfs.cc @@ -71,13 +71,21 @@ BfsIterator::ensureSize() void BfsIterator::clear() +{ + clear([] (Vertex *) {}); +} + +void +BfsIterator::clear(const VertexFn &fn) { Level level = first_level_; while (levelLessOrEqual(level, last_level_)) { VertexSeq &level_vertices = queue_[level]; for (Vertex *vertex : level_vertices) { - if (vertex) + if (vertex) { vertex->setBfsInQueue(bfs_index_, false); + fn(vertex); + } } level_vertices.clear(); incrLevel(level); @@ -116,19 +124,6 @@ BfsIterator::empty() const return levelLess(last_level_, first_level_); } -void -BfsIterator::enqueueAdjacentVertices(Vertex *vertex) -{ - enqueueAdjacentVertices(vertex, search_pred_); -} - -void -BfsIterator::enqueueAdjacentVertices(Vertex *vertex, - const Mode *mode) -{ - enqueueAdjacentVertices(vertex, search_pred_, mode); -} - int BfsIterator::visit(Level to_level, VertexVisitor *visitor) @@ -218,50 +213,6 @@ BfsIterator::visitParallel(Level to_level, return visit_count; } -bool -BfsIterator::hasNext() -{ - return hasNext(last_level_); -} - -bool -BfsIterator::hasNext(Level to_level) -{ - findNext(to_level); - return levelLessOrEqual(first_level_, last_level_) - && !queue_[first_level_].empty(); -} - -Vertex * -BfsIterator::next() -{ - VertexSeq &level_vertices = queue_[first_level_]; - Vertex *vertex = level_vertices.back(); - level_vertices.pop_back(); - vertex->setBfsInQueue(bfs_index_, false); - return vertex; -} - -void -BfsIterator::findNext(Level to_level) -{ - while (levelLessOrEqual(first_level_, last_level_) - && levelLessOrEqual(first_level_, to_level)) { - VertexSeq &level_vertices = queue_[first_level_]; - // Skip null entries from deleted vertices. - while (!level_vertices.empty()) { - Vertex *vertex = level_vertices.back(); - if (vertex == nullptr) - level_vertices.pop_back(); - else { - checkLevel(vertex, first_level_); - return; - } - } - incrLevel(first_level_); - } -} - void BfsIterator::enqueue(Vertex *vertex) { @@ -341,6 +292,52 @@ BfsIterator::remove(Vertex *vertex) //////////////////////////////////////////////////////////////// +bool +BfsIterator::hasNext() +{ + return hasNext(last_level_); +} + +bool +BfsIterator::hasNext(Level to_level) +{ + findNext(to_level); + return levelLessOrEqual(first_level_, last_level_) + && !queue_[first_level_].empty(); +} + +Vertex * +BfsIterator::next() +{ + VertexSeq &level_vertices = queue_[first_level_]; + Vertex *vertex = level_vertices.back(); + level_vertices.pop_back(); + vertex->setBfsInQueue(bfs_index_, false); + return vertex; +} + +void +BfsIterator::findNext(Level to_level) +{ + while (levelLessOrEqual(first_level_, last_level_) + && levelLessOrEqual(first_level_, to_level)) { + VertexSeq &level_vertices = queue_[first_level_]; + // Skip null entries from deleted vertices. + while (!level_vertices.empty()) { + Vertex *vertex = level_vertices.back(); + if (vertex == nullptr) + level_vertices.pop_back(); + else { + checkLevel(vertex, first_level_); + return; + } + } + incrLevel(first_level_); + } +} + +//////////////////////////////////////////////////////////////// + BfsFwdIterator::BfsFwdIterator(BfsIndex bfs_index, SearchPred *search_pred, StaState *sta) : @@ -376,15 +373,46 @@ BfsFwdIterator::levelLess(Level level1, } void -BfsFwdIterator::enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred) +BfsFwdIterator::enqueueFanout(Vertex *vertex) { - if (search_pred->searchFrom(vertex)) { + if (search_pred_->searchFrom(vertex)) { VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *to_vertex = edge->to(graph_); - if (search_pred->searchThru(edge) && search_pred->searchTo(to_vertex)) + if (search_pred_->searchThru(edge) + && search_pred_->searchTo(to_vertex)) + enqueue(to_vertex); + } + } +} + +void +BfsFwdIterator::enqueueFanout(Vertex *vertex, + const Mode *mode) +{ + if (search_pred_->searchFrom(vertex, mode)) { + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (search_pred_->searchThru(edge, mode) + && search_pred_->searchTo(to_vertex, mode)) + enqueue(to_vertex); + } + } +} + +void +BfsFwdIterator::enqueueAdjacentVertices(Vertex *vertex) +{ + if (search_pred_->searchFrom(vertex)) { + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (search_pred_->searchThru(edge) + && search_pred_->searchTo(to_vertex)) enqueue(to_vertex); } } @@ -392,16 +420,15 @@ BfsFwdIterator::enqueueAdjacentVertices(Vertex *vertex, void BfsFwdIterator::enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred, const Mode *mode) { - if (search_pred->searchFrom(vertex, mode)) { + if (search_pred_->searchFrom(vertex, mode)) { VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *to_vertex = edge->to(graph_); - if (search_pred->searchThru(edge, mode) - && search_pred->searchTo(to_vertex, mode)) + if (search_pred_->searchThru(edge, mode) + && search_pred_->searchTo(to_vertex, mode)) enqueue(to_vertex); } } @@ -444,15 +471,15 @@ BfsBkwdIterator::levelLess(Level level1, } void -BfsBkwdIterator::enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred) +BfsBkwdIterator::enqueueAdjacentVertices(Vertex *vertex) { - if (search_pred->searchTo(vertex)) { + if (search_pred_->searchTo(vertex)) { VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *from_vertex = edge->from(graph_); - if (search_pred->searchFrom(from_vertex) && search_pred->searchThru(edge)) + if (search_pred_->searchFrom(from_vertex) + && search_pred_->searchThru(edge)) enqueue(from_vertex); } } @@ -460,16 +487,46 @@ BfsBkwdIterator::enqueueAdjacentVertices(Vertex *vertex, void BfsBkwdIterator::enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred, const Mode *mode) { - if (search_pred->searchTo(vertex, mode)) { + if (search_pred_->searchTo(vertex, mode)) { + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *from_vertex = edge->from(graph_); + if (search_pred_->searchFrom(from_vertex, mode) + && search_pred_->searchThru(edge, mode)) + enqueue(from_vertex); + } + } +} + +void +BfsBkwdIterator::enqueueFanin(Vertex *vertex) +{ + if (search_pred_->searchTo(vertex)) { + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *from_vertex = edge->from(graph_); + if (search_pred_->searchFrom(from_vertex) + && search_pred_->searchThru(edge)) + enqueue(from_vertex); + } + } +} + +void +BfsBkwdIterator::enqueueFanin(Vertex *vertex, + const Mode *mode) +{ + if (search_pred_->searchTo(vertex, mode)) { VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *from_vertex = edge->from(graph_); - if (search_pred->searchFrom(from_vertex, mode) - && search_pred->searchThru(edge, mode)) + if (search_pred_->searchFrom(from_vertex, mode) + && search_pred_->searchThru(edge, mode)) enqueue(from_vertex); } } diff --git a/search/ClkNetwork.cc b/search/ClkNetwork.cc index 93a29496..991a1821 100644 --- a/search/ClkNetwork.cc +++ b/search/ClkNetwork.cc @@ -24,7 +24,8 @@ #include "ClkNetwork.hh" -#include "Bfs.hh" +#include + #include "Debug.hh" #include "Graph.hh" #include "Mode.hh" @@ -127,7 +128,6 @@ ClkNetwork::findClkPins(bool ideal_only, { const Sdc *sdc = mode_->sdc(); ClkSearchPred srch_pred(this); - BfsFwdIterator bfs(BfsIndex::other, &srch_pred, this); for (Clock *clk : sdc->clocks()) { if (!ideal_only || !clk->isPropagated()) { @@ -136,25 +136,39 @@ ClkNetwork::findClkPins(bool ideal_only, clk_pins = new PinSet(network_); clk_pins_map_[clk] = clk_pins; } + std::queue queue; + VertexSet visited = makeVertexSet(this); + auto enqueue = [&queue, &visited] (Vertex *vertex) { + if (vertex && !visited.contains(vertex)) { + visited.insert(vertex); + queue.push(vertex); + } + }; for (const Pin *pin : clk->leafPins()) { if (!ideal_only || !sdc->isPropagatedClock(pin)) { Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - bfs.enqueue(vertex); - if (bidirect_drvr_vertex) - bfs.enqueue(bidirect_drvr_vertex); + enqueue(vertex); + enqueue(bidirect_drvr_vertex); } } - while (bfs.hasNext()) { - Vertex *vertex = bfs.next(); + while (!queue.empty()) { + Vertex *vertex = queue.front(); + queue.pop(); const Pin *pin = vertex->pin(); if (!ideal_only || !sdc->isPropagatedClock(pin)) { clk_pins->insert(pin); ClockSet &pin_clks = pin_clks_map[pin]; pin_clks.insert(clk); - bfs.enqueueAdjacentVertices(vertex); + graph_->visitFanouts(vertex, &srch_pred, + [&queue, &visited] (Vertex *fanout) { + if (!visited.contains(fanout)) { + visited.insert(fanout); + queue.push(fanout); + } + }); } } } diff --git a/search/Genclks.cc b/search/Genclks.cc index 7b16a51f..2aad7074 100644 --- a/search/Genclks.cc +++ b/search/Genclks.cc @@ -271,7 +271,6 @@ Genclks::ensureMaster(Clock *gclk, // Search backward from generated clock source pin to a clock pin. GenClkMasterSearchPred srch_pred(this); VertexQueue master_queue; - VertexSet visited = makeVertexSet(this); VertexSet src_vertices = makeVertexSet(this); gclk->srcPinVertices(src_vertices, network_, graph_); for (Vertex *vertex : src_vertices) @@ -279,28 +278,25 @@ Genclks::ensureMaster(Clock *gclk, while (!master_queue.empty()) { Vertex *vertex = master_queue.front(); master_queue.pop(); - if (!visited.contains(vertex)) { - visited.insert(vertex); - Pin *pin = vertex->pin(); - if (sdc->isLeafPinClock(pin)) { - ClockSet *master_clks = sdc->findLeafPinClocks(pin); - if (master_clks) { - ClockSet::iterator master_iter = master_clks->begin(); - if (master_iter != master_clks->end()) { - master_clk = *master_iter++; - // Master source pin can actually be a clock source pin. - if (master_clk != gclk) { - gclk->setInferedMasterClk(master_clk); - debugPrint(debug_, "genclk", 2, " {} master clk {}", gclk->name(), - master_clk->name()); - master_clk_count++; - break; - } + Pin *pin = vertex->pin(); + if (sdc->isLeafPinClock(pin)) { + ClockSet *master_clks = sdc->findLeafPinClocks(pin); + if (master_clks) { + ClockSet::iterator master_iter = master_clks->begin(); + if (master_iter != master_clks->end()) { + master_clk = *master_iter++; + // Master source pin can actually be a clock source pin. + if (master_clk != gclk) { + gclk->setInferedMasterClk(master_clk); + debugPrint(debug_, "genclk", 2, " {} master clk {}", gclk->name(), + master_clk->name()); + master_clk_count++; + break; } } } - enqueueFanin(vertex, master_queue, srch_pred); } + enqueueFanin(vertex, master_queue, srch_pred); } } if (master_clk_count > 1) diff --git a/search/Property.cc b/search/Property.cc index 6ecd9ab7..ee3ddc11 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -26,6 +26,7 @@ #include #include +#include #include #include "Clock.hh" @@ -1205,7 +1206,9 @@ Properties::getProperty(const Scene *scene, || property == "full_name") return PropertyValue(scene->name()); else - throw PropertyUnknown("scene", property); + // Unknown properties throw PropertyUnknown; a user-defined property + // never set on this scene returns a none value. + return registry_scene_.getProperty(scene, property, "scene", sta_); } //////////////////////////////////////////////////////////////// @@ -1218,7 +1221,9 @@ Properties::getProperty(const Mode *mode, || property == "full_name") return PropertyValue(mode->name()); else - throw PropertyUnknown("mode", property); + // Unknown properties throw PropertyUnknown; a user-defined property + // never set on this mode returns a none value. + return registry_mode_.getProperty(mode, property, "mode", sta_); } //////////////////////////////////////////////////////////////// @@ -1360,6 +1365,119 @@ Properties::defineProperty(std::string_view property, registry_clock_.defineProperty(property, handler); } +void +Properties::defineProperty(std::string_view property, + const PropertyRegistry::PropertyHandler &handler) +{ + registry_scene_.defineProperty(property, handler); +} + +void +Properties::defineProperty(std::string_view property, + const PropertyRegistry::PropertyHandler &handler) +{ + registry_mode_.defineProperty(property, handler); +} + +//////////////////////////////////////////////////////////////// + +// Value type from the define_property -type argument. +PropertyValue::Type +Properties::propertyType(std::string_view type) +{ + if (type == "bool" || type == "boolean") + return PropertyValue::Type::bool_; + else if (type == "float" || type == "double" || type == "number") + return PropertyValue::Type::float_; + else if (type == "string") + return PropertyValue::Type::string; + else + sta_->report()->error(2210, "unknown property type '{}' (use bool, float or string).", + type); + return PropertyValue::Type::none; +} + +PropertyValue +Properties::coercePropertyValue(PropertyValue::Type type, + std::string_view value) +{ + switch (type) { + case PropertyValue::Type::bool_: + if (stringEqual(value, "true") || value == "1") + return PropertyValue(true); + if (stringEqual(value, "false") || value == "0") + return PropertyValue(false); + sta_->report()->error(2212, "'{}' is not a bool property value.", value); + return PropertyValue(); + case PropertyValue::Type::float_: { + auto [number, valid] = stringFloat(std::string(value)); + if (!valid) + sta_->report()->error(2215, "'{}' is not a float property value.", value); + return PropertyValue(number, sta_->units()->scalarUnit()); + } + default: + return PropertyValue(std::string(value)); + } +} + +PropertyKey::PropertyKey(const void *object, + std::string_view property) : + object_(object), + property_(property) +{ +} + +bool +PropertyKey::operator<(const PropertyKey &key) const +{ + return std::tie(object_, property_) + < std::tie(key.object_, key.property_); +} + +template +void +Properties::defineProperty(std::string_view object_type, + std::string_view property, + std::string_view value_type) +{ + prop_types_[{std::string(object_type), std::string(property)}] = + propertyType(value_type); + std::string name(property); + typename PropertyRegistry::PropertyHandler handler = + [this, name] (const TYPE *object, + Sta *) -> PropertyValue { + auto value_iter = prop_values_.find(PropertyKey(object, name)); + if (value_iter != prop_values_.end()) + return value_iter->second; + // Never set on this object. + return PropertyValue(); + }; + defineProperty(property, handler); +} + +// Object types the tcl interface exposes user properties on. +template void Properties::defineProperty(std::string_view, + std::string_view, + std::string_view); +template void Properties::defineProperty(std::string_view, + std::string_view, + std::string_view); + +void +Properties::setProperty(const void *object, + std::string_view object_type, + std::string_view property, + std::string_view value) +{ + auto type_iter = prop_types_.find({std::string(object_type), + std::string(property)}); + if (type_iter == prop_types_.end()) + sta_->report()->error(2211, "{} property '{}' is not defined.", + object_type, property); + prop_values_[PropertyKey(object, property)] = + coercePropertyValue(type_iter->second, value); +} + //////////////////////////////////////////////////////////////// template diff --git a/search/Property.i b/search/Property.i index 2002f901..c282f5fb 100644 --- a/search/Property.i +++ b/search/Property.i @@ -25,6 +25,7 @@ %{ #include "Property.hh" +#include "Report.hh" #include "Sta.hh" using namespace sta; @@ -137,6 +138,39 @@ mode_property(Mode *mode, return properties.getProperty(mode, property); } +void +define_property_cmd(const char *object_type, + const char *property, + const char *type) +{ + Properties &properties = Sta::sta()->properties(); + std::string_view object_type_view(object_type); + if (object_type_view == "scene") + properties.defineProperty(object_type, property, type); + else if (object_type_view == "mode") + properties.defineProperty(object_type, property, type); + else + Sta::sta()->report()->error(2209, "define_property -object_type {} not supported.", + object_type); +} + +void +set_property_cmd(void *object, + const char *object_type, + const char *property, + const char *value) +{ + Properties &properties = Sta::sta()->properties(); + std::string_view object_type_view(object_type); + if (object_type_view == "Scene") + properties.setProperty(object, "scene", property, value); + else if (object_type_view == "Mode") + properties.setProperty(object, "mode", property, value); + else + Sta::sta()->report()->error(2214, "set_property unsupported object type {}.", + object_type); +} + PropertyValue path_end_property(PathEnd *end, const char *property) diff --git a/search/Search.cc b/search/Search.cc index 879c1247..69be1f7f 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -30,6 +30,7 @@ #include "Bfs.hh" #include "ClkInfo.hh" +#include "ClkNetwork.hh" #include "Clock.hh" #include "ContainerHelpers.hh" #include "Crpr.hh" @@ -95,6 +96,8 @@ EvalPred::searchThru(Edge *edge, { const TimingRole *role = edge->role(); return SearchPred0::searchThru(edge, mode) + && (sta_->variables()->dynamicLoopBreaking() + || !edge->isDisabledLoop()) && (search_thru_latches_ || role->isLatchDtoQ() || sta_->latches()->latchDtoQState(edge, mode) == LatchEnableState::open); @@ -263,8 +266,7 @@ Search::Search(StaState *sta) : tag_group_capacity_(tag_capacity_), tag_groups_(new TagGroup *[tag_group_capacity_]), tag_group_set_(new TagGroupSet(tag_group_capacity_)), - postponed_arrivals_(makeVertexSet(this)), - postponed_clk_endpoints_(makeVertexSet(this)), + pending_arrivals_(makeVertexSet(this)), endpoints_(makeVertexSet(this)), invalid_endpoints_(makeVertexSet(this)), @@ -328,8 +330,7 @@ Search::clear() deletePathGroups(); deletePaths(); deleteTags(); - postponed_arrivals_.clear(); - postponed_clk_endpoints_.clear(); + pending_arrivals_.clear(); deleteFilter(); found_downstream_clk_pins_ = false; } @@ -546,7 +547,7 @@ Search::findFilteredArrivals(ExceptionFrom *from, // These cases do not require filtered arrivals. // -from clocks // -to - findAllArrivals(thru_latches, false); + findAllArrivals(thru_latches); } // From/thrus/to are used to make a filter exception. If the last @@ -639,32 +640,36 @@ Search::deleteFilterClkInfos() void Search::findFilteredArrivals(bool thru_latches) { + // Search always_to_endpoint to search from exisiting arrivals at + // fanin startpoints to reach -thru/-to endpoints. + arrival_visitor_->init(true, false, eval_pred_); + arrival_iter_->ensureSize(); + filtered_arrivals_.clear(); findArrivalsSeed(); seedFilterStarts(); + Level max_level = levelize_->maxLevel(); - // Search always_to_endpoint to search from exisiting arrivals at - // fanin startpoints to reach -thru/-to endpoints. - arrival_visitor_->init(true, false, eval_pred_); - enqueuePendingClkFanouts(); - bool have_pending_latch_outputs = false; - // Iterate until data arrivals at all latches stop changing. + bool have_pending_arrivals = false; for (int pass = 1; - pass == 1 || (thru_latches && have_pending_latch_outputs); + pass == 1 || (thru_latches && have_pending_arrivals); pass++) { debugPrint(debug_, "search", 1, "find arrivals pass {}", pass); int arrival_count = arrival_iter_->visitParallel(max_level, arrival_visitor_); debugPrint(debug_, "search", 1, "found {} arrivals", arrival_count); - have_pending_latch_outputs = !postponed_arrivals_.empty(); - for (Vertex *latch_output : postponed_arrivals_) - arrival_visitor_->visit(latch_output, true); - postponed_arrivals_.clear(); + // Latch D->Q and disabled loop edges have level discontinuities so their + // eval. For latches the data path crpr clk path may be evaled in another + // thread at the same time the latch D->Q edge. + // Disabled loop edges propagate pending loop paths here. + have_pending_arrivals = !pending_arrivals_.empty(); + for (Vertex *vertex : pending_arrivals_) + arrival_visitor_->visit(vertex, true); + pending_arrivals_.clear(); deleteTagsPrev(); } - arrivals_exist_ = true; } // Delete stale tag arrarys. @@ -860,14 +865,13 @@ void Search::levelsChangedBefore() { if (arrivals_exist_) { - while (arrival_iter_->hasNext()) { - Vertex *vertex = arrival_iter_->next(); + arrival_iter_->clear([this] (Vertex *vertex) { arrivalInvalid(vertex); - } - while (required_iter_->hasNext()) { - Vertex *vertex = required_iter_->next(); + }); + + required_iter_->clear([this] (Vertex *vertex) { requiredInvalid(vertex); - } + }); } } @@ -939,11 +943,16 @@ Search::requiredInvalid(Vertex *vertex) void Search::findClkArrivals() { - findAllArrivals(false, true); + debugPrint(debug_, "search", 1, "find clk arrivals"); + arrival_visitor_->init(false, true, eval_pred_); + arrival_iter_->ensureSize(); + enqueueClkRoots(); + enqueueInvalidClks(); + findArrivals2(levelize_->maxLevel()); } void -Search::seedClkVertexArrivals() +Search::enqueueClkRoots() { PinSet clk_pins(network_); findClkVertexPins(clk_pins); @@ -954,6 +963,32 @@ Search::seedClkVertexArrivals() if (bidirect_drvr_vertex) arrival_iter_->enqueue(bidirect_drvr_vertex); } + arrivals_exist_ = true; +} + +void +Search::enqueueInvalidClks() +{ + if (!invalid_arrivals_.empty()) { + for (Mode *mode : modes_) + mode->clkNetwork()->ensureClkNetwork(); + for (auto itr = invalid_arrivals_.begin(); itr != invalid_arrivals_.end();) { + Vertex *vertex = *itr; + bool is_clk = false; + for (Mode *mode : modes_) { + if (mode->clkNetwork()->isClock(vertex)) { + is_clk = true; + break; + } + } + if (is_clk) { + arrival_iter_->enqueue(vertex); + itr = invalid_arrivals_.erase(itr); + } + else + itr++; + } + } } Arrival @@ -980,49 +1015,29 @@ Search::clockInsertion(const Clock *clk, void Search::findAllArrivals() { - findAllArrivals(true, false); + findAllArrivals(true); } void -Search::findAllArrivals(bool thru_latches, - bool clks_only) +Search::findAllArrivals(bool thru_latches) { - if (!clks_only) - enqueuePendingClkFanouts(); - arrival_visitor_->init(false, clks_only, eval_pred_); - bool have_pending_latch_outputs = false; + arrival_visitor_->init(false, false, eval_pred_); + arrival_iter_->ensureSize(); + + bool have_pending_arrivals = false; // Iterate until data arrivals at all latches stop changing. for (int pass = 1; - pass == 1 || (thru_latches && have_pending_latch_outputs); + pass == 1 || (thru_latches && have_pending_arrivals); pass++) { debugPrint(debug_, "search", 1, "find arrivals pass {}", pass); findArrivals1(levelize_->maxLevel()); - have_pending_latch_outputs = !postponed_arrivals_.empty(); - for (Vertex *latch_output : postponed_arrivals_) - arrival_visitor_->visit(latch_output, true); - postponed_arrivals_.clear(); - } -} - -// Pick up where the search stopped at the clock network boundary. -void -Search::enqueuePendingClkFanouts() -{ - for (Vertex *vertex : postponed_clk_endpoints_) { - debugPrint(debug_, "search", 2, "enqueue clk fanout {}", - vertex->to_string(this)); - arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_); + have_pending_arrivals = !pending_arrivals_.empty(); + for (Vertex *vertex : pending_arrivals_) + arrival_visitor_->visit(vertex, true); + pending_arrivals_.clear(); } - postponed_clk_endpoints_.clear(); -} - -void -Search::postponeClkFanouts(Vertex *vertex) -{ - LockGuard lock(postponed_clk_endpoints_lock_); - postponed_clk_endpoints_.insert(vertex); } void @@ -1035,6 +1050,7 @@ void Search::findArrivals(Level level) { arrival_visitor_->init(false, false, eval_pred_); + arrival_iter_->ensureSize(); findArrivals1(level); } @@ -1043,13 +1059,19 @@ Search::findArrivals1(Level level) { debugPrint(debug_, "search", 1, "find arrivals to level {}", level); findArrivalsSeed(); + findArrivals2(level); +} + +// Caller seeds arrival_iter_. +void +Search::findArrivals2(Level level) +{ Stats stats(debug_, report_); int arrival_count = arrival_iter_->visitParallel(level, arrival_visitor_); deleteTagsPrev(); if (arrival_count > 0) deleteUnusedTagGroups(); stats.report("Find arrivals"); - arrivals_exist_ = true; debugPrint(debug_, "search", 1, "found {} arrivals", arrival_count); } @@ -1059,15 +1081,9 @@ Search::findArrivalsSeed() if (!arrivals_seeded_) { for (const Mode *mode : modes_) mode->genclks()->ensureInsertionDelays(); - arrival_iter_->clear(); - required_iter_->clear(); seedArrivals(); arrivals_seeded_ = true; } - else { - arrival_iter_->ensureSize(); - required_iter_->ensureSize(); - } seedInvalidArrivals(); } @@ -1173,22 +1189,18 @@ ArrivalVisitor::visit(Vertex *vertex, search_->postponeLatchDataOutputs(vertex); if ((always_to_endpoints_ || arrivals_changed)) { - if (clks_only_ && vertex->isRegClk()) { - debugPrint(debug_, "search", 3, "postponing clk fanout"); - search_->postponeClkFanouts(vertex); - } - else { - graph_->visitFanoutEdges(vertex, search_adj_, - [this] (Edge *edge, - Vertex *fanout) { - if (edge->isDisabledLoop()) { - if (hasPendingLoopPaths(edge)) - search_->postponeArrivals(fanout); - } - else - search_->arrivalIterator()->enqueue(fanout); - }); - } + graph_->visitFanoutEdges(vertex, search_adj_, + [this, vertex] (Edge *edge, + Vertex *fanout) { + if (edge->isDisabledLoop()) { + if (hasPendingLoopPaths(edge)) + search_->postponeArrivals(fanout); + } + else if (clks_only_ && vertex->isRegClk()) + search_->arrivalInvalid(fanout); + else + search_->arrivalIterator()->enqueue(fanout); + }); } if (arrivals_changed) { debugPrint(debug_, "search", 4, "arrivals changed"); @@ -1425,8 +1437,8 @@ Search::postponeLatchDataOutputs(Vertex *latch_data) Edge *edge = edge_iter.next(); if (edge->role() == TimingRole::latchDtoQ()) { Vertex *out_vertex = edge->to(graph_); - LockGuard lock(postponed_arrivals_lock_); - postponed_arrivals_.insert(out_vertex); + LockGuard lock(pending_arrivals_lock_); + pending_arrivals_.insert(out_vertex); } } } @@ -1434,8 +1446,8 @@ Search::postponeLatchDataOutputs(Vertex *latch_data) void Search::postponeArrivals(Vertex *vertex) { - LockGuard lock(postponed_arrivals_lock_); - postponed_arrivals_.insert(vertex); + LockGuard lock(pending_arrivals_lock_); + pending_arrivals_.insert(vertex); } void @@ -1448,6 +1460,7 @@ Search::seedArrivals() for (Vertex *vertex : vertices) arrival_iter_->enqueue(vertex); + arrivals_exist_ = true; } void @@ -2764,14 +2777,14 @@ Search::reportArrivals(Vertex *vertex, for (const Path *path : paths) { const Tag *tag = path->tag(this); const RiseFall *rf = tag->transition(); - bool report_prev = false; + bool report_prev = true; std::string prev_str; if (report_prev) { Path *prev_path = path->prevPath(); if (prev_path) { const Edge *prev_edge = path->prevEdge(this); TimingArc *arc = path->prevArc(this); - prev_str = sta::format("prev {} {} {} -> {} {}", + prev_str = sta::format(" prev {} {} {} -> {} {}", prev_path->to_string(this), prev_edge->from(graph_)->to_string(this), arc->fromEdge()->to_string(), @@ -2779,13 +2792,14 @@ Search::reportArrivals(Vertex *vertex, arc->toEdge()->to_string()); } else - prev_str = "prev NULL"; + prev_str = " prev NULL"; } report_->report(" {} {} {} / {} {}{}", rf->shortName(), path->minMax(this)->to_string(), delayAsString(path->arrival(), digits, this), delayAsString(path->required(), digits, this), - tag->to_string(report_tag_index, false, this), prev_str); + tag->to_string(report_tag_index, false, this), + prev_str); } } else @@ -3146,6 +3160,7 @@ Search::findRequireds(Level level) Stats stats(debug_, report_); debugPrint(debug_, "search", 1, "find requireds to level {}", level); RequiredVisitor req_visitor(this); + required_iter_->ensureSize(); if (!requireds_seeded_) seedRequireds(); seedInvalidRequireds(); @@ -3334,8 +3349,9 @@ Search::seedRequired(Vertex *vertex) required_cmp.requiredsInit(vertex, this); visit_path_ends_->visitPathEnds(vertex, &seeder); // Enqueue fanin vertices for back-propagating required times. + required_iter_->ensureSize(); if (required_cmp.requiredsSave(vertex, this)) - required_iter_->enqueueAdjacentVertices(vertex); + required_iter_->enqueueFanin(vertex); } void @@ -3347,7 +3363,7 @@ Search::seedRequiredEnqueueFanin(Vertex *vertex) visit_path_ends_->visitPathEnds(vertex, &seeder); // Enqueue fanin vertices for back-propagating required times. required_cmp.requiredsSave(vertex, this); - required_iter_->enqueueAdjacentVertices(vertex); + required_iter_->enqueueFanin(vertex); } //////////////////////////////////////////////////////////////// @@ -3455,7 +3471,7 @@ RequiredVisitor::visit(Vertex *vertex) search_->tnsInvalid(vertex); if (changed) - search_->requiredIterator()->enqueueAdjacentVertices(vertex); + search_->requiredIterator()->enqueueFanin(vertex); } bool @@ -3538,17 +3554,27 @@ void Search::ensureDownstreamClkPins() { if (!found_downstream_clk_pins_) { - // Use backward BFS from register clk pins to mark upsteam pins - // as having downstream clk pins. + // Use backward DFS from register clk pins to mark upstream pins + // as having downstream clk pins. hasDownstreamClkPin doubles as the + // visited flag since its meaning is exactly "reached here". ClkTreeSearchPred pred(this); - BfsBkwdIterator iter(BfsIndex::other, &pred, this); - for (Vertex *vertex : graph_->regClkVertices()) - iter.enqueue(vertex); - - while (iter.hasNext()) { - Vertex *vertex = iter.next(); - vertex->setHasDownstreamClkPin(true); - iter.enqueueAdjacentVertices(vertex); + std::vector stack; + for (Vertex *vertex : graph_->regClkVertices()) { + if (!vertex->hasDownstreamClkPin()) { + vertex->setHasDownstreamClkPin(true); + stack.push_back(vertex); + } + } + while (!stack.empty()) { + Vertex *vertex = stack.back(); + stack.pop_back(); + graph_->visitFanins(vertex, &pred, + [&stack] (Vertex *fanin) { + if (!fanin->hasDownstreamClkPin()) { + fanin->setHasDownstreamClkPin(true); + stack.push_back(fanin); + } + }); } } found_downstream_clk_pins_ = true; diff --git a/search/Sta.cc b/search/Sta.cc index 9eaa669e..a31721a8 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -4518,6 +4518,10 @@ Sta::makeInstanceAfter(const Instance *inst) if (pin) { Vertex *vertex, *bidir_drvr_vertex; graph_->makePinVertices(pin, vertex, bidir_drvr_vertex); + if (vertex) + search_->endpointInvalid(vertex); + if (bidir_drvr_vertex) + search_->endpointInvalid(bidir_drvr_vertex); } } graph_->makeInstanceEdges(inst); @@ -4553,16 +4557,14 @@ Sta::replaceEquivCellBefore(const Instance *inst, VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); - Vertex *to_vertex = edge->to(graph_); - if (network_->instance(to_vertex->pin()) == inst) { + if (!edge->isWire()) { TimingArcSet *from_set = edge->timingArcSet(); // Find corresponding timing arc set. TimingArcSet *to_set = to_cell->findTimingArcSet(from_set); if (to_set) edge->setTimingArcSet(to_set); else - report_->critical( - 1556, "corresponding timing arc set not found in equiv cells"); + report_->critical(1563, "corresponding timing arc set not found in equiv cells"); } } } @@ -4658,8 +4660,7 @@ Sta::replaceCellBefore(const Instance *inst, VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); - Vertex *to_vertex = edge->to(graph_); - if (network_->instance(to_vertex->pin()) == inst) + if (!edge->isWire()) deleteEdge(edge); } } diff --git a/search/test/search_network_sta_deep.ok b/search/test/search_network_sta_deep.ok index 935f2435..9ce8d353 100644 --- a/search/test/search_network_sta_deep.ok +++ b/search/test/search_network_sta_deep.ok @@ -258,15 +258,15 @@ paths: 60 --- report_tag_arrivals --- Vertex out1 Group 2 - ^ min 0.100 / -2.000 16 default clk ^ clk_src clk crpr_pin null - v min 0.099 / -2.000 17 default clk ^ clk_src clk crpr_pin null - ^ max 0.100 / 8.000 18 default clk ^ clk_src clk crpr_pin null - v max 0.099 / 8.000 19 default clk ^ clk_src clk crpr_pin null + ^ min 0.100 / -2.000 16 default clk ^ clk_src clk crpr_pin null prev buf2/Z ^ default/min 16 buf2/Z ^ -> out1 ^ + v min 0.099 / -2.000 17 default clk ^ clk_src clk crpr_pin null prev buf2/Z v default/min 17 buf2/Z v -> out1 v + ^ max 0.100 / 8.000 18 default clk ^ clk_src clk crpr_pin null prev buf2/Z ^ default/max 18 buf2/Z ^ -> out1 ^ + v max 0.099 / 8.000 19 default clk ^ clk_src clk crpr_pin null prev buf2/Z v default/max 19 buf2/Z v -> out1 v Vertex out1 - ^ min 0.100 / -2.000 default clk ^ clk_src clk crpr_pin null - v min 0.099 / -2.000 default clk ^ clk_src clk crpr_pin null - ^ max 0.100 / 8.000 default clk ^ clk_src clk crpr_pin null - v max 0.099 / 8.000 default clk ^ clk_src clk crpr_pin null + ^ min 0.100 / -2.000 default clk ^ clk_src clk crpr_pin null prev buf2/Z ^ default/min 16 buf2/Z ^ -> out1 ^ + v min 0.099 / -2.000 default clk ^ clk_src clk crpr_pin null prev buf2/Z v default/min 17 buf2/Z v -> out1 v + ^ max 0.100 / 8.000 default clk ^ clk_src clk crpr_pin null prev buf2/Z ^ default/max 18 buf2/Z ^ -> out1 ^ + v max 0.099 / 8.000 default clk ^ clk_src clk crpr_pin null prev buf2/Z v default/max 19 buf2/Z v -> out1 v --- report_arrival_entries --- --- report_required_entries --- Level 40 diff --git a/search/test/search_search_arrival_required.ok b/search/test/search_search_arrival_required.ok index 7fb42743..0ac18b74 100644 --- a/search/test/search_search_arrival_required.ok +++ b/search/test/search_search_arrival_required.ok @@ -68,15 +68,15 @@ worst_slack_path pin: out1 worst_slack_path slack: 7.899713772019368e-9 Vertex out1 Group 2 - ^ min 0.100 / -2.000 16 default clk ^ clk_src clk crpr_pin null - v min 0.099 / -2.000 17 default clk ^ clk_src clk crpr_pin null - ^ max 0.100 / 8.000 18 default clk ^ clk_src clk crpr_pin null - v max 0.099 / 8.000 19 default clk ^ clk_src clk crpr_pin null + ^ min 0.100 / -2.000 16 default clk ^ clk_src clk crpr_pin null prev buf2/Z ^ default/min 16 buf2/Z ^ -> out1 ^ + v min 0.099 / -2.000 17 default clk ^ clk_src clk crpr_pin null prev buf2/Z v default/min 17 buf2/Z v -> out1 v + ^ max 0.100 / 8.000 18 default clk ^ clk_src clk crpr_pin null prev buf2/Z ^ default/max 18 buf2/Z ^ -> out1 ^ + v max 0.099 / 8.000 19 default clk ^ clk_src clk crpr_pin null prev buf2/Z v default/max 19 buf2/Z v -> out1 v Vertex out1 - ^ min 0.100 / -2.000 default clk ^ clk_src clk crpr_pin null - v min 0.099 / -2.000 default clk ^ clk_src clk crpr_pin null - ^ max 0.100 / 8.000 default clk ^ clk_src clk crpr_pin null - v max 0.099 / 8.000 default clk ^ clk_src clk crpr_pin null + ^ min 0.100 / -2.000 default clk ^ clk_src clk crpr_pin null prev buf2/Z ^ default/min 16 buf2/Z ^ -> out1 ^ + v min 0.099 / -2.000 default clk ^ clk_src clk crpr_pin null prev buf2/Z v default/min 17 buf2/Z v -> out1 v + ^ max 0.100 / 8.000 default clk ^ clk_src clk crpr_pin null prev buf2/Z ^ default/max 18 buf2/Z ^ -> out1 ^ + v max 0.099 / 8.000 default clk ^ clk_src clk crpr_pin null prev buf2/Z v default/max 19 buf2/Z v -> out1 v --- worst_slack_vertex min --- worst_slack_vertex min pin: reg1/D worst_arrival_path min pin: reg1/D diff --git a/tcl/Property.tcl b/tcl/Property.tcl index 3f70adc6..4df5b196 100644 --- a/tcl/Property.tcl +++ b/tcl/Property.tcl @@ -120,5 +120,33 @@ proc get_property_object_type { object_type object_name quiet } { return [lindex $object 0] } +define_cmd_args "define_property" \ + {-object_type scene|mode -type bool|float|string property} + +proc define_property { args } { + parse_key_args "define_property" args keys {-object_type -type} flags {} + check_argc_eq1 "define_property" $args + if { ![info exists keys(-object_type)] } { + sta_error 2207 "define_property -object_type must be specified." + } + if { ![info exists keys(-type)] } { + sta_error 2208 "define_property -type must be specified." + } + define_property_cmd $keys(-object_type) [lindex $args 0] $keys(-type) +} + +define_cmd_args "set_property" {object property value} + +proc set_property { args } { + check_argc_eq3 "set_property" $args + set object [lindex $args 0] + set prop [lindex $args 1] + set value [lindex $args 2] + if { ![is_object $object] } { + sta_error 2213 "set_property $object is not an object." + } + set_property_cmd $object [object_type $object] $prop $value +} + # sta namespace end. } diff --git a/tcl/Sta.tcl b/tcl/Sta.tcl index 0ec12a27..cffd6673 100644 --- a/tcl/Sta.tcl +++ b/tcl/Sta.tcl @@ -112,10 +112,10 @@ proc set_scene { args } { ################################################################ -define_cmd_args "get_scenes" {[-modes mode_names] scene_names} +define_cmd_args "get_scenes" {[-modes mode_names] [-filter expr] scene_names} proc get_scenes { args } { - parse_key_args "get_scenes" args keys {-modes} flags {} + parse_key_args "get_scenes" args keys {-modes -filter} flags {} check_argc_eq0or1 "get_scenes" $args if { [llength $args] == 0 } { @@ -132,18 +132,34 @@ proc get_scenes { args } { } lappend modes $mode } - return [find_mode_scenes_matching $scene_name $modes] + set scenes [find_mode_scenes_matching $scene_name $modes] } else { - return [find_scenes_matching $scene_name] + set scenes [find_scenes_matching $scene_name] } + if { [info exists keys(-filter)] } { + set scenes [filter_scenes $keys(-filter) $scenes] + } + return $scenes } ################################################################ -define_cmd_args "get_modes" {mode_name} +define_cmd_args "get_modes" {[-filter expr] [mode_name]} proc get_modes { args } { - return [find_modes [lindex $args 0]] + parse_key_args "get_modes" args keys {-filter} flags {} + check_argc_eq0or1 "get_modes" $args + + if { [llength $args] == 0 } { + set mode_name "*" + } else { + set mode_name [lindex $args 0] + } + set modes [find_modes $mode_name] + if { [info exists keys(-filter)] } { + set modes [filter_modes $keys(-filter) $modes] + } + return $modes } ################################################################ diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index c8c62584..a89054fa 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -1218,6 +1218,10 @@ using namespace sta; $1 = tclListSeq($input, SWIGTYPE_p_Mode, interp); } +%typemap(in) ModeSeq* { + $1 = tclListSeqPtr($input, SWIGTYPE_p_Mode, interp); +} + %typemap(out) ModeSeq { seqTclList($1, SWIGTYPE_p_Mode, interp); } @@ -1251,6 +1255,10 @@ using namespace sta; $1 = tclListSeq($input, SWIGTYPE_p_Scene, interp); } +%typemap(in) SceneSeq* { + $1 = tclListSeqPtr($input, SWIGTYPE_p_Scene, interp); +} + %typemap(out) SceneSeq { seqTclList($1, SWIGTYPE_p_Scene, interp); } diff --git a/test/asap7_small.lib.gz b/test/asap7_small.lib.gz index f0381ded..47c561fb 100644 Binary files a/test/asap7_small.lib.gz and b/test/asap7_small.lib.gz differ diff --git a/test/get_scenes.ok b/test/get_scenes.ok index ab504b68..94478689 100644 --- a/test/get_scenes.ok +++ b/test/get_scenes.ok @@ -4,3 +4,30 @@ scene2 [get_modes *] mode1 mode2 +[get_scenes -filter {name == scene1}] +scene1 +[get_scenes -filter {name =~ scene*}] +scene1 +scene2 +[get_scenes -filter {name != scene1}] +scene2 +[get_modes -filter {name == mode2}] +mode2 +[get_modes -filter {name =~ mode*}] +mode1 +mode2 +[get_property scene1 active] +1 +[get_property scene2 active] (unset) +<> +[get_scenes -filter {active == true}] +scene1 +[get_scenes -filter {active == false}] (scene2 unset) +[get_scenes -filter {active}] +scene1 +[get_scenes -filter {!active}] +scene2 +[get_property scene2 active] (set false) +0 +[get_scenes -filter {active == false}] (scene2 set false) +scene2 diff --git a/test/get_scenes.tcl b/test/get_scenes.tcl index 396c32ce..0ccb98a4 100644 --- a/test/get_scenes.tcl +++ b/test/get_scenes.tcl @@ -18,3 +18,47 @@ report_object_names [get_scenes *] puts {[get_modes *]} report_object_names [get_modes *] + +puts {[get_scenes -filter {name == scene1}]} +report_object_names [get_scenes -filter {name == scene1}] + +puts {[get_scenes -filter {name =~ scene*}]} +report_object_names [get_scenes -filter {name =~ scene*}] + +puts {[get_scenes -filter {name != scene1}]} +report_object_names [get_scenes -filter {name != scene1}] + +puts {[get_modes -filter {name == mode2}]} +report_object_names [get_modes -filter {name == mode2}] + +puts {[get_modes -filter {name =~ mode*}]} +report_object_names [get_modes -filter {name =~ mode*}] + +# User-defined bool property. scene1 set active, scene2 left unset. +define_property -object_type scene -type bool active +set_property [get_scenes scene1] active true + +puts {[get_property scene1 active]} +puts [get_property [get_scenes scene1] active] +puts {[get_property scene2 active] (unset)} +puts "<[get_property [get_scenes scene2] active]>" + +puts {[get_scenes -filter {active == true}]} +report_object_names [get_scenes -filter {active == true}] + +# Unset does not match false; only an explicitly set value does. +puts {[get_scenes -filter {active == false}] (scene2 unset)} +report_object_names [get_scenes -filter {active == false}] + +puts {[get_scenes -filter {active}]} +report_object_names [get_scenes -filter {active}] + +puts {[get_scenes -filter {!active}]} +report_object_names [get_scenes -filter {!active}] + +set_property [get_scenes scene2] active false +puts {[get_property scene2 active] (set false)} +puts [get_property [get_scenes scene2] active] + +puts {[get_scenes -filter {active == false}] (scene2 set false)} +report_object_names [get_scenes -filter {active == false}]