diff --git a/.cursor/rules/cpp-coding-standards.mdc b/.cursor/rules/cpp-coding-standards.mdc index 538991fbc..fc06afdf9 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 diff --git a/Dockerfile.ubuntu24.04 b/Dockerfile.ubuntu24.04 new file mode 100644 index 000000000..d08688f59 --- /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 ea9262cf9..b1eb59bc4 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 . ``` diff --git a/cmake/FindTCL.cmake b/cmake/FindTCL.cmake index 26d6d7ced..a5978b8a4 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") diff --git a/doc/ChangeLog.txt b/doc/ChangeLog.txt index f4ab57514..c30947f8e 100644 --- a/doc/ChangeLog.txt +++ b/doc/ChangeLog.txt @@ -22,13 +22,13 @@ Release 3.0.1 2026/03/12 ------------------------ Statistical timing (SSTA) with Liberty LVF (Liberty Variation Format) -models is now supported. Statistical timing uses a probaility -distribution to represent a delay or slew ranther than a single +models is now supported. Statistical timing uses a probability +distribution to represent a delay or slew rather than a single number. Normal and skew normal probability distributions are supported. -SSTA is enabled with the sta_pocv_mode variaable. +SSTA is enabled with the sta_pocv_mode variable. set sta_pocv_mode scalar|normal|skew_normal @@ -43,10 +43,10 @@ set with the sta_pocv_quantile variable. The default value is 3 standard deviations, or sigma. -Use the variance field with report_checks or report_check_types to see +Use the variation field with report_checks or report_check_types to see distribution parameters in timing reports. -A command file for analyzing a design with statisical timing with an +A command file for analyzing a design with statistical timing with an LVF library is shown below. read_liberty lvf_library.lib.gz diff --git a/doc/OpenSTA.fodt b/doc/OpenSTA.fodt index e0d25e489..318b70a0e 100644 --- a/doc/OpenSTA.fodt +++ b/doc/OpenSTA.fodt @@ -6639,15 +6639,15 @@ This example can be found in examples/mcmm3.tcl.In the example show above the SDC for the modes is in separate files. Alternatively, the SDC can be defined in the command file using the set_mode command between SDC command groups. set_mode mode1create_clock -name m1_clk -period 1000 {clk1 clk2 clk3}set_input_delay -clock m1_clk 100 {in1 in2}set_mode mode2create_clock -name m2_clk -period 500 {clk1 clk3}set_output_delay -clock m2_clk 100 out Statistical Timing Analysis - OpenSTA also supports statistical timing .anallysis with Liberty Variation Format (LVF) libraries. Statistical timing uses a probaility distribution to represent a delay or slew ranther than a single number. - Normal and skew normal probability distributions are supported. SSTA is enabled with the sta_pocv_mode variaable. + OpenSTA also supports statistical timing analysis with Liberty Variation Format (LVF) libraries. Statistical timing uses a probability distribution to represent a delay or slew rather than a single number. + Normal and skew normal probability distributions are supported. SSTA is enabled with the sta_pocv_mode variable. set sta_pocv_mode scalar|normal|skew_normalscalar mode is for non-SSTA analysisnormal mode uses gaussian normal distributionsskew_normal mode is for skew normal LVF moment based distributions The target quantile of a delay probability distribution (confidence level) is set with the sta_pocv_quantile variable. set sta_pocv_quantile <float> The default value is 3 standard deviations, or sigma. - Use the variance field with the report_checks and report_check_types commands to see distribution parameters in timing reports. - A command file for analyzing a design with statisical timing is shown below. - read_liberty lvf_library.lib.gzread_verilog design.vlink_design topcreate_clock -period 50 clkset_input_delay -clock clk 1 {in1 in2}set sta_pocv_mode skew_normalreport_checks -fields {slew variation input_pin variation} -digits 3 + Use the variation field with the report_checks and report_check_types commands to see distribution parameters in timing reports. + A command file for analyzing a design with statistical timing is shown below. + read_liberty lvf_library.lib.gzread_verilog design.vlink_design topcreate_clock -period 50 clkset_input_delay -clock clk 1 {in1 in2}set sta_pocv_mode skew_normalreport_checks -fields {slew variation input_pin} -digits 3 Startpoint: r2 (rising edge-triggered flip-flop clocked by clk)Endpoint: r3 (rising edge-triggered flip-flop clocked by clk)Path Group: clkPath Type: max Slew Delay Variation Time Description--------------------------------------------------------------------------- 0.000 0.000 0.000 clock clk (rise edge) 0.000 0.000 clock network delay (ideal) 0.000 0.000 0.000 ^ r2/CK (FDPQ1) 12.026 mean 0.017 mean_shift 0.366 std_dev 0.000 skewness 4.648 12.409 12.409 v r2/Q (FFQ1) 4.648 0.000 12.409 v u1/A (BUF1) 6.084 mean 0.007 mean_shift 0.188 std_dev 0.000 skewness 2.513 6.137 18.546 v u1/X (BUF1) 2.513 0.000 18.546 v u2/A2 (AN21) 6.447 mean 0.008 mean_shift 0.191 std_dev 0.000 skewness 2.565 6.497 25.043 v u2/X (AN21) 2.565 0.000 25.043 v r3/D (FFQ1) 25.043 data arrival time 0.000 50.000 50.000 clock clk (rise edge) 0.000 50.000 clock network delay (ideal) 0.000 50.000 clock reconvergence pessimism 50.000 ^ r3/CK (FFQ1) -9.376 40.624 library setup time 40.624 data required time--------------------------------------------------------------------------- 40.624 data required time -25.043 data arrival time--------------------------------------------------------------------------- 15.581 slack (MET) The standard deviation for normal distributions is specified with the following liberty timing groups. ocv_sigma_cell_riseocv_sigma_cell_fallocv_sigma_rise_transitionocv_sigma_fall_transitionocv_sigma_rise_constraintocv_sigma_fall_constraint @@ -9532,7 +9532,7 @@ fields - List of capacitance|slew|input_pins|hierarchical_pins|nets|fanout|src_attr|variance + List of capacitance|slew|input_pins|hierarchical_pins|nets|fanout|src_attr|variation @@ -9611,7 +9611,7 @@ fields - List of capacitance|slew|input_pins|hierarchical_pins|nets|fanout|src_attr|variance + List of capacitance|slew|input_pins|hierarchical_pins|nets|fanout|src_attr|variation diff --git a/doc/OpenSTA.pdf b/doc/OpenSTA.pdf index 745f20950..dceb4a4f1 100644 Binary files a/doc/OpenSTA.pdf and b/doc/OpenSTA.pdf differ diff --git a/graph/Graph.cc b/graph/Graph.cc index 74996842e..f19927ffb 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -384,13 +384,6 @@ Graph::makeWireEdge(const Pin *from_pin, } } -void -Graph::makeSceneAfter() -{ - ap_count_ = dcalcAnalysisPtCount(); - initSlews(); -} - //////////////////////////////////////////////////////////////// Vertex * @@ -782,17 +775,13 @@ Graph::removeDelayAnnotated(Edge *edge) //////////////////////////////////////////////////////////////// -// This only gets called if the analysis type changes from single -// to bc_wc/ocv or visa versa. void -Graph::setDelayCount(DcalcAPIndex ap_count) +Graph::delayCountChanged() { - if (ap_count != ap_count_) { - // Discard any existing delays. - removePeriodCheckAnnotations(); - ap_count_ = ap_count; - initSlews(); - } + ap_count_ = dcalcAnalysisPtCount(); + // Discard any existing delays. + removePeriodCheckAnnotations(); + initSlews(); } void diff --git a/include/sta/DispatchQueue.hh b/include/sta/DispatchQueue.hh index ceda2e78f..e30f9d70c 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/include/sta/Graph.hh b/include/sta/Graph.hh index a35530486..572d95a7e 100644 --- a/include/sta/Graph.hh +++ b/include/sta/Graph.hh @@ -63,8 +63,7 @@ public: void makeGraph(); ~Graph() override; - // Number of arc delays and slews from sdf or delay calculation. - void setDelayCount(DcalcAPIndex ap_count); + void delayCountChanged(); size_t slewCount(); // Vertex functions. @@ -178,7 +177,6 @@ public: // Remove all delay and slew annotations. void removeDelaySlewAnnotations(); VertexSet ®ClkVertices() { return reg_clk_vertices_; } - void makeSceneAfter(); static constexpr int vertex_level_bits = 24; static constexpr int vertex_level_max = (1<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 ff2ca44be..d01b94382 100644 --- a/network/ParseBus.cc +++ b/network/ParseBus.cc @@ -167,9 +167,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()); @@ -184,7 +185,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 0542f95aa..50bc533da 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/PathGroup.cc b/search/PathGroup.cc index 725b52c7e..ee10bd6e5 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/Search.cc b/search/Search.cc index ee735a0c2..1ab8c6390 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 538b320b6..c9295d980 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 6d3c72236..a57b109b0 100644 --- a/search/Search.tcl +++ b/search/Search.tcl @@ -333,7 +333,7 @@ define_cmd_args "report_checks" \ [-sort_by_slack]\ [-path_group group_name]\ [-format full|full_clock|full_clock_expanded|short|end|slack_only|summary|json]\ - [-fields capacitance|slew|fanout|input_pin|net|src_attr]\ + [-fields capacitance|slew|fanout|input_pin|net|src_attr|variation]\ [-digits digits]\ [-no_line_splits]\ [> filename] [>> filename]} @@ -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 } } diff --git a/search/Sta.cc b/search/Sta.cc index 4389ea7c7..386848383 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -805,7 +805,7 @@ Sta::setAnalysisType(AnalysisType analysis_type, delaysInvalid(); search_->deletePathGroups(); if (graph_) - graph_->setDelayCount(dcalcAnalysisPtCount()); + graph_->delayCountChanged(); } } @@ -2311,6 +2311,8 @@ Sta::setPocvMode(PocvMode mode) } updateComponentsState(); delaysInvalid(); + if (graph_) + graph_->delayCountChanged(); } } @@ -2554,7 +2556,7 @@ Sta::makeScenes(const StringSeq &scene_names) cmd_scene_ = scenes_[0]; updateComponentsState(); if (graph_) - graph_->makeSceneAfter(); + graph_->delayCountChanged(); } void @@ -2583,7 +2585,7 @@ Sta::makeScene(const std::string &name, Scene *scene = makeScene(name, mode, parasitics_min, parasitics_max); updateComponentsState(); if (graph_) - graph_->makeSceneAfter(); + graph_->delayCountChanged(); updateSceneLiberty(scene, liberty_min_files, liberty_max_files); cmd_scene_ = scene; } @@ -3304,15 +3306,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; } } @@ -4437,7 +4435,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; } @@ -4485,8 +4484,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); diff --git a/search/test/cpp/TestSearchStaDesign.cc b/search/test/cpp/TestSearchStaDesign.cc index 814761137..32199c1df 100644 --- a/search/test/cpp/TestSearchStaDesign.cc +++ b/search/test/cpp/TestSearchStaDesign.cc @@ -1838,7 +1838,7 @@ TEST_F(StaDesignTest, SearchReportArrivals) { Search *search = sta_->search(); Vertex *v = findVertex("r1/Q"); ASSERT_NE(v, nullptr); - search->reportArrivals(v, false); + search->reportArrivals(v, false, 3); } // --- Search: reportPathCountHistogram --- @@ -4143,8 +4143,8 @@ TEST_F(StaDesignTest, SearchReportArrivals2) { Search *search = sta_->search(); Vertex *v = findVertex("r1/Q"); ASSERT_NE(v, nullptr); - search->reportArrivals(v, false); - search->reportArrivals(v, true); + search->reportArrivals(v, false, 3); + search->reportArrivals(v, true, 3); } TEST_F(StaDesignTest, SearchSeedArrival) { diff --git a/search/test/search_network_sta_deep.tcl b/search/test/search_network_sta_deep.tcl index 459685704..395ab2305 100644 --- a/search/test/search_network_sta_deep.tcl +++ b/search/test/search_network_sta_deep.tcl @@ -97,8 +97,8 @@ puts "paths: [sta::path_count]" puts "--- report_tag_arrivals ---" set v [sta::worst_slack_vertex max] if { $v != "NULL" } { - sta::report_tag_arrivals_cmd $v 1 - sta::report_tag_arrivals_cmd $v 0 + sta::report_tag_arrivals_cmd $v 1 3 + sta::report_tag_arrivals_cmd $v 0 3 } ############################################################ diff --git a/search/test/search_search_arrival_required.tcl b/search/test/search_search_arrival_required.tcl index abaa16ede..c0300a9d7 100644 --- a/search/test/search_search_arrival_required.tcl +++ b/search/test/search_search_arrival_required.tcl @@ -86,8 +86,8 @@ if { $wv != "NULL" } { } # report_tag_arrivals - sta::report_tag_arrivals_cmd $wv 1 - sta::report_tag_arrivals_cmd $wv 0 + sta::report_tag_arrivals_cmd $wv 1 3 + sta::report_tag_arrivals_cmd $wv 0 3 } puts "--- worst_slack_vertex min ---" diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index ce037eff3..bf7e9faae 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -169,6 +169,7 @@ record_public_tests { verilog_well_supplies verilog_specify verilog_write_escape + verilog_write_gzip verilog_unconnected_hpin } diff --git a/test/verilog_write_gzip.ok b/test/verilog_write_gzip.ok new file mode 100644 index 000000000..bb9762365 --- /dev/null +++ b/test/verilog_write_gzip.ok @@ -0,0 +1,33 @@ +module top (in1, + in2, + clk1, + clk2, + clk3, + out); + input in1; + input in2; + input clk1; + input clk2; + input clk3; + output out; + + wire r1q; + wire r2q; + wire u1z; + wire u2z; + + DFFHQx4_ASAP7_75t_R r1 (.Q(r1q), + .CLK(clk1), + .D(in1)); + DFFHQx4_ASAP7_75t_R r2 (.Q(r2q), + .CLK(clk2), + .D(in2)); + DFFHQx4_ASAP7_75t_R r3 (.Q(out), + .CLK(clk3), + .D(u2z)); + BUFx2_ASAP7_75t_R u1 (.Y(u1z), + .A(r2q)); + AND2x2_ASAP7_75t_R u2 (.Y(u2z), + .A(r1q), + .B(u1z)); +endmodule diff --git a/test/verilog_write_gzip.tcl b/test/verilog_write_gzip.tcl new file mode 100644 index 000000000..a2b442ef1 --- /dev/null +++ b/test/verilog_write_gzip.tcl @@ -0,0 +1,8 @@ +# Check that write_verilog supports gzip compression natively if .gz extension is provided. +source helpers.tcl +read_liberty asap7_small.lib.gz +read_verilog reg1_asap7.v +link_design top +set verilog_file [make_result_file "verilog_write_gzip.v.gz"] +write_verilog $verilog_file +report_file $verilog_file diff --git a/util/DispatchQueue.cc b/util/DispatchQueue.cc index 5347a07ae..c1cd11f7b 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_); diff --git a/verilog/VerilogParse.yy b/verilog/VerilogParse.yy index 3103c3be0..c19c2ea89 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; } diff --git a/verilog/VerilogWriter.cc b/verilog/VerilogWriter.cc index c5d3fe7af..9f299122d 100644 --- a/verilog/VerilogWriter.cc +++ b/verilog/VerilogWriter.cc @@ -33,6 +33,7 @@ #include "Error.hh" #include "Format.hh" #include "Liberty.hh" +#include "Zlib.hh" #include "Network.hh" #include "NetworkCmp.hh" #include "ParseBus.hh" @@ -47,7 +48,7 @@ class VerilogWriter VerilogWriter(const char *filename, bool include_pwr_gnd, CellSeq *remove_cells, - FILE *stream, + gzFile stream, Network *network); void writeModules(); @@ -82,7 +83,7 @@ class VerilogWriter const char *filename_; bool include_pwr_gnd_; CellSet remove_cells_; - FILE *stream_; + gzFile stream_; Network *network_; int unconnected_net_index_{1}; }; @@ -94,12 +95,13 @@ writeVerilog(const char *filename, Network *network) { if (network->topInstance()) { - FILE *stream = fopen(filename, "w"); + bool gzip = std::string_view(filename).ends_with(".gz"); + gzFile stream = gzopen(filename, gzip ? "wb" : "wT"); if (stream) { VerilogWriter writer(filename, include_pwr_gnd, remove_cells, stream, network); writer.writeModules(); - fclose(stream); + gzclose(stream); } else throw FileNotWritable(filename); @@ -109,7 +111,7 @@ writeVerilog(const char *filename, VerilogWriter::VerilogWriter(const char *filename, bool include_pwr_gnd, CellSeq *remove_cells, - FILE *stream, + gzFile stream, Network *network) : filename_(filename), include_pwr_gnd_(include_pwr_gnd),