From 0c243b564b6feb002f0ab8f15cb205c43e6219eb Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 28 May 2026 09:31:24 -0700 Subject: [PATCH 1/8] Dockerfile.ubuntu24.04 Signed-off-by: James Cherry --- Dockerfile.ubuntu24.04 | 46 ++++++++++++++++++++++++++++++++++++++++++ README.md | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 Dockerfile.ubuntu24.04 diff --git a/Dockerfile.ubuntu24.04 b/Dockerfile.ubuntu24.04 new file mode 100644 index 00000000..d08688f5 --- /dev/null +++ b/Dockerfile.ubuntu24.04 @@ -0,0 +1,46 @@ +FROM ubuntu:24.04 +LABEL author="James Cherry" +LABEL maintainer="James Cherry " + +# Install basics +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && \ + apt-get install -y \ + git \ + wget \ + cmake \ + gcc \ + gdb \ + tcl-dev \ + tcl-tclreadline \ + swig \ + bison \ + flex \ + automake \ + autotools-dev \ + libeigen3-dev \ + libfmt-dev + +# Download CUDD +RUN wget https://raw.githubusercontent.com/davidkebo/cudd/main/cudd_versions/cudd-3.0.0.tar.gz && \ + tar -xvf cudd-3.0.0.tar.gz && \ + rm cudd-3.0.0.tar.gz + +# Build CUDD +RUN cd cudd-3.0.0 && \ + mkdir ../cudd && \ + ./configure && \ + make -j`nproc` + +# Copy files and install OpenSTA +RUN mkdir OpenSTA +COPY . OpenSTA +RUN cd OpenSTA && \ + rm -rf build && \ + mkdir build && \ + cd build && \ + cmake -DCUDD_DIR=../cudd-3.0.0 .. && \ + make -j`nproc` + +# Run sta on entry +ENTRYPOINT ["OpenSTA/build/sta"] diff --git a/README.md b/README.md index ea9262cf..b1eb59bc 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,7 @@ following command builds a Docker image. ``` cd OpenSTA -docker build --file Dockerfile.ubuntu22.04 --tag opensta_ubuntu22.04 . +docker build --file Dockerfile.ubuntu24.04 --tag opensta_ubuntu24.04 . or docker build --file Dockerfile.centos7 --tag opensta_centos7 . ``` From 8f84d721f3fd7148f1bb9b20b94ef40ee90dda95 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 28 May 2026 12:43:10 -0700 Subject: [PATCH 2/8] report_tag_arrivals Signed-off-by: James Cherry --- include/sta/Search.hh | 3 ++- search/Search.cc | 7 ++++--- search/Search.i | 5 +++-- search/Search.tcl | 15 ++++++++++++--- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/include/sta/Search.hh b/include/sta/Search.hh index 841dbe6b..4dc40efe 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -353,7 +353,8 @@ public: TagGroup *tagGroup(const Vertex *vertex) const; TagGroup *tagGroup(TagGroupIndex index) const; void reportArrivals(Vertex *vertex, - bool report_tag_index) const; + bool report_tag_index, + int digits) const; Slack wnsSlack(Vertex *vertex, PathAPIndex path_ap_index); void levelsChangedBefore(); diff --git a/search/Search.cc b/search/Search.cc index ee735a0c..1ab8c639 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -2754,7 +2754,8 @@ ReportPathLess::operator()(const Path *path1, void Search::reportArrivals(Vertex *vertex, - bool report_tag_index) const + bool report_tag_index, + int digits) const { report_->report("Vertex {}", vertex->to_string(this)); TagGroup *tag_group = tagGroup(vertex); @@ -2771,7 +2772,6 @@ Search::reportArrivals(Vertex *vertex, for (const Path *path : paths) { const Tag *tag = path->tag(this); const RiseFall *rf = tag->transition(); - std::string req = delayAsString(path->required(), this); bool report_prev = false; std::string prev_str; if (report_prev) { @@ -2791,7 +2791,8 @@ Search::reportArrivals(Vertex *vertex, } report_->report(" {} {} {} / {} {}{}", rf->shortName(), path->minMax(this)->to_string(), - delayAsString(path->arrival(), this), req, + delayAsString(path->arrival(), digits, this), + delayAsString(path->required(), digits, this), tag->to_string(report_tag_index, false, this), prev_str); } } diff --git a/search/Search.i b/search/Search.i index dbf3dc0f..6f0376de 100644 --- a/search/Search.i +++ b/search/Search.i @@ -269,9 +269,10 @@ report_tag_groups() void report_tag_arrivals_cmd(Vertex *vertex, - bool report_tag_index) + bool report_tag_index, + int digits) { - Sta::sta()->search()->reportArrivals(vertex, report_tag_index); + Sta::sta()->search()->reportArrivals(vertex, report_tag_index, digits); } void diff --git a/search/Search.tcl b/search/Search.tcl index 6d3c7223..066b922b 100644 --- a/search/Search.tcl +++ b/search/Search.tcl @@ -805,10 +805,19 @@ proc report_slack { args } { ################################################################ # Internal debugging command. -proc report_tag_arrivals { pin } { - set pin [get_port_pin_error "pin" $pin] +proc report_tag_arrivals { args } { + global sta_report_default_digits + + parse_key_args "report_tag_arrivals" args keys {-digits} flags {} + set pin [get_port_pin_error "pin" [lindex $args 0]] + if [info exists keys(-digits)] { + set digits $keys(-digits) + check_positive_integer "-digits" $digits + } else { + set digits $sta_report_default_digits + } foreach vertex [$pin vertices] { - report_tag_arrivals_cmd $vertex 1 + report_tag_arrivals_cmd $vertex 1 $digits } } From 4239842d34903532534eff98913560508a89a9da Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 29 May 2026 09:49:12 -0700 Subject: [PATCH 3/8] FindTCL.cmake Signed-off-by: James Cherry --- cmake/FindTCL.cmake | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmake/FindTCL.cmake b/cmake/FindTCL.cmake index 26d6d7ce..a5978b8a 100644 --- a/cmake/FindTCL.cmake +++ b/cmake/FindTCL.cmake @@ -29,14 +29,14 @@ set(TCL_POSSIBLE_NAMES tcl85 tcl8.5 ) -# tcl lib path guesses. +# TCL lib path guesses. if (NOT TCL_LIB_PATHS) if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + file(GLOB tcl_homebrew_libs + /opt/homebrew/Cellar/tcl-tk@8/*/lib + ) set(TCL_LIB_PATHS - #/opt/homebrew/Cellar/tcl-tk/9.0.3/lib - /opt/homebrew/Cellar/tcl-tk@8/8.6.18/lib - /opt/homebrew/Cellar/tcl-tk@8/8.6.17/lib - /opt/homebrew/Cellar/tcl-tk@8/8.6.16/lib + ${tcl_homebrew_libs} /opt/homebrew/opt/tcl-tk/lib /usr/local/lib) set(TCL_NO_DEFAULT_PATH TRUE) elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") From 13cd475ffa9a03f8a476b89035970226bc5b34ea Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 29 May 2026 16:32:47 -0700 Subject: [PATCH 4/8] make_net/make_port bus bit without bus dcl pr441 Signed-off-by: James Cherry --- include/sta/Network.hh | 4 ++-- include/sta/ParseBus.hh | 3 ++- include/sta/SdcNetwork.hh | 8 ++++++++ liberty/LibertyReader.cc | 3 ++- network/ParseBus.cc | 9 +++++---- network/SdcNetwork.cc | 31 +++++++++++++++++++++++++++---- search/Sta.cc | 6 ++++-- 7 files changed, 50 insertions(+), 14 deletions(-) diff --git a/include/sta/Network.hh b/include/sta/Network.hh index 70580e90..9c45f095 100644 --- a/include/sta/Network.hh +++ b/include/sta/Network.hh @@ -502,6 +502,8 @@ class NetworkEdit : public Network public: NetworkEdit() = default; bool isEditable() const override { return true; } + virtual Port *makePort(Cell *cell, + std::string_view name) = 0; virtual Instance *makeInstance(LibertyCell *cell, std::string_view name, Instance *parent) = 0; @@ -556,8 +558,6 @@ public: virtual void setAttribute(Instance *instance, std::string_view key, std::string_view value) = 0; - virtual Port *makePort(Cell *cell, - std::string_view name) = 0; virtual Port *makeBusPort(Cell *cell, std::string_view name, int from_index, diff --git a/include/sta/ParseBus.hh b/include/sta/ParseBus.hh index e6236fa8..aa0b67d3 100644 --- a/include/sta/ParseBus.hh +++ b/include/sta/ParseBus.hh @@ -94,11 +94,12 @@ parseBusName(std::string_view name, int &to, bool &subscript_wild); -// Insert escapes before ch1 and ch2 in token. +// Insert escapes before ch1, ch2 or ch3 in token. std::string escapeChars(std::string_view token, char ch1, char ch2, + char ch3, char escape); } // namespace sta diff --git a/include/sta/SdcNetwork.hh b/include/sta/SdcNetwork.hh index ea4c8304..52192abd 100644 --- a/include/sta/SdcNetwork.hh +++ b/include/sta/SdcNetwork.hh @@ -92,6 +92,8 @@ public: int index) const override; PortMemberIterator *memberIterator(const Port *port) const override; bool hasMembers(const Port *port) const override; + Port *makePort(Cell *cell, + std::string_view name) override; ObjectId id(const Instance *instance) const override; std::string getAttribute(const Instance *inst, @@ -146,6 +148,7 @@ public: char pathEscape() const override; void setPathEscape(char escape) override; + // NetworkEdit bool isEditable() const override; LibertyLibrary *makeLibertyLibrary(std::string_view name, std::string_view filename) override; @@ -202,6 +205,8 @@ public: const PatternMatch *pattern) const override; std::string name(const Port *port) const override; std::string busName(const Port *port) const override; + Port *makePort(Cell *cell, + std::string_view name) override; std::string name(const Instance *instance) const override; std::string pathName(const Instance *instance) const override; @@ -289,5 +294,8 @@ escapeDividers(std::string_view name, std::string escapeBrackets(std::string_view name, const Network *network); +std::string +escapeDividerBrackets(std::string_view name, + const Network *network); } // namespace sta diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 56d64408..09733ca8 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -3081,7 +3081,8 @@ libertyReaderFindPort(const LibertyCell *cell, char brkt_right = library->busBrktRight(); const char escape = '\\'; // Pins at top level with bus names have escaped brackets. - std::string escaped_port_name = escapeChars(port_name, brkt_left, brkt_right, escape); + std::string escaped_port_name = escapeChars(port_name, brkt_left, brkt_right, + '\0', escape); port = cell->findLibertyPort(escaped_port_name); } return port; diff --git a/network/ParseBus.cc b/network/ParseBus.cc index 8205dead..11c66138 100644 --- a/network/ParseBus.cc +++ b/network/ParseBus.cc @@ -163,9 +163,10 @@ parseBusName(std::string_view name, std::string escapeChars(std::string_view token, - const char ch1, - const char ch2, - const char escape) + char ch1, + char ch2, + char ch3, + char escape) { std::string escaped; escaped.reserve(token.size()); @@ -180,7 +181,7 @@ escapeChars(std::string_view token, else escaped += ch; } - else if (ch == ch1 || ch == ch2) { + else if (ch == ch1 || ch == ch2 || ch == ch3) { escaped += escape; escaped += ch; } diff --git a/network/SdcNetwork.cc b/network/SdcNetwork.cc index 0542f95a..50bc533d 100644 --- a/network/SdcNetwork.cc +++ b/network/SdcNetwork.cc @@ -330,6 +330,13 @@ NetworkNameAdapter::memberIterator(const Port *port) const return network_->memberIterator(port); } +Port * +NetworkNameAdapter::makePort(Cell *cell, + std::string_view name) +{ + return network_edit_->makePort(cell, name); +} + //////////////////////////////////////////////////////////////// ObjectId @@ -736,6 +743,14 @@ SdcNetwork::busName(const Port *port) const return staToSdc(network_->busName(port)); } +Port * +SdcNetwork::makePort(Cell *cell, + std::string_view name) +{ + std::string escaped_name = escapeDividerBrackets(name, network_edit_); + return network_edit_->makePort(cell, escaped_name); +} + std::string SdcNetwork::name(const Instance *instance) const { @@ -1073,7 +1088,7 @@ SdcNetwork::makeInstance(LibertyCell *cell, std::string_view name, Instance *parent) { - std::string escaped_name = escapeDividers(std::string(name), this); + std::string escaped_name = escapeDividerBrackets(name, this); return network_edit_->makeInstance(cell, escaped_name, parent); } @@ -1081,7 +1096,7 @@ Net * SdcNetwork::makeNet(std::string_view name, Instance *parent) { - std::string escaped_name = escapeDividers(std::string(name), this); + std::string escaped_name = escapeDividerBrackets(name, this); return network_edit_->makeNet(escaped_name, parent); } @@ -1254,11 +1269,19 @@ SdcNetwork::visitMatches(const Instance *parent, //////////////////////////////////////////////////////////////// +std::string +escapeDividerBrackets(std::string_view name, + const Network *network) +{ + return escapeChars(name, network->pathDivider(), '[', ']', + network->pathEscape()); +} + std::string escapeDividers(std::string_view name, const Network *network) { - return escapeChars(name, network->pathDivider(), '\0', + return escapeChars(name, network->pathDivider(), '\0', '\0', network->pathEscape()); } @@ -1266,7 +1289,7 @@ std::string escapeBrackets(std::string_view name, const Network *network) { - return escapeChars(name, '[', ']', network->pathEscape()); + return escapeChars(name, '[', ']', '\0', network->pathEscape()); } } // namespace sta diff --git a/search/Sta.cc b/search/Sta.cc index 27b2119b..ba98b3f6 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -4412,7 +4412,8 @@ Sta::makeNet(const char *name, Instance *parent) { NetworkEdit *network = networkCmdEdit(); - Net *net = network->makeNet(name, parent); + std::string escaped = escapeBrackets(name, network); + Net *net = network->makeNet(escaped, parent); // Sta notification unnecessary. return net; } @@ -4460,8 +4461,9 @@ Sta::makePortPin(const char *port_name, ensureLinked(); NetworkReader *network = dynamic_cast(network_); Instance *top_inst = network->topInstance(); + std::string escaped = escapeBrackets(port_name, network); Cell *top_cell = network->cell(top_inst); - Port *port = network->makePort(top_cell, port_name); + Port *port = network->makePort(top_cell, escaped); network->setDirection(port, dir); Pin *pin = network->makePin(top_inst, port, nullptr); makePortPinAfter(pin); From b0869d521cfdd84761a398515888f7064fab65b8 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 4 Jun 2026 08:54:28 -0700 Subject: [PATCH 5/8] comment Signed-off-by: James Cherry --- verilog/VerilogParse.yy | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/verilog/VerilogParse.yy b/verilog/VerilogParse.yy index 3103c3be..c19c2ea8 100644 --- a/verilog/VerilogParse.yy +++ b/verilog/VerilogParse.yy @@ -242,13 +242,8 @@ stmt_seq: continuous_assign ; -/* specify blocks are used by some comercial tools to convey macro timing - * and other metadata. - * Their presence is not forbidden in structural verilog, this is a placeholder - * that just ignores them and allows verilog processing to proceed - * <> if someone in the future wants implement support for timing info - * via specify blocks, implement proper parsing here - */ +// Specify blocks are used by some comercial tools to convey macro timing +// and other metadata. specify_block: SPECIFY specify_stmts ENDSPECIFY { $$ = nullptr; } From ffe126af2af0536bdb2715f196387e6de6f272e5 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 6 Jun 2026 11:55:51 -0700 Subject: [PATCH 6/8] PathGroups::inPathGroupNamed Signed-off-by: James Cherry --- include/sta/PathGroup.hh | 5 +++-- search/PathGroup.cc | 36 +++++++++++++++++------------------- search/Sta.cc | 14 +++++--------- 3 files changed, 25 insertions(+), 30 deletions(-) diff --git a/include/sta/PathGroup.hh b/include/sta/PathGroup.hh index df180016..46b4c9b1 100644 --- a/include/sta/PathGroup.hh +++ b/include/sta/PathGroup.hh @@ -146,8 +146,9 @@ public: PathGroup *findPathGroup(const Clock *clock, const MinMax *min_max) const; PathGroupSeq pathGroups(const PathEnd *path_end) const; - static StringSeq pathGroupNames(const PathEnd *path_end, - const StaState *sta); + static bool inPathGroupNamed(const PathEnd *path_end, + std::string_view path_group_name, + const StaState *sta); static std::string_view asyncPathGroupName() { return async_group_name_; } static std::string_view pathDelayGroupName() { return path_delay_group_name_; } static std::string_view gatedClkGroupName() { return gated_clk_group_name_; } diff --git a/search/PathGroup.cc b/search/PathGroup.cc index 725b52c7..ee10bd6e 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -474,23 +474,23 @@ PathGroups::pathGroups(const PathEnd *path_end) const } // Mirrors PathGroups::pathGroup. -StringSeq -PathGroups::pathGroupNames(const PathEnd *path_end, - const StaState *sta) +bool +PathGroups::inPathGroupNamed(const PathEnd *path_end, + std::string_view path_group_name, + const StaState *sta) { - StringSeq group_names; - std::string group_name; const Search *search = sta->search(); ExceptionPathSeq group_paths = search->groupPathsTo(path_end); if (path_end->isUnconstrained()) - group_name = unconstrained_group_name_; + return path_group_name == unconstrained_group_name_; else if (!group_paths.empty()) { // GroupPaths have precedence. for (ExceptionPath *group_path : group_paths) { - if (group_path->isDefault()) - group_names.emplace_back(path_delay_group_name_); - else - group_names.emplace_back(group_path->name()); + std::string_view group_name = group_path->isDefault() + ? path_delay_group_name_ + : group_path->name(); + if (path_group_name == group_name) + return true;; } } else if (path_end->isCheck() || path_end->isLatchCheck()) { @@ -498,18 +498,18 @@ PathGroups::pathGroupNames(const PathEnd *path_end, const Clock *tgt_clk = path_end->targetClk(sta); if (check_role == TimingRole::removal() || check_role == TimingRole::recovery()) - group_name = async_group_name_; + return path_group_name == async_group_name_; else - group_name = tgt_clk->name(); + return path_group_name == tgt_clk->name(); } else if (path_end->isOutputDelay() || path_end->isDataCheck()) { const Clock *tgt_clk = path_end->targetClk(sta); if (tgt_clk) - group_name = tgt_clk->name(); + return path_group_name == tgt_clk->name(); } else if (path_end->isGatedClock()) - group_name = gated_clk_group_name_; + return path_group_name == gated_clk_group_name_; else if (path_end->isPathDelay()) { // Path delays that end at timing checks are part of the target clk group // unless -ignore_clock_latency is true. @@ -517,13 +517,11 @@ PathGroups::pathGroupNames(const PathEnd *path_end, const Clock *tgt_clk = path_end->targetClk(sta); if (tgt_clk && !path_delay->ignoreClkLatency()) - group_name = tgt_clk->name(); + return path_group_name == tgt_clk->name(); else - group_name = path_delay_group_name_; + return path_group_name == path_delay_group_name_; } - if (!group_name.empty()) - group_names.push_back(group_name); - return group_names; + return false; } GroupPath * diff --git a/search/Sta.cc b/search/Sta.cc index ba98b3f6..9599d824 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -3279,15 +3279,11 @@ EndpointPathEndVisitor::copy() const void EndpointPathEndVisitor::visit(PathEnd *path_end) { - if (path_end->minMax(sta_) == min_max_) { - StringSeq group_names = PathGroups::pathGroupNames(path_end, sta_); - for (std::string &group_name : group_names) { - if (group_name == path_group_name_) { - Slack end_slack = path_end->slack(sta_); - if (delayLess(end_slack, slack_, sta_)) - slack_ = end_slack; - } - } + if (path_end->minMax(sta_) == min_max_ + && PathGroups::inPathGroupNamed(path_end, path_group_name_, sta_)) { + Slack end_slack = path_end->slack(sta_); + if (delayLess(end_slack, slack_, sta_)) + slack_ = end_slack; } } From b3458485ba97a79b58e907b19837953b4f35d9e0 Mon Sep 17 00:00:00 2001 From: Drew Lewis Date: Sat, 6 Jun 2026 19:45:46 -0400 Subject: [PATCH 7/8] Dont spin main for queue (#443) * Use DynamicLatch in DispatchQueue to avoid main thread spinning Replace the busy-yielding pending_task_count_ loop in DispatchQueue::finishTasks with a blocking DynamicLatch. This avoids having the main thread consume CPU cycles while waiting for dispatched tasks to complete. The DynamicLatch implementation uses C++20 std::atomic::wait/notify_all for efficient blocking and wakeup, with proper release-acquire semantics to ensure task results are visible to the waiting thread. * Reformat DynamicLatch to match DispatchQueue style * Update attribution headers in DispatchQueue files to note modifications --- include/sta/DispatchQueue.hh | 54 +++++++++++++++++++++++++++++++++--- util/DispatchQueue.cc | 20 ++++++------- 2 files changed, 60 insertions(+), 14 deletions(-) diff --git a/include/sta/DispatchQueue.hh b/include/sta/DispatchQueue.hh index ceda2e78..e30f9d70 100644 --- a/include/sta/DispatchQueue.hh +++ b/include/sta/DispatchQueue.hh @@ -1,11 +1,14 @@ -// Author Phillip Johnston +// Original Author: Phillip Johnston // Licensed under CC0 1.0 Universal -// https://github.com/embeddedartistry/embedded-resources/blob/master/examples/cpp/dispatch.cpp -// https://embeddedartistry.com/blog/2017/2/1/dispatch-queues?rq=dispatch +// Original source: https://github.com/embeddedartistry/embedded-resources/blob/master/examples/cpp/dispatch.cpp +// Original article: https://embeddedartistry.com/blog/2017/2/1/dispatch-queues?rq=dispatch +// +// Modified for OpenSTA to use C++20 non-spinning DynamicLatch for synchronization. #pragma once #include +#include #include #include #include @@ -16,6 +19,49 @@ namespace sta { +class DynamicLatch +{ +public: + explicit DynamicLatch(std::ptrdiff_t initial_count = 0) : + count_(initial_count) + { + } + + // Delete copy/move constructors to prevent accidental slicing/copying of atomics + DynamicLatch(const DynamicLatch&) = delete; + DynamicLatch& operator=(const DynamicLatch&) = delete; + + // Increases the latch count (used when a new task is dispatched) + void + countUp() + { + count_.fetch_add(1, std::memory_order_release); + } + + // Decreases the latch count and wakes waiting threads if it hits zero + void + countDown(std::ptrdiff_t n = 1) + { + if (count_.fetch_sub(n, std::memory_order_release) == n) { + count_.notify_all(); + } + } + + // Blocks until the count reaches zero + void + wait() const + { + std::ptrdiff_t current = count_.load(std::memory_order_acquire); + while (current != 0) { + count_.wait(current, std::memory_order_acquire); + current = count_.load(std::memory_order_acquire); + } + } + +private: + mutable std::atomic count_{0}; +}; + class DispatchQueue { using fp_t = std::function; @@ -45,7 +91,7 @@ private: std::vector threads_; std::queue q_; std::condition_variable cv_; - std::atomic pending_task_count_; + DynamicLatch pending_task_count_latch_; bool quit_ = false; }; diff --git a/util/DispatchQueue.cc b/util/DispatchQueue.cc index 5347a07a..c1cd11f7 100644 --- a/util/DispatchQueue.cc +++ b/util/DispatchQueue.cc @@ -1,15 +1,16 @@ -// Author Phillip Johnston +// Original Author: Phillip Johnston // Licensed under CC0 1.0 Universal -// https://github.com/embeddedartistry/embedded-resources/blob/master/examples/cpp/dispatch.cpp -// https://embeddedartistry.com/blog/2017/2/1/dispatch-queues?rq=dispatch +// Original source: https://github.com/embeddedartistry/embedded-resources/blob/master/examples/cpp/dispatch.cpp +// Original article: https://embeddedartistry.com/blog/2017/2/1/dispatch-queues?rq=dispatch +// +// Modified for OpenSTA to use C++20 non-spinning DynamicLatch for synchronization. #include "DispatchQueue.hh" namespace sta { DispatchQueue::DispatchQueue(size_t thread_count) : - threads_(thread_count), - pending_task_count_(0) + threads_(thread_count) { for(size_t i = 0; i < thread_count; i++) threads_[i] = std::thread(&DispatchQueue::dispatch_thread_handler, this, i); @@ -58,8 +59,7 @@ DispatchQueue::getThreadCount() const void DispatchQueue::finishTasks() { - while (pending_task_count_.load(std::memory_order_acquire) != 0) - std::this_thread::yield(); + pending_task_count_latch_.wait(); } void @@ -67,7 +67,7 @@ DispatchQueue::dispatch(const fp_t& op) { std::unique_lock lock(lock_); q_.push(op); - pending_task_count_++; + pending_task_count_latch_.countUp(); // Manual unlocking is done before notifying, to avoid waking up // the waiting thread only to block again (see notify_one for details) @@ -80,7 +80,7 @@ DispatchQueue::dispatch(fp_t&& op) { std::unique_lock lock(lock_); q_.push(std::move(op)); - pending_task_count_++; + pending_task_count_latch_.countUp(); // Manual unlocking is done before notifying, to avoid waking up // the waiting thread only to block again (see notify_one for details) @@ -106,7 +106,7 @@ DispatchQueue::dispatch_thread_handler(size_t i) op(i); - pending_task_count_--; + pending_task_count_latch_.countDown(); lock.lock(); } } while (!quit_); From 014678a4f832aa2e1140fbaabe79234586eaf65e Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 6 Jun 2026 20:12:33 -0700 Subject: [PATCH 8/8] cursor Signed-off-by: James Cherry --- .cursor/rules/cpp-coding-standards.mdc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.cursor/rules/cpp-coding-standards.mdc b/.cursor/rules/cpp-coding-standards.mdc index 538991fb..fc06afdf 100644 --- a/.cursor/rules/cpp-coding-standards.mdc +++ b/.cursor/rules/cpp-coding-standards.mdc @@ -1,15 +1,22 @@ --- description: C++ coding standards and formatting for OpenSTA globs: ["**/*.cc", "**/*.hh", "**/*.h"] -alwaysApply: false +alwaysApply: true --- # C++ Coding Standards +## File-internal helpers (translation-unit local) + +- For helpers used only in one `.cc` file, put them in the **same namespace** as the rest of the implementation (e.g. `namespace sta { ... }`). +- Mark them **`static`** at namespace scope so they have internal linkage. +- **Do not** wrap them in an **anonymous namespace** in this project (no `namespace { ... }` file-static helpers). The user preference is `static` inside the real namespace, not a nested anonymous namespace inside `sta` or at file scope before `sta`. + ## Line Width - **Keep lines under 90 characters** to match `.clang-format` (ColumnLimit: 90). -- Break long lines at logical points: after commas, before operators, after opening parens. +- Only break a line when it would exceed 90 characters. Do not introduce unnecessary line breaks when the expression fits on one line. +- When a break is needed, break at logical points: after commas, before operators. Keep the first argument on the same line as the opening paren (do not break immediately after an opening paren). ## Naming Conventions