diff --git a/.clang-format b/.clang-format index d50ce987e..0d0670af5 100644 --- a/.clang-format +++ b/.clang-format @@ -10,7 +10,7 @@ AllowAllParametersOfDeclarationOnNextLine: false AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: false AlwaysBreakAfterReturnType: TopLevel -BinPackArguments: false +BinPackArguments: true # fails BinPackParameters: AlwaysOnePerLine BraceWrapping: @@ -22,7 +22,7 @@ BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Custom # fails if all initializers fit on one line BreakConstructorInitializers: AfterColon -ColumnLimit: 90 +ColumnLimit: 85 # fails ConstructorInitializerIndentWidth: 2 IncludeBlocks: Preserve diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 000000000..6d1a84c20 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,82 @@ +Checks: > + clang-diagnostic-*, + clang-analyzer-*, + -clang-analyzer-core.NonNullParamChecker, + -clang-analyzer-core.CallAndMessage, + -clang-analyzer-core.uninitialized.UndefReturn, + -clang-analyzer-cplusplus.NewDeleteLeaks, + -clang-analyzer-optin.performance.Padding, + readability-*, + -readability-identifier-naming, + -readability-braces-around-statements, + -readability-convert-member-functions-to-static, + -readability-else-after-return, + -readability-function-cognitive-complexity, + -readability-inconsistent-ifelse-braces, + -readability-identifier-length, + -readability-implicit-bool-conversion, + -readability-isolate-declaration, + -readability-magic-numbers, + -readability-make-member-function-const, + -readability-math-missing-parentheses, + -readability-named-parameter, + -readability-qualified-auto, + -readability-redundant-access-specifiers, + -readability-simplify-boolean-expr, + -readability-static-definition-in-anonymous-namespace, + -readability-suspicious-call-argument, + -readability-uppercase-literal-suffix, + -readability-use-anyofallof, + google-*, + -google-readability-avoid-underscore-in-googletest-name, + -google-readability-braces-around-statements, + -google-readability-casting, + -google-readability-todo, + -google-runtime-references, + -google-explicit-constructor, + performance-*, + -performance-enum-size, + bugprone-*, + -bugprone-branch-clone, + -bugprone-easily-swappable-parameters, + -bugprone-exception-escape, + -bugprone-macro-parentheses, + -bugprone-move-forwarding-reference, + -bugprone-narrowing-conversions, + -bugprone-suspicious-missing-comma, + -bugprone-throwing-static-initialization, + modernize-*, + -modernize-avoid-bind, + -modernize-avoid-c-arrays, + -modernize-concat-nested-namespaces, + -modernize-macro-to-enum, + -modernize-pass-by-value, + -modernize-raw-string-literal, + -modernize-return-braced-init-list, + -modernize-use-auto, + -modernize-use-nodiscard, + -modernize-use-trailing-return-type, + -modernize-use-transparent-functors, + misc-*, + -misc-const-correctness, + -misc-multiple-inheritance, + -misc-no-recursion, + -misc-non-private-member-variables-in-classes, + -misc-redundant-expression, + -misc-unused-parameters, + -misc-use-anonymous-namespace, + -misc-use-internal-linkage, + -misc-include-cleaner + +# Only report diagnostics in headers under this tree (app/, include/sta/, build-generated +# headers, etc.). +# Excludes system and third-party paths such as /opt/local/include. +HeaderFilterRegex: '.*/(app|cmake|dcalc|graph|liberty|network|parasitics|power|sdc|sdf|search|spice|tcl|util|verilog|include/sta|build/include/sta)/.*' + +# util/gzstream.hh +# util/FlexDisableRegister.hh +# Bison-generated parser headers (build/{Liberty,Verilog,...}Parse.hh) +# Homebrew and MacPorts third-party headers. +ExcludeHeaderFilterRegex: '(^/opt/(homebrew|local)/.*)|((.*/)?(gzstream\.hh|FlexDisableRegister\.hh|(Liberty|Verilog|Sdf|Spef|Saif|LibExpr)Parse\.hh)$)' +SystemHeaders: false +FormatStyle: none diff --git a/.cursor/rules/cpp-coding-standards.mdc b/.cursor/rules/cpp-coding-standards.mdc new file mode 100644 index 000000000..538991fbc --- /dev/null +++ b/.cursor/rules/cpp-coding-standards.mdc @@ -0,0 +1,59 @@ +--- +description: C++ coding standards and formatting for OpenSTA +globs: ["**/*.cc", "**/*.hh", "**/*.h"] +alwaysApply: false +--- + +# C++ Coding Standards + +## 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. + +## Naming Conventions + +- **Classes**: Upper camel case (`ClassName`) +- **Member functions**: Lower camel case (`memberFunction`) +- **Member variables**: Snake case with trailing underscore (`member_variable_`) +- **Functions**: Lower camel case (`functionName`) +- **Variables**: Snake case + +## Code Style + +- Use `#pragma once` for header guards. +- Return type on the line before the function name; arguments on separate lines when long. +- No braces for single-line if/for; use braces for multi-line bodies. +- Prefer `std::string` over `char*` for string members. +- Prefer pass-by-value and move for sink parameters (parameters that get stored). + +## File Extensions + +- C++ source: `.cc` +- C++ headers: `.hh` + +## Include order + +Sort `#include` lines **lexicographically by the path inside** `<>` +or `""` (case-sensitive). Separate **groups** with a single blank line. + +### Translation units (`*.cc`) + +1. **Primary header(s)** for this file only: + - `#include "Stem.hh"` where `Stem` matches the basename of the `.cc` file (e.g. `Foo.cc` → `Foo.hh`). + - If the file uses a paired private header, include `#include "StemPvt.hh"` **immediately after** `Stem.hh` (e.g. `MakeTimingModel.cc` → `MakeTimingModel.hh` then `MakeTimingModelPvt.hh`). + - Do **not** pull in other headers just because their names share a prefix with `Stem` (e.g. `SearchClass.hh` is not a “primary” include for `Search.cc`; it belongs in the project group below). + +2. **System / standard library** headers: `#include <...>` lines, sorted alphabetically. + +3. **All other project** headers: `#include "..."` lines, sorted alphabetically (including `search/Crpr.hh`-style paths and includes from `liberty/`, etc.). + +If a **comment or special block** intentionally splits the include list (e.g. a third-party note before `#include "cudd.h"`), keep that structure; sort only within each contiguous run of `#include` lines unless merging groups is clearly safe. + +### Headers (`*.hh`) + +After `#pragma once` (and the file’s license block, if present): + +1. **System** `#include <...>` lines, sorted alphabetically. + +2. **Project** `#include "..."` lines, sorted alphabetically. diff --git a/.cursor/rules/cpp-indentation.mdc b/.cursor/rules/cpp-indentation.mdc new file mode 100644 index 000000000..54bdb3755 --- /dev/null +++ b/.cursor/rules/cpp-indentation.mdc @@ -0,0 +1,43 @@ +--- +description: C++ function call indentation for OpenSTA +globs: ["**/*.cc", "**/*.hh", "**/*.h"] +alwaysApply: false +--- + +# C++ Function Call Indentation + +## Short Calls — Single Line + +When arguments fit within the column limit (90 chars), keep them on one line: + +```cpp +// ✅ GOOD +adjusted_data_arrival = delaySum(required, data_shift_to_enable_clk, this); + +// ❌ BAD +adjusted_data_arrival = delaySum(required, + data_shift_to_enable_clk, + this); +``` + +## Nested Function Calls — Align Under Inner Call + +When breaking nested calls across lines: +- Indent continuation lines of the inner call under its first argument (align with content after `innerFunc(`). +- Place remaining outer arguments on the same line as the inner call's closing `)`, indented under the outer function. + +```cpp +// ✅ GOOD +required = delayDiff(delaySum(max_delay, + search_->clkPathArrival(disable_path), + this), + margin, this); + +// ❌ BAD +required = delayDiff( + delaySum(max_delay, + search_->clkPathArrival(disable_path), + this), + margin, + this); +``` diff --git a/.cursor/rules/opensta-tests.mdc b/.cursor/rules/opensta-tests.mdc new file mode 100644 index 000000000..c49f375ed --- /dev/null +++ b/.cursor/rules/opensta-tests.mdc @@ -0,0 +1,13 @@ +--- +description: Where OpenSTA regression tests live (pvt/test vs public test/) +alwaysApply: true +--- + +# OpenSTA tests and regression + +- **Primary suite:** Most regression tests, Tcl drivers, and golden `.ok` files live under **`pvt/test/`** (private / separate repo, often opened as a second workspace root alongside `master`). +- **Public subset:** **`test/`** at the OpenSTA repo root holds a smaller set of tests shipped with the public tree (e.g. `test/regression`, `*.tcl`, `*.ok` there). + +When searching for a test name, regression lists, or updating goldens, **check `pvt/test` first**, then `test/`. + +Typical driver: `pvt/test/regression` (or `test/regression` for the public list). Built `sta` binary is usually `master/build/sta` relative to the main checkout. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..c7fc735ee --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..65c53abb9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,55 @@ +name: CI + +on: + push: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + submodules: true + + - name: Set up dependencies + run: | + sudo apt-get update && sudo apt-get install -y flex libfl-dev bison tcl-dev tcl-tclreadline libeigen3-dev ninja-build + + - name: Set up cudd-3.0.0 + run: | + wget https://github.com/oscc-ip/artifact/releases/download/cudd-3.0.0/build.tar.gz + mkdir -p cudd + tar -zxvf build.tar.gz -Ccudd + + - name: Build + run: | + mkdir build + cd build + cmake .. -G Ninja -DCUDD_DIR=$(pwd)/../cudd -DCMAKE_INSTALL_PREFIX=$(pwd)/install -DCMAKE_BUILD_TYPE=Release + cmake --build . --target all -- -j $(nproc) + cmake --install . + tar -zcvf build.tar.gz -Cinstall . + + - name: Test + run: | + cd test + ./regression + + - name: Upload Artifacts + uses: actions/upload-artifact@v7 + if: ${{ !cancelled() }} + with: + name: artifact + path: | + build/install/* + retention-days: 1 + + - name: Upload Test Result + uses: actions/upload-artifact@v7 + if: ${{ !cancelled() }} + with: + name: result + path: | + test/results/* diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index f3180c552..1d35ca1e1 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -20,7 +20,7 @@ jobs: docker run --entrypoint /bin/bash --tty --rm sta-${{ matrix.os }} -c "cd /OpenSTA/test && (./regression || (cat results/diffs && exit 1)) || (./regression -collections || (cat results/diffs && exit 1))" macos: - runs-on: [macos-14] + runs-on: [macos-15] timeout-minutes: 5 steps: - uses: actions/checkout@v4 diff --git a/CMakeLists.txt b/CMakeLists.txt index 253d37b1a..423fef6e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -22,17 +22,9 @@ # # This notice may not be removed or altered from any source distribution. -cmake_minimum_required (VERSION 3.10) -if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) -# Use standard target names -cmake_policy(SET CMP0078 NEW) -endif() -if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.14) -# Allows SWIG_MODULE_NAME to be set -cmake_policy(SET CMP0086 NEW) -endif() +cmake_minimum_required (VERSION 3.16) -project(STA VERSION 2.7.0 +project(STA VERSION 3.1.0 LANGUAGES CXX ) @@ -44,6 +36,9 @@ option(ENABLE_ASAN "Compile with address santizer enabled" OFF) # Turn on to debug compiler args. set(CMAKE_VERBOSE_MAKEFILE OFF) +# Write compile_commands.json to the build directory for clang-tidy. +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + set(STA_HOME ${PROJECT_SOURCE_DIR}) message(STATUS "STA version: ${PROJECT_VERSION}") @@ -63,6 +58,16 @@ message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") message(STATUS "Build CXX_FLAGS: ${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}}") message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}") +# Enable Link-Time Optimization (LTO) for Release builds. +include(CheckIPOSupported) +check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error) +if(ipo_supported) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE) + message(STATUS "IPO/LTO: enabled for Release builds") +else() + message(STATUS "IPO/LTO: not supported - ${ipo_error}") +endif() + ################################################################ # # Source files. @@ -72,14 +77,17 @@ message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}") set(STA_SOURCE app/StaMain.cc - dcalc/ArcDelayCalc.cc dcalc/ArcDcalcWaveforms.cc + dcalc/ArcDelayCalc.cc dcalc/ArnoldiDelayCalc.cc dcalc/ArnoldiReduce.cc dcalc/CcsCeffDelayCalc.cc - dcalc/DcalcAnalysisPt.cc + dcalc/Delay.cc dcalc/DelayCalc.cc dcalc/DelayCalcBase.cc + dcalc/DelayNormal.cc + dcalc/DelayScalar.cc + dcalc/DelaySkewNormal.cc dcalc/DmpCeff.cc dcalc/DmpDelayCalc.cc dcalc/FindRoot.cc @@ -90,9 +98,6 @@ set(STA_SOURCE dcalc/PrimaDelayCalc.cc dcalc/UnitDelayCalc.cc - graph/DelayFloat.cc - graph/DelayNormal1.cc - graph/DelayNormal2.cc graph/Graph.cc graph/GraphCmp.cc @@ -136,6 +141,7 @@ set(STA_SOURCE parasitics/SpefReaderPvt.hh power/Power.cc + power/ReportPower.cc power/VcdReader.cc power/SaifReader.cc power/VcdParse.cc @@ -150,13 +156,12 @@ set(STA_SOURCE sdc/DeratingFactors.cc sdc/DisabledPorts.cc sdc/ExceptionPath.cc - sdc/FilterExpr.cc + sdc/FilterObjects.cc sdc/InputDrive.cc sdc/PinPair.cc sdc/PortDelay.cc sdc/PortExtCap.cc sdc/Sdc.cc - sdc/SdcGraph.cc sdc/SdcCmdComment.cc sdc/Variables.cc sdc/WriteSdc.cc @@ -170,15 +175,14 @@ set(STA_SOURCE search/CheckMaxSkews.cc search/CheckMinPeriods.cc search/CheckMinPulseWidths.cc - search/CheckCapacitanceLimits.cc - search/CheckFanoutLimits.cc - search/CheckSlewLimits.cc + search/CheckCapacitances.cc + search/CheckFanouts.cc + search/CheckSlews.cc search/CheckTiming.cc search/ClkInfo.cc search/ClkLatency.cc search/ClkNetwork.cc search/ClkSkew.cc - search/Corner.cc search/Crpr.cc search/FindRegister.cc search/GatedClk.cc @@ -186,17 +190,19 @@ set(STA_SOURCE search/Latches.cc search/Levelize.cc search/MakeTimingModel.cc + search/Mode.cc search/Path.cc - search/PathAnalysisPt.cc search/Path.cc search/PathEnd.cc search/PathEnum.cc search/PathExpanded.cc search/PathGroup.cc + search/PocvMode.cc search/Property.cc search/ReportPath.cc search/Search.cc search/SearchPred.cc + search/Scene.cc search/Sim.cc search/Sta.cc search/StaState.cc @@ -217,25 +223,32 @@ set(STA_SOURCE util/Error.cc util/Fuzzy.cc util/Hash.cc - util/Machine.cc util/MinMax.cc util/PatternMatch.cc util/Report.cc util/ReportStd.cc util/ReportTcl.cc util/RiseFallMinMax.cc + util/RiseFallMinMaxDelay.cc util/RiseFallValues.cc util/Stats.cc - util/StringSeq.cc - util/StringSet.cc util/StringUtil.cc - util/TokenParser.cc util/Transition.cc verilog/VerilogReader.cc verilog/VerilogWriter.cc ) +if(APPLE) + list(APPEND STA_SOURCE util/MachineApple.cc) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + list(APPEND STA_SOURCE util/MachineLinux.cc) +elseif(WIN32) + list(APPEND STA_SOURCE util/MachineWin32.cc) +else() + list(APPEND STA_SOURCE util/MachineUnknown.cc) +endif() + # Source files. set(STA_TCL_FILES tcl/Collections.tcl @@ -316,6 +329,20 @@ bison_target(SaifParse ${STA_HOME}/power/SaifParse.yy ${CMAKE_CURRENT_BINARY_DIR}/SaifParse.cc) add_flex_bison_dependency(SaifLex SaifParse) +# Suppress -Wsign-compare in flex-generated code (yyleng vs int loop counter). +# Only needed with older GCC (e.g. CentOS 7 stock 4.8.5); newer GCC/flex handle it. +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set_source_files_properties( + ${FLEX_VerilogLex_OUTPUTS} + ${FLEX_LibertyLex_OUTPUTS} + ${FLEX_LibExprLex_OUTPUTS} + ${FLEX_SdfLex_OUTPUTS} + ${FLEX_SpefLex_OUTPUTS} + ${FLEX_SaifLex_OUTPUTS} + PROPERTIES COMPILE_FLAGS "-Wno-sign-compare" + ) +endif() + ################################################################ set(STA_TCL_INIT ${CMAKE_CURRENT_BINARY_DIR}/StaTclInitVar.cc) @@ -384,12 +411,47 @@ find_package(Threads) find_package(Eigen3 REQUIRED) -include(cmake/FindCUDD.cmake) - -if("${SSTA}" STREQUAL "") - set(SSTA 0) +# See if std::format is available and if nor install fmt. +include(CheckCXXSourceCompiles) +set(_sta_fmt_check_saved_flags "${CMAKE_REQUIRED_FLAGS}") +if(MSVC) + string(APPEND CMAKE_REQUIRED_FLAGS " /std:c++20") +else() + string(APPEND CMAKE_REQUIRED_FLAGS " -std=c++20") +endif() +check_cxx_source_compiles(" +#include +#include +int main() { (void)std::format(\"{}\", 42); return 0; } +" HAVE_CXX_STD_FORMAT) +set(CMAKE_REQUIRED_FLAGS "${_sta_fmt_check_saved_flags}") + +if(HAVE_CXX_STD_FORMAT) + message(STATUS "std::format: available") +else() + # Set the fmt dir for the ubuntu/centos docker files. + if(EXISTS "/usr/local/lib/cmake/fmt/fmt-config.cmake") + set(fmt_DIR "/usr/local/lib/cmake/fmt") + elseif(EXISTS "/usr/lib/x86_64-linux-gnu/cmake/fmt/fmt-config.cmake") + set(fmt_DIR "/usr/lib/x86_64-linux-gnu/cmake/fmt") + elseif(EXISTS "/usr/lib/aarch64-linux-gnu/cmake/fmt/fmt-config.cmake") + set(fmt_DIR "/usr/lib/aarch64-linux-gnu/cmake/fmt") + endif() + find_package(fmt QUIET) + if(fmt_FOUND) + message(STATUS "std::format: using installed fmt library") + else() + message(STATUS "std::format: building fmt library") + include(FetchContent) + FetchContent_Declare(fmt + GIT_REPOSITORY https://github.com/fmtlib/fmt.git + GIT_TAG 10.2.1 + ) + FetchContent_MakeAvailable(fmt) + endif() endif() -message(STATUS "SSTA: ${SSTA}") + +include(cmake/FindCUDD.cmake) # configure a header file to pass some of the CMake settings configure_file(${STA_HOME}/util/StaConfig.hh.cmake @@ -401,6 +463,7 @@ configure_file(${STA_HOME}/util/StaConfig.hh.cmake ########################################################### find_package(SWIG 3.0 REQUIRED) +message(STATUS "SWIG version: ${SWIG_VERSION}") include(UseSWIG) set(STA_SWIG_FILE app/StaApp.i) @@ -408,14 +471,12 @@ set(STA_SWIG_FILE app/StaApp.i) set_property(SOURCE ${STA_SWIG_FILE} PROPERTY CPLUSPLUS ON ) -# Ubuntu 18.04 cmake version 3.10.2 does not support the -# COMPILE_OPTIONS and INCLUDE_DIRECTORIES properties, so cram -# them into SWIG_FLAGS for the time being. +# SWIG flags (CMake UseSWIG). set_property(SOURCE ${STA_SWIG_FILE} - PROPERTY SWIG_FLAGS + PROPERTY COMPILE_OPTIONS -module sta - -namespace -prefix sta - -I${STA_HOME} + -namespace + -prefix sta ) set(SWIG_FILES @@ -473,6 +534,9 @@ target_include_directories(sta_swig ${CMAKE_CURRENT_BINARY_DIR}/include/sta ) +# Pass sta_swig include directories to SWIG as -I (same paths as the C++ build). +set_property(TARGET sta_swig PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE) + ########################################################### # Library ########################################################### @@ -510,6 +574,10 @@ target_link_libraries(OpenSTA ${CUDD_LIB} ) +if(NOT HAVE_CXX_STD_FORMAT) + target_link_libraries(OpenSTA fmt::fmt) +endif() + if (ZLIB_LIBRARIES) target_link_libraries(OpenSTA ${ZLIB_LIBRARIES}) endif() @@ -538,30 +606,30 @@ endif() # common to gcc/clang set(CXX_FLAGS -Wall -Wextra -pedantic -Wcast-qual -Wredundant-decls - -Wformat-security -Werror=misleading-indentation) + -Wformat-security -Werror=misleading-indentation -Wundef) if(ENABLE_TSAN) message(STATUS "Thread sanitizer: ${ENABLE_TSAN}") set(CXX_FLAGS "${CXX_FLAGS};-fsanitize=thread") - set(CMAKE_EXE_LINKER_FLAGS "-fsanitize=thread") + string(APPEND CMAKE_EXE_LINKER_FLAGS " -fsanitize=thread") endif() if(ENABLE_ASAN) message(STATUS "Address sanitizer: ${ENABLE_ASAN}") set(CXX_FLAGS "${CXX_FLAGS};-fsanitize=address") - set(CMAKE_EXE_LINKER_FLAGS "-fsanitize=address") + string(APPEND CMAKE_EXE_LINKER_FLAGS " -fsanitize=address") endif() target_compile_options(OpenSTA PRIVATE - $<$:${CXX_FLAGS}> + $<$:${CXX_FLAGS} -Wno-format-zero-length> $<$:${CXX_FLAGS} -Wno-gnu-zero-variadic-macro-arguments> $<$:${CXX_FLAGS} -Wno-gnu-zero-variadic-macro-arguments> ) # Disable compiler specific extensions like gnu++11. set_target_properties(OpenSTA PROPERTIES CXX_EXTENSIONS OFF) -target_compile_features(OpenSTA PUBLIC cxx_std_17) +target_compile_features(OpenSTA PUBLIC cxx_std_20) message(STATUS "STA library: ${CMAKE_BINARY_DIR}/libOpenSTA.a") @@ -575,6 +643,7 @@ message(STATUS "STA library: ${CMAKE_BINARY_DIR}/libOpenSTA.a") add_executable(sta app/Main.cc) target_link_libraries(sta + PRIVATE sta_swig OpenSTA ) diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 000000000..c6c2b793d --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,2 @@ +# For CI/CD issues +/.github/ @kazutoiris diff --git a/Dockerfile.centos7 b/Dockerfile.centos7 index 4f629ba86..d7a866e95 100644 --- a/Dockerfile.centos7 +++ b/Dockerfile.centos7 @@ -11,9 +11,11 @@ RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo \ RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo \ && sed -i s/^#.*baseurl=http/baseurl=http/g /etc/yum.repos.d/*.repo \ && sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo \ - && yum install -y devtoolset-11 wget cmake3 make eigen3-devel tcl swig3 flex zlib-devel valgrind \ + && yum install -y devtoolset-11 git wget cmake3 make eigen3-devel tcl swig3 flex zlib-devel valgrind \ && yum clean -y all +RUN ln -sf /usr/bin/cmake3 /usr/bin/cmake + # Download Bison RUN wget https://mirrors.dotsrc.org/gnu/bison/bison-3.8.2.tar.gz && \ tar -xvf bison-3.8.2.tar.gz && \ @@ -51,6 +53,30 @@ RUN source /opt/rh/devtoolset-11/enable && \ make -j`nproc` && \ make install +# Download and build fmt +# Ensure the Vault redirect is applied to everything including new installs +RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo && \ + sed -i s/^#.*baseurl=http/baseurl=http/g /etc/yum.repos.d/*.repo && \ + sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo && \ + yum install -y ca-certificates git && \ + update-ca-trust force-enable +# clone fmt compatible version (10.2.1) +RUN git config --global http.sslVerify false && \ + git clone --depth 1 --branch 10.2.1 https://github.com/fmtlib/fmt.git /tmp/fmt +RUN source /opt/rh/devtoolset-11/enable && \ + cd /tmp/fmt && \ + mkdir build && cd build && \ + cmake3 .. \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DFMT_TEST=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + -DFMT_DOC=OFF && \ + make -j$(nproc) && \ + make install +RUN rm -rf /tmp/fmt + +################################################################ + FROM base-dependencies AS builder COPY . /OpenSTA @@ -60,8 +86,9 @@ WORKDIR /OpenSTA RUN rm -rf build && mkdir build RUN source /opt/rh/devtoolset-11/enable && \ cd build && \ - cmake3 .. && \ - make -j`nproc` + cmake .. && \ + # LTO fails with -j + make # Run sta on entry ENTRYPOINT ["/OpenSTA/build/sta"] diff --git a/Dockerfile.ubuntu22.04 b/Dockerfile.ubuntu22.04 index 9a803c8f3..b8c94ca4a 100644 --- a/Dockerfile.ubuntu22.04 +++ b/Dockerfile.ubuntu22.04 @@ -6,17 +6,20 @@ LABEL maintainer="James Cherry " ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && \ apt-get install -y \ + git \ wget \ cmake \ gcc \ + gdb \ tcl-dev \ tcl-tclreadline \ - libeigen3-dev \ swig \ bison \ flex \ automake \ - autotools-dev + 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 && \ diff --git a/README.md b/README.md index 6814119f9..e30b10519 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ eigen 3.4.0 3.4.0 MPL2 required cudd 3.0.0 3.0.0 BSD required tclreadline 2.3.8 2.3.8 BSD optional zLib 1.2.5 1.2.8 zlib optional +libfmt 8.1.1 N/A MIT required if std::format not available ``` The [TCL readline library](https://tclreadline.sourceforge.net/tclreadline.html) @@ -143,6 +144,11 @@ make You can use the "configure --prefix" option and "make install" to install CUDD in a different directory. +Modern c++ compilers that support c++20 include support for std::format. +With older compilers like gcc 11 on Ubuntu 22.04 and Centos7 the fmt library +is used instead. If it is not installed locally, the github repository is +downloaded and compiled in the build directory. + ### Building with CMake Use the following commands to checkout the git repository and build the @@ -184,13 +190,23 @@ files in the build directory. ## Build with Docker -An alternative way to build and run OpenSTA is with +AN alternative way to build and run OpenSTA is with [Docker](https://www.docker.com). After installing Docker, the following command builds a Docker image. ``` cd OpenSTA docker build --file Dockerfile.ubuntu22.04 --tag opensta_ubuntu22.04 . +or +docker build --file Dockerfile.centos7 --tag opensta_centos7 . +``` + +The centos7 build on mac/OsX with ARM processorts requires the platform +to be specified. + +``` +docker build --file Dockerfile.centos7 --platform=linux/amd64 --tag opensta_centos7 . + ``` To run a docker container using the OpenSTA image, use the -v option diff --git a/app/Main.cc b/app/Main.cc index 4c7e56e98..8134b35e2 100644 --- a/app/Main.cc +++ b/app/Main.cc @@ -27,6 +27,8 @@ #include #include // exit +#include +#include #include #if TCL_READLINE #include @@ -38,7 +40,6 @@ namespace sta { extern const char *tcl_inits[]; } -using std::string; using sta::stringEq; using sta::findCmdLineFlag; using sta::Sta; @@ -47,7 +48,6 @@ using sta::evalTclInit; using sta::sourceTclFile; using sta::parseThreadsArg; using sta::tcl_inits; -using sta::is_regular_file; // Swig uses C linkage for init functions. extern "C" { @@ -59,19 +59,19 @@ static char **cmd_argv; static const char *init_filename = ".sta"; static void -showUsage(const char *prog, - const char *init_filename); +showUsage(std::string_view prog, + std::string_view init_filename); static int tclAppInit(Tcl_Interp *interp); static int staTclAppInit(int argc, - char *argv[], - const char *init_filename, - Tcl_Interp *interp); + char *argv[], + std::string_view init_filename, + Tcl_Interp *interp); static void initStaApp(int &argc, - char *argv[], - Tcl_Interp *interp); + char *argv[], + Tcl_Interp *interp); int main(int argc, @@ -87,20 +87,11 @@ main(int argc, } else { // Set argc to 1 so Tcl_Main doesn't source any files. - // Tcl_Main never returns. -#if 0 - // It should be possible to pass argc/argv to staTclAppInit with - // a closure but I couldn't get the signature to match Tcl_AppInitProc. - Tcl_Main(1, argv, [=](Tcl_Interp *interp) - { sta::staTclAppInit(argc, argv, interp); - return 1; - }); -#else - // Workaround. + // Store argc and argv in static variables for tclAppInit. cmd_argc = argc; cmd_argv = argv; + // Tcl_Main never returns. Tcl_Main(1, argv, tclAppInit); -#endif return 0; } } @@ -114,9 +105,9 @@ tclAppInit(Tcl_Interp *interp) // Tcl init executed inside Tcl_Main. static int staTclAppInit(int argc, - char *argv[], - const char *init_filename, - Tcl_Interp *interp) + char *argv[], + std::string_view init_filename, + Tcl_Interp *interp) { // source init.tcl if (Tcl_Init(interp) == TCL_ERROR) @@ -138,10 +129,10 @@ staTclAppInit(int argc, if (!findCmdLineFlag(argc, argv, "-no_init")) { const char *home = getenv("HOME"); if (home) { - string init_path = home; + std::string init_path = home; init_path += "/"; - init_path += init_filename; - if (is_regular_file(init_path.c_str())) + init_path.append(init_filename); + if (std::filesystem::is_regular_file(init_path.c_str())) sourceTclFile(init_path.c_str(), true, true, interp); } } @@ -157,7 +148,7 @@ staTclAppInit(int argc, if (argc == 2) { char *cmd_file = argv[1]; if (cmd_file) { - int result = sourceTclFile(cmd_file, false, false, interp); + int result = sourceTclFile(cmd_file, false, false, interp); if (exit_after_cmd_file) { int exit_code = (result == TCL_OK) ? EXIT_SUCCESS : EXIT_FAILURE; exit(exit_code); @@ -174,8 +165,8 @@ staTclAppInit(int argc, static void initStaApp(int &argc, - char *argv[], - Tcl_Interp *interp) + char *argv[], + Tcl_Interp *interp) { initSta(); Sta *sta = new Sta; @@ -193,15 +184,17 @@ initStaApp(int &argc, } static void -showUsage(const char *prog, - const char *init_filename) +showUsage(std::string_view prog, + std::string_view init_filename) { - printf("Usage: %s [-help] [-version] [-no_init] [-exit] cmd_file\n", prog); - printf(" -help show help and exit\n"); - printf(" -version show version and exit\n"); - printf(" -no_init do not read %s init file\n", init_filename); - printf(" -threads count|max use count threads\n"); - printf(" -no_splash do not show the license splash at startup\n"); - printf(" -exit exit after reading cmd_file\n"); - printf(" cmd_file source cmd_file\n"); + sta::print(stdout, "Usage: {} [-help] [-version] [-no_init] [-exit] cmd_file\n", + prog); + sta::print(stdout, " -help show help and exit\n"); + sta::print(stdout, " -version show version and exit\n"); + sta::print(stdout, " -no_init do not read {} init file\n", + init_filename); + sta::print(stdout, " -threads count|max use count threads\n"); + sta::print(stdout, " -no_splash do not show the license splash at startup\n"); + sta::print(stdout, " -exit exit after reading cmd_file\n"); + sta::print(stdout, " cmd_file source cmd_file\n"); } diff --git a/app/StaMain.cc b/app/StaMain.cc index 97040fd7b..84b0540f7 100644 --- a/app/StaMain.cc +++ b/app/StaMain.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,27 +24,28 @@ #include "StaMain.hh" +#include +#include #include #include #include #include "Machine.hh" #include "StringUtil.hh" -#include "Vector.hh" #include "Sta.hh" namespace sta { int parseThreadsArg(int &argc, - char *argv[]) + char *argv[]) { char *thread_arg = findCmdLineKey(argc, argv, "-threads"); if (thread_arg) { if (stringEqual(thread_arg, "max")) return processorCount(); else if (isDigits(thread_arg)) - return atoi(thread_arg); + return std::stoi(thread_arg); else fprintf(stderr,"Warning: -threads must be max or a positive integer.\n"); } @@ -53,15 +54,15 @@ parseThreadsArg(int &argc, bool findCmdLineFlag(int &argc, - char *argv[], - const char *flag) + char *argv[], + std::string_view flag) { for (int i = 1; i < argc; i++) { char *arg = argv[i]; - if (stringEq(arg, flag)) { + if (std::string_view(arg) == flag) { // Remove flag from argv. for (int j = i + 1; j < argc; j++, i++) - argv[i] = argv[j]; + argv[i] = argv[j]; argc--; argv[argc] = nullptr; return true; @@ -72,16 +73,16 @@ findCmdLineFlag(int &argc, char * findCmdLineKey(int &argc, - char *argv[], - const char *key) + char *argv[], + std::string_view key) { for (int i = 1; i < argc; i++) { char *arg = argv[i]; - if (stringEq(arg, key) && i + 1 < argc) { + if (std::string_view(arg) == key && i + 1 < argc) { char *value = argv[i + 1]; // Remove key and value from argv. for (int j = i + 2; j < argc; j++, i++) - argv[i] = argv[j]; + argv[i] = argv[j]; argc -= 2; argv[argc] = nullptr; return value; @@ -93,15 +94,14 @@ findCmdLineKey(int &argc, // Use overridden version of source to echo cmds and results. int sourceTclFile(const char *filename, - bool echo, - bool verbose, - Tcl_Interp *interp) + bool echo, + bool verbose, + Tcl_Interp *interp) { - std::string cmd; - stringPrint(cmd, "sta::include_file %s %s %s", - filename, - echo ? "1" : "0", - verbose ? "1" : "0"); + std::string cmd = sta::format("sta::include_file {} {} {}", + filename, + echo ? "1" : "0", + verbose ? "1" : "0"); int code = Tcl_Eval(interp, cmd.c_str()); const char *result = Tcl_GetStringResult(interp); if (result[0] != '\0') @@ -111,7 +111,7 @@ sourceTclFile(const char *filename, void evalTclInit(Tcl_Interp *interp, - const char *inits[]) + const char *inits[]) { char *unencoded = unencode(inits); if (Tcl_Eval(interp, unencoded) != TCL_OK) { @@ -148,12 +148,4 @@ unencode(const char *inits[]) return unencoded; } -// Hack until c++17 filesystem is better supported. -bool -is_regular_file(const char *filename) -{ - struct stat sb; - return stat(filename, &sb) == 0 && S_ISREG(sb.st_mode); -} - -} // namespace +} // namespace sta diff --git a/cmake/FindTCL.cmake b/cmake/FindTCL.cmake index 17cb3a625..7df9b71d3 100644 --- a/cmake/FindTCL.cmake +++ b/cmake/FindTCL.cmake @@ -23,6 +23,7 @@ # searching OSX system directories before unix directories. set(TCL_POSSIBLE_NAMES + #tcl90 tcl9.0 tcl87 tcl8.7 tcl86 tcl8.6 tcl85 tcl8.5 @@ -32,6 +33,7 @@ set(TCL_POSSIBLE_NAMES if (NOT TCL_LIB_PATHS) if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(TCL_LIB_PATHS + #/opt/homebrew/Cellar/tcl-tk/9.0.3/lib /opt/homebrew/Cellar/tcl-tk@8/8.6.17/lib /opt/homebrew/Cellar/tcl-tk@8/8.6.16/lib /opt/homebrew/opt/tcl-tk/lib /usr/local/lib) diff --git a/dcalc/ArcDcalcWaveforms.cc b/dcalc/ArcDcalcWaveforms.cc index 4b8d340d8..184607e6a 100644 --- a/dcalc/ArcDcalcWaveforms.cc +++ b/dcalc/ArcDcalcWaveforms.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,25 +22,23 @@ // // This notice may not be removed or altered from any source distribution. -#include - #include "ArcDcalcWaveforms.hh" -#include "Report.hh" -#include "Liberty.hh" -#include "Network.hh" -#include "Graph.hh" +#include + #include "ArcDelayCalc.hh" -#include "DcalcAnalysisPt.hh" +#include "Graph.hh" #include "GraphDelayCalc.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Report.hh" namespace sta { -using std::make_shared; - Waveform ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const StaState *sta) { const Network *network = sta->network(); @@ -55,19 +53,22 @@ ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg, const Vertex *in_vertex = graph->pinLoadVertex(in_pin); GraphDelayCalc *graph_dcalc = sta->graphDelayCalc(); Slew in_slew = graph_dcalc->edgeFromSlew(in_vertex, in_rf, - dcalc_arg.arc()->role(), dcalc_ap); + dcalc_arg.arc()->role(), + scene, min_max); LibertyLibrary *library = port->libertyLibrary(); float vdd; bool vdd_exists; library->supplyVoltage("VDD", vdd, vdd_exists); if (!vdd_exists) - report->error(1751, "VDD not defined in library %s", library->name()); - Waveform in_waveform = driver_waveform->waveform(delayAsFloat(in_slew)); + report->error(1751, "VDD not defined in library {}", library->name()); + float slew1 = delayAsFloat(in_slew, min_max, sta); + Waveform in_waveform = driver_waveform->waveform(slew1); // Delay time axis. - FloatSeq *time_values = new FloatSeq; - for (float time : *in_waveform.axis1()->values()) - time_values->push_back(time + dcalc_arg.inputDelay()); - TableAxisPtr time_axis = make_shared(TableAxisVariable::time, time_values); + FloatSeq time_values; + for (float time : in_waveform.axis1()->values()) + time_values.push_back(time + dcalc_arg.inputDelay()); + TableAxisPtr time_axis = std::make_shared(TableAxisVariable::time, + std::move(time_values)); // Scale the waveform from 0:vdd. FloatSeq *scaled_values = new FloatSeq; for (float value : *in_waveform.values()) { @@ -82,4 +83,4 @@ ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg, return Waveform(); } -} // namespace +} // namespace sta diff --git a/dcalc/ArcDcalcWaveforms.hh b/dcalc/ArcDcalcWaveforms.hh index 2cbcf3b5c..8b84f72f9 100644 --- a/dcalc/ArcDcalcWaveforms.hh +++ b/dcalc/ArcDcalcWaveforms.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -31,8 +31,7 @@ namespace sta { class StaState; -class Corner; -class DcalcAnalysisPt; +class Scene; class ArcDcalcArg; // Abstract class for delay calculation waveforms for ploting. @@ -47,9 +46,10 @@ public: protected: Waveform inputWaveform(ArcDcalcArg &dcalc_arg, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const StaState *sta); }; -} // namespace +} // namespace sta diff --git a/dcalc/ArcDelayCalc.cc b/dcalc/ArcDelayCalc.cc index d20ca928d..b615ca477 100644 --- a/dcalc/ArcDelayCalc.cc +++ b/dcalc/ArcDelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,12 +25,14 @@ #include "ArcDelayCalc.hh" #include +#include -#include "Units.hh" +#include "Graph.hh" #include "Liberty.hh" -#include "TimingArc.hh" #include "Network.hh" -#include "Graph.hh" +#include "StringUtil.hh" +#include "TimingArc.hh" +#include "Units.hh" namespace sta { @@ -46,27 +48,29 @@ ArcDelayCalc::gateDelay(const TimingArc *arc, const Parasitic *parasitic, float, const Pvt *, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, // Return values. ArcDelay &gate_delay, Slew &drvr_slew) { LoadPinIndexMap load_pin_index_map(network_); ArcDcalcResult dcalc_result = gateDelay(nullptr, arc, in_slew, load_cap, parasitic, - load_pin_index_map, dcalc_ap); + load_pin_index_map, scene, min_max); gate_delay = dcalc_result.gateDelay(); drvr_slew = dcalc_result.drvrSlew(); } //////////////////////////////////////////////////////////////// +// For TCL %typemap(in) ArcDcalcArg. ArcDcalcArg -makeArcDcalcArg(const char *inst_name, - const char *in_port_name, - const char *in_rf_name, - const char *drvr_port_name, - const char *drvr_rf_name, - const char *input_delay_str, +makeArcDcalcArg(std::string_view inst_name, + std::string_view in_port_name, + std::string_view in_rf_name, + std::string_view drvr_port_name, + std::string_view drvr_rf_name, + std::string_view input_delay_str, const StaState *sta) { Report *report = sta->report(); @@ -81,7 +85,8 @@ makeArcDcalcArg(const char *inst_name, if (drvr_pin) { const RiseFall *drvr_rf = RiseFall::find(drvr_rf_name); if (drvr_rf) { - float input_delay = strtof(input_delay_str, nullptr); + const std::string input_delay_buf(input_delay_str); + auto [input_delay, valid] = stringFloat(input_delay_buf); input_delay = sta->units()->timeUnit()->userToSta(input_delay); const Graph *graph = sta->graph(); @@ -93,24 +98,24 @@ makeArcDcalcArg(const char *inst_name, else { const Network *network = sta->network(); const Instance *inst = network->instance(in_pin); - report->warn(2100, "no timing arc for %s input/driver pins.", + report->warn(2100, "no timing arc for {} input/driver pins.", network->pathName(inst)); } } else - report->warn(2101, "%s not a valid rise/fall.", drvr_rf_name); + report->warn(2101, "{} not a valid rise/fall.", drvr_rf_name); } else - report->warn(2102, "Pin %s/%s not found.", inst_name, drvr_port_name); + report->warn(2102, "Pin {}/{} not found.", inst_name, drvr_port_name); } else - report->warn(2103, "%s not a valid rise/fall.", in_rf_name); + report->warn(2103, "{} not a valid rise/fall.", in_rf_name); } else - report->warn(2104, "Pin %s/%s not found.", inst_name, in_port_name); + report->warn(2104, "Pin {}/{} not found.", inst_name, in_port_name); } else - report->warn(2105, "Instance %s not found.", inst_name); + report->warn(2105, "Instance {} not found.", inst_name); return ArcDcalcArg(); } @@ -130,7 +135,7 @@ ArcDcalcArg::ArcDcalcArg(const Pin *in_pin, const Pin *drvr_pin, Edge *edge, const TimingArc *arc, - const Slew in_slew, + Slew in_slew, float load_cap, const Parasitic *parasitic) : in_pin_(in_pin), @@ -160,17 +165,7 @@ ArcDcalcArg::ArcDcalcArg(const Pin *in_pin, { } -ArcDcalcArg::ArcDcalcArg(const ArcDcalcArg &arg) : - in_pin_(arg.in_pin_), - drvr_pin_(arg.drvr_pin_), - edge_(arg.edge_), - arc_(arg.arc_), - in_slew_(arg.in_slew_), - load_cap_(arg.load_cap_), - parasitic_(arg.parasitic_), - input_delay_(arg.input_delay_) -{ -} +ArcDcalcArg::ArcDcalcArg(const ArcDcalcArg &arg) = default; const RiseFall * ArcDcalcArg::inEdge() const @@ -256,18 +251,18 @@ ArcDcalcResult::ArcDcalcResult(size_t load_count) : } void -ArcDcalcResult::setGateDelay(ArcDelay gate_delay) +ArcDcalcResult::setGateDelay(const ArcDelay &gate_delay) { gate_delay_ = gate_delay; } void -ArcDcalcResult::setDrvrSlew(Slew drvr_slew) +ArcDcalcResult::setDrvrSlew(const Slew &drvr_slew) { drvr_slew_ = drvr_slew; } -ArcDelay +const ArcDelay & ArcDcalcResult::wireDelay(size_t load_idx) const { return wire_delays_[load_idx]; @@ -275,7 +270,7 @@ ArcDcalcResult::wireDelay(size_t load_idx) const void ArcDcalcResult::setWireDelay(size_t load_idx, - ArcDelay wire_delay) + const ArcDelay &wire_delay) { wire_delays_[load_idx] = wire_delay; } @@ -287,7 +282,7 @@ ArcDcalcResult::setLoadCount(size_t load_count) load_slews_.resize(load_count); } -Slew +const Slew & ArcDcalcResult::loadSlew(size_t load_idx) const { return load_slews_[load_idx]; @@ -295,9 +290,9 @@ ArcDcalcResult::loadSlew(size_t load_idx) const void ArcDcalcResult::setLoadSlew(size_t load_idx, - Slew load_slew) + const Slew &load_slew) { load_slews_[load_idx] = load_slew; } -} // namespace +} // namespace sta diff --git a/dcalc/Arnoldi.hh b/dcalc/Arnoldi.hh index 05a11b71d..ac5775651 100644 --- a/dcalc/Arnoldi.hh +++ b/dcalc/Arnoldi.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -45,7 +45,7 @@ class arnoldi1 public: arnoldi1() { order=0; n=0; d=nullptr; e=nullptr; U=nullptr; ctot=0.0; sqc=0.0; } ~arnoldi1(); - double elmore(int term_index); + double elmore(int k); // // calculate poles/residues for given rdrive @@ -66,16 +66,15 @@ public: // n is the number of terms // The vectors U[j] are of size n class rcmodel : public ConcreteParasitic, - public arnoldi1 + public arnoldi1 { public: - rcmodel(); - virtual ~rcmodel(); - virtual float capacitance() const; - virtual PinSet unannotatedLoads(const Pin *drvr_pin, - const Parasitics *parasitics) const; + ~rcmodel() override; + float capacitance() const override; + PinSet unannotatedLoads(const Pin *drvr_pin, + const Parasitics *parasitics) const override; - const Pin **pinV; // [n] + const Pin **pinV{nullptr}; // [n] }; struct timing_table @@ -86,4 +85,4 @@ struct timing_table float in_slew; }; -} // namespace +} // namespace sta diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index 62f973c93..502a73eb7 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -28,31 +28,34 @@ #include "ArnoldiDelayCalc.hh" -#include +#include #include // abs +#include +#include +#include -#include "Report.hh" +#include "ArcDelayCalc.hh" +#include "Arnoldi.hh" +#include "ArnoldiReduce.hh" #include "Debug.hh" -#include "Units.hh" +#include "DelayCalc.hh" +#include "Graph.hh" +#include "GraphDelayCalc.hh" #include "Liberty.hh" -#include "TimingModel.hh" -#include "TimingArc.hh" -#include "TableModel.hh" -#include "PortDirection.hh" +#include "LumpedCapDelayCalc.hh" #include "Network.hh" -#include "Graph.hh" #include "Parasitics.hh" +#include "PortDirection.hh" +#include "Report.hh" #include "Sdc.hh" -#include "DcalcAnalysisPt.hh" -#include "DelayCalc.hh" -#include "ArcDelayCalc.hh" -#include "LumpedCapDelayCalc.hh" -#include "GraphDelayCalc.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "TimingModel.hh" +#include "Units.hh" #include "Variables.hh" -#include "Arnoldi.hh" -#include "ArnoldiReduce.hh" namespace sta { +// NOLINTBEGIN(modernize-avoid-c-style-cast) // wireload8 is n^2 // do not delete arnoldi parasitics @@ -64,10 +67,6 @@ namespace sta { // ra_get_r // ra_get_s -using std::string; -using std::abs; -using std::vector; - struct delay_work; struct delay_c; @@ -78,10 +77,14 @@ static void delay_work_destroy(delay_work *D); static double * delay_work_get_residues(delay_work *D, - int term_index); + int term_index); static bool -tridiagEV(int n,double *d,double *e,double *p,double **v); +tridiagEV(int n, + const double *din, + const double *ein, + double *d, + double **v); ////////////////////////////////////////////////////////////// @@ -124,22 +127,25 @@ class ArnoldiDelayCalc : public LumpedCapDelayCalc { public: ArnoldiDelayCalc(StaState *sta); - virtual ~ArnoldiDelayCalc(); + ~ArnoldiDelayCalc() override; ArcDelayCalc *copy() override; - const char *name() const override { return "arnoldi"; } + std::string_view name() const override { return "arnoldi"; } Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; Parasitic *reduceParasitic(const Parasitic *parasitic_network, const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResult inputPortDelay(const Pin *port_pin, float in_slew, const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResult gateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, @@ -147,21 +153,23 @@ class ArnoldiDelayCalc : public LumpedCapDelayCalc float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; - string reportGateDelay(const Pin *drvr_pin, - const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *parasitic, - const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, - int digits) override; + const Scene *scene, + const MinMax *min_max) override; + std::string reportGateDelay(const Pin *drvr_pin, + const TimingArc *arc, + const Slew &in_slew, + float load_cap, + const Parasitic *parasitic, + const LoadPinIndexMap &load_pin_index_map, + const Scene *scene, + const MinMax *min_max, + int digits) override; void finishDrvrPin() override; void delay_work_set_thresholds(delay_work *D, - double lo, - double hi, - bool rising, - double derate); + double lo, + double hi, + bool rising, + double derate); private: ArcDcalcResult gateDelaySlew(const LibertyCell *drvr_cell, @@ -173,71 +181,69 @@ class ArnoldiDelayCalc : public LumpedCapDelayCalc void ar1_ceff_delay(delay_work *D, timing_table *tab, arnoldi1 *mod, - double *delays, + double *delays, double *slews); double ra_rdelay_1(timing_table *tab, - double ctot); + double ctot); double ra_get_r(delay_work *D, - timing_table *tab, - double rdelay, - double ctot); + timing_table *tab, + double rdelay, + double ctot); double ra_get_s(delay_work *D, - timing_table *tab, - double r, - double c); + timing_table *tab, + double r, + double c); void ra_solve_for_s(delay_work *D, - double p, - double tlohi, - double &s); + double p, + double tlohi, + double &s); // from poles and residues, solve for t20,t50,t80 void pr_solve1(double s, - int order, - double *p, - double *rr, - double v1, - double *t1); + int order, + double *p, + double *rr, + double v1, + double *t1); void pr_solve3(double s, - int order, - double *p, - double *rr, - double vhi, - double *thi, - double vmid, - double *tmid, - double vlo, - double *tlo); + int order, + double *p, + double *rr, + double vhi, + double *thi, + double vmid, + double *tmid, + double vlo, + double *tlo); // // routines for linear drive model and ceff // double pr_ceff(double s, - double rdrive, - int order, - double *p, - double *rr, - double ceff_time); + double rdrive, + int order, + double *p, + double *rr, + double ceff_time); double ra_solve_for_t(double p, - double s, - double v); + double s, + double v); void ra_solve_for_pt(double ps, - double v, - double *pt, - double *d); + double v, + double *pt, + double *d); void ra_calc_c(double lo, - double hi, - double *c_smin, - double *c_x1, - double *c_y1); + double hi, + double *c_smin, + double *c_x1, + double *c_y1); - rcmodel *rcmodel_; + rcmodel *rcmodel_{nullptr}; int _pinNmax; double *_delayV; double *_slewV; int pin_n_; ArnoldiReduce *reduce_; delay_work *delay_work_; - vector unsaved_parasitics_; - bool pocv_enabled_; }; ArcDelayCalc * @@ -268,43 +274,42 @@ ArnoldiDelayCalc::~ArnoldiDelayCalc() free(_delayV); free(_slewV); delete reduce_; + delete rcmodel_; } Parasitic * ArnoldiDelayCalc::findParasitic(const Pin *drvr_pin, - const RiseFall *drvr_rf, - const DcalcAnalysisPt *dcalc_ap) + const RiseFall *drvr_rf, + const Scene *scene, + const MinMax *min_max) { Parasitic *parasitic = nullptr; - const Corner *corner = dcalc_ap->corner(); - // set_load net has precedence over parasitics. - if (sdc_->drvrPinHasWireCap(drvr_pin, corner) + const Sdc *sdc = scene->sdc(); + Parasitics *parasitics = scene->parasitics(min_max); + if (parasitics == nullptr + // set_load net has precedence over parasitics. || network_->direction(drvr_pin)->isInternal()) return nullptr; - const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); Parasitic *parasitic_network = - parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); - const MinMax *min_max = dcalc_ap->constraintMinMax(); + parasitics->findParasiticNetwork(drvr_pin); if (parasitic_network == nullptr) { - Wireload *wireload = sdc_->wireload(min_max); + Wireload *wireload = sdc->wireload(min_max); if (wireload) { float pin_cap, wire_cap, fanout; bool has_wire_cap; - graph_delay_calc_->netCaps(drvr_pin, drvr_rf, dcalc_ap, + graph_delay_calc_->netCaps(drvr_pin, drvr_rf, scene, min_max, pin_cap, wire_cap, fanout, has_wire_cap); - parasitic_network = parasitics_->makeWireloadNetwork(drvr_pin, wireload, - fanout, min_max, - parasitic_ap); + parasitic_network = parasitics->makeWireloadNetwork(drvr_pin, wireload, + fanout, scene, min_max); } } if (parasitic_network) { - rcmodel *rcmodel = reduce_->reduceToArnoldi(parasitic_network, drvr_pin, - parasitic_ap->couplingCapFactor(), - drvr_rf, corner, min_max, parasitic_ap); + rcmodel_ = reduce_->reduceToArnoldi(parasitic_network, drvr_pin, + parasitics->couplingCapFactor(), + drvr_rf, scene, min_max); // Arnoldi parasitics are their own class that are not saved in the parasitic db. - unsaved_parasitics_.push_back(rcmodel); - parasitic = rcmodel; + parasitic = rcmodel_; } return parasitic; } @@ -313,7 +318,8 @@ Parasitic * ArnoldiDelayCalc::reduceParasitic(const Parasitic *, const Pin *, const RiseFall *, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { // Decline because reduced arnoldi parasitics are not stored in the parasitics db. return nullptr; @@ -322,9 +328,8 @@ ArnoldiDelayCalc::reduceParasitic(const Parasitic *, void ArnoldiDelayCalc::finishDrvrPin() { - for (auto parasitic : unsaved_parasitics_) - delete parasitic; - unsaved_parasitics_.clear(); + delete rcmodel_; + rcmodel_ = nullptr; } ArcDcalcResult @@ -333,7 +338,8 @@ ArnoldiDelayCalc::inputPortDelay(const Pin *, const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { rcmodel_ = nullptr; _delayV[0] = 0.0; @@ -363,7 +369,7 @@ ArnoldiDelayCalc::inputPortDelay(const Pin *, for (int j=1;jelmore(j); - double wire_delay = 0.6931472*elmore; + double wire_delay = std::numbers::ln2 * elmore; double load_slew = in_slew + c_log*elmore/slew_derate; _delayV[j] = wire_delay; _slewV[j] = load_slew; @@ -389,28 +395,28 @@ ArnoldiDelayCalc::gateDelay(const Pin *drvr_pin, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { const LibertyCell *drvr_cell = arc->from()->libertyCell(); ConcreteParasitic *cparasitic = reinterpret_cast(const_cast(parasitic)); rcmodel_ = dynamic_cast(cparasitic); - pocv_enabled_ = variables_->pocvEnabled(); - GateTableModel *table_model = arc->gateTableModel(dcalc_ap); + GateTableModel *table_model = arc->gateTableModel(scene, min_max); if (table_model && rcmodel_) { - const Pvt *pvt = pinPvt(drvr_pin, dcalc_ap); + const Pvt *pvt = pinPvt(drvr_pin, scene, min_max); return gateDelaySlew(drvr_cell, arc, table_model, in_slew, load_pin_index_map, pvt); } else return LumpedCapDelayCalc::gateDelay(drvr_pin, arc, in_slew, load_cap, - parasitic, load_pin_index_map, dcalc_ap); + parasitic, load_pin_index_map, scene, min_max); } ArcDcalcResult ArnoldiDelayCalc::gateDelaySlew(const LibertyCell *drvr_cell, const TimingArc *arc, - const GateTableModel *table_model, - const Slew &in_slew, + const GateTableModel *table_model, + const Slew &in_slew, const LoadPinIndexMap &load_pin_index_map, const Pvt *pvt) { @@ -432,7 +438,7 @@ ArnoldiDelayCalc::gateDelaySlew(const LibertyCell *drvr_cell, double hi_thresh = drvr_library->slewUpperThreshold(rf); bool rising = (rf == RiseFall::rise()); delay_work_set_thresholds(delay_work_, lo_thresh, hi_thresh, rising, - slew_derate); + slew_derate); if (rcmodel_->order > 0) { timing_table tab; tab.table = table_model; @@ -440,7 +446,7 @@ ArnoldiDelayCalc::gateDelaySlew(const LibertyCell *drvr_cell, tab.pvt = pvt; tab.in_slew = delayAsFloat(in_slew); ar1_ceff_delay(delay_work_, &tab, rcmodel_, - _delayV, _slewV); + _delayV, _slewV); } dcalc_result.setGateDelay(_delayV[0]); dcalc_result.setDrvrSlew(_slewV[0]); @@ -451,8 +457,8 @@ ArnoldiDelayCalc::gateDelaySlew(const LibertyCell *drvr_cell, auto load_idx_itr = load_pin_index_map.find(load_pin); if (load_idx_itr != load_pin_index_map.end()) { size_t load_idx = load_idx_itr->second; - ArcDelay wire_delay = _delayV[i] - _delayV[0]; - Slew load_slew = _slewV[i]; + double wire_delay = _delayV[i] - _delayV[0]; + double load_slew = _slewV[i]; thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew); dcalc_result.setWireDelay(load_idx, wire_delay); dcalc_result.setLoadSlew(load_idx, load_slew); @@ -463,19 +469,20 @@ ArnoldiDelayCalc::gateDelaySlew(const LibertyCell *drvr_cell, return dcalc_result; } -string +std::string ArnoldiDelayCalc::reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) { return LumpedCapDelayCalc::reportGateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, load_pin_index_map, - dcalc_ap, digits); + scene, min_max, digits); } //////////////////////////////////////////////////////////////// @@ -487,7 +494,7 @@ ArnoldiDelayCalc::reportGateDelay(const Pin *drvr_pin, arnoldi1::~arnoldi1() { free(d); - free(U); + free(reinterpret_cast(U)); } double @@ -506,13 +513,16 @@ delay_work_create() int j; delay_work *D = (delay_work*)malloc(sizeof(delay_work)); D->nmax = 256; - D->resi = (double**)malloc(D->nmax*sizeof(double*)); - D->resi[0] = (double*)malloc(D->nmax*32*sizeof(double)); - for (j=1;jnmax;j++) D->resi[j] = D->resi[0] + j*32; - D->v[0] = (double*)malloc(32*32*sizeof(double)); - for (j=1;j<32;j++) D->v[j] = D->v[0] + j*32; - D->w[0] = (double*)malloc(32*D->nmax*sizeof(double)); - for (j=1;j<32;j++) D->w[j] = D->w[0] + j*D->nmax; + D->resi = (double**)malloc(static_cast(D->nmax) * sizeof(double *)); + D->resi[0] = (double*)malloc(static_cast(D->nmax) * 32u * sizeof(double)); + for (j=1;jnmax;j++) + D->resi[j] = D->resi[0] + static_cast(j) * 32; + D->v[0] = (double*)malloc(static_cast(32) * 32u * sizeof(double)); + for (j=1;j<32;j++) + D->v[j] = D->v[0] + static_cast(j) * 32; + D->w[0] = (double*)malloc(32u * static_cast(D->nmax) * sizeof(double)); + for (j=1;j<32;j++) + D->w[j] = D->w[0] + static_cast(j) * D->nmax; D->lo_thresh = 0.0; D->hi_thresh = 0.0; D->slew_derate = 0.0; @@ -535,7 +545,7 @@ static void delay_work_destroy(delay_work *D) { free(D->resi[0]); - free(D->resi); + free(reinterpret_cast(D->resi)); free(D->v[0]); free(D->w[0]); free(D); @@ -547,23 +557,25 @@ delay_work_alloc(delay_work *D,int n) if (n<=D->nmax) return; free(D->w[0]); free(D->resi[0]); - free(D->resi); + free(reinterpret_cast(D->resi)); D->nmax *= 2; - if (n > D->nmax) D->nmax = n; + D->nmax = std::max(n, D->nmax); int j; - D->resi = (double**)malloc(D->nmax*sizeof(double*)); - D->resi[0] = (double*)malloc(D->nmax*32*sizeof(double)); - for (j=1;jnmax;j++) D->resi[j] = D->resi[0] + j*32; - D->w[0] = (double*)malloc(32*D->nmax*sizeof(double)); - for (j=1;j<32;j++) D->w[j] = D->w[0] + j*D->nmax; + D->resi = (double**)malloc(static_cast(D->nmax) * sizeof(double *)); + D->resi[0] = (double*)malloc(static_cast(D->nmax) * 32u * sizeof(double)); + for (j=1;jnmax;j++) + D->resi[j] = D->resi[0] + static_cast(j) * 32; + D->w[0] = (double*)malloc(32u * static_cast(D->nmax) * sizeof(double)); + for (j=1;j<32;j++) + D->w[j] = D->w[0] + static_cast(j) * D->nmax; } void ArnoldiDelayCalc::delay_work_set_thresholds(delay_work *D, - double lo, - double hi, - bool rising, - double derate) + double lo, + double hi, + bool rising, + double derate) { double mid = 0.5; // 0.0:1.0 int i = rising?1:0; @@ -582,7 +594,7 @@ ArnoldiDelayCalc::delay_work_set_thresholds(delay_work *D, D->c->vmid = mid; D->c->vlg = log(hi/lo); ra_calc_c(lo,hi, - &(D->c->smin), &(D->c->x1),&(D->c->y1)); + &(D->c->smin), &(D->c->x1),&(D->c->y1)); } D->lo_thresh = D->c->vlo; D->hi_thresh = D->c->vhi; @@ -603,7 +615,8 @@ delay_work_get_residues(delay_work *D,int term_index) // calculate_poles_res // -void arnoldi1::calculate_poles_res(delay_work *D,double rdrive) +void arnoldi1::calculate_poles_res(delay_work *D, + double rdrive) { if (n > D->nmax) delay_work_alloc(D,n); double *p = D->poles; @@ -621,8 +634,7 @@ void arnoldi1::calculate_poles_res(delay_work *D,double rdrive) d[0] = dsave; for (h=0;h=1;h--) { iter = 0; - while (abs(e[h])>1e-18) { // 1e-6ps + while (std::abs(e[h])>1e-18) { // 1e-6ps m=0; if (m != h) { if (iter++ == 20) @@ -746,7 +762,12 @@ tridiagEV(int n,double *din,double *ein,double *d,double **v) // get a waveform point static void -pr_get_v(double t, double s, int order, double *p, double *rr, double *va) +pr_get_v(double t, + double s, + int order, + const double *p, + const double *rr, + double *va) { *va = 0.0; int h; @@ -764,8 +785,13 @@ pr_get_v(double t, double s, int order, double *p, double *rr, double *va) } static void -get_dv(double t, double s, int order, double *p, double *rr, - double *va, double *dva) +get_dv(double t, + double s, + int order, + const double *p, + const double *rr, + double *va, + double *dva) { *va = 0.0; *dva = 0.0; @@ -789,12 +815,19 @@ get_dv(double t, double s, int order, double *p, double *rr, } static double -solve_t_bracketed(double s,int order,double *p,double *rr, - double val,double x1,double x2,double v1,double v2) +solve_t_bracketed(double s, + int order, + const double *p, + const double *rr, + double val, + double x1, + double x2, + double v1, + double v2) { int j; double df,dx,dxold,f,f2,f1; - double temp,xh,xl,rts; + double temp,xh,x_lo,rts; double xacc = .001e-12; // .001ps f1 = v1-val; f2 = v2-val; @@ -802,35 +835,35 @@ solve_t_bracketed(double s,int order,double *p,double *rr, if (f2==0.0) return x2; rts = (f1*x2-f2*x1)/(f1-f2); if (f1= 0.0) - || (abs(2.0*f) > abs(dxold*df))) { + if ((((rts-xh)*df-f)*((rts-x_lo)*df-f) >= 0.0) + || (std::abs(2.0*f) > std::abs(dxold*df))) { dxold = dx; - dx = 0.5*(xh-xl); + dx = 0.5*(xh-x_lo); if (flast*f >0.0) { // 2 successive bisections in same direction, // accelerate - if (f<0.0) dx = 0.9348*(xh-xl); - else dx = 0.0625*(xh-xl); + if (f<0.0) dx = 0.9348*(xh-x_lo); + else dx = 0.0625*(xh-x_lo); } flast = f; - rts = xl+dx; - if (xl == rts) { + rts = x_lo+dx; + if (x_lo == rts) { return rts; } } else { @@ -843,33 +876,33 @@ solve_t_bracketed(double s,int order,double *p,double *rr, return rts; } } - if (abs(dx) < xacc) { + if (std::abs(dx) < xacc) { return rts; } get_dv(rts,s,order,p,rr,&f,&df); f -= val; if (f<0.0) - xl = rts; + x_lo = rts; else xh = rts; } - if (abs(f)<1e-6) // 1uV + if (std::abs(f)<1e-6) // 1uV return rts; - return 0.5*(xl+xh); + return 0.5*(x_lo+xh); } void ArnoldiDelayCalc::pr_solve1(double s, - int order, - double *p, - double *rr, - double v1, - double *t1) + int order, + double *p, + double *rr, + double v1, + double *t1) { double tmin = 0.0,tmax = 0.0,vmin = 0.0,vmax = 0.0; int h, h0 = 0; while (order>1 - && rr[order-1]<1e-8 // 1e-8V - && rr[order-1]>-1e-8) + && rr[order-1]<1e-8 // 1e-8V + && rr[order-1]>-1e-8) order--; if (rr[0]<0.5) { for (h=1;h0.3 && rr[h]>rr[0]) { h0 = h; break; } @@ -893,7 +926,7 @@ ArnoldiDelayCalc::pr_solve1(double s, // ignoring a typical error at drive node, that comes // from slight inaccuracies in rr if (!(rr[order-1]>1.0 && p[order-1]>500.0 && va>v1-0.002)) - debugPrint(debug_, "arnoldi", 1, "err, pr_solve1, vav1) - debugPrint(debug_, "arnoldi", 1, "err, pr_solve1, va>v1"); + debugPrint(debug_, "arnoldi", 1, "err, pr_solve1, va>v1"); tmax = ta; vmax = va; } } else { @@ -925,15 +958,15 @@ ArnoldiDelayCalc::pr_solve1(double s, void ArnoldiDelayCalc::pr_solve3(double s, - int order, - double *p, - double *rr, - double vhi, - double *thi, - double vmid, - double *tmid, - double vlo, - double *tlo) + int order, + double *p, + double *rr, + double vhi, + double *thi, + double vmid, + double *tmid, + double vlo, + double *tlo) { // falling, thi1 - && rr[order-1]<1e-8 // 1e-8V - && rr[order-1]>-1e-8) + && rr[order-1]<1e-8 // 1e-8V + && rr[order-1]>-1e-8) order--; if (rr[0]<0.5) { for (h=1;h0.3 && rr[h]>rr[0]) { h0 = h; break; } } double p0 = p[h0]; - if (p0>10e+9) // 1/10ns - p0=10e+9; + p0 = std::min(p0, 10e+9); // 1/10ns cap double ps,vs,ta,va; vs = 0.0; for (h=0;hvmid) { - tmin5 = tmin8 = ta; vmin5 = tmin8 = va; + tmin5 = ta; + tmin8 = ta; + vmin5 = va; ta += 0.7/p0; pr_get_v(ta,s,order,p,rr,&va); } @@ -1063,18 +1097,14 @@ ArnoldiDelayCalc::pr_solve3(double s, pr_get_v(ta,s,order,p,rr,&va); } tmax2 = ta; vmax2 = va; - if (va < vmid) { - tmax5 = ta; vmax5 = va; - } else while (va > vmid) { + while (va > vmid) { tmin5 = tmin8 = ta; vmin5 = vmin8 = va; ta += 1.0/p0; pr_get_v(ta,s,order,p,rr,&va); } tmax5 = ta; vmax5 = va; - if (va < vlo) { - tmax8 = ta; vmax8 = va; - } else while (va > vlo) { + while (va > vlo) { tmin8 = ta; vmin8 = va; ta += 1.0/p0; @@ -1111,11 +1141,11 @@ calc_integ(double p,double s,double t) double ArnoldiDelayCalc::pr_ceff(double s, - double rdrive, - int order, - double *p, - double *rr, - double ceff_time) + double rdrive, + int order, + double *p, + double *rr, + double ceff_time) { double integi = 0.0; double ceff, v0; @@ -1133,7 +1163,7 @@ ArnoldiDelayCalc::pr_ceff(double s, static double ra_hinv(double y, - Debug *debug) + Debug *debug) { double x; if (y<1.0) { @@ -1154,14 +1184,14 @@ ra_hinv(double y, ex = exp(-x); f = x+ex-1.0-y; if (f<-1e-8 || f>1e-8) - debugPrint(debug, "arnoldi", 1, "y f %g %g", y, f); + debugPrint(debug, "arnoldi", 1, "y f {:g} {:g}", y, f); return x; } double ArnoldiDelayCalc::ra_solve_for_t(double p, - double s, - double v) + double s, + double v) { double t; double ps = p*s; @@ -1180,9 +1210,9 @@ ArnoldiDelayCalc::ra_solve_for_t(double p, void ArnoldiDelayCalc::ra_solve_for_pt(double ps, - double v, - double *pt, - double *d) + double v, + double *pt, + double *d) { if (ps>30.0) { *pt = 1.0+ps*(1.0-v); @@ -1201,10 +1231,10 @@ ArnoldiDelayCalc::ra_solve_for_pt(double ps, void ArnoldiDelayCalc::ra_calc_c(double vlo, - double vhi, - double *c_smin, - double *c_x1, - double *c_y1) + double vhi, + double *c_smin, + double *c_x1, + double *c_y1) { double a = log(1.0/vhi); *c_smin = a + ra_hinv((1.0-vhi)/vhi - a, debug_); @@ -1224,9 +1254,9 @@ ArnoldiDelayCalc::ra_calc_c(double vlo, void ArnoldiDelayCalc::ra_solve_for_s(delay_work *D, - double p, - double tlohi, - double &s) + double p, + double tlohi, + double &s) { delay_c *c = D->c; double vhi = c->vhi; @@ -1247,39 +1277,40 @@ ArnoldiDelayCalc::ra_solve_for_s(delay_work *D, if (x <= x1) { y = y1 - 0.5*(x-x1); - if (y>1.0) y=1.0; + y = std::min(y, 1.0); } else { y = y1 - (x-x1)*(0.5 + 8*(x-x1)); - if (y.5e-12) // .5ps - debugPrint(debug_, "arnoldi", 1, "ra_solve_for_s p %g tlohi %s err %s", + if (std::abs(f)>.5e-12) // .5ps + debugPrint(debug_, "arnoldi", 1, "ra_solve_for_s p {:g} tlohi {} err {}", p, units_->timeUnit()->asString(tlohi), units_->timeUnit()->asString(f)); @@ -1308,9 +1339,9 @@ ArnoldiDelayCalc::ra_solve_for_s(delay_work *D, // Rough translation of ra_get_r(sy_table) used by ar1_ceff_delay. double ArnoldiDelayCalc::ra_get_r(delay_work *D, - timing_table *tab, - double rdelay, - double ctot) + timing_table *tab, + double rdelay, + double ctot) { // find the maximum r that allows a solution for s of // (s,r,ctot)-> output_slew @@ -1321,9 +1352,8 @@ ArnoldiDelayCalc::ra_get_r(delay_work *D, float c1; double tlohi,r; c1 = ctot; - ArcDelay d1; - Slew s1; - tab->table->gateDelay(tab->pvt, tab->in_slew, c1, pocv_enabled_, d1, s1); + float d1, s1; + tab->table->gateDelay(tab->pvt, tab->in_slew, c1, d1, s1); tlohi = slew_derate*delayAsFloat(s1); r = tlohi/(c_log*c1); if (rdelay>0.0 && r > rdelay) @@ -1333,18 +1363,17 @@ ArnoldiDelayCalc::ra_get_r(delay_work *D, double ArnoldiDelayCalc::ra_get_s(delay_work *D, - timing_table *tab, - double r, - double c) + timing_table *tab, + double r, + double c) { delay_c *con = D->c; double slew_derate = con->slew_derate; double c_log = con->vlg; double c_smin = con->smin; double tlohi,smin,s; - ArcDelay d1; - Slew s1; - tab->table->gateDelay(tab->pvt, tab->in_slew, c, pocv_enabled_, d1, s1); + float d1, s1; + tab->table->gateDelay(tab->pvt, tab->in_slew, c, d1, s1); tlohi = slew_derate*delayAsFloat(s1); smin = r*c*c_smin; // c_smin = ra_hinv((1-vhi)/vhi-log(vhi)) + log(vhi); if (c_log*r*c >= tlohi) { @@ -1367,17 +1396,16 @@ ArnoldiDelayCalc::ra_get_s(delay_work *D, double ArnoldiDelayCalc::ra_rdelay_1(timing_table *tab, - double ctot) + double ctot) { // determine the drive resistance from change in delay versus ctot float c1 = ctot; float c2 = 0.5*c1; if (c1==c2) return 0.0; - ArcDelay d1, d2; - Slew s1, s2; - tab->table->gateDelay(tab->pvt, tab->in_slew, c1, pocv_enabled_, d1, s1); - tab->table->gateDelay(tab->pvt, tab->in_slew, c2, pocv_enabled_, d2, s2); + float d1, d2, s1, s2; + tab->table->gateDelay(tab->pvt, tab->in_slew, c1, d1, s1); + tab->table->gateDelay(tab->pvt, tab->in_slew, c2, d2, s2); double dt50 = delayAsFloat(d1)-delayAsFloat(d2); if (dt50 <= 0.0) return 0.0; @@ -1387,10 +1415,10 @@ ArnoldiDelayCalc::ra_rdelay_1(timing_table *tab, void ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, - timing_table *tab, - arnoldi1 *mod, - double *delays, - double *slews) + timing_table *tab, + arnoldi1 *mod, + double *delays, + double *slews) { delay_c *con = D->c; double slew_derate = con->slew_derate; @@ -1398,51 +1426,45 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, double vlo = con->vlo; double ctot = mod->ctot; double ceff,tlohi,t50_sy,r,s,t50_sr,rdelay; - ArcDelay df; - Slew sf; + float df, sf; - debugPrint(debug_, "arnoldi", 1, "ctot=%s", + debugPrint(debug_, "arnoldi", 1, "ctot={}", units_->capacitanceUnit()->asString(ctot)); rdelay = ra_rdelay_1(tab,ctot); if (rdelay == 0.0) { rdelay = 1e+3; // 1kohm } - r = rdelay; r = ra_get_r(D,tab,rdelay,ctot); if (! (r>0.0 - && r<100e+3)) // 100khom + && r<100e+3)) // 100khom rdelay = 1e+3; // 1kohm bool bad = (r0.0 - && s<100e-9)) // 100ns + && s<100e-9)) // 100ns s = 0.5e-9; // .5ns if (debug_->check("arnoldi", 1)) { double p = 1.0/(r*ctot); double thix,tlox; - debugPrint(debug_, "arnoldi", 1, "at r=%s s=%s", + debugPrint(debug_, "arnoldi", 1, "at r={} s={}", units_->resistanceUnit()->asString(r), units_->timeUnit()->asString(s)); thix = ra_solve_for_t(p,s,vhi); tlox = ra_solve_for_t(p,s,vlo); - tab->table->gateDelay(tab->pvt,tab->in_slew, ctot, pocv_enabled_, df, sf); - debugPrint(debug_, "arnoldi", 1, "table slew (in_slew %s ctot %s) = %s", + tab->table->gateDelay(tab->pvt,tab->in_slew, ctot, df, sf); + debugPrint(debug_, "arnoldi", 1, "table slew (in_slew {} ctot {}) = {}", units_->timeUnit()->asString(tab->in_slew), units_->capacitanceUnit()->asString(ctot), delayAsString(sf, this)); tlohi = slew_derate*delayAsFloat(sf); - debugPrint(debug_, "arnoldi", 1, "tlohi %s %s", + debugPrint(debug_, "arnoldi", 1, "tlohi {} {}", units_->timeUnit()->asString(tlohi), units_->timeUnit()->asString(tlox-thix)); } ceff = ctot; - tab->table->gateDelay(tab->pvt, tab->in_slew, ceff, pocv_enabled_, - df, sf); - t50_sy = delayAsFloat(df); - t50_sr = ra_solve_for_t(1.0/(r*ceff),s,0.5); // calculate s,r,mod -> t50_srmod, // then t50_srmod+t50_sy-t50_sr @@ -1464,24 +1486,24 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, ceff = pr_ceff(s,r,mod->order,p,rr,ceff_time); if ((ceff-1e-20) < 0.0) { // 1e-8pf - debugPrint(debug_, "arnoldi", 1, + debugPrint(debug_, "arnoldi", 1, "Invalid effective capacitance, using total capacitance"); - ceff = ctot; + ceff = ctot; } // new mvs at ceff s = ra_get_s(D,tab,r,ceff); - debugPrint(debug_, "arnoldi", 1, "new mvs s = %s", + debugPrint(debug_, "arnoldi", 1, "new mvs s = {}", units_->timeUnit()->asString(s)); } } - debugPrint(debug_, "arnoldi", 1, "r %s s %s ceff_time %s ceff %s", + debugPrint(debug_, "arnoldi", 1, "r {} s {} ceff_time {} ceff {}", units_->resistanceUnit()->asString(r), units_->timeUnit()->asString(s), units_->timeUnit()->asString(ceff_time), units_->capacitanceUnit()->asString(ceff)); - tab->table->gateDelay(tab->pvt, tab->in_slew, ceff, pocv_enabled_, df, sf); + tab->table->gateDelay(tab->pvt, tab->in_slew, ceff, df, sf); t50_sy = delayAsFloat(df); t50_sr = ra_solve_for_t(1.0/(r*ceff),s,0.5); for (j=0;jn;j++) { @@ -1492,4 +1514,5 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, } } -} // namespace +// NOLINTEND(modernize-avoid-c-style-cast) +} // namespace sta diff --git a/dcalc/ArnoldiDelayCalc.hh b/dcalc/ArnoldiDelayCalc.hh index 1802b08ee..03441eeb6 100644 --- a/dcalc/ArnoldiDelayCalc.hh +++ b/dcalc/ArnoldiDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -32,4 +32,4 @@ class StaState; ArcDelayCalc * makeArnoldiDelayCalc(StaState *sta); -} // namespace +} // namespace sta diff --git a/dcalc/ArnoldiReduce.cc b/dcalc/ArnoldiReduce.cc index 43d57f464..c9940ae44 100644 --- a/dcalc/ArnoldiReduce.cc +++ b/dcalc/ArnoldiReduce.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. // (c) 2018 Nefelus, Inc. @@ -28,22 +28,20 @@ #include "ArnoldiReduce.hh" +#include + +#include "Arnoldi.hh" #include "Debug.hh" +#include "Format.hh" #include "MinMax.hh" -#include "Sdc.hh" #include "Network.hh" +#include "Sdc.hh" #include "Units.hh" -#include "Arnoldi.hh" #include "parasitics/ConcreteParasiticsPvt.hh" namespace sta { - -using std::string; - -rcmodel::rcmodel() : - pinV(nullptr) -{ -} +// This is legacy C-style code. +// NOLINTBEGIN(modernize-avoid-c-style-cast, bugprone-multi-level-implicit-pointer-conversion, bugprone-implicit-widening-of-multiplication-result) rcmodel::~rcmodel() { @@ -69,7 +67,7 @@ struct ts_point ParasiticNode *node_; int eN; bool is_term; - int tindex; // index into termV of corresponding term + int tindex; // index into termV of corresponding term ts_edge **eV; bool visited; ts_edge *in_edge; @@ -87,42 +85,38 @@ struct ts_edge //////////////////////////////////////////////////////////////// - const int ArnoldiReduce::ts_point_count_incr_ = 1024; const int ArnoldiReduce::ts_edge_count_incr_ = 1024; ArnoldiReduce::ArnoldiReduce(StaState *sta) : - StaState(sta), - ts_pointNmax(1024), - ts_edgeNmax(1024), - termNmax(256), - dNmax(8) + StaState(sta) { - ts_pointV = (ts_point*)malloc(ts_pointNmax*sizeof(ts_point)); - ts_ordV = (int*)malloc(ts_pointNmax*sizeof(int)); - ts_pordV = (ts_point**)malloc(ts_pointNmax*sizeof(ts_point*)); - _u0 = (double*)malloc(ts_pointNmax*sizeof(double)); - _u1 = (double*)malloc(ts_pointNmax*sizeof(double)); - y = (double*)malloc(ts_pointNmax*sizeof(double)); - iv = (double*)malloc(ts_pointNmax*sizeof(double)); - r = (double*)malloc(ts_pointNmax*sizeof(double)); - c = (double*)malloc(ts_pointNmax*sizeof(double)); - par = (int*)malloc(ts_pointNmax*sizeof(int)); - - ts_edgeV = (ts_edge*)malloc(ts_edgeNmax*sizeof(ts_edge)); - ts_stackV = (ts_edge**)malloc(ts_edgeNmax*sizeof(ts_edge*)); - ts_eV = (ts_edge**)malloc(2*ts_edgeNmax*sizeof(ts_edge*)); - - pinV = (const Pin**)malloc(termNmax*sizeof(const Pin*)); - termV = (int*)malloc(termNmax*sizeof(int)); - outV = (int*)malloc(termNmax*sizeof(int)); - - d = (double*)malloc(dNmax*sizeof(double)); - e = (double*)malloc(dNmax*sizeof(double)); - U = (double**)malloc(dNmax*sizeof(double*)); - U0 = (double*)malloc(dNmax*termNmax*sizeof(double)); + ts_pointV = (ts_point *)malloc(ts_pointNmax * sizeof(ts_point)); + ts_ordV = (int *)malloc(ts_pointNmax * sizeof(int)); + ts_pordV = (ts_point **)malloc(ts_pointNmax * sizeof(ts_point *)); + _u0 = (double *)malloc(ts_pointNmax * sizeof(double)); + _u1 = (double *)malloc(ts_pointNmax * sizeof(double)); + y = (double *)malloc(ts_pointNmax * sizeof(double)); + iv = (double *)malloc(ts_pointNmax * sizeof(double)); + r = (double *)malloc(ts_pointNmax * sizeof(double)); + c = (double *)malloc(ts_pointNmax * sizeof(double)); + par = (int *)malloc(ts_pointNmax * sizeof(int)); + + ts_edgeV = (ts_edge *)malloc(ts_edgeNmax * sizeof(ts_edge)); + ts_stackV = (ts_edge **)malloc(ts_edgeNmax * sizeof(ts_edge *)); + ts_eV = (ts_edge **)malloc(2 * ts_edgeNmax * sizeof(ts_edge *)); + + pinV = (const Pin **)malloc(termNmax * sizeof(const Pin *)); + termV = (int *)malloc(termNmax * sizeof(int)); + outV = (int *)malloc(termNmax * sizeof(int)); + + d = (double *)malloc(dNmax * sizeof(double)); + e = (double *)malloc(dNmax * sizeof(double)); + U = (double **)malloc(dNmax * sizeof(double *)); + U0 = (double *)malloc(dNmax * termNmax * sizeof(double)); int h; - for (h=0;h(parasitic); drvr_pin_ = drvr_pin; coupling_cap_factor_ = coupling_cap_factor; rf_ = rf; - corner_ = corner; + scene_ = scene; min_max_ = min_max; - ap_ = ap; + parasitics_ = scene->parasitics(min_max); + parasitic_network_ = reinterpret_cast(parasitic); + loadWork(); return makeRcmodelDrv(); } @@ -202,7 +196,7 @@ ArnoldiReduce::loadWork() ts_edge *e; int tindex; - for (p = p0; p!=pend; p++) { + for (p = p0; p != pend; p++) { p->node_ = nullptr; p->eN = 0; p->is_term = false; @@ -248,14 +242,14 @@ ArnoldiReduce::loadWork() e++; } - for (p=p0;p!=pend;p++) { + for (p = p0; p != pend; p++) { if (p->node_) { p->eV = eV; eV += p->eN; p->eN = 0; } } - for (e=e0;e!=eend;e++) { + for (e = e0; e != eend; e++) { e->from->eV[e->from->eN++] = e; if (e->to != e->from) e->to->eV[e->to->eN++] = e; @@ -269,30 +263,33 @@ ArnoldiReduce::allocPoints() free(par); free(c); free(r); - free(iv); free(y); free(_u1); free(_u0); + free(iv); + free(y); + free(_u1); + free(_u0); free(ts_pordV); free(ts_ordV); free(ts_pointV); ts_pointNmax = ts_pointN + ts_point_count_incr_; - ts_pointV = (ts_point*)malloc(ts_pointNmax*sizeof(ts_point)); - ts_ordV = (int*)malloc(ts_pointNmax*sizeof(int)); - ts_pordV = (ts_point**)malloc(ts_pointNmax*sizeof(ts_point*)); - _u0 = (double*)malloc(ts_pointNmax*sizeof(double)); - _u1 = (double*)malloc(ts_pointNmax*sizeof(double)); - y = (double*)malloc(ts_pointNmax*sizeof(double)); - iv = (double*)malloc(ts_pointNmax*sizeof(double)); - r = (double*)malloc(ts_pointNmax*sizeof(double)); - c = (double*)malloc(ts_pointNmax*sizeof(double)); - par = (int*)malloc(ts_pointNmax*sizeof(int)); + ts_pointV = (ts_point *)malloc(ts_pointNmax * sizeof(ts_point)); + ts_ordV = (int *)malloc(ts_pointNmax * sizeof(int)); + ts_pordV = (ts_point **)malloc(ts_pointNmax * sizeof(ts_point *)); + _u0 = (double *)malloc(ts_pointNmax * sizeof(double)); + _u1 = (double *)malloc(ts_pointNmax * sizeof(double)); + y = (double *)malloc(ts_pointNmax * sizeof(double)); + iv = (double *)malloc(ts_pointNmax * sizeof(double)); + r = (double *)malloc(ts_pointNmax * sizeof(double)); + c = (double *)malloc(ts_pointNmax * sizeof(double)); + par = (int *)malloc(ts_pointNmax * sizeof(int)); } if (ts_edgeN > ts_edgeNmax) { free(ts_edgeV); free(ts_eV); free(ts_stackV); ts_edgeNmax = ts_edgeN + ts_edge_count_incr_; - ts_edgeV = (ts_edge*)malloc(ts_edgeNmax*sizeof(ts_edge)); - ts_stackV = (ts_edge**)malloc(ts_edgeNmax*sizeof(ts_edge*)); - ts_eV = (ts_edge**)malloc(2*ts_edgeNmax*sizeof(ts_edge*)); + ts_edgeV = (ts_edge *)malloc(ts_edgeNmax * sizeof(ts_edge)); + ts_stackV = (ts_edge **)malloc(ts_edgeNmax * sizeof(ts_edge *)); + ts_eV = (ts_edge **)malloc(2 * ts_edgeNmax * sizeof(ts_edge *)); } } @@ -304,65 +301,69 @@ ArnoldiReduce::allocTerms(int nterms) free(outV); free(termV); free(pinV); - termNmax = nterms+256; - pinV = (const Pin**)malloc(termNmax*sizeof(const Pin*)); - termV = (int*)malloc(termNmax*sizeof(int)); - outV = (int*)malloc(termNmax*sizeof(int)); + termNmax = nterms + 256; + pinV = (const Pin **)malloc(termNmax * sizeof(const Pin *)); + termV = (int *)malloc(termNmax * sizeof(int)); + outV = (int *)malloc(termNmax * sizeof(int)); - U0 = (double*)malloc(dNmax*termNmax*sizeof(double)); + U0 = (double *)malloc(dNmax * termNmax * sizeof(double)); int h; - for (h=0;h(node)]]; + return &ts_pointV[pt_map_[reinterpret_cast(node)]]; } rcmodel * ArnoldiReduce::makeRcmodelDrv() { ParasiticNode *drv_node = - parasitics_->findParasiticNode(parasitic_network_, drvr_pin_); + parasitics_->findParasiticNode(parasitic_network_, drvr_pin_); ts_point *pdrv = findPt(drv_node); makeRcmodelDfs(pdrv); getRC(); - if (ctot_ < 1e-22) // 1e-10ps + if (ctot_ < 1e-22) // 1e-10ps return nullptr; setTerms(pdrv); makeRcmodelFromTs(); return makeRcmodelFromW(); } -#define ts_orient( pp, ee) \ - if (ee->from!=pp) { ee->to = ee->from; ee->from = pp; } +#define ts_orient(pp, ee) \ + if (ee->from != pp) { \ + ee->to = ee->from; \ + ee->from = pp; \ + } void ArnoldiReduce::makeRcmodelDfs(ts_point *pdrv) { bool loop = false; int k; - ts_point *p,*q; + ts_point *p, *q; ts_point *p0 = ts_pointV; ts_point *pend = p0 + ts_pointN; - for (p=p0;p!=pend;p++) - p->visited = 0; + for (p = p0; p != pend; p++) + p->visited = false; ts_edge *e; ts_edge **stackV = ts_stackV; int stackN = 1; stackV[0] = e = pdrv->eV[0]; - ts_orient(pdrv,e); - pdrv->visited = 1; + ts_orient(pdrv, e); + pdrv->visited = true; pdrv->in_edge = nullptr; pdrv->ts = 0; - ts_ordV[0] = pdrv-p0; + ts_ordV[0] = pdrv - p0; ts_pordV[0] = pdrv; ts_ordN = 1; - while (stackN>0) { - e = stackV[stackN-1]; + while (stackN > 0) { + e = stackV[stackN - 1]; q = e->to; if (q->visited) { @@ -370,47 +371,53 @@ ArnoldiReduce::makeRcmodelDfs(ts_point *pdrv) // ignore, and do not even set *loop if (e->to != e->from) loop = true; - } else { + } + else { // try to descend - q->visited = 1; + q->visited = true; q->ts = ts_ordN++; ts_pordV[q->ts] = q; - ts_ordV[q->ts] = q-p0; + ts_ordV[q->ts] = q - p0; q->in_edge = e; - if (q->eN>1) { - for (k=0;keN;k++) if (q->eV[k] != e) break; + if (q->eN > 1) { + for (k = 0; k < q->eN; k++) + if (q->eV[k] != e) + break; e = q->eV[k]; - ts_orient(q,e); + ts_orient(q, e); stackV[stackN++] = e; - continue; // descent + continue; // descent } } // try to ascend - while (--stackN>=0) { + while (--stackN >= 0) { e = stackV[stackN]; p = e->from; // find e in p->eV - for (k=0;keN;k++) if (p->eV[k]==e) break; + for (k = 0; k < p->eN; k++) + if (p->eV[k] == e) + break; // if (k==p->eN) notice(0,"ERROR, e not found!\n"); ++k; - if (k>=p->eN) continue; + if (k >= p->eN) + continue; e = p->eV[k]; // check that next sibling is not the incoming edge - if (stackN>0 && e==stackV[stackN-1]) { - ++k; - if (k>=p->eN) continue; - e = p->eV[k]; + if (stackN > 0 && e == stackV[stackN - 1]) { + ++k; + if (k >= p->eN) + continue; + e = p->eV[k]; } - ts_orient(p,e); + ts_orient(p, e); stackV[stackN++] = e; break; } - } // while (stackN) + } // while (stackN) if (loop) - debugPrint(debug_, "arnoldi", 1, "net %s loop", - network_->pathName(drvr_pin_)); + debugPrint(debug_, "arnoldi", 1, "net {} loop", network_->pathName(drvr_pin_)); } // makeRcmodelGetRC @@ -420,31 +427,28 @@ ArnoldiReduce::getRC() ts_point *p, *p0 = ts_pointV; ts_point *pend = p0 + ts_pointN; ctot_ = 0.0; - for (p=p0;p!=pend;p++) { + for (p = p0; p != pend; p++) { p->c = 0.0; p->r = 0.0; if (p->node_) { ParasiticNode *node = p->node_; - double cap = parasitics_->nodeGndCap(node) - + pinCapacitance(node); + double cap = parasitics_->nodeGndCap(node) + pinCapacitance(node); if (cap > 0.0) { - p->c = cap; - ctot_ += cap; + p->c = cap; + ctot_ += cap; } else - p->c = 0.0; + p->c = 0.0; if (p->in_edge && p->in_edge->resistor_) p->r = parasitics_->value(p->in_edge->resistor_); - if (!(p->r>=0.0 && p->r<100e+3)) { // 0 < r < 100kohm - debugPrint(debug_, "arnoldi", 1, - "R value %g out of range, drvr pin %s", - p->r, - network_->pathName(drvr_pin_)); + if (!(p->r >= 0.0 && p->r < 100e+3)) { // 0 < r < 100kohm + debugPrint(debug_, "arnoldi", 1, "R value {:g} out of range, drvr pin {}", + p->r, network_->pathName(drvr_pin_)); } } } for (ParasiticCapacitor *capacitor : parasitics_->capacitors(parasitic_network_)) { - float cap = parasitics_->value(capacitor) * ap_->couplingCapFactor(); + float cap = parasitics_->value(capacitor) * parasitics_->couplingCapFactor(); ParasiticNode *node1 = parasitics_->node1(capacitor); if (!parasitics_->isExternal(node1)) { ts_point *pt = findPt(node1); @@ -466,10 +470,11 @@ ArnoldiReduce::pinCapacitance(ParasiticNode *node) if (pin) { Port *port = network_->port(pin); LibertyPort *lib_port = network_->libertyPort(port); + const Sdc *sdc = scene_->sdc(); if (lib_port) - pin_cap = sdc_->pinCapacitance(pin,rf_, corner_, min_max_); + pin_cap = sdc->pinCapacitance(pin, rf_, scene_, min_max_); else if (network_->isTopLevelPort(pin)) - pin_cap = sdc_->portExtCap(port, rf_, corner_, min_max_); + pin_cap = sdc->portExtCap(port, rf_, min_max_); } return pin_cap; } @@ -480,13 +485,15 @@ ArnoldiReduce::setTerms(ts_point *pdrv) // termV: from drv-ordered to fixed order // outV: from drv-ordered to ts_pordV ts_point *p; - int k,k0; + int k, k0; termV[0] = k0 = pdrv->tindex; - for (k=1;kts; } @@ -499,38 +506,35 @@ ArnoldiReduce::makeRcmodelFromTs() ts_point *p, *p0 = ts_pointV; int n = ts_ordN; int nterms = termN; - int i,j,k,h; + int i, j, k, h; if (debug_->check("arnoldi", 1)) { - for (k=0;kts, - p-p0, + debugPrint(debug_, "arnoldi", 1, "T{} P{} c={}", p->ts, p - p0, units_->capacitanceUnit()->asString(p->c)); if (p->is_term) - debugPrint(debug_, "arnoldi", 1, " term %d", p->tindex); + debugPrint(debug_, "arnoldi", 1, " term {}", p->tindex); if (p->in_edge) - debugPrint(debug_, "arnoldi", 1, " from T%d,P%ld r=%s", - p->in_edge->from->ts, - p->in_edge->from-p0, + debugPrint(debug_, "arnoldi", 1, " from T{} P{} r={}", + p->in_edge->from->ts, p->in_edge->from - p0, units_->resistanceUnit()->asString(p->r)); } - for (i=0;ic; - for (j=1;jc; r[j] = p->r; @@ -538,92 +542,99 @@ ArnoldiReduce::makeRcmodelFromTs() } sum = 0.0; - for (j=0;jcapacitanceUnit()->asString(sum)); ctot_ = sum; sqc_ = sqrt(sum); - double sqrt_ctot_inv = 1.0/sqc_; - for (j=0;j0;j--) { - iv[j] += c[j]*u0[j]; + for (j = n - 1; j > 0; j--) { + iv[j] += c[j] * u0[j]; iv[par[j]] += iv[j]; } - iv[0] += c[0]*u0[0]; + iv[0] += c[0] * u0[0]; y[0] = 0.0; - for (j=1;jcheck("arnoldi", 1)) { - report_->reportLine("tridiagonal reduced matrix, drvr pin %s", - network_->pathName(drvr_pin_)); - report_->reportLine("order %d n %d",order,n); - for (h=0;hreportLine(" d[%d] %s e[%d] %s", - h, - units_->timeUnit()->asString(d[h]), - h, - units_->timeUnit()->asString(e[h])); + report_->report("tridiagonal reduced matrix, drvr pin {}", + network_->pathName(drvr_pin_)); + report_->report("order {} n {}", order, n); + for (h = 0; h < order; h++) { + if (h < order - 1) + report_->report(" d[{}] {} e[{}] {}", h, + units_->timeUnit()->asString(d[h]), h, + units_->timeUnit()->asString(e[h])); else - report_->reportLine(" d[%d] %s", - h, - units_->timeUnit()->asString(d[h])); - string line = stdstrPrint("U[%d]",h); - for (i=0;ireportLineString(line); + report_->report(" d[{}] {}", h, units_->timeUnit()->asString(d[h])); + std::string line = sta::format("U[{}]", h); + for (i = 0; i < nterms; i++) + line += sta::format(" {:6.2e}", U[h][i]); + report_->reportLine(line); } } } @@ -631,29 +642,33 @@ ArnoldiReduce::makeRcmodelFromTs() rcmodel * ArnoldiReduce::makeRcmodelFromW() { - int j,h; + int j, h; int n = termN; rcmodel *mod = new rcmodel(); mod->order = order; mod->n = n; - if (order>0) { - int totd = order + order - 1 + order*n; - mod->d = (double *)malloc(totd*sizeof(double)); - if (order>1) mod->e = mod->d + order; - else mod->e = nullptr; - mod->U = (double **)malloc(order*sizeof(double*)); + if (order > 0) { + int totd = order + order - 1 + order * n; + mod->d = (double *)malloc(totd * sizeof(double)); + if (order > 1) + mod->e = mod->d + order; + else + mod->e = nullptr; + mod->U = (double **)malloc(order * sizeof(double *)); mod->U[0] = mod->d + order + order - 1; - for (h=1;hU[h]=mod->U[0] + h*n; - for (h=0;hU[h] = mod->U[0] + h * n; + for (h = 0; h < order; h++) { mod->d[h] = d[h]; - if (he[h] = e[h]; - for (j=0;je[h] = e[h]; + for (j = 0; j < n; j++) mod->U[h][j] = U[h][j]; } } - mod->pinV = (const Pin **)malloc(n*sizeof(const Pin*)); - for (j=0;jpinV = (const Pin **)malloc(n * sizeof(const Pin *)); + for (j = 0; j < n; j++) { int k = termV[j]; mod->pinV[j] = pinV[k]; } @@ -663,4 +678,5 @@ ArnoldiReduce::makeRcmodelFromW() return mod; } -} // namespace +// NOLINTEND(modernize-avoid-c-style-cast, bugprone-multi-level-implicit-pointer-conversion, bugprone-implicit-widening-of-multiplication-result) +} // namespace sta diff --git a/dcalc/ArnoldiReduce.hh b/dcalc/ArnoldiReduce.hh index 14e5d68ab..16232d5cc 100644 --- a/dcalc/ArnoldiReduce.hh +++ b/dcalc/ArnoldiReduce.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -28,7 +28,8 @@ #pragma once -#include "Map.hh" +#include + #include "Transition.hh" #include "NetworkClass.hh" #include "ParasiticsClass.hh" @@ -39,26 +40,25 @@ namespace sta { class ConcreteParasiticNetwork; class ConcreteParasiticNode; -class Corner; +class Scene; class rcmodel; struct ts_edge; struct ts_point; -typedef Map ArnolidPtMap; +using ArnolidPtMap = std::map; class ArnoldiReduce : public StaState { public: ArnoldiReduce(StaState *sta); - ~ArnoldiReduce(); + ~ArnoldiReduce() override; rcmodel *reduceToArnoldi(Parasitic *parasitic, const Pin *drvr_pin, float coupling_cap_factor, const RiseFall *rf, - const Corner *corner, - const MinMax *cnst_min_max, - const ParasiticAnalysisPt *ap); + const Scene *scene, + const MinMax *min_max); protected: void loadWork(); @@ -73,24 +73,24 @@ protected: void makeRcmodelFromTs(); rcmodel *makeRcmodelFromW(); + Parasitics *parasitics_; ConcreteParasiticNetwork *parasitic_network_; const Pin *drvr_pin_; float coupling_cap_factor_; const RiseFall *rf_; - const Corner *corner_; + const Scene *scene_; const MinMax *min_max_; - const ParasiticAnalysisPt *ap_; // ParasiticNode -> ts_point index. ArnolidPtMap pt_map_; // rcWork ts_point *ts_pointV; int ts_pointN; - int ts_pointNmax; + int ts_pointNmax{1024}; static const int ts_point_count_incr_; ts_edge *ts_edgeV; int ts_edgeN; - int ts_edgeNmax; + int ts_edgeNmax{1024}; static const int ts_edge_count_incr_; ts_edge **ts_eV; ts_edge **ts_stackV; @@ -98,14 +98,14 @@ protected: ts_point **ts_pordV; int ts_ordN; - int termNmax; + int termNmax{256}; int termN; ts_point *pterm0; const Pin **pinV; // fixed order, offset from pterm0 int *termV; // from drv-ordered to fixed order int *outV; // from drv-ordered to ts_pordV - int dNmax; + int dNmax{8}; double *d; double *e; double *U0; @@ -120,4 +120,4 @@ protected: int order; }; -} // namespace +} // namespace sta diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index f5462661a..848ac227d 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -1,55 +1,51 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "CcsCeffDelayCalc.hh" +#include +#include + #include "Debug.hh" -#include "Units.hh" +#include "DmpDelayCalc.hh" +#include "FindRoot.hh" +#include "Graph.hh" +#include "GraphDelayCalc.hh" #include "Liberty.hh" -#include "TimingArc.hh" #include "Network.hh" -#include "Graph.hh" -#include "Corner.hh" -#include "DcalcAnalysisPt.hh" #include "Parasitics.hh" -#include "GraphDelayCalc.hh" -#include "DmpDelayCalc.hh" -#include "FindRoot.hh" +#include "Scene.hh" +#include "TimingArc.hh" +#include "Units.hh" namespace sta { -using std::string; - // Implementaion based on: // "Gate Delay Estimation with Library Compatible Current Source Models // and Effective Capacitance", D. Garyfallou et al, // IEEE Transactions on Very Large Scale Integration (VLSI) Systems, March 2021 -using std::abs; -using std::exp; -using std::make_shared; - ArcDelayCalc * makeCcsCeffDelayCalc(StaState *sta) { @@ -58,20 +54,13 @@ makeCcsCeffDelayCalc(StaState *sta) CcsCeffDelayCalc::CcsCeffDelayCalc(StaState *sta) : LumpedCapDelayCalc(sta), - output_waveforms_(nullptr), - // Includes the Vh:Vdd region. - region_count_(0), - vl_fail_(false), watch_pin_values_(network_), capacitance_unit_(units_->capacitanceUnit()), table_dcalc_(makeDmpCeffElmoreDelayCalc(sta)) { } -CcsCeffDelayCalc::~CcsCeffDelayCalc() -{ - delete table_dcalc_; -} +CcsCeffDelayCalc::~CcsCeffDelayCalc() { delete table_dcalc_; } ArcDelayCalc * CcsCeffDelayCalc::copy() @@ -86,19 +75,22 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { in_slew_ = delayAsFloat(in_slew); load_cap_ = load_cap; + parasitics_ = scene->parasitics(min_max); parasitic_ = parasitic; output_waveforms_ = nullptr; - GateTableModel *table_model = arc->gateTableModel(dcalc_ap); + const GateTableModel *table_model = arc->gateTableModel(scene, min_max); if (table_model && parasitic) { OutputWaveforms *output_waveforms = table_model->outputWaveforms(); - parasitics_->piModel(parasitic, c2_, rpi_, c1_); - if (output_waveforms - && rpi_ > 0.0 && c1_ > 0.0 + Parasitics *parasitics = scene->parasitics(min_max); + parasitics->piModel(parasitic, c2_, rpi_, c1_); + if (output_waveforms && rpi_ > 0.0 + && c1_ > 0.0 // Bounds check because extrapolating waveforms does not work for shit. && output_waveforms->slewAxis()->inBounds(in_slew_) && output_waveforms->capAxis()->inBounds(c2_) @@ -109,60 +101,80 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, drvr_rf_ = arc->toEdge()->asRiseFall(); drvr_library->supplyVoltage("VDD", vdd_, vdd_exists); if (!vdd_exists) - report_->error(1700, "VDD not defined in library %s", drvr_library->name()); + report_->error(1700, "VDD not defined in library {}", drvr_library->name()); vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_; vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_; vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_; - const DcalcAnalysisPtSeq &dcalc_aps = corners_->dcalcAnalysisPts(); - drvr_cell->ensureVoltageWaveforms(dcalc_aps); - in_slew_ = delayAsFloat(in_slew); + drvr_cell->ensureVoltageWaveforms(scenes_); output_waveforms_ = output_waveforms; ref_time_ = output_waveforms_->referenceTime(in_slew_); - debugPrint(debug_, "ccs_dcalc", 1, "%s %s", + debugPrint(debug_, "ccs_dcalc", 1, "{} {}", drvr_cell->name(), - drvr_rf_->to_string().c_str()); - ArcDelay gate_delay; - Slew drvr_slew; - gateDelaySlew(drvr_library, drvr_rf_, gate_delay, drvr_slew); - return makeResult(drvr_library,drvr_rf_,gate_delay,drvr_slew,load_pin_index_map); + drvr_rf_->shortName()); + + double gate_delay, drvr_slew; + gateDelaySlew(drvr_library, gate_delay, drvr_slew); + debugPrint(debug_, "ccs_dcalc", 2, "gate_delay {} drvr_slew {}", + delayAsString(gate_delay, this), delayAsString(drvr_slew, this)); + + // Fill in pocv parameters. + ArcDelay gate_delay2(gate_delay); + Slew drvr_slew2(drvr_slew); + if (variables_->pocvEnabled()) { + double ceff = region_ceff_[0]; + const Pvt *pvt = pinPvt(drvr_pin_, scene, min_max); + table_model->gateDelayPocv(pvt, in_slew_, ceff, min_max, + variables_->pocvMode(), + gate_delay2, drvr_slew2); + } + ArcDcalcResult dcalc_result(load_pin_index_map.size()); + dcalc_result.setGateDelay(gate_delay2); + dcalc_result.setDrvrSlew(drvr_slew2); + + for (const auto &[load_pin, load_idx] : load_pin_index_map) { + double wire_delay, load_slew; + loadDelaySlew(load_pin, drvr_library, drvr_slew, wire_delay, load_slew); + dcalc_result.setWireDelay(load_idx, wire_delay); + dcalc_result.setLoadSlew(load_idx, load_slew); + } + return dcalc_result; } } return table_dcalc_->gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, - load_pin_index_map, dcalc_ap); + load_pin_index_map, scene, min_max); } void CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library, - const RiseFall *rf, // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew) + double &gate_delay, + double &drvr_slew) { - initRegions(drvr_library, rf); + initRegions(drvr_library, drvr_rf_); findCsmWaveform(); ref_time_ = output_waveforms_->referenceTime(in_slew_); gate_delay = region_times_[region_vth_idx_] - ref_time_; - drvr_slew = abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]); + drvr_slew = std::abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]); debugPrint(debug_, "ccs_dcalc", 2, - "gate_delay %s drvr_slew %s (initial)", + "gate_delay {} drvr_slew {} (initial)", delayAsString(gate_delay, this), delayAsString(drvr_slew, this)); - float prev_drvr_slew = delayAsFloat(drvr_slew); + float prev_drvr_slew = drvr_slew; constexpr int max_iterations = 5; for (int iter = 0; iter < max_iterations; iter++) { - debugPrint(debug_, "ccs_dcalc", 2, "iteration %d", iter); + debugPrint(debug_, "ccs_dcalc", 2, "iteration {}", iter); // Init drvr ramp model for vl. for (size_t i = 0; i <= region_count_; i++) { region_ramp_times_[i] = region_times_[i]; if (i < region_count_) region_ramp_slopes_[i] = (region_volts_[i + 1] - region_volts_[i]) - / (region_times_[i + 1] - region_times_[i]); + / (region_times_[i + 1] - region_times_[i]); } for (size_t i = 0; i < region_count_; i++) { - double v1 = region_volts_[i]; - double v2 = region_volts_[i + 1]; + double seg_v1 = region_volts_[i]; + double seg_v2 = region_volts_[i + 1]; double t1 = region_times_[i]; double t2 = region_times_[i + 1]; @@ -171,26 +183,25 @@ CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library, // for the charge on c1 from previous segments so it does not // work well. double c1_v1, c1_v2, ignore; - vl(t1, rpi_ * c1_, c1_v1, ignore); - vl(t2, rpi_ * c1_, c1_v2, ignore); - double q1 = v1 * c2_ + c1_v1 * c1_; - double q2 = v2 * c2_ + c1_v2 * c1_; - double ceff = (q2 - q1) / (v2 - v1); + vLoad(t1, rpi_ * c1_, c1_v1, ignore); + vLoad(t2, rpi_ * c1_, c1_v2, ignore); + double q1 = seg_v1 * c2_ + c1_v1 * c1_; + double q2 = seg_v2 * c2_ + c1_v2 * c1_; + double ceff = (q2 - q1) / (seg_v2 - seg_v1); - debugPrint(debug_, "ccs_dcalc", 2, "ceff %s", + debugPrint(debug_, "ccs_dcalc", 2, "ceff {}", capacitance_unit_->asString(ceff)); region_ceff_[i] = ceff; } findCsmWaveform(); gate_delay = region_times_[region_vth_idx_] - ref_time_; - drvr_slew = abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]); - debugPrint(debug_, "ccs_dcalc", 2, - "gate_delay %s drvr_slew %s", + drvr_slew = std::abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]); + debugPrint(debug_, "ccs_dcalc", 2, "gate_delay {} drvr_slew {}", delayAsString(gate_delay, this), delayAsString(drvr_slew, this)); - if (abs(delayAsFloat(drvr_slew) - prev_drvr_slew) < .01 * prev_drvr_slew) + if (std::abs(drvr_slew - prev_drvr_slew) < .01 * prev_drvr_slew) break; - prev_drvr_slew = delayAsFloat(drvr_slew); + prev_drvr_slew = drvr_slew; } } @@ -220,85 +231,85 @@ CcsCeffDelayCalc::initRegions(const LibertyLibrary *drvr_library, double vth_vh = (vh_ - vth_); switch (region_count_) { - case 4: - region_vth_idx_ = 2; - region_volts_ = {0.0, vl_, vth_, vh_, vdd_}; - break; - case 5: { - region_vth_idx_ = 2; - double v1 = vth_ + .7 * vth_vh; - region_volts_ = {0.0, vl_, vth_, v1, vh_, vdd_}; - break; - } - case 6: { - region_vth_idx_ = 2; - double v1 = vth_ + .3 * vth_vh; - double v2 = vth_ + .6 * vth_vh; - region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, vdd_}; - break; - } - case 7: { - region_vth_idx_ = 2; - region_vh_idx_ = 5; - double v1 = vth_ + .3 * vth_vh; - double v2 = vth_ + .6 * vth_vh; - double v3 = vh_ + .5 * (vdd_ - vh_); - region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, v3, vdd_}; - break; - } - case 8: { - region_vth_idx_ = 2; - region_vh_idx_ = 6; - double v1 = vth_ + .25 * vth_vh; - double v2 = vth_ + .50 * vth_vh; - double v3 = vth_ + .75 * vth_vh; - double v4 = vh_ + .5 * (vdd_ - vh_); - region_volts_ = {0.0, vl_, vth_, v1, v2, v3, vh_, v4, vdd_}; - break; - } - case 9: { - region_vth_idx_ = 2; - region_vh_idx_ = 7; - double v1 = vth_ + .2 * vth_vh; - double v2 = vth_ + .4 * vth_vh; - double v3 = vth_ + .6 * vth_vh; - double v4 = vth_ + .8 * vth_vh; - double v5 = vh_ + .5 * (vdd_ - vh_); - region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, vdd_}; - break; - } - case 10: { - region_vth_idx_ = 2; - region_vh_idx_ = 7; - double v1 = vth_ + .2 * vth_vh; - double v2 = vth_ + .4 * vth_vh; - double v3 = vth_ + .6 * vth_vh; - double v4 = vth_ + .8 * vth_vh; - double v5 = vh_ + .3 * (vdd_ - vh_); - double v6 = vh_ + .6 * (vdd_ - vh_); - region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, v6, vdd_}; - break; - } - default: - report_->error(1701, "unsupported ccs region count."); - break; + case 4: + region_vth_idx_ = 2; + region_volts_ = {0.0, vl_, vth_, vh_, vdd_}; + break; + case 5: { + region_vth_idx_ = 2; + double v1 = vth_ + .7 * vth_vh; + region_volts_ = {0.0, vl_, vth_, v1, vh_, vdd_}; + break; + } + case 6: { + region_vth_idx_ = 2; + double v1 = vth_ + .3 * vth_vh; + double v2 = vth_ + .6 * vth_vh; + region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, vdd_}; + break; + } + case 7: { + region_vth_idx_ = 2; + region_vh_idx_ = 5; + double v1 = vth_ + .3 * vth_vh; + double v2 = vth_ + .6 * vth_vh; + double v3 = vh_ + .5 * (vdd_ - vh_); + region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, v3, vdd_}; + break; + } + case 8: { + region_vth_idx_ = 2; + region_vh_idx_ = 6; + double v1 = vth_ + .25 * vth_vh; + double v2 = vth_ + .50 * vth_vh; + double v3 = vth_ + .75 * vth_vh; + double v4 = vh_ + .5 * (vdd_ - vh_); + region_volts_ = {0.0, vl_, vth_, v1, v2, v3, vh_, v4, vdd_}; + break; + } + case 9: { + region_vth_idx_ = 2; + region_vh_idx_ = 7; + double v1 = vth_ + .2 * vth_vh; + double v2 = vth_ + .4 * vth_vh; + double v3 = vth_ + .6 * vth_vh; + double v4 = vth_ + .8 * vth_vh; + double v5 = vh_ + .5 * (vdd_ - vh_); + region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, vdd_}; + break; + } + case 10: { + region_vth_idx_ = 2; + region_vh_idx_ = 7; + double v1 = vth_ + .2 * vth_vh; + double v2 = vth_ + .4 * vth_vh; + double v3 = vth_ + .6 * vth_vh; + double v4 = vth_ + .8 * vth_vh; + double v5 = vh_ + .3 * (vdd_ - vh_); + double v6 = vh_ + .6 * (vdd_ - vh_); + region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, v6, vdd_}; + break; + } + default: + report_->error(1701, "unsupported ccs region count."); + break; } - fill(region_ceff_.begin(), region_ceff_.end(), c2_ + c1_); + std::ranges::fill(region_ceff_, c2_ + c1_); } void CcsCeffDelayCalc::findCsmWaveform() { for (size_t i = 0; i < region_count_; i++) { - double t1 = output_waveforms_->voltageTime(in_slew_, region_ceff_[i], - region_volts_[i]); + double t1 = + output_waveforms_->voltageTime(in_slew_, region_ceff_[i], region_volts_[i]); double t2 = output_waveforms_->voltageTime(in_slew_, region_ceff_[i], region_volts_[i + 1]); region_begin_times_[i] = t1; region_end_times_[i] = t2; double time_offset = (i == 0) - ? 0.0 - : t1 - (region_end_times_[i - 1] - region_time_offsets_[i - 1]); + ? 0.0 + : t1 - (region_end_times_[i - 1] - region_time_offsets_[i - 1]); region_time_offsets_[i] = time_offset; if (i == 0) @@ -309,76 +320,48 @@ CcsCeffDelayCalc::findCsmWaveform() //////////////////////////////////////////////////////////////// -ArcDcalcResult -CcsCeffDelayCalc::makeResult(const LibertyLibrary *drvr_library, - const RiseFall *rf, - ArcDelay &gate_delay, - Slew &drvr_slew, - const LoadPinIndexMap &load_pin_index_map) -{ - ArcDcalcResult dcalc_result(load_pin_index_map.size()); - debugPrint(debug_, "ccs_dcalc", 2, - "gate_delay %s drvr_slew %s", - delayAsString(gate_delay, this), - delayAsString(drvr_slew, this)); - dcalc_result.setGateDelay(gate_delay); - dcalc_result.setDrvrSlew(drvr_slew); - - for (const auto &[load_pin, load_idx] : load_pin_index_map) { - ArcDelay wire_delay; - Slew load_slew; - loadDelaySlew(load_pin, drvr_library, rf, drvr_slew, wire_delay, load_slew); - dcalc_result.setWireDelay(load_idx, wire_delay); - dcalc_result.setLoadSlew(load_idx, load_slew); - } - return dcalc_result; -} - void CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin, const LibertyLibrary *drvr_library, - const RiseFall *rf, - Slew &drvr_slew, + double drvr_slew, // Return values. - ArcDelay &wire_delay, - Slew &load_slew) + double &wire_delay, + double &load_slew) { - ArcDelay wire_delay1 = 0.0; - Slew load_slew1 = drvr_slew; + wire_delay = 0.0; + load_slew = drvr_slew; + bool elmore_exists = false; float elmore = 0.0; - if (parasitic_ - && parasitics_->isPiElmore(parasitic_)) + if (parasitic_ && parasitics_->isPiElmore(parasitic_)) parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists); if (elmore_exists && (elmore == 0.0 // Elmore delay is small compared to driver slew. - || elmore < delayAsFloat(drvr_slew) * 1e-3)) { - wire_delay1 = elmore; - load_slew1 = drvr_slew; + || elmore < drvr_slew * 1e-3)) { + wire_delay = elmore; + load_slew = drvr_slew; } else - loadDelaySlew(load_pin, drvr_slew, elmore, wire_delay1, load_slew1); + loadDelaySlew(load_pin, drvr_slew, elmore, wire_delay, load_slew); - thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew); - wire_delay = wire_delay1; - load_slew = load_slew1; + thresholdAdjust(load_pin, drvr_library, drvr_rf_, wire_delay, load_slew); } void CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin, - Slew &drvr_slew, + double &drvr_slew, float elmore, // Return values. - ArcDelay &delay, - Slew &slew) + double &delay, + double &slew) { for (size_t i = 0; i <= region_count_; i++) { region_ramp_times_[i] = region_times_[i]; if (i < region_count_) region_ramp_slopes_[i] = (region_volts_[i + 1] - region_volts_[i]) - / (region_times_[i + 1] - region_times_[i]); + / (region_times_[i + 1] - region_times_[i]); } vl_fail_ = false; @@ -394,10 +377,8 @@ CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin, slew = drvr_slew; fail("load delay threshold crossing"); } - debugPrint(debug_, "ccs_dcalc", 2, - "load %s delay %s slew %s", - network_->pathName(load_pin), - delayAsString(delay, this), + debugPrint(debug_, "ccs_dcalc", 2, "load {} delay {} slew {}", + network_->pathName(load_pin), delayAsString(delay, this), delayAsString(slew, this)); } @@ -418,11 +399,11 @@ rampElmoreV(double t, // Elmore (one pole) response to 2 segment ramps [0, vth] slew1, [vth, vdd] slew2. void -CcsCeffDelayCalc::vl(double t, - double elmore, - // Return values. - double &vl, - double &dvl_dt) +CcsCeffDelayCalc::vLoad(double t, + double elmore, + // Return values. + double &vl, + double &dvl_dt) { vl = 0.0; dvl_dt = 0.0; @@ -447,11 +428,11 @@ CcsCeffDelayCalc::vl(double t, // for debugging double -CcsCeffDelayCalc::vl(double t, - double elmore) +CcsCeffDelayCalc::vLoad(double t, + double elmore) { double vl1, dvl_dt; - vl(t, elmore, vl1, dvl_dt); + vLoad(t, elmore, vl1, dvl_dt); return vl1; } @@ -461,14 +442,13 @@ CcsCeffDelayCalc::findVlTime(double v, { double t_init = region_ramp_times_[0]; double t_final = region_ramp_times_[region_count_]; - bool root_fail = false; - double time = findRoot([&] (double t, - double &y, - double &dy) { - vl(t, elmore, y, dy); - y -= v; - }, t_init, t_final + elmore * 3.0, .001, 20, root_fail); - vl_fail_ |= root_fail; + auto [time, failed] = + findRoot([&](double t, double &y, double &dy) { + vLoad(t, elmore, y, dy); + y -= v; + }, + t_init, t_final + elmore * 3.0, .001, 20); + vl_fail_ |= failed; return time; } @@ -492,7 +472,7 @@ PinSeq CcsCeffDelayCalc::watchPins() const { PinSeq pins; - for (const auto& [pin, values] : watch_pin_values_) + for (const auto &[pin, values] : watch_pin_values_) pins.push_back(pin); return pins; } @@ -528,13 +508,14 @@ CcsCeffDelayCalc::drvrWaveform() drvr_volts->push_back(v); } } - TableAxisPtr drvr_time_axis = make_shared(TableAxisVariable::time, - drvr_times); - Table1 drvr_table(drvr_volts, drvr_time_axis); + TableAxisPtr drvr_time_axis = + std::make_shared(TableAxisVariable::time, std::move(*drvr_times)); + delete drvr_times; + Table drvr_table(drvr_volts, drvr_time_axis); return drvr_table; } else - return Table1(); + return Table(); } Waveform @@ -555,17 +536,18 @@ CcsCeffDelayCalc::loadWaveform(const Pin *load_pin) load_times->push_back(t); double ignore; - vl(t, elmore, v, ignore); + vLoad(t, elmore, v, ignore); double v1 = (drvr_rf_ == RiseFall::rise()) ? v : vdd_ - v; load_volts->push_back(v1); } - TableAxisPtr load_time_axis = make_shared(TableAxisVariable::time, - load_times); - Table1 load_table(load_volts, load_time_axis); + TableAxisPtr load_time_axis = std::make_shared( + TableAxisVariable::time, std::move(*load_times)); + delete load_times; + Table load_table(load_volts, load_time_axis); return load_table; } } - return Table1(); + return Table(); } Waveform @@ -574,17 +556,16 @@ CcsCeffDelayCalc::drvrRampWaveform(const Pin *in_pin, const Pin *drvr_pin, const RiseFall *drvr_rf, const Pin *load_pin, - const Corner *corner, + const Scene *scene, const MinMax *min_max) { bool elmore_exists = false; float elmore = 0.0; if (parasitic_) { parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists); - bool dcalc_success = makeWaveformPreamble(in_pin, in_rf, drvr_pin, - drvr_rf, corner, min_max); - if (dcalc_success - && elmore_exists) { + bool dcalc_success = + makeWaveformPreamble(in_pin, in_rf, drvr_pin, drvr_rf, scene, min_max); + if (dcalc_success && elmore_exists) { FloatSeq *load_times = new FloatSeq; FloatSeq *load_volts = new FloatSeq; for (size_t j = 0; j <= region_count_; j++) { @@ -603,13 +584,14 @@ CcsCeffDelayCalc::drvrRampWaveform(const Pin *in_pin, double v1 = (drvr_rf == RiseFall::rise()) ? v : vdd_ - v; load_volts->push_back(v1); } - TableAxisPtr load_time_axis = make_shared(TableAxisVariable::time, - load_times); - Table1 load_table(load_volts, load_time_axis); + TableAxisPtr load_time_axis = std::make_shared( + TableAxisVariable::time, std::move(*load_times)); + delete load_times; + Table load_table(load_volts, load_time_axis); return load_table; } } - return Table1(); + return Table(); } bool @@ -617,7 +599,7 @@ CcsCeffDelayCalc::makeWaveformPreamble(const Pin *in_pin, const RiseFall *in_rf, const Pin *drvr_pin, const RiseFall *drvr_rf, - const Corner *corner, + const Scene *scene, const MinMax *min_max) { Vertex *in_vertex = graph_->pinLoadVertex(in_pin); @@ -632,7 +614,7 @@ CcsCeffDelayCalc::makeWaveformPreamble(const Pin *in_pin, break; } if (edge) { - TimingArc *arc = nullptr; + TimingArc *arc = nullptr; for (TimingArc *arc1 : edge->timingArcSet()->arcs()) { if (arc1->fromEdge()->asRiseFall() == in_rf && arc1->toEdge()->asRiseFall() == drvr_rf) { @@ -641,15 +623,15 @@ CcsCeffDelayCalc::makeWaveformPreamble(const Pin *in_pin, } } if (arc) { - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - const Slew &in_slew = graph_->slew(in_vertex, in_rf, dcalc_ap->index()); - parasitic_ = arc_delay_calc_->findParasitic(drvr_pin, drvr_rf, dcalc_ap); + DcalcAPIndex slew_index = scene->dcalcAnalysisPtIndex(min_max); + const Slew &in_slew = graph_->slew(in_vertex, in_rf, slew_index); + parasitic_ = arc_delay_calc_->findParasitic(drvr_pin, drvr_rf, scene, min_max); if (parasitic_) { parasitics_->piModel(parasitic_, c2_, rpi_, c1_); LoadPinIndexMap load_pin_index_map = - graph_delay_calc_->makeLoadPinIndexMap(drvr_vertex); - gateDelay(drvr_pin, arc, in_slew, load_cap_, parasitic_, - load_pin_index_map, dcalc_ap); + graph_delay_calc_->makeLoadPinIndexMap(drvr_vertex); + gateDelay(drvr_pin, arc, in_slew, load_cap_, parasitic_, load_pin_index_map, + scene, min_max); return true; } } @@ -659,37 +641,36 @@ CcsCeffDelayCalc::makeWaveformPreamble(const Pin *in_pin, //////////////////////////////////////////////////////////////// -string +std::string CcsCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) { Parasitic *pi_elmore = nullptr; const RiseFall *rf = arc->toEdge()->asRiseFall(); if (parasitic && !parasitics_->isPiElmore(parasitic)) { - const ParasiticAnalysisPt *ap = dcalc_ap->parasiticAnalysisPt(); - pi_elmore = parasitics_->reduceToPiElmore(parasitic, drvr_pin_, rf, - dcalc_ap->corner(), - dcalc_ap->constraintMinMax(), ap); + pi_elmore = + parasitics_->reduceToPiElmore(parasitic, drvr_pin_, rf, scene, min_max); } - string report = table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap, - pi_elmore, load_pin_index_map, - dcalc_ap, digits); + std::string report = + table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap, pi_elmore, + load_pin_index_map, scene, min_max, digits); parasitics_->deleteDrvrReducedParasitics(drvr_pin); return report; } void -CcsCeffDelayCalc::fail(const char *reason) +CcsCeffDelayCalc::fail(std::string_view reason) { // Report failures with a unique debug flag. if (debug_->check("ccs_dcalc", 1) || debug_->check("dcalc_error", 1)) - report_->reportLine("delay_calc: CCS failed - %s", reason); + report_->report("delay_calc: CCS failed - {}", reason); } -} // namespace +} // namespace sta diff --git a/dcalc/CcsCeffDelayCalc.hh b/dcalc/CcsCeffDelayCalc.hh index 389a7776d..07a659b90 100644 --- a/dcalc/CcsCeffDelayCalc.hh +++ b/dcalc/CcsCeffDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,12 +24,12 @@ #pragma once -#include "LumpedCapDelayCalc.hh" #include "ArcDcalcWaveforms.hh" +#include "LumpedCapDelayCalc.hh" namespace sta { -typedef std::map WatchPinValuesMap; +using WatchPinValuesMap = std::map; ArcDelayCalc * makeCcsCeffDelayCalc(StaState *sta); @@ -39,9 +39,9 @@ class CcsCeffDelayCalc : public LumpedCapDelayCalc, { public: CcsCeffDelayCalc(StaState *sta); - virtual ~CcsCeffDelayCalc(); + ~CcsCeffDelayCalc() override; ArcDelayCalc *copy() override; - const char *name() const override { return "ccs_ceff"; } + std::string_view name() const override { return "ccs_ceff"; } bool reduceSupported() const override { return true; } ArcDcalcResult gateDelay(const Pin *drvr_pin, const TimingArc *arc, @@ -49,14 +49,16 @@ public: float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; std::string reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) override; // Record waveform for drvr/load pin. @@ -66,41 +68,34 @@ public: Waveform watchWaveform(const Pin *pin) override; protected: - typedef std::vector Region; + using Region = std::vector; void gateDelaySlew(const LibertyLibrary *drvr_library, - const RiseFall *rf, // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew); + double &gate_delay, + double &drvr_slew); void initRegions(const LibertyLibrary *drvr_library, const RiseFall *rf); void findCsmWaveform(); - ArcDcalcResult makeResult(const LibertyLibrary *drvr_library, - const RiseFall *rf, - ArcDelay &gate_delay, - Slew &drvr_slew, - const LoadPinIndexMap &load_pin_index_map); void loadDelaySlew(const Pin *load_pin, const LibertyLibrary *drvr_library, - const RiseFall *rf, - Slew &drvr_slew, + double drvr_slew, // Return values. - ArcDelay &wire_delay, - Slew &load_slew); + double &wire_delay, + double &load_slew); void loadDelaySlew(const Pin *load_pin, - Slew &drvr_slew, + double &drvr_slew, float elmore, // Return values. - ArcDelay &delay, - Slew &slew); + double &delay, + double &slew); double findVlTime(double v, double elmore); bool makeWaveformPreamble(const Pin *in_pin, const RiseFall *in_rf, const Pin *drvr_pin, const RiseFall *drvr_rf, - const Corner *corner, + const Scene *scene, const MinMax *min_max); Waveform drvrWaveform(); Waveform loadWaveform(const Pin *load_pin); @@ -109,24 +104,25 @@ protected: const Pin *drvr_pin, const RiseFall *drvr_rf, const Pin *load_pin, - const Corner *corner, + const Scene *scene, const MinMax *min_max); - void vl(double t, - double elmore, - // Return values. - double &vl, - double &dvl_dt); - double vl(double t, - double elmore); - void fail(const char *reason); + void vLoad(double t, + double elmore, + // Return values. + double &vl, + double &dvl_dt); + double vLoad(double t, + double elmore); + void fail(std::string_view reason); const Pin *drvr_pin_; const RiseFall *drvr_rf_; double in_slew_; double load_cap_; + Parasitics *parasitics_; const Parasitic *parasitic_; - OutputWaveforms *output_waveforms_; + OutputWaveforms *output_waveforms_{nullptr}; double ref_time_; float vdd_; float vth_; @@ -137,7 +133,7 @@ protected: float rpi_; float c1_; - size_t region_count_; + size_t region_count_{0}; size_t region_vl_idx_; size_t region_vth_idx_; size_t region_vh_idx_; @@ -150,7 +146,7 @@ protected: Region region_time_offsets_; Region region_ramp_times_; Region region_ramp_slopes_; - bool vl_fail_; + bool vl_fail_{false}; // Waveform recording. WatchPinValuesMap watch_pin_values_; @@ -159,4 +155,4 @@ protected: ArcDelayCalc *table_dcalc_; }; -} // namespace +} // namespace sta diff --git a/dcalc/DcalcAnalysisPt.cc b/dcalc/DcalcAnalysisPt.cc index 1c5b89eda..e69de29bb 100644 --- a/dcalc/DcalcAnalysisPt.cc +++ b/dcalc/DcalcAnalysisPt.cc @@ -1,68 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#include "StringUtil.hh" -#include "DcalcAnalysisPt.hh" -#include "Corner.hh" - -namespace sta { - -DcalcAnalysisPt::DcalcAnalysisPt(Corner *corner, - DcalcAPIndex index, - const OperatingConditions *op_cond, - const MinMax *min_max, - const MinMax *check_clk_slew_min_max) : - corner_(corner), - index_(index), - op_cond_(op_cond), - min_max_(min_max), - check_clk_slew_min_max_(check_clk_slew_min_max) -{ -} - -void -DcalcAnalysisPt::setOperatingConditions(const OperatingConditions *op_cond) -{ - op_cond_ = op_cond; -} - -ParasiticAnalysisPt * -DcalcAnalysisPt::parasiticAnalysisPt() const -{ - return corner_->findParasiticAnalysisPt(min_max_); -} - -void -DcalcAnalysisPt::setCheckClkSlewIndex(DcalcAPIndex index) -{ - check_clk_slew_index_ = index; -} - -int -DcalcAnalysisPt::libertyIndex() const -{ - return corner_->libertyIndex(min_max_); -} - -} // namespace diff --git a/dcalc/Delay.cc b/dcalc/Delay.cc new file mode 100644 index 000000000..295cd4536 --- /dev/null +++ b/dcalc/Delay.cc @@ -0,0 +1,525 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#include "Delay.hh" + +#include + +#include "Fuzzy.hh" +#include "StaConfig.hh" +#include "StaState.hh" +#include "Units.hh" +#include "Variables.hh" + +namespace sta { + +static Delay delay_init_values[MinMax::index_count]; + +void +initDelayConstants() +{ + delay_init_values[MinMax::minIndex()] = MinMax::min()->initValue(); + delay_init_values[MinMax::maxIndex()] = MinMax::max()->initValue(); +} + +Delay::Delay() noexcept : + values_{0.0, 0.0, 0.0, 0.0} +{ +} + +Delay::Delay(float mean) noexcept : + values_{mean, 0.0, 0.0, 0.0} +{ +} + +Delay::Delay(float mean, + float std_dev2) noexcept : + values_{mean, 0.0, std_dev2, 0.0} +{ +} + +Delay::Delay(float mean, + float mean_shift, + float std_dev2, + float skewness) noexcept : + values_{mean, mean_shift, std_dev2, skewness} +{ +} + +Delay & +Delay::operator=(float delay) +{ + values_[0] = delay; + values_[1] = 0.0; + values_[2] = 0.0; + values_[3] = 0.0; + return *this; +} + +void +Delay::setValues(float mean, + float mean_shift, + float std_dev2, + float skewnes) +{ + values_[0] = mean; + values_[1] = mean_shift; + values_[2] = std_dev2; + values_[3] = skewnes; +} + +void +Delay::setMean(float mean) +{ + values_[0] = mean; +} + +void +Delay::setMeanShift(float mean_shift) +{ + values_[1] = mean_shift; +} + +float +Delay::stdDev() const +{ + float std_dev2 = values_[2]; + if (std_dev2 < 0.0) + // std_dev is negative for crpr to offset std_dev in the common + // clock path. + return -std::sqrt(-std_dev2); + else + return std::sqrt(std_dev2); +} + +void +Delay::setStdDev(float std_dev) +{ + values_[2] = square(std_dev); +} + +void +Delay::setSkewness(float skewness) +{ + values_[3] = skewness; +} + +//////////////////////////////////////////////////////////////// + +DelayDbl::DelayDbl() noexcept : + values_{0.0, 0.0, 0.0, 0.0} +{ +} + +DelayDbl::DelayDbl(double mean) noexcept : + values_{mean, 0.0, 0.0, 0.0} +{ +} + +void +DelayDbl::setMean(double mean) +{ + values_[0] = mean; +} + +double +DelayDbl::stdDev() const +{ + float std_dev2 = values_[2]; + if (std_dev2 < 0.0) + // std_dev is negative for crpr to offset std_dev in the common + // clock path. + return -std::sqrt(-std_dev2); + else + return std::sqrt(std_dev2); +} + +void +DelayDbl::setValues(double mean, + double mean_shift, + double std_dev2, + double skewnes) +{ + values_[0] = mean; + values_[1] = mean_shift; + values_[2] = std_dev2; + values_[3] = skewnes; +} + +DelayDbl & +DelayDbl::operator=(double delay) +{ + values_[0] = delay; + values_[1] = 0.0; + values_[2] = 0.0; + values_[3] = 0.0; + return *this; +} + +//////////////////////////////////////////////////////////////// + +Delay +makeDelay(float mean, + float mean_shift, + float std_dev, + float skewness) +{ + return Delay(mean, mean_shift, square(std_dev), skewness); +} + +Delay +makeDelay(float mean, + float std_dev) +{ + return Delay(mean, 0.0, square(std_dev), 0.0); +} + +Delay +makeDelay2(float mean, + float std_dev) +{ + return Delay(mean, 0.0, std_dev, 0.0); +} + +void +delaySetMean(Delay &delay, + float mean) +{ + delay.setMean(mean); +} + +//////////////////////////////////////////////////////////////// + +Delay +delayDblAsDelay(DelayDbl &delay) +{ + return Delay(delay.mean(), delay.meanShift(), delay.stdDev2(), delay.skewness()); +} + +std::string +delayAsString(const Delay &delay, + const StaState *sta) +{ + return delayAsString(delay, EarlyLate::late(), + sta->units()->timeUnit()->digits(), sta); +} + +std::string +delayAsString(const Delay &delay, + int digits, + const StaState *sta) +{ + return delayAsString(delay, EarlyLate::late(), digits, sta); +} + +std::string +delayAsString(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) +{ + return delayAsString(delay, early_late, sta->units()->timeUnit()->digits(), sta); +} + +std::string +delayAsString(const Delay &delay, + const EarlyLate *early_late, + int digits, + const StaState *sta) +{ + const Unit *unit = sta->units()->timeUnit(); + float mean_std_dev = delayAsFloat(delay, early_late, sta); + return unit->asString(mean_std_dev, digits); +} + +std::string +delayAsString(const Delay &delay, + const EarlyLate *early_late, + bool report_variance, + int digits, + const StaState *sta) +{ + if (report_variance) + return sta->delayOps()->asStringVariance(delay, digits, sta); + else + return delayAsString(delay, early_late, digits, sta); +} + +float +delayAsFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) +{ + return sta->delayOps()->asFloat(delay, early_late, sta); +} + +float +delayAsFloat(const DelayDbl &delay, + const EarlyLate *early_late, + const StaState *sta) +{ + return sta->delayOps()->asFloat(delay, early_late, sta); +} + +float +delayAsFloat(const Delay &delay) +{ + return delay.mean(); +} + +const Delay & +delayInitValue(const MinMax *min_max) +{ + return delay_init_values[min_max->index()]; +} + +bool +delayIsInitValue(const Delay &delay, + const MinMax *min_max) +{ + return fuzzyEqual(delay.mean(), min_max->initValue()); +} + +bool +delayZero(const Delay &delay, + const StaState *sta) +{ + return sta->delayOps()->isZero(delay); +} + +bool +delayInf(const Delay &delay, + const StaState *sta) +{ + return sta->delayOps()->isInf(delay); +} + +bool +delayEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->equal(delay1, delay2, sta); +} + +bool +delayLess(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->less(delay1, delay2, sta); +} + +bool +delayLess(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *sta) +{ + return sta->delayOps()->less(delay1, delay2, sta); +} + +bool +delayLess(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) +{ + if (min_max == MinMax::max()) + return sta->delayOps()->less(delay1, delay2, sta); + else + return sta->delayOps()->greater(delay1, delay2, sta); +} + +bool +delayLessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->lessEqual(delay1, delay2, sta); +} + +bool +delayLessEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) +{ + if (min_max == MinMax::max()) + return sta->delayOps()->lessEqual(delay1, delay2, sta); + else + return sta->delayOps()->greaterEqual(delay1, delay2, sta); +} + +bool +delayGreater(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->greater(delay1, delay2, sta); +} + +bool +delayGreater(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) +{ + if (min_max == MinMax::max()) + return sta->delayOps()->greater(delay1, delay2, sta); + else + return sta->delayOps()->less(delay1, delay2, sta); +} + +bool +delayGreaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->greaterEqual(delay1, delay2, sta); +} + +bool +delayGreaterEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) +{ + if (min_max == MinMax::max()) + return sta->delayOps()->greaterEqual(delay1, delay2, sta); + else + return sta->delayOps()->lessEqual(delay1, delay2, sta); +} + +Delay +delayRemove(const Delay &delay1, + const Delay &delay2) +{ + return makeDelay2(delay1.mean() - delay2.mean(), + delay1.stdDev2() - delay2.stdDev2()); +} + +Delay +delaySum(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->sum(delay1, delay2); +} + +Delay +delaySum(const Delay &delay1, + float delay2, + const StaState *sta) +{ + return sta->delayOps()->sum(delay1, delay2); +} + +Delay +delayDiff(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->diff(delay1, delay2); +} + +Delay +delayDiff(const Delay &delay1, + float delay2, + const StaState *sta) +{ + return sta->delayOps()->diff(delay1, delay2); +} + +Delay +delayDiff(float delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->diff(delay1, delay2); +} + +void +delayIncr(Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + sta->delayOps()->incr(delay1, delay2); +} + +void +delayIncr(DelayDbl &delay1, + const Delay &delay2, + const StaState *sta) +{ + sta->delayOps()->incr(delay1, delay2); +} + +void +delayIncr(Delay &delay1, + float delay2, + const StaState *sta) +{ + sta->delayOps()->incr(delay1, delay2); +} + +void +delayDecr(Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + sta->delayOps()->decr(delay1, delay2); +} + +void +delayDecr(DelayDbl &delay1, + const Delay &delay2, + const StaState *sta) +{ + sta->delayOps()->decr(delay1, delay2); +} + +Delay +delayProduct(const Delay &delay1, + float delay2, + const StaState *sta) +{ + return sta->delayOps()->product(delay1, delay2); +} + +Delay +delayDiv(float delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->div(delay1, delay2); +} + +float +delayStdDev2(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) +{ + return sta->delayOps()->stdDev2(delay, early_late); +} + +} // namespace sta diff --git a/dcalc/DelayCalc.cc b/dcalc/DelayCalc.cc index 77fc7ed1b..2b8fc8261 100644 --- a/dcalc/DelayCalc.cc +++ b/dcalc/DelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,20 +24,23 @@ #include "DelayCalc.hh" -#include "Map.hh" -#include "StringUtil.hh" -#include "UnitDelayCalc.hh" -#include "LumpedCapDelayCalc.hh" -#include "DmpDelayCalc.hh" +#include +#include + #include "ArnoldiDelayCalc.hh" #include "CcsCeffDelayCalc.hh" +#include "ContainerHelpers.hh" +#include "DmpDelayCalc.hh" +#include "LumpedCapDelayCalc.hh" #include "PrimaDelayCalc.hh" +#include "StringUtil.hh" +#include "UnitDelayCalc.hh" namespace sta { -typedef Map DelayCalcMap; +using DelayCalcMap = std::map>; -static DelayCalcMap *delay_calcs = nullptr; +static DelayCalcMap delay_calcs; void registerDelayCalcs() @@ -52,26 +55,23 @@ registerDelayCalcs() } void -registerDelayCalc(const char *name, - MakeArcDelayCalc maker) +registerDelayCalc(std::string_view name, + MakeArcDelayCalc maker) { - if (delay_calcs == nullptr) - delay_calcs = new DelayCalcMap; - (*delay_calcs)[name] = maker; + delay_calcs[std::string(name)] = maker; } void deleteDelayCalcs() { - delete delay_calcs; - delay_calcs = nullptr; + delay_calcs.clear(); } ArcDelayCalc * -makeDelayCalc(const char *name, - StaState *sta) +makeDelayCalc(const std::string_view name, + StaState *sta) { - MakeArcDelayCalc maker = delay_calcs->findKey(name); + MakeArcDelayCalc maker = findStringKey(delay_calcs, name); if (maker) return maker(sta); else @@ -79,18 +79,18 @@ makeDelayCalc(const char *name, } bool -isDelayCalcName(const char *name) +isDelayCalcName(std::string_view name) { - return delay_calcs->hasKey(name); + return delay_calcs.contains(name); } StringSeq delayCalcNames() { StringSeq names; - for (const auto [name, make_dcalc] : *delay_calcs) + for (const auto &[name, make_dcalc] : delay_calcs) names.push_back(name); return names; } -} // namespace +} // namespace sta diff --git a/dcalc/DelayCalc.i b/dcalc/DelayCalc.i index e3494286f..69e23059a 100644 --- a/dcalc/DelayCalc.i +++ b/dcalc/DelayCalc.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,17 +22,15 @@ // // This notice may not be removed or altered from any source distribution. -%module dcalc +%include %{ -#include "DelayCalc.hh" #include "ArcDelayCalc.hh" +#include "DelayCalc.hh" +#include "Sta.hh" #include "dcalc/ArcDcalcWaveforms.hh" #include "dcalc/PrimaDelayCalc.hh" -#include "Sta.hh" - -using std::string; %} @@ -62,15 +60,15 @@ set_delay_calc_incremental_tolerance(float tol) Sta::sta()->setIncrementalDelayTolerance(tol); } -string +std::string report_delay_calc_cmd(Edge *edge, - TimingArc *arc, - const Corner *corner, - const MinMax *min_max, - int digits) + TimingArc *arc, + const Scene *scene, + const MinMax *min_max, + int digits) { Sta *sta = Sta::sta(); - return sta->reportDelayCalc(edge, arc, corner, min_max, digits); + return sta->reportDelayCalc(edge, arc, scene, min_max, digits); } void diff --git a/dcalc/DelayCalc.tcl b/dcalc/DelayCalc.tcl index b920e4b0e..3658c2198 100644 --- a/dcalc/DelayCalc.tcl +++ b/dcalc/DelayCalc.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -25,7 +25,7 @@ namespace eval sta { define_cmd_args "report_dcalc" \ - {[-from from_pin] [-to to_pin] [-corner corner] [-min] [-max] [-digits digits]} + {[-from from_pin] [-to to_pin] [-scene scene] [-min] [-max] [-digits digits]} proc_redirect report_dcalc { report_dcalc_cmd "report_dcalc" $args "-digits" @@ -36,9 +36,9 @@ proc report_dcalc_cmd { cmd cmd_args digits_key } { global sta_report_default_digits parse_key_args $cmd cmd_args \ - keys "$digits_key -from -to -corner" \ + keys "$digits_key -from -to -scene -corner" \ flags {-min -max} - set corner [parse_corner keys] + set scene [parse_scene keys] set min_max [parse_min_max_flags flags] check_argc_eq0 $cmd $cmd_args @@ -52,14 +52,14 @@ proc report_dcalc_cmd { cmd cmd_args digits_key } { set to_pin [get_port_pin_error "to_pin" $keys(-to)] foreach from_vertex [$from_pin vertices] { foreach to_vertex [$to_pin vertices] { - set iter [$from_vertex out_edge_iterator] - while {[$iter has_next]} { - set edge [$iter next] - if { [$edge to] == $to_vertex } { - report_edge_dcalc $edge $corner $min_max $digits - } - } - $iter finish + set iter [$from_vertex out_edge_iterator] + while {[$iter has_next]} { + set edge [$iter next] + if { [$edge to] == $to_vertex } { + report_edge_dcalc $edge $scene $min_max $digits + } + } + $iter finish } } } elseif [info exists keys(-from)] { @@ -67,8 +67,8 @@ proc report_dcalc_cmd { cmd cmd_args digits_key } { foreach from_vertex [$from_pin vertices] { set iter [$from_vertex out_edge_iterator] while {[$iter has_next]} { - set edge [$iter next] - report_edge_dcalc $edge $corner $min_max $digits + set edge [$iter next] + report_edge_dcalc $edge $scene $min_max $digits } $iter finish } @@ -77,15 +77,15 @@ proc report_dcalc_cmd { cmd cmd_args digits_key } { foreach to_vertex [$to_pin vertices] { set iter [$to_vertex in_edge_iterator] while {[$iter has_next]} { - set edge [$iter next] - report_edge_dcalc $edge $corner $min_max $digits + set edge [$iter next] + report_edge_dcalc $edge $scene $min_max $digits } $iter finish } } } -proc report_edge_dcalc { edge corner min_max digits } { +proc report_edge_dcalc { edge scene min_max digits } { set role [$edge role] if { $role != "wire" } { set from_vertex [$edge from] @@ -96,7 +96,7 @@ proc report_edge_dcalc { edge corner min_max digits } { set library [$cell library] # Filter timing checks based on min_max. if {!(($min_max == "max" && $role == "hold") \ - || ($min_max=="min" && $role=="setup"))} { + || ($min_max=="min" && $role=="setup"))} { report_line "Library: [get_name $library]" report_line "Cell: [get_name $cell]" set sense [$edge sense] @@ -106,18 +106,18 @@ proc report_edge_dcalc { edge corner min_max digits } { report_line "Arc type: $role" foreach arc [$edge timing_arcs] { - set from [get_name [$from_pin port]] - set from_rf [$arc from_edge] - set to [get_name [$to_pin port]] - set to_rf [$arc to_edge] - report_line "$from $from_rf -> $to $to_rf" - report_line [report_delay_calc_cmd $edge $arc $corner $min_max $digits] - if { [$edge delay_annotated $arc $corner $min_max] } { - set delay [$edge arc_delay $arc $corner $min_max] - report_line "Annotated value = [format_time $delay $digits]" - } - report_line "............................................." - report_line "" + set from [get_name [$from_pin port]] + set from_rf [$arc from_edge] + set to [get_name [$to_pin port]] + set to_rf [$arc to_edge] + report_line "$from $from_rf -> $to $to_rf" + report_line [report_delay_calc_cmd $edge $arc $scene $min_max $digits] + if { [$edge delay_annotated $arc $scene $min_max] } { + set delay [$edge arc_delay $arc $scene $min_max] + report_line "Annotated value = [format_time $delay $digits]" + } + report_line "............................................." + report_line "" } } } @@ -131,107 +131,105 @@ proc set_delay_calculator { alg } { if { [is_delay_calc_name $alg] } { set_delay_calculator_cmd $alg } else { - sta_error 195 "delay calculator $alg not found." + sta_error 2500 "delay calculator $alg not found." } } -define_cmd_args "set_pocv_sigma_factor" { factor } - ################################################################ define_cmd_args "set_assigned_delay" \ - {-cell|-net [-rise] [-fall] [-corner corner] [-min] [-max]\ + {-cell|-net [-rise] [-fall] [-scene scene] [-min] [-max]\ [-from from_pins] [-to to_pins] delay} # Change the delay for timing arcs between from_pins and to_pins matching # on cell (instance) or net. proc set_assigned_delay { args } { - parse_key_args "set_assigned_delay" args keys {-corner -from -to} \ + parse_key_args "set_assigned_delay" args keys {-scene -corner -from -to} \ flags {-cell -net -rise -fall -max -min} check_argc_eq1 "set_assigned_delay" $args - set corner [parse_corner keys] + set scene [parse_scene keys] set min_max [parse_min_max_all_check_flags flags] set to_rf [parse_rise_fall_flags flags] if [info exists keys(-from)] { set from_pins [get_port_pins_error "from_pins" $keys(-from)] } else { - sta_error 196 "set_assigned_delay missing -from argument." + sta_error 2501 "set_assigned_delay missing -from argument." } if [info exists keys(-to)] { set to_pins [get_port_pins_error "to_pins" $keys(-to)] } else { - sta_error 182 "set_assigned_delay missing -to argument." + sta_error 2502 "set_assigned_delay missing -to argument." } set delay [lindex $args 0] if {![string is double $delay]} { - sta_error 183 "set_assigned_delay delay is not a float." + sta_error 2503 "set_assigned_delay delay is not a float." } set delay [time_ui_sta $delay] if {[info exists flags(-cell)] && [info exists flags(-net)]} { - sta_error 184 "set_annotated_delay -cell and -net options are mutually excluive." + sta_error 2504 "set_annotated_delay -cell and -net options are mutually excluive." } elseif {[info exists flags(-cell)]} { if { $from_pins != {} } { set inst [[lindex $from_pins 0] instance] foreach pin $from_pins { - if {[$pin instance] != $inst} { - sta_error 185 "set_assigned_delay pin [get_full_name $pin] is not attached to instance [get_full_name $inst]." - } + if {[$pin instance] != $inst} { + sta_error 2505 "set_assigned_delay pin [get_full_name $pin] is not attached to instance [get_full_name $inst]." + } } foreach pin $to_pins { - if {[$pin instance] != $inst} { - sta_error 186 "set_assigned_delay pin [get_full_name $pin] is not attached to instance [get_full_name $inst]" - } + if {[$pin instance] != $inst} { + sta_error 2506 "set_assigned_delay pin [get_full_name $pin] is not attached to instance [get_full_name $inst]" + } } } } elseif {![info exists flags(-net)]} { - sta_error 187 "set_assigned_delay -cell or -net required." + sta_error 2508 "set_assigned_delay -cell or -net required." } foreach from_pin $from_pins { set from_vertices [$from_pin vertices] set_assigned_delay1 [lindex $from_vertices 0] \ - $to_pins $to_rf $corner $min_max $delay + $to_pins $to_rf $scene $min_max $delay if { [llength $from_vertices] == 2 } { set_assigned_delay1 [lindex $from_vertices 1] \ - $to_pins $to_rf $corner $min_max $delay + $to_pins $to_rf $scene $min_max $delay } } } -proc set_assigned_delay1 { from_vertex to_pins to_rf corner min_max delay } { +proc set_assigned_delay1 { from_vertex to_pins to_rf scene min_max delay } { foreach to_pin $to_pins { set to_vertices [$to_pin vertices] set_assigned_delay2 $from_vertex [lindex $to_vertices 0] \ - $to_rf $corner $min_max $delay + $to_rf $scene $min_max $delay if { [llength $to_vertices] == 2 } { # Bidirect driver. set_assigned_delay2 $from_vertex [lindex $to_vertices 1] \ - $to_rf $corner $min_max $delay + $to_rf $scene $min_max $delay } } } -proc set_assigned_delay2 {from_vertex to_vertex to_rf corner min_max delay} { +proc set_assigned_delay2 {from_vertex to_vertex to_rf scene min_max delay} { set matched 0 set edge_iter [$from_vertex out_edge_iterator] while {[$edge_iter has_next]} { set edge [$edge_iter next] if { [$edge to] == $to_vertex \ - && ![timing_role_is_check [$edge role]] } { + && ![timing_role_is_check [$edge role]] } { foreach arc [$edge timing_arcs] { - if { $to_rf == "rise_fall" \ - || $to_rf eq [$arc to_edge_name] } { - set_arc_delay $edge $arc $corner $min_max $delay + if { $to_rf == "rise_fall" \ + || $to_rf eq [$arc to_edge_name] } { + set_arc_delay $edge $arc $scene $min_max $delay set matched 1 - } + } } } } $edge_iter finish if { !$matched } { - sta_error 193 "set_assigned_delay no timing arcs found between from/to pins." + sta_error 2509 "set_assigned_delay no timing arcs found between from/to pins." } } @@ -239,39 +237,39 @@ proc set_assigned_delay2 {from_vertex to_vertex to_rf corner min_max delay} { define_cmd_args "set_assigned_check" \ {-setup|-hold|-recovery|-removal [-rise] [-fall]\ - [-corner corner] [-min] [-max]\ + [-scene scene] [-min] [-max]\ [-from from_pins] [-to to_pins] [-clock rise|fall]\ [-cond sdf_cond] check_value} proc set_assigned_check { args } { parse_key_args "set_assigned_check" args \ - keys {-from -to -corner -clock -cond} \ + keys {-from -to -scene -corner -clock -cond} \ flags {-setup -hold -recovery -removal -rise -fall -max -min} check_argc_eq1 "set_assigned_check" $args if { [info exists keys(-from)] } { set from_pins [get_port_pins_error "from_pins" $keys(-from)] } else { - sta_error 188 "set_assigned_check missing -from argument." + sta_error 2510 "set_assigned_check missing -from argument." } set from_rf "rise_fall" if { [info exists keys(-clock)] } { set clk_arg $keys(-clock) if { $clk_arg eq "rise" \ - || $clk_arg eq "fall" } { + || $clk_arg eq "fall" } { set from_rf $clk_arg } else { - sta_error 189 "set_assigned_check -clock must be rise or fall." + sta_error 2511 "set_assigned_check -clock must be rise or fall." } } if { [info exists keys(-to)] } { set to_pins [get_port_pins_error "to_pins" $keys(-to)] } else { - sta_error 190 "set_assigned_check missing -to argument." + sta_error 2512 "set_assigned_check missing -to argument." } set to_rf [parse_rise_fall_flags flags] - set corner [parse_corner keys] + set scene [parse_scene keys] set min_max [parse_min_max_all_check_flags flags] if { [info exists flags(-setup)] } { @@ -283,7 +281,7 @@ proc set_assigned_check { args } { } elseif { [info exists flags(-removal)] } { set role "removal" } else { - sta_error 191 "set_assigned_check missing -setup|-hold|-recovery|-removal check type.." + sta_error 2513 "set_assigned_check missing -setup|-hold|-recovery|-removal check type.." } set cond "" if { [info exists key(-cond)] } { @@ -291,87 +289,87 @@ proc set_assigned_check { args } { } set check_value [lindex $args 0] if { ![string is double $check_value] } { - sta_error 192 "set_assigned_check check_value is not a float." + sta_error 2514 "set_assigned_check check_value is not a float." } set check_value [time_ui_sta $check_value] foreach from_pin $from_pins { set from_vertices [$from_pin vertices] set_assigned_check1 [lindex $from_vertices 0] $from_rf \ - $to_pins $to_rf $role $corner $min_max $cond $check_value + $to_pins $to_rf $role $scene $min_max $cond $check_value if { [llength $from_vertices] == 2 } { set_assigned_check1 [lindex $from_vertices 1] $from_rf \ - $to_pins $to_rf $role $corner $min_max $cond $check_value + $to_pins $to_rf $role $scene $min_max $cond $check_value } } } proc set_assigned_check1 { from_vertex from_rf to_pins to_rf \ - role corner min_max cond check_value } { + role scene min_max cond check_value } { foreach to_pin $to_pins { set to_vertices [$to_pin vertices] set_assigned_check2 $from_vertex $from_rf [lindex $to_vertices 0] \ - $to_rf $role $corner $min_max $cond $check_value + $to_rf $role $scene $min_max $cond $check_value if { [llength $to_vertices] == 2 } { # Bidirect driver. set_assigned_check2 $from_vertex $from_rf \ - [lindex $to_vertices 1] $to_rf $role $corner $min_max \ - $cond $check_value + [lindex $to_vertices 1] $to_rf $role $scene $min_max \ + $cond $check_value } } } proc set_assigned_check2 { from_vertex from_rf to_vertex to_rf \ - role corner min_max cond check_value } { + role scene min_max cond check_value } { set edge_iter [$from_vertex out_edge_iterator] set matched 0 while {[$edge_iter has_next]} { set edge [$edge_iter next] if { [$edge to] == $to_vertex } { foreach arc [$edge timing_arcs] { - if { ($from_rf eq "rise_fall" \ - || $from_rf eq [$arc from_edge_name]) \ - && ($to_rf eq "rise_fall" \ - || $to_rf eq [$arc to_edge_name]) \ - && [$arc role] eq $role \ - && ($cond eq "" || [$arc sdf_cond] eq $cond) } { - set_arc_delay $edge $arc $corner $min_max $check_value + if { ($from_rf eq "rise_fall" \ + || $from_rf eq [$arc from_edge_name]) \ + && ($to_rf eq "rise_fall" \ + || $to_rf eq [$arc to_edge_name]) \ + && [$arc role] eq $role \ + && ($cond eq "" || [$arc sdf_cond] eq $cond) } { + set_arc_delay $edge $arc $scene $min_max $check_value set matched 1 - } + } } } } $edge_iter finish if { !$matched } { - sta_error 194 "set_assigned_check no check arcs found between from/to pins." + sta_error 2516 "set_assigned_check no check arcs found between from/to pins." } } ################################################################a define_cmd_args "set_assigned_transition" \ - {[-rise] [-fall] [-corner corner] [-min] [-max] slew pins} + {[-rise] [-fall] [-scene scene] [-min] [-max] slew pins} # Change the slew on a list of ports. proc set_assigned_transition { args } { - parse_key_args "set_assigned_transition" args keys {-corner} \ + parse_key_args "set_assigned_transition" args keys {-scene -corner} \ flags {-rise -fall -max -min} - set corner [parse_corner keys] + set scene [parse_scene keys] set min_max [parse_min_max_all_check_flags flags] set tr [parse_rise_fall_flags flags] check_argc_eq2 "set_assigned_transition" $args set slew [lindex $args 0] if {![string is double $slew]} { - sta_error 210 "set_assigned_transition transition is not a float." + sta_error 2518 "set_assigned_transition transition is not a float." } set slew [time_ui_sta $slew] set pins [get_port_pins_error "pins" [lindex $args 1]] foreach pin $pins { set vertices [$pin vertices] set vertex [lindex $vertices 0] - set_annotated_slew $vertex $corner $min_max $tr $slew + set_annotated_slew $vertex $scene $min_max $tr $slew if { [llength $vertices] == 2 } { # Bidirect driver. set vertex [lindex $vertices 1] @@ -380,5 +378,36 @@ proc set_assigned_transition { args } { } } +################################################################ + +define_cmd_args "report_slews" {[-scenes scenes] [-digits digits]\ + [-report_variance] pin} + +proc report_slews { args } { + global sta_report_default_digits + + parse_key_args "report_slews" args keys {-corner -scenes -digits} \ + flags {-report_variance} + check_argc_eq1 "report_slews" $args + + set scenes [parse_scenes_or_all keys] + 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 + } + set report_variance [info exists flags(-report_variance)] + + foreach vertex [$pin vertices] { + set rise_min [$vertex slew_scenes_string rise $scenes min $report_variance $digits] + set rise_max [$vertex slew_scenes_string rise $scenes max $report_variance $digits] + set fall_min [$vertex slew_scenes_string fall $scenes min $report_variance $digits] + set fall_max [$vertex slew_scenes_string fall $scenes max $report_variance $digits] + report_line "[vertex_path_name $vertex] [rise_short_name] $rise_min:$rise_max [fall_short_name] $fall_min:$fall_max" + } +} + # sta namespace end } diff --git a/dcalc/DelayCalcBase.cc b/dcalc/DelayCalcBase.cc index 8c15bfdb9..214aec7d3 100644 --- a/dcalc/DelayCalcBase.cc +++ b/dcalc/DelayCalcBase.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,24 +24,20 @@ #include "DelayCalcBase.hh" +#include "Graph.hh" +#include "GraphDelayCalc.hh" #include "Liberty.hh" -#include "TimingArc.hh" -#include "TimingModel.hh" -#include "TableModel.hh" #include "Network.hh" #include "Parasitics.hh" -#include "Graph.hh" +#include "Scene.hh" #include "Sdc.hh" -#include "Corner.hh" -#include "DcalcAnalysisPt.hh" -#include "GraphDelayCalc.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "TimingModel.hh" #include "Variables.hh" namespace sta { -using std::string; -using std::log; - DelayCalcBase::DelayCalcBase(StaState *sta) : ArcDelayCalc(sta) { @@ -50,7 +46,7 @@ DelayCalcBase::DelayCalcBase(StaState *sta) : void DelayCalcBase::reduceParasitic(const Parasitic *parasitic_network, const Net *net, - const Corner *corner, + const Scene *scene, const MinMaxAll *min_max) { NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); @@ -59,16 +55,12 @@ DelayCalcBase::reduceParasitic(const Parasitic *parasitic_network, if (network_->isDriver(pin)) { for (const RiseFall *rf : RiseFall::range()) { for (const MinMax *min_max : min_max->range()) { - if (corner == nullptr) { - for (const Corner *corner1 : *corners_) { - DcalcAnalysisPt *dcalc_ap = corner1->findDcalcAnalysisPt(min_max); - reduceParasitic(parasitic_network, pin, rf, dcalc_ap); - } - } - else { - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - reduceParasitic(parasitic_network, pin, rf, dcalc_ap); + if (scene == nullptr) { + for (const Scene *scene1 : scenes_) + reduceParasitic(parasitic_network, pin, rf, scene1, min_max); } + else + reduceParasitic(parasitic_network, pin, rf, scene, min_max); } } } @@ -89,10 +81,10 @@ DelayCalcBase::finishDrvrPin() void DelayCalcBase::dspfWireDelaySlew(const Pin *load_pin, const RiseFall *rf, - Slew drvr_slew, + double drvr_slew, float elmore, - ArcDelay &wire_delay, - Slew &load_slew) + double &wire_delay, + double &load_slew) { LibertyLibrary *load_library = thresholdLibrary(load_pin); @@ -106,17 +98,17 @@ DelayCalcBase::dspfWireDelaySlew(const Pin *load_pin, vh = load_library->slewUpperThreshold(rf); slew_derate = load_library->slewDerateFromLibrary(); } - wire_delay = -elmore * log(1.0 - vth); - load_slew = drvr_slew + elmore * log((1.0 - vl) / (1.0 - vh)) / slew_derate; - load_slew = drvr_slew + elmore * log((1.0 - vl) / (1.0 - vh)) / slew_derate; + wire_delay = -elmore * std::log(1.0 - vth); + load_slew = drvr_slew + elmore * std::log((1.0 - vl) / (1.0 - vh)) / slew_derate; + load_slew = drvr_slew + elmore * std::log((1.0 - vl) / (1.0 - vh)) / slew_derate; } void DelayCalcBase::thresholdAdjust(const Pin *load_pin, const LibertyLibrary *drvr_library, const RiseFall *rf, - ArcDelay &load_delay, - Slew &load_slew) + double &wire_delay, + double &load_slew) { LibertyLibrary *load_library = thresholdLibrary(load_pin); if (load_library @@ -126,17 +118,18 @@ DelayCalcBase::thresholdAdjust(const Pin *load_pin, float load_vth = load_library->inputThreshold(rf); float drvr_slew_delta = drvr_library->slewUpperThreshold(rf) - drvr_library->slewLowerThreshold(rf); - float load_delay_delta = + float wire_delay_delta = delayAsFloat(load_slew) * ((load_vth - drvr_vth) / drvr_slew_delta); - load_delay += (rf == RiseFall::rise()) - ? load_delay_delta - : -load_delay_delta; + wire_delay += (rf == RiseFall::rise()) + ? wire_delay_delta + : -wire_delay_delta; + float load_slew_delta = load_library->slewUpperThreshold(rf) - load_library->slewLowerThreshold(rf); float drvr_slew_derate = drvr_library->slewDerateFromLibrary(); float load_slew_derate = load_library->slewDerateFromLibrary(); load_slew = load_slew * ((load_slew_delta / load_slew_derate) - / (drvr_slew_delta / drvr_slew_derate)); + / (drvr_slew_delta / drvr_slew_derate)); } } @@ -162,61 +155,68 @@ DelayCalcBase::checkDelay(const Pin *check_pin, const Slew &from_slew, const Slew &to_slew, float related_out_cap, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { - CheckTimingModel *model = arc->checkModel(dcalc_ap); + CheckTimingModel *model = arc->checkModel(scene, min_max); if (model) { float from_slew1 = delayAsFloat(from_slew); float to_slew1 = delayAsFloat(to_slew); - return model->checkDelay(pinPvt(check_pin, dcalc_ap), from_slew1, to_slew1, - related_out_cap, - variables_->pocvEnabled()); + return model->checkDelay(pinPvt(check_pin, scene, min_max), + from_slew1, to_slew1, related_out_cap, + min_max, variables_->pocvMode()); } else return delay_zero; } -string +std::string DelayCalcBase::reportCheckDelay(const Pin *check_pin, const TimingArc *arc, const Slew &from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, const Slew &to_slew, float related_out_cap, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) { - CheckTimingModel *model = arc->checkModel(dcalc_ap); + CheckTimingModel *model = arc->checkModel(scene, min_max); if (model) { float from_slew1 = delayAsFloat(from_slew); float to_slew1 = delayAsFloat(to_slew); - return model->reportCheckDelay(pinPvt(check_pin, dcalc_ap), from_slew1, - from_slew_annotation, to_slew1, - related_out_cap, false, digits); + return model->reportCheckDelay(pinPvt(check_pin, scene, min_max), + from_slew1, from_slew_annotation, + to_slew1, related_out_cap, min_max, + PocvMode::scalar, digits); } return ""; } const Pvt * DelayCalcBase::pinPvt(const Pin *pin, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { const Instance *drvr_inst = network_->instance(pin); - const Pvt *pvt = sdc_->pvt(drvr_inst, dcalc_ap->constraintMinMax()); + const Sdc *sdc = scene->sdc(); + const Pvt *pvt = sdc->pvt(drvr_inst, min_max); if (pvt == nullptr) - pvt = dcalc_ap->operatingConditions(); + pvt = sdc->operatingConditions(min_max); return pvt; } void DelayCalcBase::setDcalcArgParasiticSlew(ArcDcalcArg &gate, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { const Pin *drvr_pin = gate.drvrPin(); if (drvr_pin) { const Parasitic *parasitic; float load_cap; - graph_delay_calc_->parasiticLoad(drvr_pin, gate.drvrEdge(), dcalc_ap, + graph_delay_calc_->parasiticLoad(drvr_pin, gate.drvrEdge(), + scene, min_max, nullptr, this, load_cap, parasitic); gate.setLoadCap(load_cap); @@ -224,17 +224,19 @@ DelayCalcBase::setDcalcArgParasiticSlew(ArcDcalcArg &gate, const Pin *in_pin = gate.inPin(); const Vertex *in_vertex = graph_->pinLoadVertex(in_pin); const Slew &in_slew = graph_delay_calc_->edgeFromSlew(in_vertex, gate.inEdge(), - gate.edge(), dcalc_ap); + gate.edge(), + scene, min_max); gate.setInSlew(in_slew); } } void DelayCalcBase::setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { for (ArcDcalcArg &gate : gates) - setDcalcArgParasiticSlew(gate, dcalc_ap); + setDcalcArgParasiticSlew(gate, scene, min_max); } -} // namespace +} // namespace sta diff --git a/dcalc/DelayCalcBase.hh b/dcalc/DelayCalcBase.hh index 82597f7ef..a2d4666be 100644 --- a/dcalc/DelayCalcBase.hh +++ b/dcalc/DelayCalcBase.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -34,31 +34,35 @@ class GateTableModel; class DelayCalcBase : public ArcDelayCalc { public: - explicit DelayCalcBase(StaState *sta); + DelayCalcBase(StaState *sta); void finishDrvrPin() override; void reduceParasitic(const Parasitic *parasitic_network, const Net *net, - const Corner *corner, + const Scene *scene, const MinMaxAll *min_max) override; void setDcalcArgParasiticSlew(ArcDcalcArg &gate, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; void setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDelay checkDelay(const Pin *check_pin, const TimingArc *arc, const Slew &from_slew, const Slew &to_slew, float related_out_cap, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; std::string reportCheckDelay(const Pin *check_pin, const TimingArc *arc, const Slew &from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, const Slew &to_slew, float related_out_cap, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) override; protected: @@ -68,20 +72,21 @@ protected: void thresholdAdjust(const Pin *load_pin, const LibertyLibrary *drvr_library, const RiseFall *rf, - ArcDelay &load_delay, - Slew &load_slew); + double &wire_delay, + double &load_slew); // Helper function for input ports driving dspf parasitic. void dspfWireDelaySlew(const Pin *load_pin, - const RiseFall *rf, - Slew drvr_slew, + const RiseFall *rf, + double drvr_slew, float elmore, // Return values. - ArcDelay &wire_delay, - Slew &load_slew); + double &wire_delay, + double &load_slew); const Pvt *pinPvt(const Pin *pin, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); using ArcDelayCalc::reduceParasitic; }; -} // namespace +} // namespace sta diff --git a/dcalc/DelayNormal.cc b/dcalc/DelayNormal.cc new file mode 100644 index 000000000..de26bfd96 --- /dev/null +++ b/dcalc/DelayNormal.cc @@ -0,0 +1,232 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#include "DelayNormal.hh" + +#include // sqrt + +#include "Error.hh" +#include "Format.hh" +#include "Fuzzy.hh" +#include "StaState.hh" +#include "Units.hh" +#include "Variables.hh" + +namespace sta { + +float +DelayOpsNormal::stdDev2(const Delay &delay, + const EarlyLate *) const +{ + return delay.stdDev2(); +} + +float +DelayOpsNormal::asFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) const +{ + float quantile = sta->variables()->pocvQuantile(); + if (early_late == EarlyLate::early()) + return delay.mean() - delay.stdDev() * quantile; + else // (early_late == EarlyLate::late()) + return delay.mean() + delay.stdDev() * quantile; +} + +double +DelayOpsNormal::asFloat(const DelayDbl &delay, + const EarlyLate *early_late, + const StaState *sta) const +{ + double quantile = sta->variables()->pocvQuantile(); + if (early_late == EarlyLate::early()) + return delay.mean() - delay.stdDev() * quantile; + else // (early_late == EarlyLate::late()) + return delay.mean() + delay.stdDev() * quantile; +} + +bool +DelayOpsNormal::isZero(const Delay &delay) const +{ + return fuzzyZero(delay.mean()) + && fuzzyZero(delay.stdDev2()); +} + +bool +DelayOpsNormal::isInf(const Delay &delay) const +{ + return fuzzyInf(delay.mean()); +} + +bool +DelayOpsNormal::equal(const Delay &delay1, + const Delay &delay2, + const StaState *) const +{ + return fuzzyEqual(delay1.mean(), delay2.mean()) + && fuzzyEqual(delay1.stdDev2(), delay2.stdDev2()); +} + +bool +DelayOpsNormal::less(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const +{ + return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); +} + +bool +DelayOpsNormal::less(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *sta) const +{ + return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); +} + +bool +DelayOpsNormal::lessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const +{ + return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); +} + +bool +DelayOpsNormal::greater(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const +{ + return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), + delayAsFloat(delay2, EarlyLate::late(), sta)); +} + +bool +DelayOpsNormal::greaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const +{ + return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), + delayAsFloat(delay2, EarlyLate::late(), sta)); +} + +Delay +DelayOpsNormal::sum(const Delay &delay1, + const Delay &delay2) const +{ + return Delay(delay1.mean() + delay2.mean(), + delay1.stdDev2() + delay2.stdDev2()); +} + +Delay +DelayOpsNormal::sum(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() + delay2, + delay1.stdDev2()); +} + +Delay +DelayOpsNormal::diff(const Delay &delay1, + const Delay &delay2) const +{ + return Delay(delay1.mean() - delay2.mean(), + delay1.stdDev2() + delay2.stdDev2()); +} + +Delay +DelayOpsNormal::diff(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() - delay2, + delay1.stdDev2()); +} + +Delay +DelayOpsNormal::diff(float delay1, + const Delay &delay2) const +{ + return Delay(delay1 - delay2.mean(), + delay2.stdDev2()); +} + +void +DelayOpsNormal::incr(Delay &delay1, + const Delay &delay2) const +{ + delay1.setValues(delay1.mean() + delay2.mean(), 0.0, + delay1.stdDev2() + delay2.stdDev2(), 0.0); +} + +void +DelayOpsNormal::incr(DelayDbl &delay1, + const Delay &delay2) const +{ + delay1.setValues(delay1.mean() + delay2.mean(), 0.0, + delay1.stdDev2() + delay2.stdDev2(), 0.0); +} + +void +DelayOpsNormal::decr(Delay &delay1, + const Delay &delay2) const +{ + delay1.setMean(delay1.mean() - delay2.mean()); +} + +void +DelayOpsNormal::decr(DelayDbl &delay1, + const Delay &delay2) const +{ + delay1.setMean(delay1.mean() - delay2.mean()); +} + +Delay +DelayOpsNormal::product(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() * delay2, + delay1.stdDev2() * square(delay2)); +} + +Delay +DelayOpsNormal::div(float delay1, + const Delay &delay2) const +{ + return Delay(delay1 / delay2.mean()); +} + +std::string +DelayOpsNormal::asStringVariance(const Delay &delay, + int digits, + const StaState *sta) const +{ + const Unit *unit = sta->units()->timeUnit(); + return sta::format("{}[{}]", + unit->asString(delay.mean(), digits), + unit->asString(delay.stdDev(), digits)); +} + +} // namespace sta diff --git a/dcalc/DelayScalar.cc b/dcalc/DelayScalar.cc new file mode 100644 index 000000000..0ec8543ee --- /dev/null +++ b/dcalc/DelayScalar.cc @@ -0,0 +1,206 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +// Delay as floats, non-SSTA. + +#include "DelayScalar.hh" + +#include "Fuzzy.hh" +#include "StaState.hh" +#include "Units.hh" + +namespace sta { + +float +DelayOpsScalar::stdDev2(const Delay &, + const EarlyLate *) const +{ + return 0.0; +} + +float +DelayOpsScalar::asFloat(const Delay &delay, + const EarlyLate *, + const StaState *) const +{ + return delay.mean(); +} + +double +DelayOpsScalar::asFloat(const DelayDbl &delay, + const EarlyLate *, + const StaState *) const +{ + return delay.mean(); +} + +bool +DelayOpsScalar::isZero(const Delay &delay) const +{ + return fuzzyZero(delay.mean()); +} + +bool +DelayOpsScalar::isInf(const Delay &delay) const +{ + return fuzzyInf(delay.mean()); +} + +bool +DelayOpsScalar::equal(const Delay &delay1, + const Delay &delay2, + const StaState *) const +{ + return fuzzyEqual(delay1.mean(), delay2.mean()); +} + +bool +DelayOpsScalar::less(const Delay &delay1, + const Delay &delay2, + const StaState *) const +{ + return fuzzyLess(delay1.mean(), delay2.mean()); +} + +bool +DelayOpsScalar::less(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *) const +{ + return fuzzyLess(delay1.mean(), delay2.mean()); +} + +bool +DelayOpsScalar::lessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *) const +{ + return fuzzyLessEqual(delay1.mean(), delay2.mean()); +} + +bool +DelayOpsScalar::greater(const Delay &delay1, + const Delay &delay2, + const StaState *) const +{ + return fuzzyGreater(delay1.mean(), delay2.mean()); +} + +bool +DelayOpsScalar::greaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *) const +{ + return fuzzyGreaterEqual(delay1.mean(), delay2.mean()); +} + +Delay +DelayOpsScalar::sum(const Delay &delay1, + const Delay &delay2) const +{ + return Delay(delay1.mean() + delay2.mean()); +} + +Delay +DelayOpsScalar::sum(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() + delay2); +} + +Delay +DelayOpsScalar::diff(const Delay &delay1, + const Delay &delay2) const +{ + return Delay(delay1.mean() - delay2.mean()); +} + +Delay +DelayOpsScalar::diff(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() - delay2); +} + +Delay +DelayOpsScalar::diff(float delay1, + const Delay &delay2) const +{ + return Delay(delay1 - delay2.mean()); +} + + +void +DelayOpsScalar::incr(Delay &delay1, + const Delay &delay2) const +{ + delay1.setMean(delay1.mean() + delay2.mean()); +} + +void +DelayOpsScalar::incr(DelayDbl &delay1, + const Delay &delay2) const +{ + delay1.setMean(delay1.mean() + delay2.mean()); +} + +void +DelayOpsScalar::decr(Delay &delay1, + const Delay &delay2) const +{ + delay1.setMean(delay1.mean() - delay2.mean()); +} + +void +DelayOpsScalar::decr(DelayDbl &delay1, + const Delay &delay2) const +{ + delay1.setMean(delay1.mean() - delay2.mean()); +} + +Delay +DelayOpsScalar::product(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() * delay2); +} + +Delay +DelayOpsScalar::div(float delay1, + const Delay &delay2) const +{ + return Delay(delay1 / delay2.mean()); +} + +std::string +DelayOpsScalar::asStringVariance(const Delay &delay, + int digits, + const StaState *sta) const +{ + const Unit *unit = sta->units()->timeUnit(); + return unit->asString(delay.mean(), digits); +} + +} // namespace sta + diff --git a/dcalc/DelaySkewNormal.cc b/dcalc/DelaySkewNormal.cc new file mode 100644 index 000000000..7e492a036 --- /dev/null +++ b/dcalc/DelaySkewNormal.cc @@ -0,0 +1,293 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#include "DelaySkewNormal.hh" + +#include // sqrt + +#include "Error.hh" +#include "Format.hh" +#include "Fuzzy.hh" +#include "StaState.hh" +#include "Units.hh" +#include "Variables.hh" + +namespace sta { + +float +DelayOpsSkewNormal::stdDev2(const Delay &delay, + const EarlyLate *) const +{ + return delay.stdDev2(); +} + +float +DelayOpsSkewNormal::asFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) const +{ + // LVF: mean + mean_shift + sigma * sigma_factor with skewness consideration. + float quantile = sta->variables()->pocvQuantile(); + if (early_late == EarlyLate::early()) + return delay.mean() + delay.meanShift() + - delay.stdDev() * (quantile + delay.skewness() * (square(quantile)-1.0) / 6.0); + else // (early_late == EarlyLate::late()) + return delay.mean() + delay.meanShift() + + delay.stdDev() * (quantile + delay.skewness() * (square(quantile)-1.0) / 6.0); +} + +double +DelayOpsSkewNormal::asFloat(const DelayDbl &delay, + const EarlyLate *early_late, + const StaState *sta) const +{ + // LVF: mean + mean_shift + sigma * sigma_factor with skewness consideration. + double quantile = sta->variables()->pocvQuantile(); + if (early_late == EarlyLate::early()) + return delay.mean() + delay.meanShift() + - delay.stdDev() * (quantile + delay.skewness() * (square(quantile)-1.0) / 6.0); + else // (early_late == EarlyLate::late()) + return delay.mean() + delay.meanShift() + + delay.stdDev() * (quantile + delay.skewness() * (square(quantile)-1.0) / 6.0); +} + +bool +DelayOpsSkewNormal::isZero(const Delay &delay) const +{ + return fuzzyZero(delay.mean()) + && fuzzyZero(delay.meanShift()) + && fuzzyZero(delay.stdDev2()) + && fuzzyZero(delay.skewness()); +} + +bool +DelayOpsSkewNormal::isInf(const Delay &delay) const +{ + return fuzzyInf(delay.mean()); +} + +bool +DelayOpsSkewNormal::equal(const Delay &delay1, + const Delay &delay2, + const StaState *) const +{ + return fuzzyEqual(delay1.mean(), delay2.mean()) + && fuzzyEqual(delay1.meanShift(), delay2.meanShift()) + && fuzzyEqual(delay1.stdDev2(), delay2.stdDev2()) + && fuzzyEqual(delay1.skewness(), delay2.skewness()); +} + +bool +DelayOpsSkewNormal::less(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const +{ + return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); +} + +bool +DelayOpsSkewNormal::less(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *sta) const +{ + return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); +} + +bool +DelayOpsSkewNormal::lessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const +{ + return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); +} + +bool +DelayOpsSkewNormal::greater(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const +{ + return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), + delayAsFloat(delay2, EarlyLate::late(), sta)); +} + +bool +DelayOpsSkewNormal::greaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const +{ + return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), + delayAsFloat(delay2, EarlyLate::late(), sta)); +} + +Delay +DelayOpsSkewNormal::sum(const Delay &delay1, + const Delay &delay2) const +{ + return Delay(delay1.mean() + delay2.mean(), + delay1.meanShift() + delay2.meanShift(), + delay1.stdDev2() + delay2.stdDev2(), + skewnessSum(delay1, delay2)); +} + +float +DelayOpsSkewNormal::skewnessSum(const Delay &delay1, + const Delay &delay2) const +{ + return skewnessSum(delay1.stdDev(), delay1.skewness(), + delay2.stdDev(), delay2.skewness()); +} + +// Helper function to compute combined skewness from std dev and skewness values. +double +DelayOpsSkewNormal::skewnessSum(double std_dev1, + double skewness1, + double std_dev2, + double skewness2) const +{ + double std_dev_sum = square(std_dev1) + square(std_dev2); + if (std_dev_sum == 0.0) + return 0.0; + else { + // Un-normalize the skews so they are third moments so they can be added. + double skew = (skewness1 * cube(std_dev1) + skewness2 * cube(std_dev2)) + // std_dev_sum^(3/2) + / (std_dev_sum * std::sqrt(std_dev_sum)); + return skew; + } +} + +Delay +DelayOpsSkewNormal::sum(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() + delay2, + delay1.meanShift(), + delay1.stdDev2(), + delay1.skewness()); +} + +Delay +DelayOpsSkewNormal::diff(const Delay &delay1, + const Delay &delay2) const +{ + return Delay(delay1.mean() - delay2.mean(), + delay1.meanShift() - delay2.meanShift(), + delay1.stdDev2() + delay2.stdDev2(), + skewnessSum(delay1, delay2)); +} + +Delay +DelayOpsSkewNormal::diff(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() - delay2, + delay1.meanShift(), + delay1.stdDev2(), + delay1.skewness()); +} + +Delay +DelayOpsSkewNormal::diff(float delay1, + const Delay &delay2) const +{ + return Delay(delay1 - delay2.mean(), + delay2.meanShift(), + delay2.stdDev2(), + delay2.skewness()); +} + +void +DelayOpsSkewNormal::incr(Delay &delay1, + const Delay &delay2) const +{ + delay1.setValues(delay1.mean() + delay2.mean(), + delay1.meanShift() + delay2.meanShift(), + delay1.stdDev2() + delay2.stdDev2(), + skewnessSum(delay1, delay2)); +} + +void +DelayOpsSkewNormal::incr(DelayDbl &delay1, + const Delay &delay2) const +{ + delay1.setValues(delay1.mean() + delay2.mean(), + delay1.meanShift() + delay2.meanShift(), + delay1.stdDev2() + delay2.stdDev2(), + skewnessSum(delay1.stdDev(), delay1.skewness())); +} + +void +DelayOpsSkewNormal::decr(Delay &delay1, + const Delay &delay2) const +{ + delay1.setValues(delay1.mean() - delay2.mean(), + delay1.meanShift() + delay2.meanShift(), + delay1.stdDev2() + delay2.stdDev2(), + skewnessSum(delay1, delay2)); +} + +void +DelayOpsSkewNormal::decr(DelayDbl &delay1, + const Delay &delay2) const +{ + delay1.setValues(delay1.mean() - delay2.mean(), + delay1.meanShift() + delay2.meanShift(), + delay1.stdDev2() + delay2.stdDev2(), + skewnessSum(delay1.stdDev(), delay1.skewness())); +} + +Delay +DelayOpsSkewNormal::product(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() * delay2, + delay1.meanShift() * delay2, + delay1.stdDev2() * square(delay2), + delay1.skewness() * cube(delay2)); +} + +Delay +DelayOpsSkewNormal::div(float delay1, + const Delay &delay2) const +{ + return Delay(delay1 / delay2.mean()); +} + +std::string +DelayOpsSkewNormal::asStringVariance(const Delay &delay, + int digits, + const StaState *sta) const +{ + const Unit *unit = sta->units()->timeUnit(); + return sta::format("{}[{},{},{}]", + unit->asString(delay.mean(), digits), + unit->asString(delay.meanShift(), digits), + unit->asString(delay.stdDev(), digits), + sta->units()->scalarUnit()->asString(delay.skewness(), digits)); +} + +} // namespace sta diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 4d2301c3c..6e05f3007 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. // "Performance Computation for Precharacterized CMOS Gates with RC Loads", @@ -32,44 +32,31 @@ #include "DmpCeff.hh" -#include // abs, min -#include // sqrt, log +#include +#include +#include +#include +#include +#include +#include -#include "Report.hh" +#include "ArcDelayCalc.hh" #include "Debug.hh" -#include "Units.hh" -#include "TimingArc.hh" -#include "TableModel.hh" +#include "FindRoot.hh" +#include "Format.hh" #include "Liberty.hh" #include "Network.hh" +#include "Parasitics.hh" +#include "Report.hh" #include "Sdc.hh" #include "Sta.hh" -#include "Parasitics.hh" -#include "DcalcAnalysisPt.hh" -#include "ArcDelayCalc.hh" -#include "FindRoot.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "Units.hh" #include "Variables.hh" namespace sta { -using std::string; -using std::abs; -using std::min; -using std::max; -using std::sqrt; -using std::log; -using std::isnan; -using std::function; - -// Tolerance (as a scale of value) for driver parameters (Ceff, delta t, t0). -static const double driver_param_tol = .01; -// Waveform threshold crossing time tolerance (1.0 = 100%). -static const double vth_time_tol = .01; -// A small number used by luDecomp. -static const double tiny_double = 1.0e-20; -// Max iterations for findRoot. -static const int find_root_max_iter = 20; - // Indices of Newton-Raphson parameter vector. enum DmpParam { t0, dt, ceff }; @@ -86,47 +73,21 @@ exp2(double x); class DmpError : public Exception { public: - DmpError(const char *what); - virtual ~DmpError() {} - virtual const char *what() const noexcept { return what_; } + DmpError(std::string_view what); + const char *what() const noexcept override { return what_.c_str(); } private: - const char *what_; + std::string what_; }; static double gateModelRd(const LibertyCell *cell, - const GateTableModel *gate_model, - const RiseFall *rf, - double in_slew, - double c2, - double c1, - const Pvt *pvt, - bool pocv_enabled); -static void -newtonRaphson(const int max_iter, - double x[], - const int n, - const double x_tol, - // eval(state) is called to fill fvec and fjac. - function eval, - // Temporaries supplied by caller. - double *fvec, - double **fjac, - int *index, - double *p, - double *scale); -static void -luSolve(double **a, - const int size, - const int *index, - double b[]); -static void -luDecomp(double **a, - const int size, - int *index, - double *scale); - + const GateTableModel *gate_model, + const RiseFall *rf, + double in_slew, + double c2, + double c1, + const Pvt *pvt); //////////////////////////////////////////////////////////////// // Base class for Dartu/Menezes/Pileggi algorithm. @@ -134,105 +95,81 @@ luDecomp(double **a, class DmpAlg : public StaState { public: - DmpAlg(int nr_order, StaState *sta); - virtual ~DmpAlg(); - virtual const char *name() = 0; + DmpAlg(int nr_order, + StaState *sta); + ~DmpAlg() override = default; + virtual std::string_view name() = 0; // Set driver model and pi model parameters for delay calculation. virtual void init(const LibertyLibrary *library, - const LibertyCell *drvr_cell, - const Pvt *pvt, - const GateTableModel *gate_model, - const RiseFall *rf, - double rd, - double in_slew, - double c2, - double rpi, - double c1); - virtual void gateDelaySlew(// Return values. - double &delay, - double &slew) = 0; - virtual void loadDelaySlew(const Pin *load_pin, - double elmore, - // Return values. - ArcDelay &delay, - Slew &slew); + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const RiseFall *rf, + double rd, + double in_slew, + double c2, + double rpi, + double c1); + virtual std::pair gateDelaySlew() = 0; + virtual std::pair loadDelaySlew(const Pin *load_pin, + double elmore); double ceff() { return ceff_; } // Given x_ as a vector of input parameters, fill fvec_ with the // equations evaluated at x_ and fjac_ with the jabobian evaluated at x_. virtual void evalDmpEqns() = 0; - // Output response to vs(t) ramp driving pi model load. - void Vo(double t, - // Return values. - double &vo, - double &dol_dt); - // Load responce to driver waveform. - void Vl(double t, - // Return values. - double &vl, - double &dvl_dt); + // Output response to vs(t) ramp driving pi model load (vo, dvo_dt). + std::pair Vo(double t); + // Load response to driver waveform (vl, dvl/dt). + std::pair Vl(double t); protected: + void luDecomp(); + void luSolve(); + void newtonRaphson(); // Find driver parameters t0, delta_t, Ceff. void findDriverParams(double ceff); - void gateCapDelaySlew(double cl, - // Return values. - double &delay, - double &slew); - void gateDelays(double ceff, - // Return values. - double &t_vth, - double &t_vl, - double &slew); - // Partial derivatives of y(t) (jacobian). - void dy(double t, - double t0, - double dt, - double cl, - // Return values. - double &dydt0, - double &dyddt, - double &dydcl); + std::pair gateCapDelaySlew(double ceff); + std::tuple gateDelays(double ceff); + // Partial derivatives of y(t) jacobian (dydt0, dyddt, dydcl). + std::tuple dy(double t, + double t0, + double dt, + double cl); double y0dt(double t, - double cl); + double cl); double y0dcl(double t, - double cl); + double cl); void showX(); void showFvec(); void showJacobian(); - void findDriverDelaySlew(// Return values. - double &delay, - double &slew); + std::pair findDriverDelaySlew(); double findVoCrossing(double vth, - double lower_bound, - double upper_bound); + double t_lower, + double t_upper); void showVo(); double findVlCrossing(double vth, - double lower_bound, - double upper_bound); + double t_lower, + double t_upper); void showVl(); - void fail(const char *reason); + void fail(std::string_view reason); - // Output response to vs(t) ramp driving capacitive load. - double y(double t, - double t0, - double dt, - double cl); + // Output response to vs(t) ramp driving capacitive load (y, t1). + std::pair y(double t, + double t0, + double dt, + double cl); // Output response to unit ramp driving capacitive load. double y0(double t, - double cl); + double cl); // Output response to unit ramp driving pi model load. - virtual void V0(double t, - // Return values. - double &vo, - double &dvo_dt) = 0; + // Unit ramp output at pi load (vo, dvo_dt). + virtual std::pair V0(double t) = 0; // Upper bound on time that vo crosses vh. virtual double voCrossingUpperBound() = 0; // Load responce to driver unit ramp. - virtual void Vl0(double t, - // Return values. - double &vl, - double &dvl_dt) = 0; + // Unit ramp load response (vl, dvl_dt). + virtual std::pair Vl0(double t) = 0; // Upper bound on time that vl crosses vh. double vlCrossingUpperBound(); @@ -242,9 +179,9 @@ class DmpAlg : public StaState const Pvt *pvt_; const GateTableModel *gate_model_; double in_slew_; - double c2_; - double rpi_; - double c1_; + double c2_{0.0}; + double rpi_{0.0}; + double c1_{0.0}; double rd_; // Logic threshold (percentage of supply voltage). @@ -267,13 +204,12 @@ class DmpAlg : public StaState static constexpr int max_nr_order_ = 3; - double x_[max_nr_order_]; - double fvec_[max_nr_order_]; - double fjac_storage_[max_nr_order_ * max_nr_order_]; - double *fjac_[max_nr_order_]; - double scale_[max_nr_order_]; - double p_[max_nr_order_ ]; - int index_[max_nr_order_]; + std::array x_; + std::array fvec_; + std::array, max_nr_order_> fjac_; + std::array scale_; + std::array p_; + std::array index_; // Driver slew used to check load delay. double drvr_slew_; @@ -283,35 +219,37 @@ class DmpAlg : public StaState // Load rspf elmore delay. double elmore_; double p3_; + + // Tolerance (as a scale of value) for driver parameters (Ceff, delta t, t0). + static constexpr double driver_param_tol_ = .01; + // Waveform threshold crossing time tolerance (1.0 = 100%). + static constexpr double vth_time_tol_ = .01; + // Max iterations for findRoot. + static constexpr int find_root_max_iter_ = 20; + static inline int newton_raphson_max_iter_ = 100; + // A small number used by luDecomp. + static constexpr double tiny_double_ = 1.0e-20; }; DmpAlg::DmpAlg(int nr_order, - StaState *sta): + StaState *sta) : StaState(sta), - c2_(0.0), - rpi_(0.0), - c1_(0.0), nr_order_(nr_order) { - for (int i = 0; i < nr_order_; i++) - // Only use the upper left block of the matrix - fjac_[i] = fjac_storage_ + i * max_nr_order_; } -DmpAlg::~DmpAlg() = default; - void DmpAlg::init(const LibertyLibrary *drvr_library, - const LibertyCell *drvr_cell, - const Pvt *pvt, - const GateTableModel *gate_model, - const RiseFall *rf, - double rd, - double in_slew, - // Pi model. - double c2, - double rpi, - double c1) + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const RiseFall *rf, + double rd, + double in_slew, + // Pi model. + double c2, + double rpi, + double c1) { drvr_library_ = drvr_library; drvr_cell_ = drvr_cell; @@ -335,113 +273,103 @@ DmpAlg::findDriverParams(double ceff) { if (nr_order_ == 3) x_[DmpParam::ceff] = ceff; - double t_vth, t_vl, slew; - gateDelays(ceff, t_vth, t_vl, slew); + auto [t_vth, t_vl, slew] = gateDelays(ceff); // Scale slew to 0-100% double dt = slew / (vh_ - vl_); - double t0 = t_vth + log(1.0 - vth_) * rd_ * ceff - vth_ * dt; + double t0 = t_vth + std::log(1.0 - vth_) * rd_ * ceff - vth_ * dt; x_[DmpParam::dt] = dt; x_[DmpParam::t0] = t0; - newtonRaphson(100, x_, nr_order_, driver_param_tol, - [this] () { evalDmpEqns(); }, - fvec_, fjac_, index_, p_, scale_); + newtonRaphson(); t0_ = x_[DmpParam::t0]; dt_ = x_[DmpParam::dt]; - debugPrint(debug_, "dmp_ceff", 3, " t0 = %s dt = %s ceff = %s", - units_->timeUnit()->asString(t0_), - units_->timeUnit()->asString(dt_), + debugPrint(debug_, "dmp_ceff", 3, " t0 = {} dt = {} ceff = {}", + units_->timeUnit()->asString(t0_), units_->timeUnit()->asString(dt_), units_->capacitanceUnit()->asString(x_[DmpParam::ceff])); if (debug_->check("dmp_ceff", 4)) showVo(); } -void -DmpAlg::gateCapDelaySlew(double ceff, - // Return values. - double &delay, - double &slew) -{ - ArcDelay model_delay; - Slew model_slew; - gate_model_->gateDelay(pvt_, in_slew_, ceff, - variables_->pocvEnabled(), - model_delay, model_slew); - delay = delayAsFloat(model_delay); - slew = delayAsFloat(model_slew); +std::pair +DmpAlg::gateCapDelaySlew(double ceff) +{ + float model_delay, model_slew; + gate_model_->gateDelay(pvt_, in_slew_, ceff, model_delay, model_slew); + double delay = model_delay; + double slew = model_slew; + return {delay, slew}; } -void -DmpAlg::gateDelays(double ceff, - // Return values. - double &t_vth, - double &t_vl, - double &slew) -{ - double table_slew; - gateCapDelaySlew(ceff, t_vth, table_slew); +std::tuple +DmpAlg::gateDelays(double ceff) +{ + auto [t_vth, table_slew] = gateCapDelaySlew(ceff); // Convert reported/table slew to measured slew. - slew = table_slew * slew_derate_; - t_vl = t_vth - slew * (vth_ - vl_) / (vh_ - vl_); + double slew = table_slew * slew_derate_; + double t_vl = t_vth - slew * (vth_ - vl_) / (vh_ - vl_); + return {t_vth, t_vl, slew}; } -double +std::pair DmpAlg::y(double t, - double t0, - double dt, - double cl) + double t0, + double dt, + double cl) { double t1 = t - t0; - if (t1 <= 0.0) - return 0.0; - else if (t1 <= dt) - return y0(t1, cl) / dt; - else - return (y0(t1, cl) - y0(t1 - dt, cl)) / dt; + if (t1 <= 0.0) { + double y = 0.0; + return {y, t1}; + } + if (t1 <= dt) { + double y = y0(t1, cl) / dt; + return {y, t1}; + } + double y = (y0(t1, cl) - y0(t1 - dt, cl)) / dt; + return {y, t1}; } double DmpAlg::y0(double t, - double cl) + double cl) { return t - rd_ * cl * (1.0 - exp2(-t / (rd_ * cl))); } -void +std::tuple DmpAlg::dy(double t, - double t0, - double dt, - double cl, - // Return values. - double &dydt0, - double &dyddt, - double &dydcl) + double t0, + double dt, + double cl) { double t1 = t - t0; - if (t1 <= 0.0) - dydt0 = dyddt = dydcl = 0.0; - else if (t1 <= dt) { - dydt0 = -y0dt(t1, cl) / dt; - dyddt = -y0(t1, cl) / (dt * dt); - dydcl = y0dcl(t1, cl) / dt; + if (t1 <= 0.0) { + double dydt0 = 0.0; + double dyddt = 0.0; + double dydcl = 0.0; + return {dydt0, dyddt, dydcl}; } - else { - dydt0 = -(y0dt(t1, cl) - y0dt(t1 - dt, cl)) / dt; - dyddt = -(y0(t1, cl) + y0(t1 - dt, cl)) / (dt * dt) - + y0dt(t1 - dt, cl) / dt; - dydcl = (y0dcl(t1, cl) - y0dcl(t1 - dt, cl)) / dt; + if (t1 <= dt) { + double dydt0 = -y0dt(t1, cl) / dt; + double dyddt = -y0(t1, cl) / (dt * dt); + double dydcl = y0dcl(t1, cl) / dt; + return {dydt0, dyddt, dydcl}; } + double dydt0 = -(y0dt(t1, cl) - y0dt(t1 - dt, cl)) / dt; + double dyddt = -(y0(t1, cl) + y0(t1 - dt, cl)) / (dt * dt) + y0dt(t1 - dt, cl) / dt; + double dydcl = (y0dcl(t1, cl) - y0dcl(t1 - dt, cl)) / dt; + return {dydt0, dyddt, dydcl}; } double DmpAlg::y0dt(double t, - double cl) + double cl) { return 1.0 - exp2(-t / (rd_ * cl)); } double DmpAlg::y0dcl(double t, - double cl) + double cl) { return rd_ * ((1.0 + t / (rd_ * cl)) * exp2(-t / (rd_ * cl)) - 1); } @@ -450,43 +378,42 @@ void DmpAlg::showX() { for (int i = 0; i < nr_order_; i++) - report_->reportLine("%4s %12.3e", dmp_param_index_strings[i], x_[i]); + report_->report("{:4} {:12.3e}", dmp_param_index_strings[i], x_[i]); } void DmpAlg::showFvec() { for (int i = 0; i < nr_order_; i++) - report_->reportLine("%4s %12.3e", dmp_func_index_strings[i], fvec_[i]); + report_->report("{:4} {:12.3e}", dmp_func_index_strings[i], fvec_[i]); } void DmpAlg::showJacobian() { - string line = " "; + std::string line = " "; for (int j = 0; j < nr_order_; j++) - line += stdstrPrint("%12s", dmp_param_index_strings[j]); - report_->reportLineString(line); - line.clear(); + line += sta::format("{:>12}", dmp_param_index_strings[j]); + report_->reportLine(line); for (int i = 0; i < nr_order_; i++) { - line += stdstrPrint("%4s ", dmp_func_index_strings[i]); + line.clear(); + line += sta::format("{:4} ", dmp_func_index_strings[i]); for (int j = 0; j < nr_order_; j++) - line += stdstrPrint("%12.3e ", fjac_[i][j]); - report_->reportLineString(line); + line += sta::format("{:12.3e} ", fjac_[i][j]); + report_->reportLine(line); } } -void -DmpAlg::findDriverDelaySlew(// Return values. - double &delay, - double &slew) +std::pair +DmpAlg::findDriverDelaySlew() { double t_upper = voCrossingUpperBound(); - delay = findVoCrossing(vth_, t0_, t_upper); + double delay = findVoCrossing(vth_, t0_, t_upper); double tl = findVoCrossing(vl_, t0_, delay); double th = findVoCrossing(vh_, delay, t_upper); // Convert measured slew to table slew. - slew = (th - tl) / slew_derate_; + double slew = (th - tl) / slew_derate_; + return {delay, slew}; } // Find t such that vo(t)=v. @@ -495,115 +422,104 @@ DmpAlg::findVoCrossing(double vth, double t_lower, double t_upper) { - FindRootFunc vo_func = [&] (double t, - double &y, - double &dy) { - double vo, vo_dt; - Vo(t, vo, vo_dt); + FindRootFunc vo_func = [&](double t, double &y, double &dy) { + auto [vo, dvo_dt] = Vo(t); y = vo - vth; - dy = vo_dt; + dy = dvo_dt; }; - bool fail; - double t_vth = findRoot(vo_func, t_lower, t_upper, vth_time_tol, - find_root_max_iter, fail); - if (fail) + auto [t_vth, failed] = findRoot(vo_func, t_lower, t_upper, vth_time_tol_, + find_root_max_iter_); + if (failed) throw DmpError("find Vo crossing failed"); return t_vth; } -void -DmpAlg::Vo(double t, - // Return values. - double &vo, - double &dvo_dt) +std::pair +DmpAlg::Vo(double t) { double t1 = t - t0_; if (t1 <= 0.0) { - vo = 0.0; - dvo_dt = 0.0; + double vo = 0.0; + double dvo_dt = 0.0; + return {vo, dvo_dt}; } - else if (t1 <= dt_) { - double v0, dv0_dt; - V0(t1, v0, dv0_dt); + if (t1 <= dt_) { + auto [v0, dv0_dt] = V0(t1); - vo = v0 / dt_; - dvo_dt = dv0_dt / dt_; + double vo = v0 / dt_; + double dvo_dt = dv0_dt / dt_; + return {vo, dvo_dt}; } - else { - double v0, dv0_dt; - V0(t1, v0, dv0_dt); + auto [v0, dv0_dt] = V0(t1); - double v0_dt, dv0_dt_dt; - V0(t1 - dt_, v0_dt, dv0_dt_dt); + auto [v0_dt, dv0_dt_dt] = V0(t1 - dt_); - vo = (v0 - v0_dt) / dt_; - dvo_dt = (dv0_dt - dv0_dt_dt) / dt_; - } + double vo = (v0 - v0_dt) / dt_; + double dvo_dt = (dv0_dt - dv0_dt_dt) / dt_; + return {vo, dvo_dt}; } void DmpAlg::showVo() { - report_->reportLine(" t vo(t)"); + report_->report(" t vo(t)"); double ub = voCrossingUpperBound(); - for (double t = t0_; t < t0_ + ub; t += dt_ / 10.0) { - double vo, dvo_dt; - Vo(t, vo, dvo_dt); - report_->reportLine(" %g %g", t, vo); + const double step = dt_ / 10.0; + for (int i = 0;; ++i) { + double t = t0_ + step * i; + if (!(t < t0_ + ub)) + break; + report_->report(" {:g} {:g}", t, Vo(t).first); } } -void +std::pair DmpAlg::loadDelaySlew(const Pin *, - double elmore, - ArcDelay &delay, - Slew &slew) + double elmore) { if (!driver_valid_ || elmore == 0.0 // Elmore delay is small compared to driver slew. || elmore < drvr_slew_ * 1e-3) { - delay = elmore; - slew = drvr_slew_; + double delay = elmore; + double slew = drvr_slew_; + return {delay, slew}; } - else { - // Use the driver thresholds and rely on thresholdAdjust to - // convert the delay and slew to the load's thresholds. - try { - if (debug_->check("dmp_ceff", 4)) - showVl(); - elmore_ = elmore; - p3_ = 1.0 / elmore; - double t_lower = t0_; - double t_upper = vlCrossingUpperBound(); - double load_delay = findVlCrossing(vth_, t_lower, t_upper); - double tl = findVlCrossing(vl_, t_lower, load_delay); - double th = findVlCrossing(vh_, load_delay, t_upper); - // Measure delay from Vo, the load dependent source excitation. - double delay1 = load_delay - vo_delay_; - // Convert measured slew to reported/table slew. - double slew1 = (th - tl) / slew_derate_; - if (delay1 < 0.0) { - // Only report a problem if the difference is significant. - if (-delay1 > vth_time_tol * vo_delay_) - fail("load delay less than zero"); - // Use elmore delay. - delay1 = elmore; - } - if (slew1 < drvr_slew_) { - // Only report a problem if the difference is significant. - if ((drvr_slew_ - slew1) > vth_time_tol * drvr_slew_) - fail("load slew less than driver slew"); - slew1 = drvr_slew_; - } - delay = delay1; - slew = slew1; + // Use the driver thresholds and rely on thresholdAdjust to + // convert the delay and slew to the load's thresholds. + try { + elmore_ = elmore; + p3_ = 1.0 / elmore; + if (debug_->check("dmp_ceff", 4)) + showVl(); + double t_lower = t0_; + double t_upper = vlCrossingUpperBound(); + double load_delay = findVlCrossing(vth_, t_lower, t_upper); + double tl = findVlCrossing(vl_, t_lower, load_delay); + double th = findVlCrossing(vh_, load_delay, t_upper); + // Measure delay from Vo, the load dependent source excitation. + double delay = load_delay - vo_delay_; + // Convert measured slew to reported/table slew. + double slew = (th - tl) / slew_derate_; + if (delay < 0.0) { + // Only report a problem if the difference is significant. + if (-delay > vth_time_tol_ * vo_delay_) + fail("load delay less than zero"); + // Use elmore delay. + delay = elmore; } - catch (DmpError &error) { - fail(error.what()); - delay = elmore_; + if (slew < drvr_slew_) { + // Only report a problem if the difference is significant. + if ((drvr_slew_ - slew) > vth_time_tol_ * drvr_slew_) + fail("load slew less than driver slew"); slew = drvr_slew_; } + return {delay, slew}; + } catch (DmpError &error) { + fail(error.what()); + double delay = elmore_; + double slew = drvr_slew_; + return {delay, slew}; } } @@ -613,18 +529,14 @@ DmpAlg::findVlCrossing(double vth, double t_lower, double t_upper) { - FindRootFunc vl_func = [&] (double t, - double &y, - double &dy) { - double vl, vl_dt; - Vl(t, vl, vl_dt); + FindRootFunc vl_func = [&](double t, double &y, double &dy) { + auto [vl, vl_dt] = Vl(t); y = vl - vth; dy = vl_dt; }; - bool fail; - double t_vth = findRoot(vl_func, t_lower, t_upper, vth_time_tol, - find_root_max_iter, fail); - if (fail) + auto [t_vth, failed] = findRoot(vl_func, t_lower, t_upper, vth_time_tol_, + find_root_max_iter_); + if (failed) throw DmpError("find Vl crossing failed"); return t_vth; } @@ -635,58 +547,50 @@ DmpAlg::vlCrossingUpperBound() return voCrossingUpperBound() + elmore_ * 2.0; } -void -DmpAlg::Vl(double t, - // Return values. - double &vl, - double &dvl_dt) +std::pair +DmpAlg::Vl(double t) { double t1 = t - t0_; - if (t1 <= 0.0) { - vl = 0.0; - dvl_dt = 0.0; - } - else if (t1 <= dt_) { - double vl0, dvl0_dt; - Vl0(t1, vl0, dvl0_dt); - vl = vl0 / dt_; - dvl_dt = dvl0_dt / dt_; + if (t1 <= 0.0) + return {0.0, 0.0}; + if (t1 <= dt_) { + auto [vl0, dvl0_dt] = Vl0(t1); + return {vl0 / dt_, dvl0_dt / dt_}; } - else { - double vl0, dvl0_dt; - Vl0(t1, vl0, dvl0_dt); + auto [vl0, dvl0_dt] = Vl0(t1); - double vl0_dt, dvl0_dt_dt; - Vl0(t1 - dt_, vl0_dt, dvl0_dt_dt); + auto [vl0_dt, dvl0_dt_dt] = Vl0(t1 - dt_); - vl = (vl0 - vl0_dt) / dt_; - dvl_dt = (dvl0_dt - dvl0_dt_dt) / dt_; - } + double vl = (vl0 - vl0_dt) / dt_; + double dvl_dt = (dvl0_dt - dvl0_dt_dt) / dt_; + return {vl, dvl_dt}; } void DmpAlg::showVl() { - report_->reportLine(" t vl(t)"); + report_->report(" t vl(t)"); double ub = vlCrossingUpperBound(); - for (double t = t0_; t < t0_ + ub * 2.0; t += ub / 10.0) { - double vl, dvl_dt; - Vl(t, vl, dvl_dt); - report_->reportLine(" %g %g", t, vl); + const double step = ub / 10.0; + const double t_end = t0_ + ub * 2.0; + for (int i = 0;; ++i) { + double t = t0_ + step * i; + if (!(t < t_end)) + break; + report_->report(" {:g} {:g}", t, Vl(t).first); } } void -DmpAlg::fail(const char *reason) +DmpAlg::fail(std::string_view reason) { // Report failures with a unique debug flag. if (debug_->check("dmp_ceff", 1) || debug_->check("dcalc_error", 1)) - report_->reportLine("delay_calc: DMP failed - %s c2=%s rpi=%s c1=%s rd=%s", - reason, - units_->capacitanceUnit()->asString(c2_), - units_->resistanceUnit()->asString(rpi_), - units_->capacitanceUnit()->asString(c1_), - units_->resistanceUnit()->asString(rd_)); + report_->report("delay_calc: DMP failed - {} c2={} rpi={} c1={} rd={}", reason, + units_->capacitanceUnit()->asString(c2_), + units_->resistanceUnit()->asString(rpi_), + units_->capacitanceUnit()->asString(c1_), + units_->resistanceUnit()->asString(rd_)); } //////////////////////////////////////////////////////////////// @@ -696,7 +600,7 @@ class DmpCap : public DmpAlg { public: DmpCap(StaState *sta); - const char *name() override { return "cap"; } + std::string_view name() override { return "cap"; } void init(const LibertyLibrary *library, const LibertyCell *drvr_cell, const Pvt *pvt, @@ -707,70 +611,58 @@ class DmpCap : public DmpAlg double c2, double rpi, double c1) override; - void gateDelaySlew(// Return values. - double &delay, - double &slew) override; - void loadDelaySlew(const Pin *, - double elmore, - // Return values. - ArcDelay &delay, - Slew &slew) override; + std::pair gateDelaySlew() override; + std::pair loadDelaySlew(const Pin *, + double elmore) override; void evalDmpEqns() override; - double voCrossingUpperBound() override; -private: - void V0(double t, - // Return values. - double &vo, - double &dvo_dt) override; - void Vl0(double t, - // Return values. - double &vl, - double &dvl_dt) override; +protected: + double voCrossingUpperBound() override; + std::pair V0(double t) override; + std::pair Vl0(double t) override; }; -DmpCap::DmpCap(StaState *sta): - DmpAlg(1, sta) +DmpCap::DmpCap(StaState *sta) : + DmpAlg(1, + sta) { } void DmpCap::init(const LibertyLibrary *drvr_library, - const LibertyCell *drvr_cell, - const Pvt *pvt, - const GateTableModel *gate_model, - const RiseFall *rf, - double rd, - double in_slew, - double c2, - double rpi, - double c1) + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const RiseFall *rf, + double rd, + double in_slew, + double c2, + double rpi, + double c1) { debugPrint(debug_, "dmp_ceff", 3, "Using DMP cap"); - DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, - rd, in_slew, c2, rpi, c1); + DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, + c2, rpi, c1); ceff_ = c1 + c2; } -void -DmpCap::gateDelaySlew(// Return values. - double &delay, - double &slew) +std::pair +DmpCap::gateDelaySlew() { - debugPrint(debug_, "dmp_ceff", 3, " ceff = %s", + debugPrint(debug_, "dmp_ceff", 3, " ceff = {}", units_->capacitanceUnit()->asString(ceff_)); - gateCapDelaySlew(ceff_, delay, slew); + auto [delay, slew] = gateCapDelaySlew(ceff_); drvr_slew_ = slew; + return {delay, slew}; } -void +std::pair DmpCap::loadDelaySlew(const Pin *, - double elmore, - ArcDelay &delay, - Slew &slew) + double elmore) { - delay = elmore; - slew = drvr_slew_; + double delay = elmore; + double slew = drvr_slew_; + return {delay, slew}; } void @@ -778,14 +670,12 @@ DmpCap::evalDmpEqns() { } -void -DmpCap::V0(double, - // Return values. - double &vo, - double &dvo_dt) +std::pair +DmpCap::V0(double) { - vo = 0.0; - dvo_dt = 0.0; + double vo = 0.0; + double dvo_dt = 0.0; + return {vo, dvo_dt}; } double @@ -794,14 +684,12 @@ DmpCap::voCrossingUpperBound() return 0.0; } -void -DmpCap::Vl0(double , - // Return values. - double &vl, - double &dvl_dt) +std::pair +DmpCap::Vl0(double) { - vl = 0.0; - dvl_dt = 0.0; + double vl = 0.0; + double dvl_dt = 0.0; + return {vl, dvl_dt}; } //////////////////////////////////////////////////////////////// @@ -811,7 +699,7 @@ class DmpPi : public DmpAlg { public: DmpPi(StaState *sta); - const char *name() override { return "Pi"; } + std::string_view name() override { return "Pi"; } void init(const LibertyLibrary *library, const LibertyCell *drvr_cell, const Pvt *pvt, @@ -822,81 +710,65 @@ class DmpPi : public DmpAlg double c2, double rpi, double c1) override; - void gateDelaySlew(// Return values. - double &delay, - double &slew) override; + std::pair gateDelaySlew() override; void evalDmpEqns() override; + +protected: double voCrossingUpperBound() override; + std::pair V0(double t) override; + std::pair Vl0(double t) override; private: void findDriverParamsPi(); double ipiIceff(double t0, - double dt, - double ceff_time, - double ceff); - void V0(double t, - // Return values. - double &vo, - double &dvo_dt) override; - void Vl0(double t, - // Return values. - double &vl, - double &dvl_dt) override; + double dt, + double ceff_time, + double ceff); // Poles/zero. - double p1_; - double p2_; - double z1_; + double p1_{0.0}; + double p2_{0.0}; + double z1_{0.0}; // Residues. - double k0_; - double k1_; - double k2_; - double k3_; - double k4_; + double k0_{0.0}; + double k1_{0.0}; + double k2_{0.0}; + double k3_{0.0}; + double k4_{0.0}; // Ipi coefficients. - double A_; - double B_; - double D_; + double A_{0.0}; + double B_{0.0}; + double D_{0.0}; }; DmpPi::DmpPi(StaState *sta) : - DmpAlg(3, sta), - p1_(0.0), - p2_(0.0), - z1_(0.0), - k0_(0.0), - k1_(0.0), - k2_(0.0), - k3_(0.0), - k4_(0.0), - A_(0.0), - B_(0.0), - D_(0.0) + DmpAlg(3, + sta) { } void DmpPi::init(const LibertyLibrary *drvr_library, - const LibertyCell *drvr_cell, - const Pvt *pvt, - const GateTableModel *gate_model, - const RiseFall *rf, - double rd, - double in_slew, - double c2, - double rpi, - double c1) + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const RiseFall *rf, + double rd, + double in_slew, + double c2, + double rpi, + double c1) { debugPrint(debug_, "dmp_ceff", 3, "Using DMP Pi"); - DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, - in_slew, c2, rpi, c1); + DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, + c2, rpi, c1); // Find poles/zeros. z1_ = 1.0 / (rpi_ * c1_); k0_ = 1.0 / (rd_ * c2_); double a = rpi_ * rd_ * c1_ * c2_; double b = rd_ * (c1_ + c2_) + rpi_ * c1_; - double sqrt_ = sqrt(b * b - 4 * a); + double sqrt_ = std::sqrt(b * b - 4 * a); p1_ = (b + sqrt_) / (2 * a); p2_ = (b - sqrt_) / (2 * a); @@ -912,41 +784,38 @@ DmpPi::init(const LibertyLibrary *drvr_library, D_ = (z_ - p2_) / (p2_ * (p2_ - p1_)); } -void -DmpPi::gateDelaySlew(// Return values. - double &delay, - double &slew) +std::pair +DmpPi::gateDelaySlew() { driver_valid_ = false; + double delay = 0.0; + double slew = 0.0; try { findDriverParamsPi(); ceff_ = x_[DmpParam::ceff]; - double table_delay, table_slew; - gateCapDelaySlew(ceff_, table_delay, table_slew); + auto [table_delay, table_slew] = gateCapDelaySlew(ceff_); delay = table_delay; - //slew = table_slew; + // slew = table_slew; try { - double vo_delay, vo_slew; - findDriverDelaySlew(vo_delay, vo_slew); + auto [vo_delay, vo_slew] = findDriverDelaySlew(); driver_valid_ = true; // Save Vo delay to measure load wire delay waveform. vo_delay_ = vo_delay; - //delay = vo_delay; + // delay = vo_delay; slew = vo_slew; - } - catch (DmpError &error) { + } catch (DmpError &error) { fail(error.what()); // Fall back to table slew. slew = table_slew; } - } - catch (DmpError &error) { + } catch (DmpError &error) { fail(error.what()); // Driver calculation failed - use Ceff=c1+c2. ceff_ = c1_ + c2_; - gateCapDelaySlew(ceff_, delay, slew); + std::tie(delay, slew) = gateCapDelaySlew(ceff_); } drvr_slew_ = slew; + return {delay, slew}; } void @@ -954,8 +823,7 @@ DmpPi::findDriverParamsPi() { try { findDriverParams(c2_ + c1_); - } - catch (DmpError &) { + } catch (DmpError &) { findDriverParams(c2_); } } @@ -974,14 +842,12 @@ DmpPi::evalDmpEqns() if (ceff > (c1_ + c2_)) throw DmpError("eqn eval failed: ceff > c2 + c1"); - double t_vth, t_vl, slew; - gateDelays(ceff, t_vth, t_vl, slew); + auto [t_vth, t_vl, slew] = gateDelays(ceff); if (slew == 0.0) throw DmpError("eqn eval failed: slew = 0"); double ceff_time = slew / (vh_ - vl_); - if (ceff_time > 1.4 * dt) - ceff_time = 1.4 * dt; + ceff_time = std::min(ceff_time, 1.4 * dt); if (dt <= 0.0) throw DmpError("eqn eval failed: dt < 0"); @@ -990,88 +856,82 @@ DmpPi::evalDmpEqns() double exp_p2_dt = exp2(-p2_ * dt); double exp_dt_rd_ceff = exp2(-dt / (rd_ * ceff)); - double y50 = y(t_vth, t0, dt, ceff); + double y50 = y(t_vth, t0, dt, ceff).first; // Match Vl. - double y20 = y(t_vl, t0, dt, ceff); + double y20 = y(t_vl, t0, dt, ceff).first; fvec_[DmpFunc::ipi] = ipiIceff(t0, dt, ceff_time, ceff); fvec_[DmpFunc::y50] = y50 - vth_; fvec_[DmpFunc::y20] = y20 - vl_; fjac_[DmpFunc::ipi][DmpParam::t0] = 0.0; fjac_[DmpFunc::ipi][DmpParam::dt] = - (-A_ * dt + B_ * dt * exp_p1_dt - (2 * B_ / p1_) * (1.0 - exp_p1_dt) - + D_ * dt * exp_p2_dt - (2 * D_ / p2_) * (1.0 - exp_p2_dt) - + rd_ * ceff * (dt + dt * exp_dt_rd_ceff - - 2 * rd_ * ceff * (1.0 - exp_dt_rd_ceff))) - / (rd_ * dt * dt * dt); + (-A_ * dt + B_ * dt * exp_p1_dt - (2 * B_ / p1_) * (1.0 - exp_p1_dt) + + D_ * dt * exp_p2_dt - (2 * D_ / p2_) * (1.0 - exp_p2_dt) + + rd_ * ceff + * (dt + dt * exp_dt_rd_ceff - 2 * rd_ * ceff * (1.0 - exp_dt_rd_ceff))) + / (rd_ * dt * dt * dt); fjac_[DmpFunc::ipi][DmpParam::ceff] = - (2 * rd_ * ceff - dt - (2 * rd_ * ceff + dt) * exp2(-dt / (rd_ * ceff))) - / (dt * dt); + (2 * rd_ * ceff - dt - (2 * rd_ * ceff + dt) * exp2(-dt / (rd_ * ceff))) + / (dt * dt); - dy(t_vl, t0, dt, ceff, - fjac_[DmpFunc::y20][DmpParam::t0], - fjac_[DmpFunc::y20][DmpParam::dt], - fjac_[DmpFunc::y20][DmpParam::ceff]); + std::tie(fjac_[DmpFunc::y20][DmpParam::t0], + fjac_[DmpFunc::y20][DmpParam::dt], + fjac_[DmpFunc::y20][DmpParam::ceff]) = dy(t_vl, t0, dt, ceff); - dy(t_vth, t0, dt, ceff, - fjac_[DmpFunc::y50][DmpParam::t0], - fjac_[DmpFunc::y50][DmpParam::dt], - fjac_[DmpFunc::y50][DmpParam::ceff]); + std::tie(fjac_[DmpFunc::y50][DmpParam::t0], + fjac_[DmpFunc::y50][DmpParam::dt], + fjac_[DmpFunc::y50][DmpParam::ceff]) = dy(t_vth, t0, dt, ceff); if (debug_->check("dmp_ceff", 4)) { showX(); showFvec(); showJacobian(); - report_->reportLine("................."); + report_->report("................."); } } // Eqn 13, Eqn 14. double -DmpPi::ipiIceff(double, double dt, - double ceff_time, - double ceff) +DmpPi::ipiIceff(double, + double dt, + double ceff_time, + double ceff) { double exp_p1_dt = exp2(-p1_ * ceff_time); double exp_p2_dt = exp2(-p2_ * ceff_time); double exp_dt_rd_ceff = exp2(-ceff_time / (rd_ * ceff)); double ipi = (A_ * ceff_time + (B_ / p1_) * (1.0 - exp_p1_dt) - + (D_ / p2_) * (1.0 - exp_p2_dt)) - / (rd_ * ceff_time * dt); - double iceff = (rd_ * ceff * ceff_time - (rd_ * ceff) * (rd_ * ceff) - * (1.0 - exp_dt_rd_ceff)) - / (rd_ * ceff_time * dt); + + (D_ / p2_) * (1.0 - exp_p2_dt)) + / (rd_ * ceff_time * dt); + double iceff = + (rd_ * ceff * ceff_time - (rd_ * ceff) * (rd_ * ceff) * (1.0 - exp_dt_rd_ceff)) + / (rd_ * ceff_time * dt); return ipi - iceff; } -void -DmpPi::V0(double t, - // Return values. - double &vo, - double &dvo_dt) +std::pair +DmpPi::V0(double t) { double exp_p1 = exp2(-p1_ * t); double exp_p2 = exp2(-p2_ * t); - vo = k0_ * (k1_ + k2_ * t + k3_ * exp_p1 + k4_ * exp_p2); - dvo_dt = k0_ * (k2_ - k3_ * p1_ * exp_p1 - k4_ * p2_ * exp_p2); + double vo = k0_ * (k1_ + k2_ * t + k3_ * exp_p1 + k4_ * exp_p2); + double dvo_dt = k0_ * (k2_ - k3_ * p1_ * exp_p1 - k4_ * p2_ * exp_p2); + return {vo, dvo_dt}; } -void -DmpPi::Vl0(double t, - // Return values. - double &vl, - double &dvl_dt) +std::pair +DmpPi::Vl0(double t) { double D1 = k0_ * (k1_ - k2_ / p3_); double D3 = -p3_ * k0_ * k3_ / (p1_ - p3_); double D4 = -p3_ * k0_ * k4_ / (p2_ - p3_); - double D5 = k0_ * (k2_ / p3_ - k1_ + p3_ * k3_ / (p1_ - p3_) - + p3_ * k4_ / (p2_ - p3_)); + double D5 = + k0_ * (k2_ / p3_ - k1_ + p3_ * k3_ / (p1_ - p3_) + p3_ * k4_ / (p2_ - p3_)); double exp_p1 = exp2(-p1_ * t); double exp_p2 = exp2(-p2_ * t); double exp_p3 = exp2(-p3_ * t); - vl = D1 + t + D3 * exp_p1 + D4 * exp_p2 + D5 * exp_p3; - dvl_dt = 1.0 - D3 * p1_ * exp_p1 - D4 * p2_ * exp_p2 - - D5 * p3_ * exp_p3; + double vl = D1 + t + D3 * exp_p1 + D4 * exp_p2 + D5 * exp_p3; + double dvl_dt = 1.0 - D3 * p1_ * exp_p1 - D4 * p2_ * exp_p2 - D5 * p3_ * exp_p3; + return {vl, dvl_dt}; } double @@ -1089,11 +949,14 @@ class DmpOnePole : public DmpAlg public: DmpOnePole(StaState *sta); void evalDmpEqns() override; + +protected: double voCrossingUpperBound() override; }; DmpOnePole::DmpOnePole(StaState *sta) : - DmpAlg(2, sta) + DmpAlg(2, + sta) { } @@ -1103,33 +966,31 @@ DmpOnePole::evalDmpEqns() double t0 = x_[DmpParam::t0]; double dt = x_[DmpParam::dt]; - double t_vth, t_vl, ignore1, ignore2; - gateDelays(ceff_, t_vth, t_vl, ignore1); + auto [t_vth, t_vl, ignore1] = gateDelays(ceff_); + double ignore2; if (dt <= 0.0) dt = x_[DmpParam::dt] = (t_vl - t_vth) / 100; - fvec_[DmpFunc::y50] = y(t_vth, t0, dt, ceff_) - vth_; - fvec_[DmpFunc::y20] = y(t_vl, t0, dt, ceff_) - vl_; + fvec_[DmpFunc::y50] = y(t_vth, t0, dt, ceff_).first - vth_; + fvec_[DmpFunc::y20] = y(t_vl, t0, dt, ceff_).first - vl_; if (debug_->check("dmp_ceff", 4)) { showX(); showFvec(); } - dy(t_vl, t0, dt, ceff_, - fjac_[DmpFunc::y20][DmpParam::t0], - fjac_[DmpFunc::y20][DmpParam::dt], - ignore2); + std::tie(fjac_[DmpFunc::y20][DmpParam::t0], + fjac_[DmpFunc::y20][DmpParam::dt], + ignore2) = dy(t_vl, t0, dt, ceff_); - dy(t_vth, t0, dt, ceff_, - fjac_[DmpFunc::y50][DmpParam::t0], - fjac_[DmpFunc::y50][DmpParam::dt], - ignore2); + std::tie(fjac_[DmpFunc::y50][DmpParam::t0], + fjac_[DmpFunc::y50][DmpParam::dt], + ignore2) = dy(t_vth, t0, dt, ceff_); if (debug_->check("dmp_ceff", 4)) { showJacobian(); - report_->reportLine("................."); + report_->report("................."); } } @@ -1146,7 +1007,7 @@ class DmpZeroC2 : public DmpOnePole { public: DmpZeroC2(StaState *sta); - const char *name() override { return "c2=0"; } + std::string_view name() override { return "c2=0"; } void init(const LibertyLibrary *drvr_library, const LibertyCell *drvr_cell, const Pvt *pvt, @@ -1157,57 +1018,44 @@ class DmpZeroC2 : public DmpOnePole double c2, double rpi, double c1) override; - void gateDelaySlew(// Return values. - double &delay, - double &slew) override; + std::pair gateDelaySlew() override; -private: - void V0(double t, - // Return values. - double &vo, - double &dvo_dt) override; - void Vl0(double t, - // Return values. - double &vl, - double &dvl_dt) override; +protected: + std::pair V0(double t) override; + std::pair Vl0(double t) override; double voCrossingUpperBound() override; +private: // Pole/zero. - double p1_; - double z1_; + double p1_{0.0}; + double z1_{0.0}; // Residues. - double k0_; - double k1_; - double k2_; - double k3_; + double k0_{0.0}; + double k1_{0.0}; + double k2_{0.0}; + double k3_{0.0}; }; DmpZeroC2::DmpZeroC2(StaState *sta) : - DmpOnePole(sta), - p1_(0.0), - z1_(0.0), - k0_(0.0), - k1_(0.0), - k2_(0.0), - k3_(0.0) + DmpOnePole(sta) { } void DmpZeroC2::init(const LibertyLibrary *drvr_library, - const LibertyCell *drvr_cell, - const Pvt *pvt, - const GateTableModel *gate_model, - const RiseFall *rf, - double rd, - double in_slew, - double c2, - double rpi, - double c1) + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const RiseFall *rf, + double rd, + double in_slew, + double c2, + double rpi, + double c1) { debugPrint(debug_, "dmp_ceff", 3, "Using DMP C2=0"); - DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, - in_slew, c2, rpi, c1); + DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, + c2, rpi, c1); ceff_ = c1; z1_ = 1.0 / (rpi_ * c1_); @@ -1219,15 +1067,15 @@ DmpZeroC2::init(const LibertyLibrary *drvr_library, k3_ = -k1_; } -void -DmpZeroC2::gateDelaySlew(// Return values. - double &delay, - double &slew) +std::pair +DmpZeroC2::gateDelaySlew() { + double delay = 0.0; + double slew = 0.0; try { findDriverParams(c1_); ceff_ = c1_; - findDriverDelaySlew(delay, slew); + std::tie(delay, slew) = findDriverDelaySlew(); driver_valid_ = true; vo_delay_ = delay; } @@ -1236,35 +1084,32 @@ DmpZeroC2::gateDelaySlew(// Return values. // Fall back to table slew. driver_valid_ = false; ceff_ = c1_; - gateCapDelaySlew(ceff_, delay, slew); + std::tie(delay, slew) = gateCapDelaySlew(ceff_); } drvr_slew_ = slew; + return {delay, slew}; } -void -DmpZeroC2::V0(double t, - // Return values. - double &vo, - double &dvo_dt) +std::pair +DmpZeroC2::V0(double t) { double exp_p1 = exp2(-p1_ * t); - vo = k0_ * (k1_ + k2_ * t + k3_ * exp_p1); - dvo_dt = k0_ * (k2_ - k3_ * p1_ * exp_p1); + double vo = k0_ * (k1_ + k2_ * t + k3_ * exp_p1); + double dvo_dt = k0_ * (k2_ - k3_ * p1_ * exp_p1); + return {vo, dvo_dt}; } -void -DmpZeroC2::Vl0(double t, - // Return values. - double &vl, - double &dvl_dt) +std::pair +DmpZeroC2::Vl0(double t) { double D1 = k0_ * (k1_ - k2_ / p3_); double D3 = -p3_ * k0_ * k3_ / (p1_ - p3_); double D5 = k0_ * (k2_ / p3_ - k1_ + p3_ * k3_ / (p1_ - p3_)); double exp_p1 = exp2(-p1_ * t); double exp_p3 = exp2(-p3_ * t); - vl = D1 + t + D3 * exp_p1 + D5 * exp_p3; - dvl_dt = 1.0 - D3 * p1_ * exp_p1 - D5 * p3_ * exp_p3; + double vl = D1 + t + D3 * exp_p1 + D5 * exp_p3; + double dvl_dt = 1.0 - D3 * p1_ * exp_p1 - D5 * p3_ * exp_p3; + return {vl, dvl_dt}; } double @@ -1276,38 +1121,29 @@ DmpZeroC2::voCrossingUpperBound() //////////////////////////////////////////////////////////////// // Newton-Raphson iteration to find zeros of a function. -// x_tol is percentage that all changes in x must be less than (1.0 = 100%). -// Eval(state) is called to fill fvec and fjac (returns false if fails). -// Return error msg on failure. -static void -newtonRaphson(const int max_iter, - double x[], - const int size, - const double x_tol, - function eval, - // Temporaries supplied by caller. - double *fvec, - double **fjac, - int *index, - double *p, - double *scale) -{ - for (int k = 0; k < max_iter; k++) { - eval(); - for (int i = 0; i < size; i++) +// driver_param_tol_ is the scale that all changes in x must be under (1.0 = 100%). +// evalDmpEqns() fills fvec_ and fjac_. +void +DmpAlg::newtonRaphson() +{ + for (int k = 0; k < newton_raphson_max_iter_; k++) { + evalDmpEqns(); + for (int i = 0; i < nr_order_; i++) // Right-hand side of linear equations. - p[i] = -fvec[i]; - luDecomp(fjac, size, index, scale); - luSolve(fjac, size, index, p); + p_[i] = -fvec_[i]; + luDecomp(); + luSolve(); bool all_under_x_tol = true; - for (int i = 0; i < size; i++) { - if (abs(p[i]) > abs(x[i]) * x_tol) - all_under_x_tol = false; - x[i] += p[i]; + for (int i = 0; i < nr_order_; i++) { + if (std::abs(p_[i]) > std::abs(x_[i]) * driver_param_tol_) + all_under_x_tol = false; + x_[i] += p_[i]; } - if (all_under_x_tol) + if (all_under_x_tol) { + evalDmpEqns(); return; + } } throw DmpError("Newton-Raphson max iterations exceeded"); } @@ -1317,41 +1153,36 @@ newtonRaphson(const int max_iter, // ftp://ftp.mcc.ac.uk/pub/matclass/libmat.tar.Z // Crout's Method of LU decomposition of square matrix, with implicit -// partial pivoting. A is overwritten. U is explicit in the upper +// partial pivoting. fjac_ is overwritten. U is explicit in the upper // triangle and L is in multiplier form in the subdiagionals i.e. subdiag // a[i,j] is the multiplier used to eliminate the [i,j] term. // -// Replaces a[0..size-1][0..size-1] by the LU decomposition. -// index[0..size-1] is an output vector of the row permutations. -// Return error msg on failure. +// Replaces fjac_[0..nr_order_-1][*] by the LU decomposition. +// index_[0..nr_order_-1] is an output vector of the row permutations. void -luDecomp(double **a, - const int size, - int *index, - // Temporary supplied by caller. - // scale stores the implicit scaling of each row. - double *scale) +DmpAlg::luDecomp() { + const int size = nr_order_; + // Find implicit scaling factors. for (int i = 0; i < size; i++) { double big = 0.0; for (int j = 0; j < size; j++) { - double temp = abs(a[i][j]); - if (temp > big) - big = temp; + double temp = std::abs(fjac_[i][j]); + big = std::max(temp, big); } if (big == 0.0) throw DmpError("LU decomposition: no non-zero row element"); - scale[i] = 1.0 / big; + scale_[i] = 1.0 / big; } int size_1 = size - 1; for (int j = 0; j < size; j++) { // Run down jth column from top to diag, to form the elements of U. for (int i = 0; i < j; i++) { - double sum = a[i][j]; + double sum = fjac_[i][j]; for (int k = 0; k < i; k++) - sum -= a[i][k] * a[k][j]; - a[i][j] = sum; + sum -= fjac_[i][k] * fjac_[k][j]; + fjac_[i][j] = sum; } // Run down jth subdiag to form the residuals after the elimination // of the first j-1 subdiags. These residuals diviyded by the @@ -1361,114 +1192,70 @@ luDecomp(double **a, double big = 0.0; int imax = 0; for (int i = j; i < size; i++) { - double sum = a[i][j]; + double sum = fjac_[i][j]; for (int k = 0; k < j; k++) - sum -= a[i][k] * a[k][j]; - a[i][j] = sum; - double dum = scale[i] * abs(sum); + sum -= fjac_[i][k] * fjac_[k][j]; + fjac_[i][j] = sum; + double dum = scale_[i] * std::abs(sum); if (dum >= big) { - big = dum; - imax = i; + big = dum; + imax = i; } } // Permute current row with imax. if (j != imax) { // Yes, do so... for (int k = 0; k < size; k++) { - double dum = a[imax][k]; - a[imax][k] = a[j][k]; - a[j][k] = dum; + double dum = fjac_[imax][k]; + fjac_[imax][k] = fjac_[j][k]; + fjac_[j][k] = dum; } - scale[imax] = scale[j]; + scale_[imax] = scale_[j]; } - index[j] = imax; + index_[j] = imax; // If diag term is not zero divide subdiag to form multipliers. - if (a[j][j] == 0.0) - a[j][j] = tiny_double; + if (fjac_[j][j] == 0.0) + fjac_[j][j] = tiny_double_; if (j != size_1) { - double pivot = 1.0 / a[j][j]; + double pivot = 1.0 / fjac_[j][j]; for (int i = j + 1; i < size; i++) - a[i][j] *= pivot; + fjac_[i][j] *= pivot; } } } -// Solves the set of size linear equations a*x=b, assuming A is LU form -// but assume b has not been transformed. -// a[0..size-1] is LU decomposition -// Returns the solution vector x in b. -// a and index are not modified. +// Solves fjac_ * x = p_ for x, assuming fjac_ is LU form from luDecomp. +// Solution overwrites p_. void -luSolve(double **a, - const int size, - const int *index, - double b[]) +DmpAlg::luSolve() { - // Transform b allowing for leading zeros. + const int size = nr_order_; + + // Transform p_ allowing for leading zeros. int non_zero = -1; for (int i = 0; i < size; i++) { - int iperm = index[i]; - double sum = b[iperm]; - b[iperm] = b[i]; + int iperm = index_[i]; + double sum = p_[iperm]; + p_[iperm] = p_[i]; if (non_zero != -1) { for (int j = non_zero; j <= i - 1; j++) - sum -= a[i][j] * b[j]; + sum -= fjac_[i][j] * p_[j]; } else { if (sum != 0.0) - non_zero = i; + non_zero = i; } - b[i] = sum; + p_[i] = sum; } // Backsubstitution. for (int i = size - 1; i >= 0; i--) { - double sum = b[i]; + double sum = p_[i]; for (int j = i + 1; j < size; j++) - sum -= a[i][j] * b[j]; - b[i] = sum / a[i][i]; + sum -= fjac_[i][j] * p_[j]; + p_[i] = sum / fjac_[i][i]; } } -#if 0 -// Solve: -// x + y = 5 -// x - y = 1 -// x = 3 -// y = 2 -void -testLuDecomp1() -{ - double a0[2] = {1, 1}; - double a1[2] = {1, -1}; - double *a[2] = {a0, a1}; - int index[2]; - double b[2] = {5, 1}; - double scale[2]; - luDecomp(a, 2, index, scale); - luSolve(a, 2, index, b); - printf("x = %f y= %f\n", b[0], b[1]); -} - -// Solve -// x + 2y = 3 -// 3x - 4y = 19 -// x = 5 -// y = -1 -void -testLuDecomp2() -{ - double a0[2] = {1, 2}; - double a1[2] = {3, -4}; - double *a[2] = {a0, a1}; - int index[2]; - double b[2] = {3, 19}; - double scale[2]; - luDecomp(a, 2, index, scale); - luSolve(a, 2, index, b); - printf("x = %f y= %f\n", b[0], b[1]); -} -#endif - //////////////////////////////////////////////////////////////// bool DmpCeffDelayCalc::unsuppored_model_warned_ = false; @@ -1477,8 +1264,7 @@ DmpCeffDelayCalc::DmpCeffDelayCalc(StaState *sta) : LumpedCapDelayCalc(sta), dmp_cap_(new DmpCap(sta)), dmp_pi_(new DmpPi(sta)), - dmp_zero_c2_(new DmpZeroC2(sta)), - dmp_alg_(nullptr) + dmp_zero_c2_(new DmpZeroC2(sta)) { } @@ -1496,46 +1282,63 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { + parasitics_ = scene->parasitics(min_max); const RiseFall *rf = arc->toEdge()->asRiseFall(); const LibertyCell *drvr_cell = arc->from()->libertyCell(); const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary(); - GateTableModel *table_model = arc->gateTableModel(dcalc_ap); ArcDcalcResult dcalc_result; + GateTableModel *table_model = arc->gateTableModel(scene, min_max); if (table_model && parasitic) { float in_slew1 = delayAsFloat(in_slew); float c2, rpi, c1; parasitics_->piModel(parasitic, c2, rpi, c1); - if (isnan(c2) || isnan(c1) || isnan(rpi)) + if (std::isnan(c2) || std::isnan(c1) || std::isnan(rpi)) report_->error(1040, "parasitic Pi model has NaNs."); - setCeffAlgorithm(drvr_library, drvr_cell, pinPvt(drvr_pin, dcalc_ap), + const Pvt *pvt = pinPvt(drvr_pin, scene, min_max); + setCeffAlgorithm(drvr_library, drvr_cell, pvt, table_model, rf, in_slew1, c2, rpi, c1); - double gate_delay, drvr_slew; - gateDelaySlew(gate_delay, drvr_slew); + auto [gate_delay, drvr_slew] = gateDelaySlew(); + + // Fill in pocv parameters. + double ceff = dmp_alg_->ceff(); + ArcDelay gate_delay2(gate_delay); + Slew drvr_slew2(drvr_slew); + if (variables_->pocvEnabled()) + table_model->gateDelayPocv(pvt, in_slew1, ceff, min_max, + variables_->pocvMode(), + gate_delay2, drvr_slew2); dcalc_result = ArcDcalcResult(load_pin_index_map.size()); - dcalc_result.setGateDelay(gate_delay); - dcalc_result.setDrvrSlew(drvr_slew); + dcalc_result.setGateDelay(gate_delay2); + dcalc_result.setDrvrSlew(drvr_slew2); for (const auto &[load_pin, load_idx] : load_pin_index_map) { - ArcDelay wire_delay; - Slew load_slew; + double wire_delay; + double load_slew; loadDelaySlew(load_pin, drvr_slew, rf, drvr_library, parasitic, wire_delay, load_slew); - dcalc_result.setWireDelay(load_idx, wire_delay); - dcalc_result.setLoadSlew(load_idx, load_slew); + // Copy pocv params from driver. + ArcDelay wire_delay2(gate_delay2); + Slew load_slew2(drvr_slew2); + delaySetMean(wire_delay2, wire_delay); + delaySetMean(load_slew2, load_slew); + dcalc_result.setWireDelay(load_idx, wire_delay2); + dcalc_result.setLoadSlew(load_idx, load_slew2); } } else { dcalc_result = - LumpedCapDelayCalc::gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, - load_pin_index_map, dcalc_ap); - if (parasitic - && !unsuppored_model_warned_) { + LumpedCapDelayCalc::gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, + load_pin_index_map, scene, min_max); + if (parasitic && !unsuppored_model_warned_) { unsuppored_model_warned_ = true; - report_->warn(1041, "cell %s delay model not supported on SPF parasitics by DMP delay calculator", - drvr_cell->name()); + report_->warn(1041, + "cell {} delay model not supported on SPF parasitics by DMP " + "delay calculator", + drvr_cell->name()); } } @@ -1554,25 +1357,24 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin, void DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library, - const LibertyCell *drvr_cell, - const Pvt *pvt, - const GateTableModel *gate_model, - const RiseFall *rf, - double in_slew, - double c2, - double rpi, - double c1) + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const RiseFall *rf, + double in_slew, + double c2, + double rpi, + double c1) { double rd = 0.0; if (gate_model) { - rd = gateModelRd(drvr_cell, gate_model, rf, in_slew, c2, c1, - pvt, variables_->pocvEnabled()); + rd = gateModelRd(drvr_cell, gate_model, rf, in_slew, c2, c1, pvt); // Zero Rd means the table is constant and thus independent of load cap. if (rd < 1e-2 - // Rpi is small compared to Rd, which makes the load capacitive. - || rpi < rd * 1e-3 - // c1/Rpi can be ignored. - || (c1 == 0.0 || c1 < c2 * 1e-3 || rpi == 0.0)) + // Rpi is small compared to Rd, which makes the load capacitive. + || rpi < rd * 1e-3 + // c1/Rpi can be ignored. + || (c1 == 0.0 || c1 < c2 * 1e-3 || rpi == 0.0)) dmp_alg_ = dmp_cap_; else if (c2 < c1 * 1e-3) dmp_alg_ = dmp_zero_c2_; @@ -1582,33 +1384,34 @@ DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library, } else dmp_alg_ = dmp_cap_; - dmp_alg_->init(drvr_library, drvr_cell, pvt, gate_model, - rf, rd, in_slew, c2, rpi, c1); + dmp_alg_->init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, + c2, rpi, c1); debugPrint(debug_, "dmp_ceff", 3, - " DMP in_slew = %s c2 = %s rpi = %s c1 = %s Rd = %s (%s alg)", + " DMP in_slew = {} c2 = {} rpi = {} c1 = {} Rd = {} ({} alg)", units_->timeUnit()->asString(in_slew), units_->capacitanceUnit()->asString(c2), units_->resistanceUnit()->asString(rpi), units_->capacitanceUnit()->asString(c1), - units_->resistanceUnit()->asString(rd), - dmp_alg_->name()); + units_->resistanceUnit()->asString(rd), dmp_alg_->name()); } -string +std::string DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *parasitic, + const Slew &in_slew, + float load_cap, + const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, - int digits) -{ - ArcDcalcResult dcalc_result = gateDelay(drvr_pin, arc, in_slew, load_cap, - parasitic, load_pin_index_map, dcalc_ap); - GateTableModel *model = arc->gateTableModel(dcalc_ap); + const Scene *scene, + const MinMax *min_max, + int digits) +{ + ArcDcalcResult dcalc_result = + gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, load_pin_index_map, + scene, min_max); + GateTableModel *model = arc->gateTableModel(scene, min_max); float c_eff = 0.0; - string result; + std::string result; const LibertyCell *drvr_cell = arc->to()->libertyCell(); const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary(); const Units *units = drvr_library->units(); @@ -1626,9 +1429,11 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, } if (parasitic && dmp_alg_) { + Parasitics *parasitics = scene->parasitics(min_max); + c_eff = dmp_alg_->ceff(); float c2, rpi, c1; - parasitics_->piModel(parasitic, c2, rpi, c1); + parasitics->piModel(parasitic, c2, rpi, c1); result += "Pi model C2="; result += cap_unit->asString(c2, digits); result += " Rpi="; @@ -1642,13 +1447,12 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, else c_eff = load_cap; if (model) { - const Unit *time_unit = units->timeUnit(); float in_slew1 = delayAsFloat(in_slew); - result += model->reportGateDelay(pinPvt(drvr_pin, dcalc_ap), in_slew1, c_eff, - variables_->pocvEnabled(), digits); + result += model->reportGateDelay(pinPvt(drvr_pin, scene, min_max), + in_slew1, c_eff, min_max, + variables_->pocvMode(), digits); result += "Driver waveform slew = "; - float drvr_slew = delayAsFloat(dcalc_result.drvrSlew()); - result += time_unit->asString(drvr_slew, digits); + result += delayAsString(dcalc_result.drvrSlew(), min_max, digits, this); result += '\n'; } return result; @@ -1656,41 +1460,36 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, static double gateModelRd(const LibertyCell *cell, - const GateTableModel *gate_model, - const RiseFall *rf, - double in_slew, - double c2, - double c1, - const Pvt *pvt, - bool pocv_enabled) + const GateTableModel *gate_model, + const RiseFall *rf, + double in_slew, + double c2, + double c1, + const Pvt *pvt) { float cap1 = c1 + c2; float cap2 = cap1 + 1e-15; - ArcDelay d1, d2; - Slew s1, s2; - gate_model->gateDelay(pvt, in_slew, cap1, pocv_enabled, d1, s1); - gate_model->gateDelay(pvt, in_slew, cap2, pocv_enabled, d2, s2); + float d1, d2, s1, s2; + gate_model->gateDelay(pvt, in_slew, cap1, d1, s1); + gate_model->gateDelay(pvt, in_slew, cap2, d2, s2); double vth = cell->libertyLibrary()->outputThreshold(rf); - float rd = -log(vth) * abs(delayAsFloat(d1) - delayAsFloat(d2)) / (cap2 - cap1); + float rd = -std::log(vth) * std::abs(d1 - d2) / (cap2 - cap1); return rd; } -void -DmpCeffDelayCalc::gateDelaySlew(// Return values. - double &delay, - double &slew) +std::pair +DmpCeffDelayCalc::gateDelaySlew() { - dmp_alg_->gateDelaySlew(delay, slew); + return dmp_alg_->gateDelaySlew(); } -void +std::optional> DmpCeffDelayCalc::loadDelaySlewElmore(const Pin *load_pin, - double elmore, - ArcDelay &delay, - Slew &slew) + double elmore) { if (dmp_alg_) - dmp_alg_->loadDelaySlew(load_pin, elmore, delay, slew); + return dmp_alg_->loadDelaySlew(load_pin, elmore); + return std::nullopt; } // Notify algorithm components. @@ -1703,10 +1502,10 @@ DmpCeffDelayCalc::copyState(const StaState *sta) dmp_zero_c2_->copyState(sta); } -DmpError::DmpError(const char *what) : +DmpError::DmpError(std::string_view what) : what_(what) { - //printf("DmpError %s\n", what); + //sta::print(stdout, "DmpError {}\n", what); } // This saves about 2.5% in overall run time on designs with SPEF. @@ -1735,4 +1534,4 @@ exp2(double x) } } -} // namespace +} // namespace sta diff --git a/dcalc/DmpCeff.hh b/dcalc/DmpCeff.hh index e85151515..1ca6209e9 100644 --- a/dcalc/DmpCeff.hh +++ b/dcalc/DmpCeff.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,9 @@ #pragma once +#include +#include + #include "LibertyClass.hh" #include "LumpedCapDelayCalc.hh" @@ -41,7 +44,7 @@ class DmpCeffDelayCalc : public LumpedCapDelayCalc { public: DmpCeffDelayCalc(StaState *sta); - virtual ~DmpCeffDelayCalc(); + ~DmpCeffDelayCalc() override; bool reduceSupported() const override { return true; } ArcDcalcResult gateDelay(const Pin *drvr_pin, const TimingArc *arc, @@ -49,14 +52,16 @@ public: float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; std::string reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) override; void copyState(const StaState *sta) override; @@ -67,26 +72,24 @@ protected: const LibertyLibrary *drvr_library, const Parasitic *parasitic, // Return values. - ArcDelay &wire_delay, - Slew &load_slew) = 0; - void gateDelaySlew(// Return values. - double &delay, - double &slew); - void loadDelaySlewElmore(const Pin *load_pin, - double elmore, - ArcDelay &delay, - Slew &slew); + double &wire_delay, + double &load_slew) = 0; + std::pair gateDelaySlew(); + std::optional> + loadDelaySlewElmore(const Pin *load_pin, + double elmore); // Select the appropriate special case Dartu/Menezes/Pileggi algorithm. void setCeffAlgorithm(const LibertyLibrary *library, - const LibertyCell *cell, - const Pvt *pvt, - const GateTableModel *gate_model, - const RiseFall *rf, - double in_slew, - double c2, - double rpi, - double c1); + const LibertyCell *cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const RiseFall *rf, + double in_slew, + double c2, + double rpi, + double c1); + const Parasitics *parasitics_; static bool unsuppored_model_warned_; private: @@ -95,7 +98,7 @@ private: DmpCap *dmp_cap_; DmpPi *dmp_pi_; DmpZeroC2 *dmp_zero_c2_; - DmpAlg *dmp_alg_; + DmpAlg *dmp_alg_{nullptr}; }; -} // namespace +} // namespace sta diff --git a/dcalc/DmpDelayCalc.cc b/dcalc/DmpDelayCalc.cc index 9081a65dd..f94d20863 100644 --- a/dcalc/DmpDelayCalc.cc +++ b/dcalc/DmpDelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,16 +24,15 @@ #include "DmpDelayCalc.hh" -#include "TableModel.hh" -#include "TimingArc.hh" +#include "DmpCeff.hh" +#include "GraphDelayCalc.hh" #include "Liberty.hh" -#include "PortDirection.hh" #include "Network.hh" -#include "Sdc.hh" #include "Parasitics.hh" -#include "DcalcAnalysisPt.hh" -#include "GraphDelayCalc.hh" -#include "DmpCeff.hh" +#include "PortDirection.hh" +#include "Sdc.hh" +#include "TableModel.hh" +#include "TimingArc.hh" namespace sta { @@ -44,13 +43,14 @@ class DmpCeffElmoreDelayCalc : public DmpCeffDelayCalc public: DmpCeffElmoreDelayCalc(StaState *sta); ArcDelayCalc *copy() override; - const char *name() const override { return "dmp_ceff_elmore"; } + std::string_view name() const override { return "dmp_ceff_elmore"; } ArcDcalcResult inputPortDelay(const Pin *port_pin, float in_slew, const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; protected: void loadDelaySlew(const Pin *load_pin, @@ -59,8 +59,8 @@ class DmpCeffElmoreDelayCalc : public DmpCeffDelayCalc const LibertyLibrary *drvr_library, const Parasitic *parasitic, // Return values. - ArcDelay &wire_delay, - Slew &load_slew) override; + double &wire_delay, + double &load_slew) override; }; ArcDelayCalc * @@ -86,17 +86,19 @@ DmpCeffElmoreDelayCalc::inputPortDelay(const Pin *, const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *) + const Scene *scene, + const MinMax *min_max) { + Parasitics *parasitics = scene->parasitics(min_max); ArcDcalcResult dcalc_result(load_pin_index_map.size()); LibertyLibrary *drvr_library = network_->defaultLibertyLibrary(); for (auto [load_pin, load_idx] : load_pin_index_map) { - ArcDelay wire_delay = 0.0; - Slew load_slew = in_slew; + double wire_delay = 0.0; + double load_slew = in_slew; bool elmore_exists = false; float elmore = 0.0; if (parasitic) - parasitics_->findElmore(parasitic, load_pin, elmore, elmore_exists); + parasitics->findElmore(parasitic, load_pin, elmore, elmore_exists); if (elmore_exists) // Input port with no external driver. dspfWireDelaySlew(load_pin, rf, in_slew, elmore, wire_delay, load_slew); @@ -114,8 +116,8 @@ DmpCeffElmoreDelayCalc::loadDelaySlew(const Pin *load_pin, const LibertyLibrary *drvr_library, const Parasitic *parasitic, // Return values. - ArcDelay &wire_delay, - Slew &load_slew) + double &wire_delay, + double &load_slew) { wire_delay = 0.0; load_slew = drvr_slew; @@ -123,8 +125,12 @@ DmpCeffElmoreDelayCalc::loadDelaySlew(const Pin *load_pin, float elmore = 0.0; if (parasitic) parasitics_->findElmore(parasitic, load_pin, elmore, elmore_exists); - if (elmore_exists) - loadDelaySlewElmore(load_pin, elmore, wire_delay, load_slew); + if (elmore_exists) { + if (auto r = loadDelaySlewElmore(load_pin, elmore)) { + wire_delay = r->first; + load_slew = r->second; + } + } thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew); } @@ -137,55 +143,60 @@ class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc public: DmpCeffTwoPoleDelayCalc(StaState *sta); ArcDelayCalc *copy() override; - const char *name() const override { return "dmp_ceff_two_pole"; } + std::string_view name() const override { return "dmp_ceff_two_pole"; } Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResult inputPortDelay(const Pin *port_pin, float in_slew, const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResult gateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; -private: +protected: void loadDelaySlew(const Pin *load_pin, double drvr_slew, const RiseFall *rf, const LibertyLibrary *drvr_library, const Parasitic *parasitic, // Return values. - ArcDelay &wire_delay, - Slew &load_slew) override; + double &wire_delay, + double &load_slew) override; + +private: void loadDelay(double drvr_slew, Parasitic *pole_residue, - double p1, - double k1, - ArcDelay &wire_delay, - Slew &load_slew); + double p1, + double k1, + double &wire_delay, + double &load_slew); float loadDelay(double vth, - double p1, - double p2, - double k1, - double k2, - double B, - double k1_p1_2, - double k2_p2_2, - double tt, - double y_tt); + double p1, + double p2, + double k1, + double k2, + double B, + double k1_p1_2, + double k2_p2_2, + double tt, + double y_tt); - bool parasitic_is_pole_residue_; - float vth_; - float vl_; - float vh_; - float slew_derate_; + bool parasitic_is_pole_residue_{false}; + float vth_{0.0F}; + float vl_{0.0F}; + float vh_{0.0F}; + float slew_derate_{0.0F}; }; ArcDelayCalc * @@ -195,12 +206,7 @@ makeDmpCeffTwoPoleDelayCalc(StaState *sta) } DmpCeffTwoPoleDelayCalc::DmpCeffTwoPoleDelayCalc(StaState *sta) : - DmpCeffDelayCalc(sta), - parasitic_is_pole_residue_(false), - vth_(0.0), - vl_(0.0), - vh_(0.0), - slew_derate_(0.0) + DmpCeffDelayCalc(sta) { } @@ -212,43 +218,41 @@ DmpCeffTwoPoleDelayCalc::copy() Parasitic * DmpCeffTwoPoleDelayCalc::findParasitic(const Pin *drvr_pin, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) { Parasitic *parasitic = nullptr; - const Corner *corner = dcalc_ap->corner(); - // set_load net has precedence over parasitics. - if (sdc_->drvrPinHasWireCap(drvr_pin, corner) + const Sdc *sdc = scene->sdc(); + Parasitics *parasitics = scene->parasitics(min_max); + if (parasitics == nullptr + // set_load net has precedence over parasitics. || network_->direction(drvr_pin)->isInternal()) return nullptr; - const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); // Prefer PiPoleResidue. - parasitic = parasitics_->findPiPoleResidue(drvr_pin, rf, parasitic_ap); + parasitic = parasitics->findPiPoleResidue(drvr_pin, rf, min_max); if (parasitic) return parasitic; - parasitic = parasitics_->findPiElmore(drvr_pin, rf, parasitic_ap); + parasitic = parasitics->findPiElmore(drvr_pin, rf, min_max); if (parasitic) return parasitic; Parasitic *parasitic_network = - parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + parasitics->findParasiticNetwork(drvr_pin); if (parasitic_network) { - parasitic = parasitics_->reduceToPiPoleResidue2(parasitic_network, drvr_pin, rf, - corner, - dcalc_ap->constraintMinMax(), - parasitic_ap); + parasitic = parasitics->reduceToPiPoleResidue2(parasitic_network, drvr_pin, rf, + scene, min_max); if (parasitic) return parasitic; } - const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); - Wireload *wireload = sdc_->wireload(cnst_min_max); + Wireload *wireload = sdc->wireload(min_max); if (wireload) { float pin_cap, wire_cap, fanout; bool has_wire_cap; - graph_delay_calc_->netCaps(drvr_pin, rf, dcalc_ap, pin_cap, wire_cap, + graph_delay_calc_->netCaps(drvr_pin, rf, scene, min_max, pin_cap, wire_cap, fanout, has_wire_cap); - parasitic = parasitics_->estimatePiElmore(drvr_pin, rf, wireload, - fanout, pin_cap, corner, - cnst_min_max); + parasitic = parasitics->estimatePiElmore(drvr_pin, rf, wireload, + fanout, pin_cap, + scene, min_max); } return parasitic; } @@ -259,21 +263,23 @@ DmpCeffTwoPoleDelayCalc::inputPortDelay(const Pin *, const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *) + const Scene *scene, + const MinMax *min_max) { + const Parasitics *parasitics = scene->parasitics(min_max); ArcDcalcResult dcalc_result(load_pin_index_map.size()); - ArcDelay wire_delay = 0.0; - Slew load_slew = in_slew; + double wire_delay = 0.0; + double load_slew = in_slew; LibertyLibrary *drvr_library = network_->defaultLibertyLibrary(); for (const auto [load_pin, load_idx] : load_pin_index_map) { - if (parasitics_->isPiPoleResidue(parasitic)) { - const Parasitic *pole_residue = parasitics_->findPoleResidue(parasitic, load_pin); + if (parasitics->isPiPoleResidue(parasitic)) { + const Parasitic *pole_residue = parasitics->findPoleResidue(parasitic, load_pin); if (pole_residue) { - size_t pole_count = parasitics_->poleResidueCount(pole_residue); + size_t pole_count = parasitics->poleResidueCount(pole_residue); if (pole_count >= 1) { ComplexFloat pole1, residue1; // Find the 1st (elmore) pole. - parasitics_->poleResidue(pole_residue, 0, pole1, residue1); + parasitics->poleResidue(pole_residue, 0, pole1, residue1); if (pole1.imag() == 0.0 && residue1.imag() == 0.0) { float p1 = pole1.real(); @@ -297,8 +303,10 @@ DmpCeffTwoPoleDelayCalc::gateDelay(const Pin *drvr_pin, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { + parasitics_ = scene->parasitics(min_max); const LibertyLibrary *drvr_library = arc->to()->libertyLibrary(); const RiseFall *rf = arc->toEdge()->asRiseFall(); vth_ = drvr_library->outputThreshold(rf); @@ -306,7 +314,7 @@ DmpCeffTwoPoleDelayCalc::gateDelay(const Pin *drvr_pin, vh_ = drvr_library->slewUpperThreshold(rf); slew_derate_ = drvr_library->slewDerateFromLibrary(); return DmpCeffDelayCalc::gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, - load_pin_index_map, dcalc_ap) ; + load_pin_index_map, scene, min_max) ; } void @@ -316,14 +324,14 @@ DmpCeffTwoPoleDelayCalc::loadDelaySlew(const Pin *load_pin, const LibertyLibrary *drvr_library, const Parasitic *parasitic, // Return values. - ArcDelay &wire_delay, - Slew &load_slew) + double &wire_delay, + double &load_slew) { parasitic_is_pole_residue_ = parasitics_->isPiPoleResidue(parasitic); // Should handle PiElmore parasitic. wire_delay = 0.0; load_slew = drvr_slew; - Parasitic *pole_residue = 0; + Parasitic *pole_residue = nullptr; if (parasitic_is_pole_residue_) pole_residue = parasitics_->findPoleResidue(parasitic, load_pin); if (pole_residue) { @@ -333,9 +341,9 @@ DmpCeffTwoPoleDelayCalc::loadDelaySlew(const Pin *load_pin, // Find the 1st (elmore) pole. parasitics_->poleResidue(pole_residue, 0, pole1, residue1); if (pole1.imag() == 0.0 - && residue1.imag() == 0.0) { - float p1 = pole1.real(); - float k1 = residue1.real(); + && residue1.imag() == 0.0) { + float p1 = pole1.real(); + float k1 = residue1.real(); if (pole_count >= 2) loadDelay(drvr_slew, pole_residue, p1, k1, wire_delay, load_slew); else { @@ -352,15 +360,15 @@ DmpCeffTwoPoleDelayCalc::loadDelaySlew(const Pin *load_pin, void DmpCeffTwoPoleDelayCalc::loadDelay(double drvr_slew, Parasitic *pole_residue, - double p1, + double p1, double k1, - // Return values. - ArcDelay &wire_delay, - Slew &load_slew) + // Return values. + double &wire_delay, + double &load_slew) { ComplexFloat pole2, residue2; parasitics_->poleResidue(pole_residue, 1, pole2, residue2); - if (!delayZero(drvr_slew) + if (!delayZero(drvr_slew, this) && pole2.imag() == 0.0 && residue2.imag() == 0.0) { double p2 = pole2.real(); @@ -371,7 +379,7 @@ DmpCeffTwoPoleDelayCalc::loadDelay(double drvr_slew, // Convert tt to 0:1 range. float tt = delayAsFloat(drvr_slew) * slew_derate_ / (vh_ - vl_); double y_tt = (tt - B + k1_p1_2 * exp(-p1 * tt) - + k2_p2_2 * exp(-p2 * tt)) / tt; + + k2_p2_2 * exp(-p2 * tt)) / tt; wire_delay = loadDelay(vth_, p1, p2, k1, k2, B, k1_p1_2, k2_p2_2, tt, y_tt) - tt * vth_; @@ -383,15 +391,15 @@ DmpCeffTwoPoleDelayCalc::loadDelay(double drvr_slew, float DmpCeffTwoPoleDelayCalc::loadDelay(double vth, - double p1, - double p2, - double k1, - double k2, - double B, - double k1_p1_2, - double k2_p2_2, - double tt, - double y_tt) + double p1, + double p2, + double k1, + double k2, + double B, + double k1_p1_2, + double k2_p2_2, + double tt, + double y_tt) { if (y_tt < vth) { // t1 > tt @@ -403,9 +411,9 @@ DmpCeffTwoPoleDelayCalc::loadDelay(double vth, double exp_p1_t1_tt = exp(-p1 * (t1 - tt)); double exp_p2_t1_tt = exp(-p2 * (t1 - tt)); double y_t1 = (tt - k1_p1_2 * (exp_p1_t1_tt - exp_p1_t1) - - k2_p2_2 * (exp_p2_t1_tt - exp_p2_t1)) / tt; + - k2_p2_2 * (exp_p2_t1_tt - exp_p2_t1)) / tt; double yp_t1 = (k1 / p1 * (exp_p1_t1_tt - exp_p1_t1) - - k2 / p2 * (exp_p2_t1_tt - exp_p2_t1)) / tt; + - k2 / p2 * (exp_p2_t1_tt - exp_p2_t1)) / tt; double delay = t1 - (y_t1 - vth) / yp_t1; return static_cast(delay); } @@ -417,12 +425,12 @@ DmpCeffTwoPoleDelayCalc::loadDelay(double vth, double exp_p1_t1 = exp(-p1 * t1); double exp_p2_t1 = exp(-p2 * t1); double y_t1 = (t1 - B + k1_p1_2 * exp_p1_t1 - + k2_p2_2 * exp_p1_t1) / tt; + + k2_p2_2 * exp_p1_t1) / tt; double yp_t1 = (1 - k1 / p1 * exp_p1_t1 - - k2 / p2 * exp_p2_t1) / tt; + - k2 / p2 * exp_p2_t1) / tt; double delay = t1 - (y_t1 - vth) / yp_t1; return static_cast(delay); } } -} // namespace +} // namespace sta diff --git a/dcalc/DmpDelayCalc.hh b/dcalc/DmpDelayCalc.hh index aba00ac24..518d78184 100644 --- a/dcalc/DmpDelayCalc.hh +++ b/dcalc/DmpDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -34,4 +34,4 @@ makeDmpCeffElmoreDelayCalc(StaState *sta); ArcDelayCalc * makeDmpCeffTwoPoleDelayCalc(StaState *sta); -} // namespace +} // namespace sta diff --git a/dcalc/FindRoot.cc b/dcalc/FindRoot.cc index ded4bec07..58e8dde8e 100644 --- a/dcalc/FindRoot.cc +++ b/dcalc/FindRoot.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -28,63 +28,52 @@ namespace sta { -using std::abs; - -double -findRoot(FindRootFunc func, - double x1, - double x2, - double x_tol, - int max_iter, - // Return value. - bool &fail) +std::pair +findRoot(const FindRootFunc &func, + double x1, + double x2, + double x_tol, + int max_iter) { double y1, y2, dy1; func(x1, y1, dy1); func(x2, y2, dy1); - return findRoot(func, x1, y1, x2, y2, x_tol, max_iter, fail); + return findRoot(func, x1, y1, x2, y2, x_tol, max_iter); } -double -findRoot(FindRootFunc func, - double x1, - double y1, +std::pair +findRoot(const FindRootFunc &func, + double x1, + double y1, double x2, - double y2, + double y2, double x_tol, - int max_iter, - // Return value. - bool &fail) + int max_iter) { if ((y1 > 0.0 && y2 > 0.0) || (y1 < 0.0 && y2 < 0.0)) { // Initial bounds do not surround a root. - fail = true; - return 0.0; + return {0.0, true}; } - if (y1 == 0.0) { - fail = false; - return x1; - } + if (y1 == 0.0) + return {x1, false}; - if (y2 == 0.0) { - fail = false; - return x2; - } + if (y2 == 0.0) + return {x2, false}; if (y1 > 0.0) // Swap x1/x2 so func(x1) < 0. std::swap(x1, x2); double root = (x1 + x2) * 0.5; - double dx_prev = abs(x2 - x1); + double dx_prev = std::abs(x2 - x1); double dx = dx_prev; double y, dy; func(root, y, dy); for (int iter = 0; iter < max_iter; iter++) { // Newton/raphson out of range. if ((((root - x2) * dy - y) * ((root - x1) * dy - y) > 0.0) - // Not decreasing fast enough. - || (abs(2.0 * y) > abs(dx_prev * dy))) { + // Not decreasing fast enough. + || (std::abs(2.0 * y) > std::abs(dx_prev * dy))) { // Bisect x1/x2 interval. dx_prev = dx; dx = (x2 - x1) * 0.5; @@ -95,10 +84,9 @@ findRoot(FindRootFunc func, dx = y / dy; root -= dx; } - if (abs(dx) <= x_tol * abs(root)) { + if (std::abs(dx) <= x_tol * std::abs(root)) { // Converged. - fail = false; - return root; + return {root, false}; } func(root, y, dy); @@ -107,8 +95,7 @@ findRoot(FindRootFunc func, else x2 = root; } - fail = true; - return root; + return {root, true}; } -} // namespace +} // namespace sta diff --git a/dcalc/FindRoot.hh b/dcalc/FindRoot.hh index e4d4d8542..61daac86e 100644 --- a/dcalc/FindRoot.hh +++ b/dcalc/FindRoot.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,32 +25,31 @@ #pragma once #include +#include namespace sta { -typedef const std::function FindRootFunc; +using FindRootFunc = const std::function; -double -findRoot(FindRootFunc func, - double x1, - double x2, - double x_tol, - int max_iter, - // Return value. - bool &fail); +// first: root estimate; second: true if the search failed. +std::pair +findRoot(const FindRootFunc &func, + double x1, + double x2, + double x_tol, + int max_iter); -double -findRoot(FindRootFunc func, - double x1, - double y1, +// first: root estimate; second: true if the search failed. +std::pair +findRoot(const FindRootFunc &func, + double x1, + double y1, double x2, - double y2, - double x_tol, - int max_iter, - // Return value. - bool &fail); + double y2, + double x_tol, + int max_iter); -} // namespace +} // namespace sta diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 6fcaa8702..741353bf9 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,62 +24,136 @@ #include "GraphDelayCalc.hh" +#include +#include +#include +#include + +#include "ArcDelayCalc.hh" +#include "Bfs.hh" +#include "ClkNetwork.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" -#include "Stats.hh" +#include "Graph.hh" +#include "InputDrive.hh" +#include "Liberty.hh" #include "MinMax.hh" +#include "Mode.hh" #include "Mutex.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" -#include "Liberty.hh" -#include "PortDirection.hh" +#include "NetCaps.hh" #include "Network.hh" -#include "InputDrive.hh" -#include "Sdc.hh" -#include "Graph.hh" #include "Parasitics.hh" -#include "search/Levelize.hh" -#include "Corner.hh" +#include "PortDirection.hh" +#include "Sdc.hh" +#include "Scene.hh" #include "SearchPred.hh" -#include "Bfs.hh" -#include "ArcDelayCalc.hh" -#include "DcalcAnalysisPt.hh" -#include "NetCaps.hh" -#include "ClkNetwork.hh" +#include "Stats.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" #include "Variables.hh" +#include "search/Latches.hh" +#include "search/Levelize.hh" namespace sta { -using std::string; -using std::abs; -using std::array; - static const Slew default_slew = 0.0; static bool isLeafDriver(const Pin *pin, const Network *network); +//////////////////////////////////////////////////////////////// + +class DcalcPred : public SearchPred +{ +public: + DcalcPred(const StaState *sta); + bool searchFrom(const Vertex *from_vertex, + const Mode *mode) const override; + bool searchThru(Edge *edge, + const Mode *mode) const override; + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; +}; + +DcalcPred::DcalcPred(const StaState *sta) : + SearchPred(sta) +{ +} + +bool +DcalcPred::searchFrom(const Vertex *from_vertex, + const Mode *mode) const +{ + const Pin *from_pin = from_vertex->pin(); + const Sdc *sdc = mode->sdc(); + const Network *network = sta_->network(); + Net *net = network->net(from_pin); + return !(sdc->isDisabledConstraint(from_pin) + || (net && (network->isPower(net) + || network->isGround(net)))); +} + +bool +DcalcPred::searchThru(Edge *edge, + const Mode *mode) const +{ + const Sdc *sdc = mode->sdc(); + const Variables *variables = sta_->variables(); + const TimingRole *role = edge->role(); + return !(role->isTimingCheck() + || (role == TimingRole::regSetClr() + && !variables->presetClrArcsEnabled()) + || edge->isDisabledLoop() + || sdc->isDisabledConstraint(edge) + || sdc->isDisabledCondDefault(edge) + || (edge->isBidirectInstPath() + && !variables->bidirectInstPathsEnabled())); +} + +bool +DcalcPred::searchTo(const Vertex *, + const Mode *) const +{ + return true; +} + +class DcalcNonLatchPred : public DcalcPred +{ +public: + DcalcNonLatchPred(const StaState *sta); + bool searchThru(Edge *edge, + const Mode *mode) const override; +}; + +DcalcNonLatchPred::DcalcNonLatchPred(const StaState *sta) : + DcalcPred(sta) +{ +} + +bool +DcalcNonLatchPred::searchThru(Edge *edge, + const Mode *mode) const +{ + return DcalcPred::searchThru(edge, mode) + && !edge->role()->isLatchDtoQ(); +} + +//////////////////////////////////////////////////////////////// + GraphDelayCalc::GraphDelayCalc(StaState *sta) : StaState(sta), - observer_(nullptr), - delays_seeded_(false), - incremental_(false), - delays_exist_(false), - invalid_delays_(new VertexSet(graph_)), - search_pred_(new SearchPred1(sta)), - search_non_latch_pred_(new SearchPredNonLatch2(sta)), - clk_pred_(new ClkTreeSearchPred(sta)), - iter_(new BfsFwdIterator(BfsIndex::dcalc, search_non_latch_pred_, sta)), - incremental_delay_tolerance_(0.0) + invalid_delays_(makeVertexSet(this)), + search_pred_(new DcalcPred(sta)), + search_non_latch_pred_(new DcalcNonLatchPred(sta)), + iter_(new BfsFwdIterator(BfsIndex::dcalc, search_non_latch_pred_, sta)) { } GraphDelayCalc::~GraphDelayCalc() { delete search_pred_; - delete invalid_delays_; delete search_non_latch_pred_; - delete clk_pred_; delete iter_; deleteMultiDrvrNets(); delete observer_; @@ -88,16 +162,14 @@ GraphDelayCalc::~GraphDelayCalc() void GraphDelayCalc::deleteMultiDrvrNets() { - Set drvr_nets; - MultiDrvrNetMap::Iterator multi_iter(multi_drvr_net_map_); - while (multi_iter.hasNext()) { - MultiDrvrNet *multi_drvr = multi_iter.next(); + std::set drvr_nets; + for (auto [vertex, multi_drvr] : multi_drvr_net_map_) { // Multiple drvr pins point to the same drvr PinSet, // so collect them into a set. drvr_nets.insert(multi_drvr); } multi_drvr_net_map_.clear(); - drvr_nets.deleteContents(); + deleteContents(drvr_nets); } void @@ -106,6 +178,8 @@ GraphDelayCalc::copyState(const StaState *sta) StaState::copyState(sta); // Notify sub-components. iter_->copyState(sta); + search_pred_->copyState(sta); + search_non_latch_pred_->copyState(sta); } void @@ -143,7 +217,7 @@ GraphDelayCalc::delaysInvalid() incremental_ = false; iter_->clear(); // No need to keep track of incremental updates any more. - invalid_delays_->clear(); + invalid_delays_.clear(); invalid_check_edges_.clear(); invalid_latch_edges_.clear(); } @@ -173,14 +247,14 @@ GraphDelayCalc::delayInvalid(const Pin *pin) void GraphDelayCalc::delayInvalid(Vertex *vertex) { - debugPrint(debug_, "delay_calc", 2, "delay invalid %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "delay_calc", 2, "delay invalid {}", + vertex->to_string(this)); if (graph_ && incremental_) { - invalid_delays_->insert(vertex); + invalid_delays_.insert(vertex); // Invalidate driver that triggers dcalc for multi-driver nets. MultiDrvrNet *multi_drvr = multiDrvrNet(vertex); if (multi_drvr) - invalid_delays_->insert(multi_drvr->dcalcDrvr()); + invalid_delays_.insert(multi_drvr->dcalcDrvr()); } } @@ -202,7 +276,7 @@ GraphDelayCalc::deleteVertexBefore(Vertex *vertex) { iter_->deleteVertexBefore(vertex); if (incremental_) - invalid_delays_->erase(vertex); + invalid_delays_.erase(vertex); MultiDrvrNet *multi_drvr = multiDrvrNet(vertex); if (multi_drvr) { // Don't bother incrementally updating MultiDrvrNet. @@ -218,9 +292,9 @@ class FindVertexDelays : public VertexVisitor { public: FindVertexDelays(GraphDelayCalc *graph_delay_calc1); - virtual ~FindVertexDelays(); - virtual void visit(Vertex *vertex); - virtual VertexVisitor *copy() const; + ~FindVertexDelays() override; + void visit(Vertex *vertex) override; + VertexVisitor *copy() const override; protected: GraphDelayCalc *graph_delay_calc_; @@ -262,7 +336,7 @@ GraphDelayCalc::findDelays(Level level) if (arc_delay_calc_) { Stats stats(debug_, report_); int dcalc_count = 0; - debugPrint(debug_, "delay_calc", 1, "find delays to level %d", level); + debugPrint(debug_, "delay_calc", 1, "find delays to level {}", level); if (!delays_seeded_) { iter_->clear(); seedRootSlews(); @@ -290,7 +364,7 @@ GraphDelayCalc::findDelays(Level level) delays_exist_ = true; incremental_ = true; - debugPrint(debug_, "delay_calc", 1, "found %d delays", dcalc_count); + debugPrint(debug_, "delay_calc", 1, "found {} delays", dcalc_count); stats.report("Delay calc"); } } @@ -298,15 +372,9 @@ GraphDelayCalc::findDelays(Level level) void GraphDelayCalc::seedInvalidDelays() { - for (Vertex *vertex : *invalid_delays_) { - if (vertex->isRoot()) - seedRootSlew(vertex, arc_delay_calc_); - else { - if (search_non_latch_pred_->searchFrom(vertex)) + for (Vertex *vertex : invalid_delays_) iter_->enqueue(vertex); - } - } - invalid_delays_->clear(); + invalid_delays_.clear(); } void @@ -332,34 +400,37 @@ GraphDelayCalc::seedDrvrSlew(Vertex *drvr_vertex, ArcDelayCalc *arc_delay_calc) { const Pin *drvr_pin = drvr_vertex->pin(); - debugPrint(debug_, "delay_calc", 2, "seed driver slew %s", - drvr_vertex->to_string(this).c_str()); - InputDrive *drive = 0; - if (network_->isTopLevelPort(drvr_pin)) { - Port *port = network_->port(drvr_pin); - drive = sdc_->findInputDrive(port); - } - for (const RiseFall *rf : RiseFall::range()) { - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - if (drive) { - const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); - const LibertyCell *drvr_cell; - const LibertyPort *from_port, *to_port; - float *from_slews; - drive->driveCell(rf, cnst_min_max, drvr_cell, from_port, - from_slews, to_port); - if (drvr_cell) { - if (from_port == nullptr) - from_port = driveCellDefaultFromPort(drvr_cell, to_port); - findInputDriverDelay(drvr_cell, drvr_pin, drvr_vertex, rf, - from_port, from_slews, to_port, dcalc_ap); - } - else - seedNoDrvrCellSlew(drvr_vertex, drvr_pin, rf, drive, dcalc_ap, - arc_delay_calc); + debugPrint(debug_, "delay_calc", 2, "seed driver slew {}", + drvr_vertex->to_string(this)); + for (const Scene *scene : scenes_) { + const Sdc *sdc = scene->sdc(); + for (const MinMax *min_max : MinMax::range()) { + for (const RiseFall *rf : RiseFall::range()) { + InputDrive *drive = nullptr; + if (network_->isTopLevelPort(drvr_pin)) { + Port *port = network_->port(drvr_pin); + drive = sdc->findInputDrive(port); + } + if (drive) { + const LibertyCell *drvr_cell; + const LibertyPort *from_port, *to_port; + const DriveCellSlews *from_slews; + drive->driveCell(rf, min_max, drvr_cell, from_port, + from_slews, to_port); + if (drvr_cell) { + if (from_port == nullptr) + from_port = driveCellDefaultFromPort(drvr_cell, to_port); + findInputDriverDelay(drvr_cell, drvr_pin, drvr_vertex, rf, + from_port, from_slews, to_port, scene, min_max, + arc_delay_calc); + } + else + seedNoDrvrCellSlew(drvr_vertex, drvr_pin, rf, drive, scene, min_max, + arc_delay_calc); + } + else + seedNoDrvrSlew(drvr_vertex, drvr_pin, rf, scene, min_max, arc_delay_calc); } - else - seedNoDrvrSlew(drvr_vertex, drvr_pin, rf, dcalc_ap, arc_delay_calc); } } } @@ -369,15 +440,15 @@ GraphDelayCalc::seedNoDrvrCellSlew(Vertex *drvr_vertex, const Pin *drvr_pin, const RiseFall *rf, const InputDrive *drive, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc) { - DcalcAPIndex ap_index = dcalc_ap->index(); - const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); Slew slew = default_slew; float drive_slew; bool exists; - drive->slew(rf, cnst_min_max, drive_slew, exists); + drive->slew(rf, min_max, drive_slew, exists); if (exists) slew = drive_slew; else { @@ -390,24 +461,23 @@ GraphDelayCalc::seedNoDrvrCellSlew(Vertex *drvr_vertex, } Delay drive_delay = delay_zero; float drive_res; - drive->driveResistance(rf, cnst_min_max, drive_res, exists); + drive->driveResistance(rf, min_max, drive_res, exists); const Parasitic *parasitic; float load_cap; - parasiticLoad(drvr_pin, rf, dcalc_ap, nullptr, arc_delay_calc, + parasiticLoad(drvr_pin, rf, scene, min_max, nullptr, arc_delay_calc, load_cap, parasitic); if (exists) { drive_delay = load_cap * drive_res; slew = load_cap * drive_res; } - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - if (!drvr_vertex->slewAnnotated(rf, slew_min_max)) + if (!drvr_vertex->slewAnnotated(rf, min_max)) graph_->setSlew(drvr_vertex, rf, ap_index, slew); LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); ArcDcalcResult dcalc_result = arc_delay_calc->inputPortDelay(drvr_pin, delayAsFloat(slew), rf, parasitic, - load_pin_index_map, dcalc_ap); + load_pin_index_map, scene, min_max); annotateLoadDelays(drvr_vertex, rf, dcalc_result, load_pin_index_map, - drive_delay, false, dcalc_ap); + drive_delay, false, scene, min_max); arc_delay_calc->finishDrvrPin(); } @@ -426,27 +496,27 @@ void GraphDelayCalc::seedNoDrvrSlew(Vertex *drvr_vertex, const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc) { - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - DcalcAPIndex ap_index = dcalc_ap->index(); - Slew slew(default_slew); + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + Slew slew = default_slew; // Top level bidirect driver uses load slew unless // bidirect instance paths are disabled. if (bidirectDrvrSlewFromLoad(drvr_pin)) { Vertex *load_vertex = graph_->pinLoadVertex(drvr_pin); slew = graph_->slew(load_vertex, rf, ap_index); } - if (!drvr_vertex->slewAnnotated(rf, slew_min_max)) + if (!drvr_vertex->slewAnnotated(rf, min_max)) graph_->setSlew(drvr_vertex, rf, ap_index, slew); - Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin, rf, dcalc_ap); + Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin, rf, scene, min_max); LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); ArcDcalcResult dcalc_result = arc_delay_calc->inputPortDelay(drvr_pin, delayAsFloat(slew), rf, parasitic, - load_pin_index_map, dcalc_ap); + load_pin_index_map, scene, min_max); annotateLoadDelays(drvr_vertex, rf, dcalc_result, load_pin_index_map, delay_zero, - false, dcalc_ap); + false, scene, min_max); arc_delay_calc->finishDrvrPin(); } @@ -454,27 +524,27 @@ void GraphDelayCalc::seedLoadSlew(Vertex *vertex) { const Pin *pin = vertex->pin(); - debugPrint(debug_, "delay_calc", 2, "seed load slew %s", - vertex->to_string(this).c_str()); - ClockSet *clks = sdc_->findLeafPinClocks(pin); + debugPrint(debug_, "delay_calc", 2, "seed load slew {}", + vertex->to_string(this)); initSlew(vertex); - for (const RiseFall *rf : RiseFall::range()) { - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - if (!vertex->slewAnnotated(rf, slew_min_max)) { - float slew = 0.0; - if (clks) { - slew = slew_min_max->initValue(); - ClockSet::Iterator clk_iter(clks); - while (clk_iter.hasNext()) { - Clock *clk = clk_iter.next(); - float clk_slew = clk->slew(rf, slew_min_max); - if (slew_min_max->compare(clk_slew, slew)) - slew = clk_slew; - } - } - DcalcAPIndex ap_index = dcalc_ap->index(); - graph_->setSlew(vertex, rf, ap_index, slew); + for (const Scene *scene : scenes_) { + const Sdc *sdc = scene->sdc(); + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + for (const RiseFall *rf : RiseFall::range()) { + ClockSet *clks = sdc->findLeafPinClocks(pin); + if (!vertex->slewAnnotated(rf, min_max)) { + float slew = 0.0; + if (clks) { + slew = min_max->initValue(); + for (Clock *clk : *clks) { + float clk_slew = clk->slew(rf, min_max); + if (min_max->compare(clk_slew, slew)) + slew = clk_slew; + } + } + graph_->setSlew(vertex, rf, ap_index, slew); + } } } } @@ -487,9 +557,9 @@ LibertyPort * GraphDelayCalc::driveCellDefaultFromPort(const LibertyCell *cell, const LibertyPort *to_port) { - LibertyPort *from_port = 0; + LibertyPort *from_port = nullptr; int from_port_index = 0; - for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, to_port)) { + for (TimingArcSet *arc_set : cell->timingArcSetsTo(to_port)) { LibertyPort *set_from_port = arc_set->from(); int set_from_port_index = findPortIndex(cell, set_from_port); if (from_port == nullptr @@ -524,22 +594,24 @@ GraphDelayCalc::findInputDriverDelay(const LibertyCell *drvr_cell, Vertex *drvr_vertex, const RiseFall *rf, const LibertyPort *from_port, - float *from_slews, + const DriveCellSlews *from_slews, const LibertyPort *to_port, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max, + ArcDelayCalc *arc_delay_calc) { - debugPrint(debug_, "delay_calc", 2, " driver cell %s %s", + debugPrint(debug_, "delay_calc", 2, " driver cell {} {}", drvr_cell->name(), - rf->to_string().c_str()); + rf->shortName()); for (TimingArcSet *arc_set : drvr_cell->timingArcSets(from_port, to_port)) { for (TimingArc *arc : arc_set->arcs()) { if (arc->toEdge()->asRiseFall() == rf) { - float from_slew = from_slews[arc->fromEdge()->index()]; - findInputArcDelay(drvr_pin, drvr_vertex, arc, from_slew, dcalc_ap); + float from_slew = (*from_slews)[arc->fromEdge()->index()]; + findInputArcDelay(drvr_pin, drvr_vertex, arc, from_slew, scene, min_max, + arc_delay_calc); } } } - arc_delay_calc_->finishDrvrPin(); } // Driving cell delay is the load dependent delay, which is the gate @@ -550,46 +622,49 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, Vertex *drvr_vertex, const TimingArc *arc, float from_slew, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max, + ArcDelayCalc *arc_delay_calc) { - debugPrint(debug_, "delay_calc", 3, " %s %s -> %s %s (%s)", + debugPrint(debug_, "delay_calc", 3, " {} {} -> {} {} ({})", arc->from()->name(), - arc->fromEdge()->to_string().c_str(), + arc->fromEdge()->to_string(), arc->to()->name(), - arc->toEdge()->to_string().c_str(), - arc->role()->to_string().c_str()); + arc->toEdge()->to_string(), + arc->role()->to_string()); const RiseFall *drvr_rf = arc->toEdge()->asRiseFall(); if (drvr_rf) { - DcalcAPIndex ap_index = dcalc_ap->index(); + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + const Parasitic *parasitic; float load_cap; - parasiticLoad(drvr_pin, drvr_rf, dcalc_ap, nullptr, arc_delay_calc_, + parasiticLoad(drvr_pin, drvr_rf, scene, min_max, nullptr, arc_delay_calc, load_cap, parasitic); LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); ArcDcalcResult intrinsic_result = - arc_delay_calc_->gateDelay(drvr_pin, arc, Slew(from_slew), 0.0, nullptr, - load_pin_index_map, dcalc_ap); - ArcDelay intrinsic_delay = intrinsic_result.gateDelay(); - - ArcDcalcResult gate_result = arc_delay_calc_->gateDelay(drvr_pin, arc, - Slew(from_slew), load_cap, - parasitic, - load_pin_index_map, - dcalc_ap); - ArcDelay gate_delay = gate_result.gateDelay(); - Slew gate_slew = gate_result.drvrSlew(); - - ArcDelay load_delay = gate_delay - intrinsic_delay; + arc_delay_calc->gateDelay(drvr_pin, arc, from_slew, 0.0, nullptr, + load_pin_index_map, scene, min_max); + const ArcDelay &intrinsic_delay = intrinsic_result.gateDelay(); + + ArcDcalcResult gate_result = arc_delay_calc->gateDelay(drvr_pin, arc, + from_slew, load_cap, + parasitic, + load_pin_index_map, + scene, min_max); + const ArcDelay &gate_delay = gate_result.gateDelay(); + const Slew &gate_slew = gate_result.drvrSlew(); + + const ArcDelay load_delay = delayDiff(gate_delay, intrinsic_delay, this); debugPrint(debug_, "delay_calc", 3, - " gate delay = %s intrinsic = %s slew = %s", + " gate delay = {} intrinsic = {} slew = {}", delayAsString(gate_delay, this), delayAsString(intrinsic_delay, this), delayAsString(gate_slew, this)); graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew); annotateLoadDelays(drvr_vertex, drvr_rf, gate_result, load_pin_index_map, - load_delay, false, dcalc_ap); - arc_delay_calc_->finishDrvrPin(); + load_delay, false, scene, min_max); + arc_delay_calc->finishDrvrPin(); } } @@ -605,14 +680,11 @@ GraphDelayCalc::findVertexDelay(Vertex *vertex, bool propagate) { const Pin *pin = vertex->pin(); - debugPrint(debug_, "delay_calc", 2, "find delays %s (%s)", - vertex->to_string(this).c_str(), + debugPrint(debug_, "delay_calc", 2, "find delays {} ({})", + vertex->to_string(this), network_->cellName(network_->instance(pin))); - if (vertex->isRoot()) { + if (vertex->isRoot()) seedRootSlew(vertex, arc_delay_calc); - if (propagate) - iter_->enqueueAdjacentVertices(vertex); - } else { if (network_->isLeaf(pin)) { if (vertex->isDriver(network_)) { @@ -656,9 +728,8 @@ GraphDelayCalc::loadSlews(LoadPinIndexMap &load_pin_index_map) Vertex *load_vertex = graph_->pinLoadVertex(pin); SlewSeq &slews = load_slews[index];; slews.resize(slew_count); - Slew *vertex_slews = load_vertex->slews(); for (size_t i = 0; i < slew_count; i++) - slews[i] = vertex_slews[i]; + slews[i] = graph_->slew(load_vertex, i); } return load_slews; } @@ -671,9 +742,9 @@ GraphDelayCalc::loadSlewsChanged(DrvrLoadSlews &load_slews_prev, for (auto const [pin, index] : load_pin_index_map) { Vertex *load_vertex = graph_->pinLoadVertex(pin); SlewSeq &slews_prev = load_slews_prev[index];; - const Slew *slews = load_vertex->slews(); for (size_t i = 0; i < slew_count; i++) { - if (!delayEqual(slews[i], slews_prev[i])) + const Slew slew = graph_->slew(load_vertex, i); + if (!delayEqual(slew, slews_prev[i], this)) return true; } } @@ -793,7 +864,7 @@ isLeafDriver(const Pin *pin, MultiDrvrNet * GraphDelayCalc::multiDrvrNet(const Vertex *drvr_vertex) const { - return multi_drvr_net_map_.findKey(drvr_vertex); + return findKey(multi_drvr_net_map_, drvr_vertex); } MultiDrvrNet * @@ -813,7 +884,7 @@ GraphDelayCalc::makeMultiDrvrNet(Vertex *drvr_vertex) Vertex *drvr = edge->from(graph_); const Pin *drvr_pin = drvr->pin(); if (isLeafDriver(drvr_pin, network_)) { - debugPrint(debug_, "delay_calc", 3, " %s", + debugPrint(debug_, "delay_calc", 3, " {}", network_->pathName(drvr_pin)); multi_drvr_net_map_[drvr] = multi_drvr; drvr_vertices.push_back(drvr); @@ -827,7 +898,7 @@ GraphDelayCalc::makeMultiDrvrNet(Vertex *drvr_vertex) } } multi_drvr->setDcalcDrvr(max_drvr); - multi_drvr->findCaps(sdc_); + multi_drvr->findCaps(this); return multi_drvr; } report_->critical(1101, "mult_drvr missing load."); @@ -842,13 +913,14 @@ GraphDelayCalc::initLoadSlews(Vertex *drvr_vertex) Edge *wire_edge = edge_iter.next(); if (wire_edge->isWire()) { Vertex *load_vertex = wire_edge->to(graph_); - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - Slew slew_init_value(slew_min_max->initValue()); - DcalcAPIndex ap_index = dcalc_ap->index(); - for (const RiseFall *rf : RiseFall::range()) { - if (!load_vertex->slewAnnotated(rf, slew_min_max)) - graph_->setSlew(load_vertex, rf, ap_index, slew_init_value); + for (Scene *scene : scenes_) { + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + Slew slew_init_value(min_max->initValue()); + for (const RiseFall *rf : RiseFall::range()) { + if (!load_vertex->slewAnnotated(rf, min_max)) + graph_->setSlew(load_vertex, rf, ap_index, slew_init_value); + } } } } @@ -864,15 +936,11 @@ GraphDelayCalc::findDriverDelays1(Vertex *drvr_vertex, initSlew(drvr_vertex); initWireDelays(drvr_vertex); bool delay_changed = false; - array delay_exists = {false, false}; + std::array delay_exists = {false, false}; VertexInEdgeIterator edge_iter(drvr_vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); - Vertex *from_vertex = edge->from(graph_); - // Don't let disabled edges set slews that influence downstream delays. - if (search_pred_->searchFrom(from_vertex) - && search_pred_->searchThru(edge) - && !edge->role()->isLatchDtoQ()) + if (!edge->role()->isLatchDtoQ()) delay_changed |= findDriverEdgeDelays(drvr_vertex, multi_drvr, edge, arc_delay_calc, load_pin_index_map, delay_exists); @@ -891,12 +959,13 @@ GraphDelayCalc::findDriverDelays1(Vertex *drvr_vertex, void GraphDelayCalc::initRootSlews(Vertex *vertex) { - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - DcalcAPIndex ap_index = dcalc_ap->index(); - for (const RiseFall *rf : RiseFall::range()) { - if (!vertex->slewAnnotated(rf, slew_min_max)) - graph_->setSlew(vertex, rf, ap_index, default_slew); + for (Scene *scene : scenes_) { + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + for (const RiseFall *rf : RiseFall::range()) { + if (!vertex->slewAnnotated(rf, min_max)) + graph_->setSlew(vertex, rf, ap_index, default_slew); + } } } } @@ -907,9 +976,9 @@ GraphDelayCalc::findLatchEdgeDelays(Edge *edge) Vertex *drvr_vertex = edge->to(graph_); const Pin *drvr_pin = drvr_vertex->pin(); Instance *drvr_inst = network_->instance(drvr_pin); - debugPrint(debug_, "delay_calc", 2, "find latch D->Q %s", + debugPrint(debug_, "delay_calc", 2, "find latch D->Q {}", sdc_network_->pathName(drvr_inst)); - array delay_exists = {false, false}; + std::array delay_exists = {false, false}; LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); bool delay_changed = findDriverEdgeDelays(drvr_vertex, nullptr, edge, arc_delay_calc_, load_pin_index_map, @@ -925,18 +994,25 @@ GraphDelayCalc::findDriverEdgeDelays(Vertex *drvr_vertex, ArcDelayCalc *arc_delay_calc, LoadPinIndexMap &load_pin_index_map, // Return value. - array &delay_exists) + std::array &delay_exists) { Vertex *from_vertex = edge->from(graph_); const TimingArcSet *arc_set = edge->timingArcSet(); bool delay_changed = false; - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { + + for (Scene *scene : scenes_) { + const Mode *mode = scene->mode(); + if (search_pred_->searchFrom(from_vertex, mode) + && search_pred_->searchThru(edge, mode)) { + for (const MinMax *min_max : MinMax::range()) { for (const TimingArc *arc : arc_set->arcs()) { delay_changed |= findDriverArcDelays(drvr_vertex, multi_drvr, edge, arc, - dcalc_ap, arc_delay_calc, + scene, min_max, arc_delay_calc, load_pin_index_map); delay_exists[arc->toEdge()->asRiseFall()->index()] = true; } + } + } } if (delay_changed && observer_) { observer_->delayChangedFrom(from_vertex); @@ -950,12 +1026,13 @@ void GraphDelayCalc::findDriverArcDelays(Vertex *drvr_vertex, Edge *edge, const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc) { MultiDrvrNet *multi_drvr = multiDrvrNet(drvr_vertex); LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); - findDriverArcDelays(drvr_vertex, multi_drvr, edge, arc, dcalc_ap, + findDriverArcDelays(drvr_vertex, multi_drvr, edge, arc, scene, min_max, arc_delay_calc, load_pin_index_map); } @@ -964,7 +1041,8 @@ GraphDelayCalc::findDriverArcDelays(Vertex *drvr_vertex, const MultiDrvrNet *multi_drvr, Edge *edge, const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc, LoadPinIndexMap &load_pin_index_map) { @@ -975,33 +1053,33 @@ GraphDelayCalc::findDriverArcDelays(Vertex *drvr_vertex, const Pin *drvr_pin = drvr_vertex->pin(); const Parasitic *parasitic; float load_cap; - parasiticLoad(drvr_pin, drvr_rf, dcalc_ap, multi_drvr, arc_delay_calc, + parasiticLoad(drvr_pin, drvr_rf, scene, min_max, multi_drvr, arc_delay_calc, load_cap, parasitic); if (multi_drvr && multi_drvr->parallelGates(network_)) { ArcDcalcArgSeq dcalc_args = makeArcDcalcArgs(drvr_vertex, multi_drvr, - edge, arc, dcalc_ap, + edge, arc, scene, min_max, arc_delay_calc); ArcDcalcResultSeq dcalc_results = - arc_delay_calc->gateDelays(dcalc_args, load_pin_index_map, dcalc_ap); + arc_delay_calc->gateDelays(dcalc_args, load_pin_index_map, scene, min_max); for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) { ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx]; ArcDcalcResult &dcalc_result = dcalc_results[drvr_idx]; delay_changed |= annotateDelaysSlews(dcalc_arg.edge(), dcalc_arg.arc(), dcalc_result, load_pin_index_map, - dcalc_ap); + scene, min_max); } } else { Vertex *from_vertex = edge->from(graph_); - const Slew in_slew = edgeFromSlew(from_vertex, from_rf, edge, dcalc_ap); + const Slew in_slew = edgeFromSlew(from_vertex, from_rf, edge, scene, min_max); ArcDcalcResult dcalc_result = arc_delay_calc->gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, load_pin_index_map, - dcalc_ap); + scene, min_max); delay_changed |= annotateDelaysSlews(edge, arc, dcalc_result, - load_pin_index_map, dcalc_ap); + load_pin_index_map, scene, min_max); } arc_delay_calc->finishDrvrPin(); } @@ -1013,7 +1091,8 @@ GraphDelayCalc::makeArcDcalcArgs(Vertex *drvr_vertex, const MultiDrvrNet *multi_drvr, Edge *edge, const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc) { ArcDcalcArgSeq dcalc_args; @@ -1032,11 +1111,12 @@ GraphDelayCalc::makeArcDcalcArgs(Vertex *drvr_vertex, const Pin *from_pin = from_vertex->pin(); const RiseFall *from_rf = arc1->fromEdge()->asRiseFall(); const RiseFall *drvr_rf = arc1->toEdge()->asRiseFall(); - const Slew in_slew = edgeFromSlew(from_vertex, from_rf, edge1, dcalc_ap); + const Slew in_slew = edgeFromSlew(from_vertex, from_rf, edge1, scene, min_max); + const Pin *drvr_pin1 = drvr_vertex1->pin(); float load_cap; const Parasitic *parasitic; - parasiticLoad(drvr_pin1, drvr_rf, dcalc_ap, multi_drvr, arc_delay_calc, + parasiticLoad(drvr_pin1, drvr_rf, scene, min_max, multi_drvr, arc_delay_calc, load_cap, parasitic); dcalc_args.emplace_back(from_pin, drvr_pin1, edge1, arc1, in_slew, load_cap, parasitic); @@ -1062,7 +1142,8 @@ GraphDelayCalc::findParallelEdge(Vertex *vertex, VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { edge = edge_iter.next(); - if (edge->timingArcSet() == arc->set()) + if (edge->timingArcSet() == arc->set() + && !edge->isBidirectInstPath()) return; } } @@ -1072,7 +1153,8 @@ GraphDelayCalc::findParallelEdge(Vertex *vertex, edge = edge_iter.next(); for (TimingArc *arc1 : edge->timingArcSet()->arcs()) { if (arc1->fromEdge() == drvr_arc->fromEdge() - && arc1->toEdge() == drvr_arc->toEdge()) { + && arc1->toEdge() == drvr_arc->toEdge() + && !edge->isBidirectInstPath()) { arc = arc1; return; } @@ -1088,17 +1170,18 @@ GraphDelayCalc::annotateDelaysSlews(Edge *edge, const TimingArc *arc, ArcDcalcResult &dcalc_result, LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { bool delay_changed = annotateDelaySlew(edge, arc, dcalc_result.gateDelay(), - dcalc_result.drvrSlew(), dcalc_ap); + dcalc_result.drvrSlew(), scene, min_max); if (!edge->role()->isLatchDtoQ()) { Vertex *drvr_vertex = edge->to(graph_); delay_changed |= annotateLoadDelays(drvr_vertex, arc->toEdge()->asRiseFall(), dcalc_result, load_pin_index_map, delay_zero, true, - dcalc_ap); + scene, min_max); } return delay_changed; } @@ -1109,40 +1192,40 @@ GraphDelayCalc::annotateDelaysSlews(Edge *edge, bool GraphDelayCalc::annotateDelaySlew(Edge *edge, const TimingArc *arc, - ArcDelay &gate_delay, - Slew &gate_slew, - const DcalcAnalysisPt *dcalc_ap) + const ArcDelay &gate_delay, + const Slew &gate_slew, + const Scene *scene, + const MinMax *min_max) { - bool delay_changed = false; - DcalcAPIndex ap_index = dcalc_ap->index(); + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); debugPrint(debug_, "delay_calc", 3, - " %s %s -> %s %s (%s) corner:%s/%s", + " {} {} -> {} {} ({}) scene:{}/{}", arc->from()->name(), - arc->fromEdge()->to_string().c_str(), + arc->fromEdge()->to_string(), arc->to()->name(), - arc->toEdge()->to_string().c_str(), - arc->role()->to_string().c_str(), - dcalc_ap->corner()->name(), - dcalc_ap->delayMinMax()->to_string().c_str()); + arc->toEdge()->to_string(), + arc->role()->to_string(), + scene->name(), + min_max->to_string()); debugPrint(debug_, "delay_calc", 3, - " gate delay = %s slew = %s", + " gate delay = {} slew = {}", delayAsString(gate_delay, this), delayAsString(gate_slew, this)); + bool delay_changed = false; Vertex *drvr_vertex = edge->to(graph_); const RiseFall *drvr_rf = arc->toEdge()->asRiseFall(); // Merge slews. - const Slew &drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index); - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - if (delayGreater(gate_slew, drvr_slew, slew_min_max, this) - && !drvr_vertex->slewAnnotated(drvr_rf, slew_min_max) + const Slew drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index); + if (delayGreater(gate_slew, drvr_slew, min_max, this) + && !drvr_vertex->slewAnnotated(drvr_rf, min_max) && !edge->role()->isLatchDtoQ()) graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew); if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) { - const ArcDelay &prev_gate_delay = graph_->arcDelay(edge,arc,ap_index); + const ArcDelay prev_gate_delay = graph_->arcDelay(edge,arc,ap_index); float gate_delay1 = delayAsFloat(gate_delay); float prev_gate_delay1 = delayAsFloat(prev_gate_delay); if (prev_gate_delay1 == 0.0 - || (abs(gate_delay1 - prev_gate_delay1) / prev_gate_delay1 + || (std::abs(gate_delay1 - prev_gate_delay1) / prev_gate_delay1 > incremental_delay_tolerance_)) delay_changed = true; graph_->setArcDelay(edge, arc, ap_index, gate_delay); @@ -1160,11 +1243,11 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex, LoadPinIndexMap &load_pin_index_map, const ArcDelay &extra_delay, bool merge, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); bool changed = false; - DcalcAPIndex ap_index = dcalc_ap->index(); - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); while (edge_iter.hasNext()) { Edge *wire_edge = edge_iter.next(); @@ -1172,39 +1255,38 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex, Vertex *load_vertex = wire_edge->to(graph_); Pin *load_pin = load_vertex->pin(); size_t load_idx = load_pin_index_map[load_pin]; - ArcDelay wire_delay = dcalc_result.wireDelay(load_idx); - Slew load_slew = dcalc_result.loadSlew(load_idx); + const ArcDelay &wire_delay = dcalc_result.wireDelay(load_idx); + const Slew &load_slew = dcalc_result.loadSlew(load_idx); debugPrint(debug_, "delay_calc", 3, - " %s load delay = %s slew = %s", - load_vertex->to_string(this).c_str(), + " {} load delay = {} slew = {}", + load_vertex->to_string(this), delayAsString(wire_delay, this), delayAsString(load_slew, this)); bool load_changed = false; - if (!load_vertex->slewAnnotated(drvr_rf, slew_min_max)) { - if (drvr_vertex->slewAnnotated(drvr_rf, slew_min_max)) { - // Copy the driver slew to the load if it is annotated. - const Slew &drvr_slew = graph_->slew(drvr_vertex,drvr_rf,ap_index); - graph_->setSlew(load_vertex, drvr_rf, ap_index, drvr_slew); + if (!load_vertex->slewAnnotated(drvr_rf, min_max)) { + if (drvr_vertex->slewAnnotated(drvr_rf, min_max)) { + // Copy the driver slew to the load if it is annotated. + const Slew drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index); + graph_->setSlew(load_vertex, drvr_rf, ap_index, drvr_slew); load_changed = true; - } - else { - const Slew &slew = graph_->slew(load_vertex, drvr_rf, ap_index); - if (!merge - || delayGreater(load_slew, slew, slew_min_max, this)) { - graph_->setSlew(load_vertex, drvr_rf, ap_index, load_slew); + } + else { + const Slew slew = graph_->slew(load_vertex, drvr_rf, ap_index); + if (!merge + || delayGreater(load_slew, slew, min_max, this)) { + graph_->setSlew(load_vertex, drvr_rf, ap_index, load_slew); load_changed = true; } - } + } } if (!graph_->wireDelayAnnotated(wire_edge, drvr_rf, ap_index)) { // Multiple timing arcs with the same output transition // annotate the same wire edges so they must be combined // rather than set. const ArcDelay &delay = graph_->wireArcDelay(wire_edge, drvr_rf, ap_index); - Delay wire_delay_extra = extra_delay + wire_delay; - const MinMax *delay_min_max = dcalc_ap->delayMinMax(); + Delay wire_delay_extra = delaySum(extra_delay, wire_delay, this); if (!merge - || delayGreater(wire_delay_extra, delay, delay_min_max, this)) { + || delayGreater(wire_delay_extra, delay, min_max, this)) { graph_->setWireArcDelay(wire_edge, drvr_rf, ap_index, wire_delay_extra); load_changed = true; } @@ -1241,12 +1323,12 @@ GraphDelayCalc::makeLoadPinIndexMap(Vertex *drvr_vertex) // External float GraphDelayCalc::loadCap(const Pin *drvr_pin, - const DcalcAnalysisPt *dcalc_ap) const + const Scene *scene, + const MinMax *min_max) const { - const MinMax *min_max = dcalc_ap->constraintMinMax(); float load_cap = min_max->initValue(); for (const RiseFall *drvr_rf : RiseFall::range()) { - float cap = loadCap(drvr_pin, drvr_rf, dcalc_ap); + float cap = loadCap(drvr_pin, drvr_rf, scene, min_max); load_cap = min_max->minMax(cap, load_cap); } arc_delay_calc_->finishDrvrPin(); @@ -1257,10 +1339,11 @@ GraphDelayCalc::loadCap(const Pin *drvr_pin, float GraphDelayCalc::loadCap(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) const + const Scene *scene, + const MinMax *min_max) const { float pin_cap, wire_cap; - loadCap(drvr_pin, rf, dcalc_ap, pin_cap, wire_cap); + loadCap(drvr_pin, rf, scene, min_max, pin_cap, wire_cap); return pin_cap + wire_cap; } @@ -1268,7 +1351,8 @@ GraphDelayCalc::loadCap(const Pin *drvr_pin, void GraphDelayCalc::loadCap(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, float &pin_cap, float &wire_cap) const { @@ -1278,7 +1362,7 @@ GraphDelayCalc::loadCap(const Pin *drvr_pin, multi_drvr = multiDrvrNet(drvr_vertex); } const Parasitic *parasitic; - parasiticLoad(drvr_pin, rf, dcalc_ap, multi_drvr, arc_delay_calc_, + parasiticLoad(drvr_pin, rf, scene, min_max, multi_drvr, arc_delay_calc_, pin_cap, wire_cap, parasitic); arc_delay_calc_->finishDrvrPin(); } @@ -1286,12 +1370,13 @@ GraphDelayCalc::loadCap(const Pin *drvr_pin, float GraphDelayCalc::loadCap(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc) const { const Parasitic *parasitic; float pin_cap, wire_cap; - parasiticLoad(drvr_pin, rf, dcalc_ap, nullptr, arc_delay_calc, + parasiticLoad(drvr_pin, rf, scene, min_max, nullptr, arc_delay_calc, pin_cap, wire_cap, parasitic); return pin_cap + wire_cap; } @@ -1299,7 +1384,8 @@ GraphDelayCalc::loadCap(const Pin *drvr_pin, void GraphDelayCalc::parasiticLoad(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const MultiDrvrNet *multi_drvr, ArcDelayCalc *arc_delay_calc, // Return values. @@ -1307,7 +1393,7 @@ GraphDelayCalc::parasiticLoad(const Pin *drvr_pin, const Parasitic *¶sitic) const { float pin_cap, wire_cap; - parasiticLoad(drvr_pin, rf, dcalc_ap, multi_drvr, arc_delay_calc, + parasiticLoad(drvr_pin, rf, scene, min_max, multi_drvr, arc_delay_calc, pin_cap, wire_cap, parasitic); load_cap = pin_cap + wire_cap; } @@ -1315,7 +1401,8 @@ GraphDelayCalc::parasiticLoad(const Pin *drvr_pin, void GraphDelayCalc::parasiticLoad(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const MultiDrvrNet *multi_drvr, ArcDelayCalc *arc_delay_calc, // Return values. @@ -1323,19 +1410,20 @@ GraphDelayCalc::parasiticLoad(const Pin *drvr_pin, float &wire_cap, const Parasitic *¶sitic) const { + Parasitics *parasitics = scene->parasitics(min_max); bool has_net_load; float fanout; - netCaps(drvr_pin, rf, dcalc_ap, multi_drvr, + netCaps(drvr_pin, rf, scene, min_max, multi_drvr, pin_cap, wire_cap, fanout, has_net_load); - parasitic = arc_delay_calc->findParasitic(drvr_pin, rf, dcalc_ap); + parasitic = arc_delay_calc->findParasitic(drvr_pin, rf, scene, min_max); // set_load net has precedence over parasitics. if (!has_net_load && parasitic) { - if (parasitics_->isParasiticNetwork(parasitic)) - wire_cap += parasitics_->capacitance(parasitic); + if (parasitics->isParasiticNetwork(parasitic)) + wire_cap += parasitics->capacitance(parasitic); else { // PiModel includes both pin and external caps. - float parasitic_cap = parasitics_->capacitance(parasitic); + float parasitic_cap = parasitics->capacitance(parasitic); if (parasitic_cap >= pin_cap) wire_cap = parasitic_cap - pin_cap; else { @@ -1350,7 +1438,8 @@ GraphDelayCalc::parasiticLoad(const Pin *drvr_pin, void GraphDelayCalc::netCaps(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, // Return values. float &pin_cap, float &wire_cap, @@ -1362,14 +1451,15 @@ GraphDelayCalc::netCaps(const Pin *drvr_pin, Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin); multi_drvr = multiDrvrNet(drvr_vertex); } - netCaps(drvr_pin, rf, dcalc_ap, multi_drvr, + netCaps(drvr_pin, rf, scene, min_max, multi_drvr, pin_cap, wire_cap, fanout, has_net_load); } void GraphDelayCalc::netCaps(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const MultiDrvrNet *multi_drvr, // Return values. float &pin_cap, @@ -1378,13 +1468,12 @@ GraphDelayCalc::netCaps(const Pin *drvr_pin, bool &has_net_load) const { if (multi_drvr) - multi_drvr->netCaps(rf, dcalc_ap, + multi_drvr->netCaps(rf, scene, min_max, pin_cap, wire_cap, fanout, has_net_load); else { - const Corner *corner = dcalc_ap->corner(); - const MinMax *min_max = dcalc_ap->constraintMinMax(); + const Sdc *sdc = scene->sdc(); // Find pin and external pin/wire capacitance. - sdc_->connectedCap(drvr_pin, rf, corner, min_max, + sdc->connectedCap(drvr_pin, rf, scene, min_max, pin_cap, wire_cap, fanout, has_net_load); } } @@ -1392,12 +1481,12 @@ GraphDelayCalc::netCaps(const Pin *drvr_pin, void GraphDelayCalc::initSlew(Vertex *vertex) { + for (Scene *scene : scenes_) { + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); for (const RiseFall *rf : RiseFall::range()) { - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - if (!vertex->slewAnnotated(rf, slew_min_max)) { - DcalcAPIndex ap_index = dcalc_ap->index(); - graph_->setSlew(vertex, rf, ap_index, slew_min_max->initValue()); + if (!vertex->slewAnnotated(rf, min_max)) + graph_->setSlew(vertex, rf, ap_index, min_max->initValue()); } } } @@ -1407,26 +1496,25 @@ void GraphDelayCalc::zeroSlewAndWireDelays(Vertex *drvr_vertex, const RiseFall *rf) { - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - DcalcAPIndex ap_index = dcalc_ap->index(); - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - // Init drvr slew. - if (!drvr_vertex->slewAnnotated(rf, slew_min_max)) { - DcalcAPIndex ap_index = dcalc_ap->index(); - graph_->setSlew(drvr_vertex, rf, ap_index, slew_min_max->initValue()); - } + for (Scene *scene : scenes_) { + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + // Init drvr slew. + if (!drvr_vertex->slewAnnotated(rf, min_max)) + graph_->setSlew(drvr_vertex, rf, ap_index, min_max->initValue()); - // Init wire delays and slews. - VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); - while (edge_iter.hasNext()) { - Edge *wire_edge = edge_iter.next(); - if (wire_edge->isWire()) { - Vertex *load_vertex = wire_edge->to(graph_); - if (!graph_->wireDelayAnnotated(wire_edge, rf, ap_index)) - graph_->setWireArcDelay(wire_edge, rf, ap_index, 0.0); - // Init load vertex slew. - if (!load_vertex->slewAnnotated(rf, slew_min_max)) - graph_->setSlew(load_vertex, rf, ap_index, 0.0); + // Init wire delays and slews. + VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *wire_edge = edge_iter.next(); + if (wire_edge->isWire()) { + Vertex *load_vertex = wire_edge->to(graph_); + if (!graph_->wireDelayAnnotated(wire_edge, rf, ap_index)) + graph_->setWireArcDelay(wire_edge, rf, ap_index, 0.0); + // Init load vertex slew. + if (!load_vertex->slewAnnotated(rf, min_max)) + graph_->setSlew(load_vertex, rf, ap_index, 0.0); + } } } } @@ -1439,10 +1527,11 @@ GraphDelayCalc::initWireDelays(Vertex *drvr_vertex) while (edge_iter.hasNext()) { Edge *wire_edge = edge_iter.next(); if (wire_edge->isWire()) { - for (const DcalcAnalysisPt * dcalc_ap : corners_->dcalcAnalysisPts()) { - const MinMax *delay_min_max = dcalc_ap->delayMinMax(); - Delay delay_init_value(delay_min_max->initValue()); - DcalcAPIndex ap_index = dcalc_ap->index(); + for (Scene *scene : scenes_) { + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + + Delay delay_init_value(min_max->initValue()); for (const RiseFall *rf : RiseFall::range()) { if (!graph_->wireDelayAnnotated(wire_edge, rf, ap_index)) graph_->setWireArcDelay(wire_edge, rf, ap_index, delay_init_value); @@ -1450,15 +1539,17 @@ GraphDelayCalc::initWireDelays(Vertex *drvr_vertex) } } } + } } Slew GraphDelayCalc::edgeFromSlew(const Vertex *from_vertex, const RiseFall *from_rf, const Edge *edge, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { - return edgeFromSlew(from_vertex, from_rf, edge->role(), dcalc_ap); + return edgeFromSlew(from_vertex, from_rf, edge->role(), scene, min_max); } // Use clock slew for register/latch clk->q edges. @@ -1466,15 +1557,17 @@ Slew GraphDelayCalc::edgeFromSlew(const Vertex *from_vertex, const RiseFall *from_rf, const TimingRole *role, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { - + const ClkNetwork *clk_network = scene->mode()->clkNetwork(); if (role->genericRole() == TimingRole::regClkToQ() - && clk_network_->isIdealClock(from_vertex->pin())) - return clk_network_->idealClkSlew(from_vertex->pin(), from_rf, - dcalc_ap->slewMinMax()); - else - return graph_->slew(from_vertex, from_rf, dcalc_ap->index()); + && clk_network->isIdealClock(from_vertex)) + return clk_network->idealClkSlew(from_vertex->pin(), from_rf, min_max); + else { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + return graph_->slew(from_vertex, from_rf, ap_index); + } } void @@ -1486,7 +1579,7 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge, TimingArcSet *arc_set = edge->timingArcSet(); const Pin *to_pin = to_vertex->pin(); Instance *inst = network_->instance(to_pin); - debugPrint(debug_, "delay_calc", 2, "find check %s %s -> %s", + debugPrint(debug_, "delay_calc", 2, "find check {} {} -> {}", sdc_network_->pathName(inst), network_->portName(from_vertex->pin()), network_->portName(to_pin)); @@ -1496,42 +1589,45 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge, const RiseFall *to_rf = arc->toEdge()->asRiseFall(); if (from_rf && to_rf) { const LibertyPort *related_out_port = arc_set->relatedOut(); - const Pin *related_out_pin = 0; + const Pin *related_out_pin = nullptr; if (related_out_port) - related_out_pin = network_->findPin(inst, related_out_port); - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - DcalcAPIndex ap_index = dcalc_ap->index(); - if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) { - const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, - dcalc_ap); - int slew_index = dcalc_ap->checkDataSlewIndex(); - const Slew &to_slew = graph_->slew(to_vertex, to_rf, slew_index); - debugPrint(debug_, "delay_calc", 3, - " %s %s -> %s %s (%s) corner:%s/%s", - arc_set->from()->name(), - arc->fromEdge()->to_string().c_str(), - arc_set->to()->name(), - arc->toEdge()->to_string().c_str(), - arc_set->role()->to_string().c_str(), - dcalc_ap->corner()->name(), - dcalc_ap->delayMinMax()->to_string().c_str()); - debugPrint(debug_, "delay_calc", 3, - " from_slew = %s to_slew = %s", - delayAsString(from_slew, this), - delayAsString(to_slew, this)); - float related_out_cap = 0.0; - if (related_out_pin) - related_out_cap = loadCap(related_out_pin, to_rf,dcalc_ap,arc_delay_calc); - ArcDelay check_delay = arc_delay_calc->checkDelay(to_pin, arc, from_slew, - to_slew, related_out_cap, - dcalc_ap); - debugPrint(debug_, "delay_calc", 3, - " check_delay = %s", - delayAsString(check_delay, this)); - graph_->setArcDelay(edge, arc, ap_index, check_delay); - delay_changed = true; - arc_delay_calc_->finishDrvrPin(); - } + related_out_pin = network_->findPin(inst, related_out_port); + + for (Scene *scene : scenes_) { + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) { + const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, + scene, min_max); + const Slew to_slew = graph_->slew(to_vertex, to_rf, ap_index); + debugPrint(debug_, "delay_calc", 3, + " {} {} -> {} {} ({}) scene:{}/{}", + arc_set->from()->name(), + arc->fromEdge()->to_string(), + arc_set->to()->name(), + arc->toEdge()->to_string(), + arc_set->role()->to_string(), + scene->name(), + min_max->to_string()); + debugPrint(debug_, "delay_calc", 3, + " from_slew = {} to_slew = {}", + delayAsString(from_slew, this), + delayAsString(to_slew, this)); + float related_out_cap = 0.0; + if (related_out_pin) + related_out_cap = loadCap(related_out_pin, to_rf,scene,min_max, + arc_delay_calc); + ArcDelay check_delay = arc_delay_calc->checkDelay(to_pin, arc, from_slew, + to_slew, related_out_cap, + scene, min_max); + debugPrint(debug_, "delay_calc", 3, + " check_delay = {}", + delayAsString(check_delay, this)); + graph_->setArcDelay(edge, arc, ap_index, check_delay); + delay_changed = true; + arc_delay_calc_->finishDrvrPin(); + } + } } } } @@ -1544,21 +1640,24 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge, Slew GraphDelayCalc::checkEdgeClkSlew(const Vertex *from_vertex, const RiseFall *from_rf, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { - if (clk_network_->isIdealClock(from_vertex->pin())) - return clk_network_->idealClkSlew(from_vertex->pin(), from_rf, - dcalc_ap->checkClkSlewMinMax()); + const ClkNetwork *clk_network = scene->mode()->clkNetwork(); + if (clk_network->isIdealClock(from_vertex)) + return clk_network->idealClkSlew(from_vertex->pin(), from_rf, + scene->checkClkSlewMinMax(min_max)); else - return graph_->slew(from_vertex, from_rf, dcalc_ap->checkClkSlewIndex()); + return graph_->slew(from_vertex, from_rf, + scene->checkClkSlewIndex(min_max)); } //////////////////////////////////////////////////////////////// -string +std::string GraphDelayCalc::reportDelayCalc(const Edge *edge, const TimingArc *arc, - const Corner *corner, + const Scene *scene, const MinMax *min_max, int digits) { @@ -1568,38 +1667,39 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge, const TimingRole *role = arc->role(); const Instance *inst = network_->instance(to_pin); const TimingArcSet *arc_set = edge->timingArcSet(); - string result; - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); + std::string result; const RiseFall *from_rf = arc->fromEdge()->asRiseFall(); const RiseFall *to_rf = arc->toEdge()->asRiseFall(); if (from_rf && to_rf) { const LibertyPort *related_out_port = arc_set->relatedOut(); - const Pin *related_out_pin = 0; + const Pin *related_out_pin = nullptr; if (related_out_port) related_out_pin = network_->findPin(inst, related_out_port); float related_out_cap = 0.0; if (related_out_pin) - related_out_cap = loadCap(related_out_pin, to_rf, dcalc_ap, arc_delay_calc_); + related_out_cap = loadCap(related_out_pin, to_rf, scene, min_max, arc_delay_calc_); if (role->isTimingCheck()) { - const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, dcalc_ap); - int slew_index = dcalc_ap->checkDataSlewIndex(); - const Slew &to_slew = graph_->slew(to_vertex, to_rf, slew_index); - bool from_ideal_clk = clk_network_->isIdealClock(from_vertex->pin()); - const char *from_slew_annotation = from_ideal_clk ? " (ideal clock)" : nullptr; + const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, scene, min_max); + DcalcAPIndex slew_index = scene->dcalcAnalysisPtIndex(min_max); + const Slew to_slew = graph_->slew(to_vertex, to_rf, slew_index); + const ClkNetwork *clk_network = scene->mode()->clkNetwork(); + bool from_ideal_clk = clk_network->isIdealClock(from_vertex); + std::string_view from_slew_annotation = + from_ideal_clk ? std::string_view(" (ideal clock)") : std::string_view{}; result = arc_delay_calc_->reportCheckDelay(to_pin, arc, from_slew, from_slew_annotation, to_slew, - related_out_cap, dcalc_ap, digits); + related_out_cap, scene, min_max, digits); } else { - const Slew &from_slew = edgeFromSlew(from_vertex, from_rf, edge, dcalc_ap); + const Slew from_slew = edgeFromSlew(from_vertex, from_rf, edge, scene, min_max); const Parasitic *to_parasitic; float load_cap; - parasiticLoad(to_pin, to_rf, dcalc_ap, nullptr, arc_delay_calc_, + parasiticLoad(to_pin, to_rf, scene, min_max, nullptr, arc_delay_calc_, load_cap, to_parasitic); LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(to_vertex); result = arc_delay_calc_->reportGateDelay(to_pin, arc, from_slew, load_cap, to_parasitic, load_pin_index_map, - dcalc_ap, digits); + scene, min_max, digits); } arc_delay_calc_->finishDrvrPin(); } @@ -1610,19 +1710,18 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge, void GraphDelayCalc::minPeriod(const Pin *pin, - const Corner *corner, + const Scene *scene, // Return values. float &min_period, bool &exists) { exists = false; const MinMax *min_max = MinMax::max(); - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); + const DcalcAPIndex dcalc_ap_index = scene->dcalcAnalysisPtIndex(min_max); // Sdf annotation. float min_period1 = 0.0; bool exists1 = false; - graph_->periodCheckAnnotation(pin, dcalc_ap->index(), - min_period, exists); + graph_->periodCheckAnnotation(pin, dcalc_ap_index, min_period, exists); if (exists1 && (!exists || min_period1 < min_period)) { min_period = min_period1; @@ -1636,7 +1735,7 @@ GraphDelayCalc::minPeriod(const Pin *pin, graph_->minPeriodArc(vertex, RiseFall::rise(), edge, arc); if (edge) { exists = true; - min_period = delayAsFloat(graph_->arcDelay(edge, arc, dcalc_ap->index())); + min_period = delayAsFloat(graph_->arcDelay(edge, arc, dcalc_ap_index)); } } if (!exists) { @@ -1644,30 +1743,36 @@ GraphDelayCalc::minPeriod(const Pin *pin, LibertyPort *port = network_->libertyPort(pin); if (port) { Instance *inst = network_->instance(pin); - OperatingConditions *op_cond = sdc_->operatingConditions(min_max); - const Pvt *pvt = inst ? sdc_->pvt(inst, min_max) : nullptr; - port->minPeriod(op_cond, pvt, min_period, exists); + for (const Mode *mode : modes_) { + const Sdc *sdc = mode->sdc(); + OperatingConditions *op_cond = sdc->operatingConditions(min_max); + const Pvt *pvt = inst ? sdc->pvt(inst, min_max) : nullptr; + float min_period1 = 0.0; + bool exists1 = false; + port->minPeriod(op_cond, pvt, min_period1, exists1); + if (exists1 + && (!exists || min_period1 < min_period)) { + min_period = min_period1; + exists = true; + } + } } } } //////////////////////////////////////////////////////////////// -MultiDrvrNet::MultiDrvrNet() : - dcalc_drvr_(nullptr) -{ -} - void MultiDrvrNet::netCaps(const RiseFall *drvr_rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, // Return values. float &pin_cap, float &wire_cap, float &fanout, bool &has_net_load) const { - int index = dcalc_ap->index() * RiseFall::index_count + int index = scene->dcalcAnalysisPtIndex(min_max) * RiseFall::index_count + drvr_rf->index(); const NetCaps &net_caps = net_caps_[index]; pin_cap = net_caps.pinCap(); @@ -1677,16 +1782,15 @@ MultiDrvrNet::netCaps(const RiseFall *drvr_rf, } void -MultiDrvrNet::findCaps(const Sdc *sdc) +MultiDrvrNet::findCaps(const StaState *sta) { - Corners *corners = sdc->corners(); - int count = RiseFall::index_count * corners->dcalcAnalysisPtCount(); + int count = RiseFall::index_count * sta->dcalcAnalysisPtCount(); net_caps_.resize(count); const Pin *drvr_pin = dcalc_drvr_->pin(); - for (const DcalcAnalysisPt *dcalc_ap : corners->dcalcAnalysisPts()) { - DcalcAPIndex ap_index = dcalc_ap->index(); - const Corner *corner = dcalc_ap->corner(); - const MinMax *min_max = dcalc_ap->constraintMinMax(); + for (Scene *scene : sta->scenes()) { + const Sdc *sdc = scene->sdc(); + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); for (const RiseFall *drvr_rf : RiseFall::range()) { int drvr_rf_index = drvr_rf->index(); int index = ap_index * RiseFall::index_count + drvr_rf_index; @@ -1694,11 +1798,12 @@ MultiDrvrNet::findCaps(const Sdc *sdc) float pin_cap, wire_cap, fanout; bool has_net_load; // Find pin and external pin/wire capacitance. - sdc->connectedCap(drvr_pin, drvr_rf, corner, min_max, + sdc->connectedCap(drvr_pin, drvr_rf, scene, min_max, pin_cap, wire_cap, fanout, has_net_load); net_caps.init(pin_cap, wire_cap, fanout, has_net_load); } } + } } void @@ -1713,4 +1818,4 @@ MultiDrvrNet::parallelGates(const Network *network) const return network->direction(dcalc_drvr_->pin())->isOutput(); } -} // namespace +} // namespace sta diff --git a/dcalc/LumpedCapDelayCalc.cc b/dcalc/LumpedCapDelayCalc.cc index 334366627..41ef42502 100644 --- a/dcalc/LumpedCapDelayCalc.cc +++ b/dcalc/LumpedCapDelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -27,23 +27,19 @@ #include // isnan #include "Debug.hh" -#include "Units.hh" -#include "TimingArc.hh" -#include "TimingModel.hh" +#include "GraphDelayCalc.hh" #include "Liberty.hh" -#include "PortDirection.hh" #include "Network.hh" -#include "Sdc.hh" #include "Parasitics.hh" -#include "DcalcAnalysisPt.hh" -#include "GraphDelayCalc.hh" +#include "PortDirection.hh" +#include "Sdc.hh" +#include "TimingArc.hh" +#include "TimingModel.hh" +#include "Units.hh" #include "Variables.hh" namespace sta { -using std::string; -using std::isnan; - ArcDelayCalc * makeLumpedCapDelayCalc(StaState *sta) { @@ -63,36 +59,38 @@ LumpedCapDelayCalc::copy() Parasitic * LumpedCapDelayCalc::findParasitic(const Pin *drvr_pin, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) { Parasitic *parasitic = nullptr; - const Corner *corner = dcalc_ap->corner(); - // set_load net has precedence over parasitics. - if (sdc_->drvrPinHasWireCap(drvr_pin, corner) + Parasitics *parasitics = scene->parasitics(min_max); + const Sdc *sdc = scene->sdc(); + if (parasitics == nullptr + // set_load net has precedence over parasitics. + || sdc->drvrPinHasWireCap(drvr_pin) || network_->direction(drvr_pin)->isInternal()) - return nullptr; - const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); + return nullptr; + // Prefer PiElmore. - parasitic = parasitics_->findPiElmore(drvr_pin, rf, parasitic_ap); + parasitic = parasitics->findPiElmore(drvr_pin, rf, min_max); if (parasitic) return parasitic; - Parasitic *parasitic_network = parasitics_->findParasiticNetwork(drvr_pin, - parasitic_ap); + Parasitic *parasitic_network = parasitics->findParasiticNetwork(drvr_pin); if (parasitic_network) { - parasitic = reduceParasitic(parasitic_network, drvr_pin, rf, dcalc_ap); + parasitic = reduceParasitic(parasitic_network, drvr_pin, rf, scene, min_max); if (parasitic) return parasitic; } - const MinMax *min_max = dcalc_ap->constraintMinMax(); - Wireload *wireload = sdc_->wireload(min_max); + + Wireload *wireload = sdc->wireload(min_max); if (wireload) { float pin_cap, wire_cap, fanout; bool has_net_load; - graph_delay_calc_->netCaps(drvr_pin, rf, dcalc_ap, + graph_delay_calc_->netCaps(drvr_pin, rf, scene, min_max, pin_cap, wire_cap, fanout, has_net_load); - parasitic = parasitics_->estimatePiElmore(drvr_pin, rf, wireload, fanout, - pin_cap, corner, min_max); + parasitic = parasitics->estimatePiElmore(drvr_pin, rf, wireload, fanout, + pin_cap, scene, min_max); } return parasitic; } @@ -101,14 +99,13 @@ Parasitic * LumpedCapDelayCalc::reduceParasitic(const Parasitic *parasitic_network, const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { - const Corner *corner = dcalc_ap->corner(); - const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); - return parasitics_->reduceToPiElmore(parasitic_network, drvr_pin, rf, - corner, dcalc_ap->constraintMinMax(), - parasitic_ap); + Parasitics *parasitics = scene->parasitics(min_max); + return parasitics->reduceToPiElmore(parasitic_network, drvr_pin, rf, + scene, min_max); } ArcDcalcResult @@ -117,39 +114,49 @@ LumpedCapDelayCalc::inputPortDelay(const Pin *, const RiseFall *rf, const Parasitic *, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { const LibertyLibrary *drvr_library = network_->defaultLibertyLibrary(); - return makeResult(drvr_library,rf, 0.0, in_slew, load_pin_index_map); + return makeResult(drvr_library,rf, delay_zero, in_slew, load_pin_index_map); } ArcDcalcResult LumpedCapDelayCalc::gateDelay(const Pin *drvr_pin, const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *, + const Slew &in_slew, + float load_cap, + const Parasitic *, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { - GateTimingModel *model = arc->gateModel(dcalc_ap); + GateTimingModel *model = arc->gateModel(scene, min_max); debugPrint(debug_, "delay_calc", 3, - " in_slew = %s load_cap = %s lumped", + " in_slew = {} load_cap = {} lumped", delayAsString(in_slew, this), units()->capacitanceUnit()->asString(load_cap)); const RiseFall *rf = arc->toEdge()->asRiseFall(); const LibertyLibrary *drvr_library = arc->to()->libertyLibrary(); if (model) { - ArcDelay gate_delay; - Slew drvr_slew; + float gate_delay, drvr_slew; float in_slew1 = delayAsFloat(in_slew); // NaNs cause seg faults during table lookup. - if (isnan(load_cap) || isnan(delayAsFloat(in_slew))) - report_->error(1350, "gate delay input variable is NaN"); - model->gateDelay(pinPvt(drvr_pin, dcalc_ap), in_slew1, load_cap, - variables_->pocvEnabled(), - gate_delay, drvr_slew); - return makeResult(drvr_library, rf, gate_delay, drvr_slew, load_pin_index_map); + if (std::isnan(load_cap)) + report_->error(1350, "gate delay load cap is NaN"); + if (std::isnan(in_slew.mean())) + report_->error(1351, "gate delay input slew is NaN"); + const Pvt *pvt = pinPvt(drvr_pin, scene, min_max); + model->gateDelay(pvt, in_slew1, load_cap, gate_delay, drvr_slew); + + // Fill in pocv parameters. + ArcDelay gate_delay2(gate_delay); + Slew drvr_slew2(drvr_slew); + if (variables_->pocvEnabled()) + model->gateDelayPocv(pvt, in_slew1, load_cap, min_max, variables_->pocvMode(), + gate_delay2, drvr_slew2); + + return makeResult(drvr_library, rf, gate_delay2, drvr_slew2, load_pin_index_map); } else return makeResult(drvr_library, rf, delay_zero, delay_zero, load_pin_index_map); @@ -158,40 +165,44 @@ LumpedCapDelayCalc::gateDelay(const Pin *drvr_pin, ArcDcalcResult LumpedCapDelayCalc::makeResult(const LibertyLibrary *drvr_library, const RiseFall *rf, - ArcDelay gate_delay, - Slew drvr_slew, + const ArcDelay &gate_delay, + const Slew &drvr_slew, const LoadPinIndexMap &load_pin_index_map) { ArcDcalcResult dcalc_result(load_pin_index_map.size()); dcalc_result.setGateDelay(gate_delay); dcalc_result.setDrvrSlew(drvr_slew); + double drvr_slew1 = delayAsFloat(drvr_slew); for (const auto [load_pin, load_idx] : load_pin_index_map) { - ArcDelay wire_delay = 0.0; - thresholdAdjust(load_pin, drvr_library, rf, wire_delay, drvr_slew); + double wire_delay = 0.0; + double load_slew = drvr_slew1; + thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew); dcalc_result.setWireDelay(load_idx, wire_delay); - dcalc_result.setLoadSlew(load_idx, drvr_slew); + dcalc_result.setLoadSlew(load_idx, load_slew); } return dcalc_result; } -string +std::string LumpedCapDelayCalc::reportGateDelay(const Pin *check_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *, const LoadPinIndexMap &, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) { - GateTimingModel *model = arc->gateModel(dcalc_ap); + GateTimingModel *model = arc->gateModel(scene, min_max); if (model) { float in_slew1 = delayAsFloat(in_slew); - return model->reportGateDelay(pinPvt(check_pin, dcalc_ap), in_slew1, load_cap, - false, digits); + return model->reportGateDelay(pinPvt(check_pin, scene, min_max), + in_slew1, load_cap, min_max, + PocvMode::scalar, digits); } return ""; } -} // namespace +} // namespace sta diff --git a/dcalc/LumpedCapDelayCalc.hh b/dcalc/LumpedCapDelayCalc.hh index 5170d7a56..d9b24d74b 100644 --- a/dcalc/LumpedCapDelayCalc.hh +++ b/dcalc/LumpedCapDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -35,42 +35,47 @@ class LumpedCapDelayCalc : public ParallelDelayCalc public: LumpedCapDelayCalc(StaState *sta); ArcDelayCalc *copy() override; - const char *name() const override { return "lumped_cap"; } + std::string_view name() const override { return "lumped_cap"; } Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; bool reduceSupported() const override { return true; } Parasitic *reduceParasitic(const Parasitic *parasitic_network, const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResult inputPortDelay(const Pin *port_pin, float in_slew, const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResult gateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; - std::string reportGateDelay(const Pin *drvr_pin, + const Scene *scene, + const MinMax *min_max) override; + std::string reportGateDelay(const Pin *check_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) override; protected: ArcDcalcResult makeResult(const LibertyLibrary *drvr_library, const RiseFall *rf, - ArcDelay gate_delay, - Slew drvr_slew, + const ArcDelay &gate_delay, + const Slew &drvr_slew, const LoadPinIndexMap &load_pin_index_map); using ArcDelayCalc::reduceParasitic; @@ -79,4 +84,4 @@ protected: ArcDelayCalc * makeLumpedCapDelayCalc(StaState *sta); -} // namespace +} // namespace sta diff --git a/dcalc/NetCaps.cc b/dcalc/NetCaps.cc index 5aa200ed5..d1eaa45b4 100644 --- a/dcalc/NetCaps.cc +++ b/dcalc/NetCaps.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,14 +26,10 @@ namespace sta { -NetCaps::NetCaps() -{ -} - NetCaps::NetCaps(float pin_cap, - float wire_cap, - float fanout, - bool has_net_load) : + float wire_cap, + float fanout, + bool has_net_load) : pin_cap_(pin_cap), wire_cap_(wire_cap), fanout_(fanout), @@ -43,9 +39,9 @@ NetCaps::NetCaps(float pin_cap, void NetCaps::init(float pin_cap, - float wire_cap, - float fanout, - bool has_net_load) + float wire_cap, + float fanout, + bool has_net_load) { pin_cap_ = pin_cap; wire_cap_ = wire_cap; @@ -53,4 +49,4 @@ NetCaps::init(float pin_cap, has_net_load_ = has_net_load; } -} // namespace +} // namespace sta diff --git a/dcalc/NetCaps.hh b/dcalc/NetCaps.hh index 5679d7aed..a968ddda1 100644 --- a/dcalc/NetCaps.hh +++ b/dcalc/NetCaps.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -30,15 +30,15 @@ namespace sta { class NetCaps { public: - NetCaps(); + NetCaps() = default; NetCaps(float pin_cap, - float wire_cap, - float fanout, - bool has_net_load); + float wire_cap, + float fanout, + bool has_net_load); void init(float pin_cap, - float wire_cap, - float fanout, - bool has_net_load); + float wire_cap, + float fanout, + bool has_net_load); float pinCap() const { return pin_cap_; } float wireCap() const{ return wire_cap_; } float fanout() const{ return fanout_; } @@ -51,4 +51,4 @@ private: bool has_net_load_; }; -} // namespace +} // namespace sta diff --git a/dcalc/ParallelDelayCalc.cc b/dcalc/ParallelDelayCalc.cc index 8bb5b18c3..8a47956ce 100644 --- a/dcalc/ParallelDelayCalc.cc +++ b/dcalc/ParallelDelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,18 +24,16 @@ #include "ParallelDelayCalc.hh" -#include "TimingArc.hh" -#include "Corner.hh" -#include "Network.hh" #include "Graph.hh" -#include "Sdc.hh" -#include "Liberty.hh" #include "GraphDelayCalc.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Scene.hh" +#include "Sdc.hh" +#include "TimingArc.hh" namespace sta { -using std::vector; - ParallelDelayCalc::ParallelDelayCalc(StaState *sta): DelayCalcBase(sta) { @@ -44,56 +42,59 @@ ParallelDelayCalc::ParallelDelayCalc(StaState *sta): ArcDcalcResultSeq ParallelDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { if (dcalc_args.size() == 1) { ArcDcalcArg &dcalc_arg = dcalc_args[0]; ArcDcalcResult dcalc_result = gateDelay(dcalc_arg.drvrPin(), dcalc_arg.arc(), dcalc_arg.inSlew(), dcalc_arg.loadCap(), dcalc_arg.parasitic(), - load_pin_index_map, dcalc_ap); + load_pin_index_map, + scene, min_max); ArcDcalcResultSeq dcalc_results; dcalc_results.push_back(dcalc_result); return dcalc_results; } - return gateDelaysParallel(dcalc_args, load_pin_index_map, dcalc_ap); + return gateDelaysParallel(dcalc_args, load_pin_index_map, scene, min_max); } ArcDcalcResultSeq ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { size_t drvr_count = dcalc_args.size(); ArcDcalcResultSeq dcalc_results(drvr_count); Slew slew_sum = 0.0; ArcDelay load_delay_sum = 0.0; - vector intrinsic_delays(dcalc_args.size()); - vector load_delays(dcalc_args.size()); + std::vector intrinsic_delays(dcalc_args.size()); + std::vector load_delays(dcalc_args.size()); for (size_t drvr_idx = 0; drvr_idx < drvr_count; drvr_idx++) { ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx]; ArcDcalcResult &dcalc_result = dcalc_results[drvr_idx]; const Pin *drvr_pin = dcalc_arg.drvrPin(); const TimingArc *arc = dcalc_arg.arc(); - Slew in_slew = dcalc_arg.inSlew(); + const Slew &in_slew = dcalc_arg.inSlew(); ArcDcalcResult intrinsic_result = gateDelay(drvr_pin, arc, in_slew, 0.0, nullptr, - load_pin_index_map, dcalc_ap); + load_pin_index_map, scene, min_max); ArcDelay intrinsic_delay = intrinsic_result.gateDelay(); intrinsic_delays[drvr_idx] = intrinsic_result.gateDelay(); ArcDcalcResult gate_result = gateDelay(drvr_pin, arc, in_slew, dcalc_arg.loadCap(), dcalc_arg.parasitic(), - load_pin_index_map, dcalc_ap); + load_pin_index_map, scene, min_max); ArcDelay gate_delay = gate_result.gateDelay(); Slew drvr_slew = gate_result.drvrSlew(); - ArcDelay load_delay = gate_delay - intrinsic_delay; + ArcDelay load_delay = delayDiff(gate_delay, intrinsic_delay, this); load_delays[drvr_idx] = load_delay; - if (!delayZero(load_delay)) - load_delay_sum += 1.0 / load_delay; - if (!delayZero(drvr_slew)) - slew_sum += 1.0 / drvr_slew; + if (!delayZero(load_delay, this)) + delayIncr(load_delay_sum, delayDiv(1.0, load_delay, this), this); + if (!delayZero(drvr_slew, this)) + delayIncr(slew_sum, delayDiv(1.0, drvr_slew, this), this); dcalc_result.setLoadCount(load_pin_index_map.size()); for (const auto &[load_pin, load_idx] : load_pin_index_map) { @@ -102,17 +103,19 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args, } } - ArcDelay gate_load_delay = delayZero(load_delay_sum) + ArcDelay gate_load_delay = delayZero(load_delay_sum, this) + ? delay_zero + : delayDiv(1.0, load_delay_sum, this); + ArcDelay drvr_slew = delayZero(slew_sum, this) ? delay_zero - : 1.0 / load_delay_sum; - ArcDelay drvr_slew = delayZero(slew_sum) ? delay_zero : 1.0 / slew_sum; + : delayDiv(1.0, slew_sum, this); for (size_t drvr_idx = 0; drvr_idx < drvr_count; drvr_idx++) { ArcDcalcResult &dcalc_result = dcalc_results[drvr_idx]; - dcalc_result.setGateDelay(intrinsic_delays[drvr_idx] + gate_load_delay); + dcalc_result.setGateDelay(delaySum(intrinsic_delays[drvr_idx], gate_load_delay, this)); dcalc_result.setDrvrSlew(drvr_slew); } return dcalc_results; } -} // namespace +} // namespace sta diff --git a/dcalc/ParallelDelayCalc.hh b/dcalc/ParallelDelayCalc.hh index 40c8bc140..f20f4b741 100644 --- a/dcalc/ParallelDelayCalc.hh +++ b/dcalc/ParallelDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,8 +24,8 @@ #pragma once -#include #include +#include #include "DelayCalcBase.hh" @@ -38,11 +38,13 @@ public: ParallelDelayCalc(StaState *sta); ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; protected: ArcDcalcResultSeq gateDelaysParallel(ArcDcalcArgSeq &dcalc_args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); }; -} // namespace +} // namespace sta diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc index bce55d364..86456936f 100644 --- a/dcalc/PrimaDelayCalc.cc +++ b/dcalc/PrimaDelayCalc.cc @@ -1,57 +1,50 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "PrimaDelayCalc.hh" -#include // abs +#include +#include +#include // abs +#include #include "Debug.hh" -#include "Units.hh" -#include "TimingArc.hh" +#include "DmpDelayCalc.hh" +#include "Format.hh" +#include "Graph.hh" +#include "GraphDelayCalc.hh" #include "Liberty.hh" -#include "PortDirection.hh" #include "Network.hh" -#include "Sdc.hh" -#include "DcalcAnalysisPt.hh" -#include "Corner.hh" -#include "Graph.hh" #include "Parasitics.hh" -#include "GraphDelayCalc.hh" -#include "DmpDelayCalc.hh" - -#include -#include +#include "PortDirection.hh" +#include "Scene.hh" +#include "Sdc.hh" +#include "TimingArc.hh" +#include "Units.hh" namespace sta { -using std::string; -using std::abs; -using std::make_shared; -using Eigen::SparseLU; -using Eigen::HouseholderQR; -using Eigen::ColPivHouseholderQR; - // Lawrence Pillage - “Electronic Circuit & System Simulation Methods” 1998 // McGraw-Hill, Inc. New York, NY. @@ -63,14 +56,7 @@ makePrimaDelayCalc(StaState *sta) PrimaDelayCalc::PrimaDelayCalc(StaState *sta) : DelayCalcBase(sta), - dcalc_args_(nullptr), - load_pin_index_map_(nullptr), pin_node_map_(network_), - node_index_map_(ParasiticNodeLess(parasitics_, network_)), - prima_order_(3), - make_waveforms_(false), - waveform_drvr_pin_(nullptr), - waveform_load_pin_(nullptr), watch_pin_values_(network_), table_dcalc_(makeDmpCeffElmoreDelayCalc(sta)) { @@ -78,14 +64,9 @@ PrimaDelayCalc::PrimaDelayCalc(StaState *sta) : PrimaDelayCalc::PrimaDelayCalc(const PrimaDelayCalc &dcalc) : DelayCalcBase(dcalc), - dcalc_args_(nullptr), - load_pin_index_map_(nullptr), pin_node_map_(network_), - node_index_map_(ParasiticNodeLess(parasitics_, network_)), + node_index_map_(dcalc.node_index_map_), prima_order_(dcalc.prima_order_), - make_waveforms_(false), - waveform_drvr_pin_(nullptr), - waveform_load_pin_(nullptr), watch_pin_values_(network_), table_dcalc_(makeDmpCeffElmoreDelayCalc(this)) { @@ -113,27 +94,26 @@ PrimaDelayCalc::copyState(const StaState *sta) Parasitic * PrimaDelayCalc::findParasitic(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { - const Corner *corner = dcalc_ap->corner(); - const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); - // set_load net has precidence over parasitics. - if (sdc_->drvrPinHasWireCap(drvr_pin, corner) + const Sdc *sdc = scene->sdc(); + Parasitics *parasitics = scene->parasitics(min_max); + if (parasitics == nullptr + // set_load net has precedence over parasitics. || network_->direction(drvr_pin)->isInternal()) return nullptr; - Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + Parasitic *parasitic = parasitics->findParasiticNetwork(drvr_pin); if (parasitic) return parasitic; - const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); - Wireload *wireload = sdc_->wireload(cnst_min_max); + Wireload *wireload = sdc->wireload(min_max); if (wireload) { float pin_cap, wire_cap, fanout; bool has_wire_cap; - graph_delay_calc_->netCaps(drvr_pin, rf, dcalc_ap, pin_cap, wire_cap, + graph_delay_calc_->netCaps(drvr_pin, rf, scene, min_max, pin_cap, wire_cap, fanout, has_wire_cap); - parasitic = parasitics_->makeWireloadNetwork(drvr_pin, wireload, - fanout, cnst_min_max, - parasitic_ap); + parasitic = + parasitics->makeWireloadNetwork(drvr_pin, wireload, fanout, scene, min_max); } return parasitic; } @@ -142,7 +122,8 @@ Parasitic * PrimaDelayCalc::reduceParasitic(const Parasitic *, const Pin *, const RiseFall *, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { return nullptr; } @@ -153,28 +134,26 @@ PrimaDelayCalc::inputPortDelay(const Pin *drvr_pin, const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { + Parasitics *parasitics = scene->parasitics(min_max); ArcDcalcResult dcalc_result(load_pin_index_map.size()); LibertyLibrary *drvr_library = network_->defaultLibertyLibrary(); - const Parasitic *pi_elmore = nullptr; - if (parasitic && parasitics_->isParasiticNetwork(parasitic)) { - const ParasiticAnalysisPt *ap = dcalc_ap->parasiticAnalysisPt(); - pi_elmore = parasitics_->reduceToPiElmore(parasitic, drvr_pin, rf, - dcalc_ap->corner(), - dcalc_ap->constraintMinMax(), ap); - } + if (parasitic && parasitics->isParasiticNetwork(parasitic)) + pi_elmore = + parasitics->reduceToPiElmore(parasitic, drvr_pin, rf, scene, min_max); for (auto load_pin_index : load_pin_index_map) { const Pin *load_pin = load_pin_index.first; size_t load_idx = load_pin_index.second; - ArcDelay wire_delay = 0.0; - Slew load_slew = in_slew; + double wire_delay = 0.0; + double load_slew = in_slew; bool elmore_exists = false; float elmore = 0.0; if (pi_elmore) - parasitics_->findElmore(pi_elmore, load_pin, elmore, elmore_exists); + parasitics->findElmore(pi_elmore, load_pin, elmore, elmore_exists); if (elmore_exists) // Input port with no external driver. dspfWireDelaySlew(load_pin, rf, in_slew, elmore, wire_delay, load_slew); @@ -192,70 +171,135 @@ PrimaDelayCalc::gateDelay(const Pin *drvr_pin, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { ArcDcalcArgSeq dcalc_args; - dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, load_cap, parasitic); - ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_pin_index_map, dcalc_ap); + dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, load_cap, + parasitic); + ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_pin_index_map, + scene, min_max); return dcalc_results[0]; } ArcDcalcResultSeq PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { dcalc_args_ = &dcalc_args; load_pin_index_map_ = &load_pin_index_map; drvr_count_ = dcalc_args.size(); - dcalc_ap_ = dcalc_ap; + scene_ = scene; + min_max_ = min_max; drvr_rf_ = dcalc_args[0].arc()->toEdge()->asRiseFall(); parasitic_network_ = dcalc_args[0].parasitic(); load_cap_ = dcalc_args[0].loadCap(); + parasitics_ = scene->parasitics(min_max); + node_index_map_ = NodeIndexMap(ParasiticNodeLess(parasitics_, network_)); - bool failed = false; + bool arg_fail = checkArgs(dcalc_args, scene, min_max); + if (arg_fail) + return tableDcalcResults(); + else { + simulate(); + return dcalcResults(); + } +} + +// Return true on failure. +// Use falureReason() to get failure string. +bool +PrimaDelayCalc::checkArgs(ArcDcalcArgSeq &dcalc_args, + const Scene *scene, + const MinMax *min_max) +{ + drvr_count_ = dcalc_args.size(); output_waveforms_.resize(drvr_count_); - const DcalcAnalysisPtSeq &dcalc_aps = corners_->dcalcAnalysisPts(); + failure_reason_ = nullptr; + failure_arg_ = nullptr; for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) { ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx]; - GateTableModel *table_model = dcalc_arg.arc()->gateTableModel(dcalc_ap); - if (table_model && dcalc_arg.parasitic()) { - OutputWaveforms *output_waveforms = table_model->outputWaveforms(); - float in_slew = dcalc_arg.inSlewFlt(); - if (output_waveforms + GateTableModel *table_model = dcalc_arg.arc()->gateTableModel(scene, min_max); + if (table_model) { + if (dcalc_arg.parasitic()) { + OutputWaveforms *output_waveforms = table_model->outputWaveforms(); + float in_slew = dcalc_arg.inSlewFlt(); + if (output_waveforms) { + const LibertyLibrary *drvr_library = dcalc_arg.drvrLibrary(); + float vdd; + bool vdd_exists; + drvr_library->supplyVoltage("VDD", vdd, vdd_exists); + if (vdd_exists) { + if (drvr_idx == 0) { + // Assume drivers are in the same library. + const RiseFall *drvr_rf = dcalc_arg.drvrEdge(); + vdd_ = vdd; + vth_ = drvr_library->outputThreshold(drvr_rf) * vdd_; + vl_ = drvr_library->slewLowerThreshold(drvr_rf) * vdd_; + vh_ = drvr_library->slewUpperThreshold(drvr_rf) * vdd_; + } + } + else { + failure_reason_ = "vdd not defined"; + failure_arg_ = &dcalc_arg; + } + // Bounds check because extrapolating waveforms does not work for shit. - && output_waveforms->slewAxis()->inBounds(in_slew) - && output_waveforms->capAxis()->inBounds(dcalc_arg.loadCap())) { - output_waveforms_[drvr_idx] = output_waveforms; - debugPrint(debug_, "ccs_dcalc", 1, "%s %s", - dcalc_arg.drvrCell()->name(), - drvr_rf_->to_string().c_str()); - LibertyCell *drvr_cell = dcalc_arg.drvrCell(); - const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary(); - bool vdd_exists; - drvr_library->supplyVoltage("VDD", vdd_, vdd_exists); - if (!vdd_exists) - report_->error(1720, "VDD not defined in library %s", drvr_library->name()); - drvr_cell->ensureVoltageWaveforms(dcalc_aps); - if (drvr_idx == 0) { - vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_; - vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_; - vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_; + if (output_waveforms->slewAxis()->inBounds(in_slew)) { + if (output_waveforms->capAxis()->inBounds(dcalc_arg.loadCap())) { + output_waveforms_[drvr_idx] = output_waveforms; + debugPrint(debug_, "prima", 1, "{} {}", + dcalc_arg.drvrCell()->name(), + dcalc_arg.drvrEdge()->to_string().c_str()); + LibertyCell *drvr_cell = dcalc_arg.drvrCell(); + drvr_cell->ensureVoltageWaveforms(scenes_); + } + else { + failure_reason_ = "load cap out of bounds"; + failure_arg_ = &dcalc_arg; + } + } + else { + failure_reason_ = "input slew out of bounds"; + failure_arg_ = &dcalc_arg; + } + } + else { + failure_reason_ = "no output waveforms"; + failure_arg_ = &dcalc_arg; } } - else - failed = true; + else { + failure_reason_ = "no parasitic"; + failure_arg_ = &dcalc_arg; + } + } + else { + failure_reason_ = "no table model"; + failure_arg_ = &dcalc_arg; } - else - failed = true; } - - if (failed) - return tableDcalcResults(); - else { - simulate(); - return dcalcResults(); + if (failure_reason_) { + std::string reason = failureReason(); + debugPrint(debug_,"prima", 1, "arg check failed {}.", reason.c_str()); } + return failure_reason_ != nullptr; +} + +std::string +PrimaDelayCalc::failureReason() +{ + const Pin *drvr_pin = failure_arg_->drvrPin(); + const Instance *inst = network_->instance(drvr_pin); + LibertyPort *from = failure_arg_->arc()->from(); + LibertyPort *to = failure_arg_->arc()->to(); + return sta::format("{} {} -> {} {}", + sdc_network_->pathName(inst), + from->name(), + to->name(), + failure_reason_); } ArcDcalcResultSeq @@ -266,11 +310,13 @@ PrimaDelayCalc::tableDcalcResults() const Pin *drvr_pin = dcalc_arg.drvrPin(); if (drvr_pin) { const RiseFall *rf = dcalc_arg.drvrEdge(); - const Parasitic *parasitic = table_dcalc_->findParasitic(drvr_pin, rf, dcalc_ap_); + const Parasitic *parasitic = + table_dcalc_->findParasitic(drvr_pin, rf, scene_, min_max_); dcalc_arg.setParasitic(parasitic); } } - return table_dcalc_->gateDelays(*dcalc_args_, *load_pin_index_map_, dcalc_ap_); + return table_dcalc_->gateDelays(*dcalc_args_, *load_pin_index_map_, scene_, + min_max_); } void @@ -280,8 +326,7 @@ PrimaDelayCalc::simulate() stampEqns(); setXinit(); - if (prima_order_ > 0 - && node_count_ > prima_order_) { + if (prima_order_ > 0 && node_count_ > prima_order_) { primaReduce(); simulate1(Gq_, Cq_, Bq_, xq_init_, Vq_, prima_order_); } @@ -293,11 +338,11 @@ PrimaDelayCalc::simulate() void PrimaDelayCalc::simulate1(const MatrixSd &G, - const MatrixSd &C, - const Eigen::MatrixXd &B, - const Eigen::VectorXd &x_init, - const Eigen::MatrixXd &x_to_v, - const size_t order) + const MatrixSd &C, + const Eigen::MatrixXd &B, + const Eigen::VectorXd &x_init, + const Eigen::MatrixXd &x_to_v, + size_t order) { Eigen::VectorXd x(order); Eigen::VectorXd x_prev(order); @@ -311,12 +356,13 @@ PrimaDelayCalc::simulate1(const MatrixSd &G, v_ = v_prev_ = x_to_v * x_init; time_step_ = time_step_prev_ = timeStep(); - debugPrint(debug_, "ccs_dcalc", 1, "time step %s", delayAsString(time_step_, this)); + debugPrint(debug_, "ccs_dcalc", 1, "time step {}", + delayAsString(time_step_, this)); MatrixSd A(order, order); A = G + (2.0 / time_step_) * C; A.makeCompressed(); - SparseLU A_solver; + Eigen::SparseLU A_solver; A_solver.compute(A); // Initial time depends on ceff which impact delay, so use a sim step @@ -332,22 +378,25 @@ PrimaDelayCalc::simulate1(const MatrixSd &G, v_ = v_prev_ = x_to_v * x_init; // voltageTime is always for a rising waveform so 0.0v is initial voltage. - double time_begin = output_waveforms_[0]->voltageTime((*dcalc_args_)[0].inSlewFlt(), - ceff_[0], 0.0); + double time_begin = output_waveforms_[0]->voltageTime( + (*dcalc_args_)[0].inSlewFlt(), ceff_[0], 0.0); // Limit in case load voltage waveforms don't get to final value. double time_end = time_begin + maxTime(); if (make_waveforms_) recordWaveformStep(time_begin); - for (double time = time_begin; time <= time_end; time += time_step_) { + for (size_t step = 0;; ++step) { + const double time = time_begin + step * time_step_; + if (time > time_end) + break; setPortCurrents(); rhs = B * u_ + (1.0 / time_step_) * C * (3.0 * x_prev - x_prev2); x = A_solver.solve(rhs); v_ = x_to_v * x; - + const ArcDcalcArg &dcalc_arg = (*dcalc_args_)[0]; - debugPrint(debug_, "ccs_dcalc", 3, "%s ceff %s VDrvr %.4f Idrvr %s", + debugPrint(debug_, "ccs_dcalc", 3, "{} ceff {} VDrvr {:.4f} Idrvr {}", delayAsString(time, this), units_->capacitanceUnit()->asString(ceff_[0]), voltage(dcalc_arg.drvrPin()), @@ -380,7 +429,7 @@ double PrimaDelayCalc::maxTime() { return (*dcalc_args_)[0].inSlewFlt() - + (driverResistance() + resistance_sum_) * load_cap_ * 4; + + (driverResistance() + resistance_sum_) * load_cap_ * 4; } float @@ -388,14 +437,14 @@ PrimaDelayCalc::driverResistance() { const Pin *drvr_pin = (*dcalc_args_)[0].drvrPin(); LibertyPort *drvr_port = network_->libertyPort(drvr_pin); - const MinMax *min_max = dcalc_ap_->delayMinMax(); - return drvr_port->driveResistance(drvr_rf_, min_max); + return drvr_port->driveResistance(drvr_rf_, min_max_); } void PrimaDelayCalc::initSim() { ceff_.resize(drvr_count_); + ceff_vth_.resize(drvr_count_); drvr_current_.resize(drvr_count_); findNodeCount(); @@ -426,9 +475,8 @@ PrimaDelayCalc::findNodeCount() const Pin *pin = parasitics_->pin(node); if (pin) { pin_node_map_[pin] = node_idx; - debugPrint(debug_, "ccs_dcalc", 1, "pin %s node %lu", - network_->pathName(pin), - node_idx); + debugPrint(debug_, "ccs_dcalc", 1, "pin {} node {}", + network_->pathName(pin), node_idx); } double cap = parasitics_->nodeGndCap(node) + pinCapacitance(node); node_capacitances_.push_back(cap); @@ -438,14 +486,12 @@ PrimaDelayCalc::findNodeCount() for (ParasiticCapacitor *capacitor : parasitics_->capacitors(parasitic_network_)) { float cap = parasitics_->value(capacitor) * coupling_cap_multiplier_; ParasiticNode *node1 = parasitics_->node1(capacitor); - if (node1 - && !parasitics_->isExternal(node1)) { + if (node1 && !parasitics_->isExternal(node1)) { size_t node_idx = node_index_map_[node1]; node_capacitances_[node_idx] += cap; } ParasiticNode *node2 = parasitics_->node2(capacitor); - if (node2 - && !parasitics_->isExternal(node2)) { + if (node2 && !parasitics_->isExternal(node2)) { size_t node_idx = node_index_map_[node2]; node_capacitances_[node_idx] += cap; } @@ -458,17 +504,16 @@ PrimaDelayCalc::pinCapacitance(ParasiticNode *node) { const Pin *pin = parasitics_->pin(node); float pin_cap = 0.0; + const Sdc *sdc = scene_->sdc(); if (pin) { Port *port = network_->port(pin); LibertyPort *lib_port = network_->libertyPort(port); - const Corner *corner = dcalc_ap_->corner(); - const MinMax *cnst_min_max = dcalc_ap_->constraintMinMax(); if (lib_port) { if (!includes_pin_caps_) - pin_cap = sdc_->pinCapacitance(pin, drvr_rf_, corner, cnst_min_max); + pin_cap = sdc->pinCapacitance(pin, drvr_rf_, scene_, min_max_); } else if (network_->isTopLevelPort(pin)) - pin_cap = sdc_->portExtCap(port, drvr_rf_, corner, cnst_min_max); + pin_cap = sdc->portExtCap(port, drvr_rf_, min_max_); } return pin_cap; } @@ -494,9 +539,8 @@ PrimaDelayCalc::initCeffIdrvr() const ArcDcalcArg &dcalc_arg = (*dcalc_args_)[drvr_idx]; ceff_[drvr_idx] = load_cap_; // voltageTime is always for a rising waveform so 0.0v is initial voltage. - drvr_current_[drvr_idx] = - output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlewFlt(), - ceff_[drvr_idx], 0.0); + drvr_current_[drvr_idx] = output_waveforms_[drvr_idx]->voltageCurrent( + dcalc_arg.inSlewFlt(), ceff_[drvr_idx], 0.0); } } @@ -615,36 +659,39 @@ PrimaDelayCalc::updateCeffIdrvr() double v2 = voltagePrev(node_idx); double dv = v1 - v2; if (drvr_rf_ == RiseFall::rise()) { - if (drvr_current != 0.0 - && dv > 0.0) { + if (drvr_current != 0.0 && dv > 0.0) { double ceff = drvr_current * time_step_ / dv; - if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff)) + if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff)) { ceff_[drvr_idx] = ceff; + // Record the Ceff at Vth. + if (v1 >= vth_ && v2 < vth_) + ceff_vth_[drvr_idx] = ceff; + } } if (v1 > (vdd_ - .01)) // Whoa partner. Head'n for the weeds. drvr_current_[drvr_idx] = 0.0; else - drvr_current_[drvr_idx] = - output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlewFlt(), - ceff_[drvr_idx], v1); + drvr_current_[drvr_idx] = output_waveforms_[drvr_idx]->voltageCurrent( + dcalc_arg.inSlewFlt(), ceff_[drvr_idx], v1); } else { - if (drvr_current != 0.0 - && dv < 0.0) { + if (drvr_current != 0.0 && dv < 0.0) { double ceff = drvr_current * time_step_ / dv; - if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff)) + if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff)) { ceff_[drvr_idx] = ceff; + // Record the Ceff at Vth. + if (v1 <= vth_ && v2 > vth_) + ceff_vth_[drvr_idx] = ceff; + } } if (v1 < 0.01) { // Whoa partner. Head'n for the weeds. drvr_current_[drvr_idx] = 0.0; } else - drvr_current_[drvr_idx] = - output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlewFlt(), - ceff_[drvr_idx], - vdd_ - v1); + drvr_current_[drvr_idx] = output_waveforms_[drvr_idx]->voltageCurrent( + dcalc_arg.inSlewFlt(), ceff_[drvr_idx], vdd_ - v1); } } } @@ -655,10 +702,8 @@ PrimaDelayCalc::loadWaveformsFinished() for (auto pin_node : pin_node_map_) { size_t node_idx = pin_node.second; double v = voltage(node_idx); - if ((drvr_rf_ == RiseFall::rise() - && v < vh_ + (vdd_ - vh_) * .5) - || (drvr_rf_ == RiseFall::fall() - && (v > vl_ * .5))) { + if ((drvr_rf_ == RiseFall::rise() && v < vh_ + (vdd_ - vh_) * .5) + || (drvr_rf_ == RiseFall::fall() && (v > vl_ * .5))) { return false; } } @@ -676,12 +721,10 @@ PrimaDelayCalc::measureThresholds(double time) double v_prev = voltagePrev(node_idx); for (size_t m = 0; m < measure_threshold_count_; m++) { double th = measure_thresholds_[m]; - if ((v_prev < th && th <= v) - || (v_prev > th && th >= v)) { - double t_cross = time - time_step_ + (th - v_prev) * time_step_ / (v - v_prev); - debugPrint(debug_, "ccs_measure", 1, "node %lu cross %.2f %s", - node_idx, - th, + if ((v_prev < th && th <= v) || (v_prev > th && th >= v)) { + double t_cross = + time - time_step_ + (th - v_prev) * time_step_ / (v - v_prev); + debugPrint(debug_, "ccs_measure", 1, "node {} cross {:.2f} {}", node_idx, th, delayAsString(t_cross, this)); threshold_times_[node_idx][m] = t_cross; } @@ -720,14 +763,17 @@ PrimaDelayCalc::dcalcResults() size_t drvr_node = pin_node_map_[drvr_pin]; ThresholdTimes &drvr_times = threshold_times_[drvr_node]; float ref_time = output_waveforms_[drvr_idx]->referenceTime(dcalc_arg.inSlewFlt()); - ArcDelay gate_delay = drvr_times[threshold_vth] - ref_time; - Slew drvr_slew = abs(drvr_times[threshold_vh] - drvr_times[threshold_vl]); - dcalc_result.setGateDelay(gate_delay); - dcalc_result.setDrvrSlew(drvr_slew); - debugPrint(debug_, "ccs_dcalc", 2, - "%s gate delay %s slew %s", - network_->pathName(drvr_pin), - delayAsString(gate_delay, this), + double gate_delay = drvr_times[threshold_vth] - ref_time; + double drvr_slew = std::abs(drvr_times[threshold_vh] - drvr_times[threshold_vl]); + + ArcDelay gate_delay2(gate_delay); + Slew drvr_slew2(drvr_slew); + delaySlewPocv(dcalc_arg, drvr_idx, gate_delay2, drvr_slew2); + dcalc_result.setGateDelay(gate_delay2); + dcalc_result.setDrvrSlew(drvr_slew2); + + debugPrint(debug_, "ccs_dcalc", 2, "{} gate delay {} slew {}", + network_->pathName(drvr_pin), delayAsString(gate_delay, this), delayAsString(drvr_slew, this)); dcalc_result.setLoadCount(load_pin_index_map_->size()); @@ -737,12 +783,11 @@ PrimaDelayCalc::dcalcResults() size_t load_node = pin_node_map_[load_pin]; ThresholdTimes &wire_times = threshold_times_[load_node]; ThresholdTimes &drvr_times = threshold_times_[drvr_node]; - ArcDelay wire_delay = wire_times[threshold_vth] - drvr_times[threshold_vth]; - Slew load_slew = abs(wire_times[threshold_vh] - wire_times[threshold_vl]); - debugPrint(debug_, "ccs_dcalc", 2, - "load %s %s delay %s slew %s", + double wire_delay = wire_times[threshold_vth] - drvr_times[threshold_vth]; + double load_slew = std::abs(wire_times[threshold_vh] - wire_times[threshold_vl]); + debugPrint(debug_, "ccs_dcalc", 2, "load {} {} delay {} slew {}", network_->pathName(load_pin), - drvr_rf_->to_string().c_str(), + drvr_rf_->shortName(), delayAsString(wire_delay, this), delayAsString(load_slew, this)); @@ -754,6 +799,28 @@ PrimaDelayCalc::dcalcResults() return dcalc_results; } +// Fill in pocv parameters in gate_delay/drvr_slew. +void +PrimaDelayCalc::delaySlewPocv(ArcDcalcArg &dcalc_arg, + size_t drvr_idx, + ArcDelay &gate_delay, + Slew &drvr_slew) +{ + if (variables_->pocvEnabled()) { + GateTableModel *table_model = dcalc_arg.arc()->gateTableModel(scene_, min_max_); + if (table_model) { + double ceff = ceff_vth_[drvr_idx]; + if (ceff == 0.0) + ceff = dcalc_arg.loadCap(); + float in_slew = delayAsFloat(dcalc_arg.inSlew()); + const Pvt *pvt = pinPvt(dcalc_arg.drvrPin(), scene_, min_max_); + table_model->gateDelayPocv(pvt, in_slew, ceff, min_max_, + variables_->pocvMode(), + gate_delay, drvr_slew); + } + } +} + //////////////////////////////////////////////////////////////// void @@ -769,7 +836,7 @@ PrimaDelayCalc::primaReduce() { G_.makeCompressed(); // Step 3: solve G*R = B for R - SparseLU G_solver(G_); + Eigen::SparseLU G_solver(G_); if (G_solver.info() != Eigen::Success) report_->error(1752, "G matrix is singular."); Eigen::MatrixXd R(order_, port_count_); @@ -847,16 +914,18 @@ PrimaDelayCalc::primaReduce2() // Modified Gram-Schmidt orthonormalization for (size_t j = 0; j < k; j++) { - Eigen::MatrixXd H = Vq.block(0, j * port_count_, order_, port_count_).transpose() - * Vq.block(0, k * port_count_, order_, port_count_); + Eigen::MatrixXd H = + Vq.block(0, j * port_count_, order_, port_count_).transpose() + * Vq.block(0, k * port_count_, order_, port_count_); Vq.block(0, k * port_count_, order_, port_count_) = - Vq.block(0, k * port_count_, order_, port_count_) - Vq.block(0, j * port_count_, order_, port_count_) * H; + Vq.block(0, k * port_count_, order_, port_count_) + - Vq.block(0, j * port_count_, order_, port_count_) * H; } Eigen::MatrixXd Vq_k = Vq.block(0, k * port_count_, order_, port_count_); Eigen::HouseholderQR Vq_k_solver(Vq_k); Eigen::MatrixXd VqQ = Vq_k_solver.householderQ(); - Vq.block(0, k * port_count_, order_, port_count_) = - VqQ.block(0, 0, order_, port_count_); + Vq.block(0, k * port_count_, order_, port_count_) = + VqQ.block(0, 0, order_, port_count_); } Vq_.resize(order_, prima_order_); Vq_ = Vq.block(0, 0, order_, prima_order_); @@ -903,23 +972,40 @@ PrimaDelayCalc::recordWaveformStep(double time) //////////////////////////////////////////////////////////////// -string +std::string PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, - const Parasitic *, - const LoadPinIndexMap &, - const DcalcAnalysisPt *dcalc_ap, + const Parasitic *parasitic, + const LoadPinIndexMap &load_pin_index_map, + const Scene *scene, + const MinMax *min_max, int digits) { - GateTimingModel *model = arc->gateModel(dcalc_ap); - if (model) { + ArcDcalcArgSeq dcalc_args; + dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, + load_cap, parasitic); + bool arg_fail = checkArgs(dcalc_args, scene, min_max); + if (arg_fail) { + const RiseFall *rf = arc->toEdge()->asRiseFall(); + const Parasitic *reduced = table_dcalc_->findParasitic(drvr_pin, rf, + scene, min_max); + return table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap, + reduced, load_pin_index_map, scene, + min_max, digits); + } + else { + GateTimingModel *model = arc->gateModel(scene, min_max); + // Delay calc to find ceff. + gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, + load_pin_index_map, scene, min_max); float in_slew1 = delayAsFloat(in_slew); - return model->reportGateDelay(pinPvt(drvr_pin, dcalc_ap), in_slew1, load_cap, - false, digits); + float ceff = ceff_vth_[0]; + return model->reportGateDelay(pinPvt(drvr_pin, scene, min_max), + in_slew1, ceff, min_max, + PocvMode::scalar, digits); } - return ""; } //////////////////////////////////////////////////////////////// @@ -942,7 +1028,7 @@ PinSeq PrimaDelayCalc::watchPins() const { PinSeq pins; - for (auto pin_values : watch_pin_values_) { + for (const auto &pin_values : watch_pin_values_) { const Pin *pin = pin_values.first; pins.push_back(pin); } @@ -953,58 +1039,55 @@ Waveform PrimaDelayCalc::watchWaveform(const Pin *pin) { FloatSeq &voltages = watch_pin_values_[pin]; - TableAxisPtr time_axis = make_shared(TableAxisVariable::time, - new FloatSeq(times_)); - Table1 waveform(new FloatSeq(voltages), time_axis); + TableAxisPtr time_axis = + std::make_shared(TableAxisVariable::time, FloatSeq(times_)); + Table waveform(new FloatSeq(voltages), time_axis); return waveform; } //////////////////////////////////////////////////////////////// void -PrimaDelayCalc::reportMatrix(const char *name, +PrimaDelayCalc::reportMatrix(std::string_view name, MatrixSd &matrix) { - report_->reportLine("%s", name); + report_->report("{}", name); reportMatrix(matrix); } void -PrimaDelayCalc::reportMatrix(const char *name, +PrimaDelayCalc::reportMatrix(std::string_view name, Eigen::MatrixXd &matrix) { - report_->reportLine("%s", name); + report_->report("{}", name); reportMatrix(matrix); } void -PrimaDelayCalc::reportMatrix(const char *name, +PrimaDelayCalc::reportMatrix(std::string_view name, Eigen::VectorXd &matrix) { - report_->reportLine("%s", name); + report_->report("{}", name); reportMatrix(matrix); } void -PrimaDelayCalc::reportVector(const char *name, +PrimaDelayCalc::reportVector(std::string_view name, std::vector &matrix) { - report_->reportLine("%s", name); + report_->report("{}", name); reportVector(matrix); } - + void PrimaDelayCalc::reportMatrix(MatrixSd &matrix) { for (Eigen::Index i = 0; i < matrix.rows(); i++) { - string line = "| "; - for (Eigen::Index j = 0; j < matrix.cols(); j++) { - std::string entry = stdstrPrint("%10.3e", matrix.coeff(i, j)); - line += entry; - line += " "; - } + std::string line = "| "; + for (Eigen::Index j = 0; j < matrix.cols(); j++) + line += sta::format("{:10.3e}", matrix.coeff(i, j)) + " "; line += "|"; - report_->reportLineString(line); + report_->reportLine(line); } } @@ -1013,13 +1096,10 @@ PrimaDelayCalc::reportMatrix(Eigen::MatrixXd &matrix) { for (Eigen::Index i = 0; i < matrix.rows(); i++) { std::string line = "| "; - for (Eigen::Index j = 0; j < matrix.cols(); j++) { - std::string entry = stdstrPrint("%10.3e", matrix.coeff(i, j)); - line += entry; - line += " "; - } + for (Eigen::Index j = 0; j < matrix.cols(); j++) + line += sta::format("{:10.3e}", matrix.coeff(i, j)) + " "; line += "|"; - report_->reportLineString(line); + report_->reportLine(line); } } @@ -1027,26 +1107,20 @@ void PrimaDelayCalc::reportMatrix(Eigen::VectorXd &matrix) { std::string line = "| "; - for (Eigen::Index i = 0; i < matrix.rows(); i++) { - std::string entry = stdstrPrint("%10.3e", matrix.coeff(i)); - line += entry; - line += " "; - } + for (Eigen::Index i = 0; i < matrix.rows(); i++) + line += sta::format("{:10.3e}", matrix.coeff(i)) + " "; line += "|"; - report_->reportLineString(line); + report_->reportLine(line); } void PrimaDelayCalc::reportVector(std::vector &matrix) { std::string line = "| "; - for (size_t i = 0; i < matrix.size(); i++) { - std::string entry = stdstrPrint("%10.3e", matrix[i]); - line += entry; - line += " "; - } + for (const double &entry : matrix) + line += sta::format("{:10.3e}", entry) + " "; line += "|"; - report_->reportLineString(line); + report_->reportLine(line); } -} // namespace +} // namespace sta diff --git a/dcalc/PrimaDelayCalc.hh b/dcalc/PrimaDelayCalc.hh index 454fdc987..4d5b13469 100644 --- a/dcalc/PrimaDelayCalc.hh +++ b/dcalc/PrimaDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,30 +24,30 @@ #pragma once -#include -#include #include #include +#include +#include +#include -#include "Map.hh" -#include "LumpedCapDelayCalc.hh" #include "ArcDcalcWaveforms.hh" +#include "LumpedCapDelayCalc.hh" #include "Parasitics.hh" namespace sta { class ArcDelayCalc; class StaState; -class Corner; +class Scene; -typedef Map PinNodeMap; -typedef std::map NodeIndexMap; -typedef Map PortIndexMap; -typedef Eigen::SparseMatrix MatrixSd; -typedef Map PinLMap; -typedef std::map WatchPinValuesMap; +using PinNodeMap = std::map; +using NodeIndexMap = std::map; +using PortIndexMap = std::map; +using MatrixSd = Eigen::SparseMatrix; +using PinLMap = std::map; +using WatchPinValuesMap = std::map; -typedef Table1 Waveform; +using Waveform = Table; ArcDelayCalc * makePrimaDelayCalc(StaState *sta); @@ -58,43 +58,53 @@ class PrimaDelayCalc : public DelayCalcBase, public: PrimaDelayCalc(StaState *sta); PrimaDelayCalc(const PrimaDelayCalc &dcalc); - ~PrimaDelayCalc(); + ~PrimaDelayCalc() override; ArcDelayCalc *copy() override; void copyState(const StaState *sta) override; - const char *name() const override { return "prima"; } + std::string_view name() const override { return "prima"; } void setPrimaReduceOrder(size_t order); Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; bool reduceSupported() const override { return false; } Parasitic *reduceParasitic(const Parasitic *parasitic_network, const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResult inputPortDelay(const Pin *drvr_pin, float in_slew, const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResult gateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; std::string reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) override; + bool checkArgs(ArcDcalcArgSeq &dcalc_args, + const Scene *scene, + const MinMax *min_max); + std::string failureReason(); // Record waveform for drvr/load pin. void watchPin(const Pin *pin) override; @@ -103,6 +113,10 @@ public: Waveform watchWaveform(const Pin *pin) override; protected: + void delaySlewPocv(ArcDcalcArg &dcalc_arg, + size_t drvr_idx, + ArcDelay &gate_delay, + Slew &drvr_slew); ArcDcalcResultSeq tableDcalcResults(); void simulate(); void simulate1(const MatrixSd &G, @@ -110,7 +124,7 @@ protected: const Eigen::MatrixXd &B, const Eigen::VectorXd &x_init, const Eigen::MatrixXd &x_to_v, - const size_t order); + size_t order); double maxTime(); double timeStep(); float driverResistance(); @@ -147,31 +161,33 @@ protected: const Pin *drvr_pin, const RiseFall *drvr_rf, const Pin *load_pin, - const Corner *corner, + const Scene *scene, const MinMax *min_max); void primaReduce(); void primaReduce2(); - void reportMatrix(const char *name, + void reportMatrix(std::string_view name, MatrixSd &matrix); - void reportMatrix(const char *name, + void reportMatrix(std::string_view name, Eigen::MatrixXd &matrix); - void reportMatrix(const char *name, + void reportMatrix(std::string_view name, Eigen::VectorXd &matrix); - void reportVector(const char *name, + void reportVector(std::string_view name, std::vector &matrix); void reportMatrix(MatrixSd &matrix); void reportMatrix(Eigen::MatrixXd &matrix); void reportMatrix(Eigen::VectorXd &matrix); void reportVector(std::vector &matrix); - ArcDcalcArgSeq *dcalc_args_; + ArcDcalcArgSeq *dcalc_args_{nullptr}; size_t drvr_count_; float load_cap_; - const DcalcAnalysisPt *dcalc_ap_; - const Parasitic *parasitic_network_; + const Scene *scene_{nullptr}; + const MinMax *min_max_{nullptr}; + Parasitics *parasitics_{nullptr}; + const Parasitic *parasitic_network_{nullptr}; const RiseFall *drvr_rf_; - const LoadPinIndexMap *load_pin_index_map_; + const LoadPinIndexMap *load_pin_index_map_{nullptr}; PinNodeMap pin_node_map_; // Parasitic pin -> array index NodeIndexMap node_index_map_; // Parasitic node -> array index @@ -195,7 +211,7 @@ protected: Eigen::VectorXd u_; // Prima reduced MNA eqns - size_t prima_order_; + size_t prima_order_{3}; Eigen::MatrixXd Vq_; MatrixSd Gq_; MatrixSd Cq_; @@ -208,15 +224,17 @@ protected: // Indexed by driver index. std::vector ceff_; + // Ceff at Vth + std::vector ceff_vth_; std::vector drvr_current_; double time_step_; double time_step_prev_; // Waveform recording. - bool make_waveforms_; - const Pin *waveform_drvr_pin_; - const Pin *waveform_load_pin_; + bool make_waveforms_{false}; + const Pin *waveform_drvr_pin_{nullptr}; + const Pin *waveform_load_pin_{nullptr}; FloatSeq drvr_voltages_; FloatSeq load_voltages_; WatchPinValuesMap watch_pin_values_; @@ -231,7 +249,7 @@ protected: static constexpr size_t threshold_vth = 1; static constexpr size_t threshold_vh = 2; static constexpr size_t measure_threshold_count_ = 3; - typedef std::array ThresholdTimes; + using ThresholdTimes = std::array; // Vl Vth Vh ThresholdTimes measure_thresholds_; // Indexed by node number. @@ -240,7 +258,10 @@ protected: // Delay calculator to use when ccs waveforms are missing from liberty. ArcDelayCalc *table_dcalc_; + const char *failure_reason_; + ArcDcalcArg *failure_arg_; + using ArcDelayCalc::reduceParasitic; }; -} // namespacet +} // namespace sta diff --git a/dcalc/UnitDelayCalc.cc b/dcalc/UnitDelayCalc.cc index 63f88a4b3..f376ec7b8 100644 --- a/dcalc/UnitDelayCalc.cc +++ b/dcalc/UnitDelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -28,8 +28,6 @@ namespace sta { -using std::string; - ArcDelayCalc * makeUnitDelayCalc(StaState *sta) { @@ -49,8 +47,9 @@ UnitDelayCalc::copy() Parasitic * UnitDelayCalc::findParasitic(const Pin *, - const RiseFall *, - const DcalcAnalysisPt *) + const RiseFall *, + const Scene *, + const MinMax *) { return nullptr; } @@ -59,7 +58,8 @@ Parasitic * UnitDelayCalc::reduceParasitic(const Parasitic *, const Pin *, const RiseFall *, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { return nullptr; } @@ -67,30 +67,33 @@ UnitDelayCalc::reduceParasitic(const Parasitic *, void UnitDelayCalc::reduceParasitic(const Parasitic *, const Net *, - const Corner *, + const Scene *, const MinMaxAll *) { } void UnitDelayCalc::setDcalcArgParasiticSlew(ArcDcalcArg &, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { } void UnitDelayCalc::setDcalcArgParasiticSlew(ArcDcalcArgSeq &, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { } ArcDcalcResult UnitDelayCalc::inputPortDelay(const Pin *, - float, - const RiseFall *, - const Parasitic *, + float, + const RiseFall *, + const Parasitic *, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { return unitDelayResult(load_pin_index_map); } @@ -98,11 +101,12 @@ UnitDelayCalc::inputPortDelay(const Pin *, ArcDcalcResult UnitDelayCalc::gateDelay(const Pin *, const TimingArc *, - const Slew &, - float, - const Parasitic *, + const Slew &, + float, + const Parasitic *, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { return unitDelayResult(load_pin_index_map); } @@ -110,7 +114,8 @@ UnitDelayCalc::gateDelay(const Pin *, ArcDcalcResultSeq UnitDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { size_t drvr_count = dcalc_args.size(); ArcDcalcResultSeq dcalc_results(drvr_count); @@ -135,17 +140,18 @@ UnitDelayCalc::unitDelayResult(const LoadPinIndexMap &load_pin_index_map) return dcalc_result; } -string +std::string UnitDelayCalc::reportGateDelay(const Pin *, const TimingArc *, - const Slew &, - float, - const Parasitic *, + const Slew &, + float, + const Parasitic *, const LoadPinIndexMap &, - const DcalcAnalysisPt *, - int) + const Scene *, + const MinMax *, + int) { - string result("Delay = 1.0\n"); + std::string result("Delay = 1.0\n"); result += "Slew = 0.0\n"; return result; } @@ -153,23 +159,25 @@ UnitDelayCalc::reportGateDelay(const Pin *, ArcDelay UnitDelayCalc::checkDelay(const Pin *, const TimingArc *, - const Slew &, - const Slew &, - float, - const DcalcAnalysisPt *) + const Slew &, + const Slew &, + float, + const Scene *, + const MinMax *) { return units_->timeUnit()->scale(); } -string +std::string UnitDelayCalc::reportCheckDelay(const Pin *, const TimingArc *, - const Slew &, - const char *, - const Slew &, - float, - const DcalcAnalysisPt *, - int) + const Slew &, + std::string_view, + const Slew &, + float, + const Scene *, + const MinMax *, + int) { return "Check = 1.0\n"; } @@ -179,4 +187,4 @@ UnitDelayCalc::finishDrvrPin() { } -} // namespace +} // namespace sta diff --git a/dcalc/UnitDelayCalc.hh b/dcalc/UnitDelayCalc.hh index b0491c0b9..3eaca081a 100644 --- a/dcalc/UnitDelayCalc.hh +++ b/dcalc/UnitDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -34,29 +34,34 @@ class UnitDelayCalc : public ArcDelayCalc public: UnitDelayCalc(StaState *sta); ArcDelayCalc *copy() override; - const char *name() const override { return "unit"; } + std::string_view name() const override { return "unit"; } Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; bool reduceSupported() const override { return false; } Parasitic *reduceParasitic(const Parasitic *parasitic_network, const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; void reduceParasitic(const Parasitic *parasitic_network, const Net *net, - const Corner *corner, + const Scene *scene, const MinMaxAll *min_max) override; void setDcalcArgParasiticSlew(ArcDcalcArg &gate, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; void setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResult inputPortDelay(const Pin *port_pin, float in_slew, const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResult gateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, @@ -64,31 +69,36 @@ public: float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDelay checkDelay(const Pin *check_pin, const TimingArc *arc, const Slew &from_slew, const Slew &to_slew, float related_out_cap, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; std::string reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) override; std::string reportCheckDelay(const Pin *check_pin, const TimingArc *arc, const Slew &from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, const Slew &to_slew, float related_out_cap, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) override; void finishDrvrPin() override; @@ -99,4 +109,4 @@ protected: ArcDelayCalc * makeUnitDelayCalc(StaState *sta); -} // namespace +} // namespace sta diff --git a/doc/ApiChanges.txt b/doc/ApiChanges.txt index d2ad43f88..7b62f9813 100644 --- a/doc/ApiChanges.txt +++ b/doc/ApiChanges.txt @@ -24,6 +24,86 @@ This file summarizes STA API changes for each release. +Release 3.1.0 2026/03/25 +------------------------ + +OpenSTA now uses std::string and std::string_view instead of const char *. +Lookup funtions such as Network::findPin use std::string_view that accept +a const char *, std::string, or std::string_view caller argument. + +2026/03/19 +---------- + +LibertyCell::footprint() returns const std::string& instead of const char*. +LibertyCell::userFunctionClass returns const std::string& instead of const char*. + +The Sdc, Liberty, ConcreteLibrary, ConcreteNetwork classes have been updated to +use std::string and std::string_view instead of const char *. std::string_view +is used when the lifetime of the string argument is only while the function is +called. std::string is used when the string value outlives the function call +because it is stored in data structures. + +The LibertyPort functions + relatedGroundPin + relatedPowerPin +are renamed to + relatedGroundPort + relatedPowerPort +and return LibertyPort's instead of strings. + +2026/03/12 +---------- + +The Report class used for reporting and error messages now uses std::format +instead of printf. + +sta::format is a wrapper for std::format that will compile on gcc8, which +centos7 uses and does not support std::format. + +stdstrPrint, strintPrint, stringAppend have been removed. Use sta::format. + +reportLineString is now reportLine + +Release 3.0.0 2026/01/03 +------------------------ + +OpenSTA now requires c++ 20. + +Corner replaced by Scene + mode() + parasitics(min_max) +DcalcAnalysisPt replaced by scene/min_min +PathAnalysisPt replaced by scene/min_min +StaState::sdc_ moved to Mode +StaState::sim_ moved to Mode +StaState::clk_network__ moved to Mode +StaState::parasitics_ moved to Scene + +Sta::findPathEnds group_paths arg has been changed from PathGroupNameSet* +to StringSeq&. + +Sta::isClock has been removed. Use mode->clkNetwork()->isClock instead. + +Sta::vertexSlew renamed to slew +Sta::vertexSlack renamed to slack +Sta::vertexSlacks renamed to slacks +Sta::vertexArrival renamed to arrival +Sta::vertexRequired renamed to required +Sta::pinSlack renamed to slack +Sta::pinArrival renamed to arrival + +FuncExpr::Operator::op_* renamed to FuncExpr::Op::* +FuncExprPortIterator has been removed. Use FuncExpr::ports(). + +Sdc::clocks() now returns ClockSeq&. +Sdc::clks() has been removed. + +The Vector/Map/Set/UnorderedSet classes have been removed and replaced by +the std containers. The member functions are now templated functions found +in ContainerHelpers.hh. + +The Graph slew_rf_count option is no longer supported. + Release 2.6.2 2025/03/30 ------------------------ diff --git a/doc/ChangeLog.txt b/doc/ChangeLog.txt index 932339f8c..b62382469 100644 --- a/doc/ChangeLog.txt +++ b/doc/ChangeLog.txt @@ -2,6 +2,323 @@ OpenSTA Timing Analyzer Release Notes ------------------------------------- This file summarizes user visible changes for each release. +See ApiChangeLog.txt for changes to the STA api. + +2026/03/23 +---------- + +The write_path_spice command -spice_directory has been changed to +-spice_file, which is a prefix for the spice filenames. Successive +paths are written in files name _.sp. + +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 +number. + +Normal and skew normal probability distributions are supported. + +SSTA is enabled with the sta_pocv_mode variaable. + + set sta_pocv_mode scalar|normal|skew_normal + +scalar mode is for non-SSTA analysis +normal mode uses gaussian normal distributions +skew_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. + + sta_pocv_quantile + +The default value is 3 standard deviations, or sigma. + +Use the variance 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 +LVF library is shown below. + +read_liberty lvf_library.lib.gz +read_verilog design.v +link_design top +create_clock -period 50 clk +set_input_delay -clock clk 1 {in1 in2} +set sta_pocv_mode skew_normal +report_checks -fields {slew variation input_pin variation} -digits 3 + +Startpoint: r2 (rising edge-triggered flip-flop clocked by clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by clk) +Path Group: clk +Path 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 following commands now support a -report_variance arggument. + + report_arrival [-report_variance] + report_required [-report_variance] + report_slack [-report_variance] + report_slews [-report_variance] + report_edges [-report_variance] + +The following commands now support a -digits option. + + report_edges [-digits digits] + report_slews [-digits digits] + +The standard deviation for normal distributions is specified with the +following liberty timing groups. + + ocv_sigma_cell_rise + ocv_sigma_cell_fall + ocv_sigma_rise_transition + ocv_sigma_fall_transition + ocv_sigma_rise_constraint + ocv_sigma_fall_constraint + +LVF skew normal distributions are specified with liberty groups below. + + ocv_std_dev_cell_rise + ocv_std_dev_cell_fall + ocv_mean_shift_cell_rise + ocv_mean_shift_cell_fall + ocv_skewness_cell_rise + ocv_skewness_cell_fall + + ocv_std_dev_rise_transition + ocv_std_dev_fall_transition + ocv_skewness_rise_transition + ocv_skewness_fall_transition + ocv_mean_shift_rise_transition + ocv_mean_shift_fall_transition + + ocv_std_dev_rise_constraint + ocv_std_dev_fall_constraint + ocv_skewness_rise_constraint + ocv_skewness_fall_constraint + ocv_mean_shift_rise_constraint + ocv_mean_shift_fall_constraint + +2026/02/24 +---------- + +The define_scene -library argument now takes a the library name or a +library filename. If a filename is used, it must be the same as the +filename used to read the library with read_liberty. + +Release 3.0.0 2025/11/26 +------------------------ + +This release adds multi-corner multi-mode (mcmm) support. The SDC +constraints in each mode describe a different operating mode, such as +mission mode or scan mode. + +A "scene" is the combination of a mode and corner. Each scene can have +separate min/max liberty and spef files. + +THe basic structure of a multi-corner/multi-mode command file is + read_liberty + read_verilog + link_design + read_sdc -mode... or set_mode followed by sdc commands + read_spef -name... + define_scene... + report_checks [-scenes] + +This is an example script with 2 corners, 2 modes and 3 scenes. + +read_liberty bc.lib +read_liberty wc.lib + +read_verilog design.v +link_design top + +read_sdc -mode run design.sdc +read_sdc -mode scan design_scan.sdc + +read_spef -name bc bc.spef +read_spef -name wc wc.spef + +define_scene bc \ + -mode run \ + -liberty bc \ + -spef bc +define_scene wc \ + -mode run \ + -liberty wc \ + -spef wc +define_scene scan \ + -mode scan \ + -liberty wc \ + -spef wc + +report_checks +report_checks -scenes bc +report_checks -scenes wc +report_checks -scenes scan + +................ + +Alternatively, the set_mode command can be used to define commands +for each mode at the command level instead of using SDC files. + +set_mode run +create_clock -period 10 clock +set_input_delay 0 -clock clock [all_inputs -no_clocks] +set_output_delay 0 -clock clock [all_outputs] + +set_mode scan +create_clock -period 100 scan_clock +set_input_delay 0 -clock scan_clock scan_in +set_output_delay 0 -clock scan_clock scan_out + +................ + +The define_corners command is supported for compatiblity but should +not be used with mcmm flows. Similarly, the -min/-max arguemnts to +read_liberty and read_spaf are supported for compabibility but should +not be used with mcmm flows. + +................ + +An initial mode and scene named "default" are defined for single mode, +single corner analysis. SDC commands defined interactively and read +with read_sdc without a -mode argument are defined in the "default" +mode. + +Use the set_mode command to define a mode or set the command +interpreter to add following commands to mode mode_name. + + set_mode mode_name + +If mode_name does not exist it is created. When modes are created the +default mode is deleted. + +The read_sdc command has a -mode argument to assign the commands in the file +to a mode. + + read_sdc [-mode mode_name] + +If the mode does not exist it is created. Multiple SDC files can +append commands to a mode by using the -mode_name argument for each +one. If no -mode arguement is is used the commands are added to the +current mode. + +................ + +The define_scene command defines a scene for a mode (SDC), liberty files +and spef parasitics. + + define_scene -mode mode_name + -liberty liberty_files | -liberty_min liberty_min_files -liberty_max liberty_max_files + [-spef spef_file | -spef_min spef_min_file -spef_max spef_max_file] + +Use get_scenes to find defined scenes. + + get_scenes [-modes mode_names] scene_name + +................ + +Use the read_spef -name argument to append multiple parasitics files +to annotate hierarchical blocks. Scene definitions use the spef_name +to specify which parasitices to use for each scene. + + read_spef -name spef_name + report_parasitic_annotation [-name spef_name] + +If -name is omitted the base name of the file name is used. + +The read_spef -corner/-min/-max arguments are supported for comppatibility +but will be removed in a future release. + +The read_spef -reduce options don't work because sdc, liberty ap isn't known + +................ + +The report_checks and report_check_typescommands support a -scenes +argument to report timing checks/paths from multiple scenes. + + report_checks -scenes + report_check_types -scenes + report_slews -scenes + report_clock_latency -scenes + +................ + +To annotate delays with SDF when there are multiple scenes, use +the -scene argument. + + read_sdf -scene + report_annotated_delay -scene + report_annotated_check -scene + +SDF annotation for mcmm analysis must follow the scene definitions. + +................ + +VCD annotation with read_vcd now supports a -mode arguement. + + read_vcd [-mode mode_name] + +................ + +The -corner args has been removed from the following commands because they are no +longer necessary. + set_load -corner + set_port_fanout_number -corner + +................ + +The report_pulse_width_checks command is no longer supported. Use +report_check_types -min_pulse_width. + +................ + +Delay calculation slew values now propagate through set_case_analysis +and set_logic_zero, set_logic_one, set_logic_dc constraints. + +Power analysis now ignores set_case_analysis and set_logic_zero, +set_logic_one, set_logic_dc. Release 2.7.0 2025/05/19 ------------------------- @@ -19,6 +336,9 @@ to remove paths through identical pins and rise/fall edges. Instances now have pins for verilog netlist power/ground connections, +Sta::findPathEnds group_paths arg has been changed from PathGroupNameSet* +to StringSeq&. + Release 2.6.1 2025/03/30 ------------------------- @@ -93,6 +413,8 @@ timing groups. report_clock_skew -include_internal_latency report_clock_latency -include_internal_latency +The report_clock_skew requires a -scene argument if multiple scenes are defined. + The all_inputs command now supports the -no_clocks argument to exclude clocks from the list. diff --git a/doc/OpenSTA.fodt b/doc/OpenSTA.fodt index f7a17c627..e0d25e489 100644 --- a/doc/OpenSTA.fodt +++ b/doc/OpenSTA.fodt @@ -1,30 +1,31 @@ - Parallax STA documentationJames Cherry4832025-03-17T12:59:52.4638705382010-07-31T21:07:002025-11-04T12:25:14.489956000P117DT14H41M24SLibreOffice/25.8.1.1$MacOSX_AARCH64 LibreOffice_project/54047653041915e595ad4e45cccea684809c77b5PDF files: James CherryJames Cherry12.00000falsefalsefalsefalse + Parallax STA documentationJames Cherry5282025-03-17T12:59:52.4638705382010-07-31T21:07:002026-03-21T12:31:10.522816000P123DT2H14M54SLibreOffice/26.2.1.2$MacOSX_AARCH64 LibreOffice_project/8399f6259d8c87f40e7255cdb3c9b958f5e08948PDF files: James CherryJames Cherry12.00000falsefalsefalsefalse - 981699 + 2413538 0 - 30224 - 17736 + 30134 + 17268 true false view2 - 16529 - 988826 + 18244 + 2424328 0 - 981699 - 30222 - 999434 + 2413538 + 30133 + 2430805 0 1 false 90 false false + 172,145,1382,863;1;,,,; true true false @@ -45,11 +46,13 @@ false false + true false false false false false + false false false false @@ -89,7 +92,7 @@ false true false - 25757696 + 27556207 0 false @@ -198,7 +201,7 @@ - + @@ -364,7 +367,7 @@ - + @@ -452,6 +455,7 @@ + @@ -534,6 +538,12 @@ + + + + + + @@ -1373,22 +1383,22 @@ - + - + - + - + - + - + @@ -1790,6 +1800,24 @@ + + + + + + + + + + + + + + + + + + @@ -1980,14 +2008,29 @@ - + + + + + + + + + + + + + + + + @@ -2348,24 +2391,6 @@ - - - - - - - - - - - - - - - - - - @@ -3149,6 +3174,21 @@ + + + + + + + + + + + + + + + @@ -3857,21 +3897,6 @@ - - - - - - - - - - - - - - - @@ -4007,6 +4032,21 @@ + + + + + + + + + + + + + + + @@ -4104,7 +4144,7 @@ - + @@ -4116,1054 +4156,1217 @@ + + + + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + - + - + + - + - + - + + - + - + + + + - + - - + + + + + + + + + + + + + + - + - + - + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + + + + + + + + + + - + - + + + + + - + - + - - - - - + - + - - - - + + - + - + - + - + - + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + - + - + - + + + + + + + + + - + + + + + - + - + - + - + - + - + - + + + + + + + + - + - + + + + + - + + + + + - + - + + + + - + - + - + - + - + - + - + - + - + - + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - + - - - - - + - + + + + - + - + - + - + - + - + - + + + + + - + - + + + + + - + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + - + - + - + + @@ -5181,1029 +5384,1101 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + - + - + - - - - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - + - - - - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + - + - + - + - + - + - + - + - + - + - + - - + + - + + + + + + + + + + @@ -6324,92 +6599,116 @@ Table of Contents - Command Line Arguments1 - Example Command Scripts1 - Timing Analysis using SDF2 - Timing Analysis with Multiple Process Corners2 - Power Analysis2 - TCL Interpreter3 - Debugging Timing4 - No paths found4 - No path reported an endpoint5 - Commands6 - Filter Expressions80 - Variables80 + Command Line Arguments1 + Example Command Scripts1 + Timing Analysis using SDF2 + Timing Analysis with Multiple Process Corners2 + Timing Analysis with Multiple Corners and Modes3 + Power Analysis3 + TCL Interpreter5 + Debugging Timing6 + No paths found6 + No path reported an endpoint7 + Commands7 + Filter Expressions84 + Variables85 - Command Line Arguments + Command Line Arguments The command line arguments for sta are shown below. sta -help show help and exit -version show version and exit -no_init do not read ~/.sta -no_splash do not print the splash message -threads count|max use count threads -exit exit after reading cmd_file cmd_file source cmd_file When OpenSTA starts up, commands are first read from the user initialization file ~/.sta if it exists. If a TCL command file cmd_file is specified on the command line, commands are read from the file and executed before entering an interactive TCL command interpreter. If -exit is specified the application exits after reading cmd_file. Use the TCL exit command to exit the application. The –threads option specifies how many parallel threads to use. Use –threads max to use one thread per processor. - Example Command Scripts + Example Command Scripts To read a design into OpenSTA use the read_liberty command to read Liberty library files. Next, read hierarchical structural Verilog files with the read_verilog command. The link_design command links the Verilog to the Liberty timing cells. Any number of Liberty and Verilog files can be read before linking the design. Delays used for timing analysis are calculated using the Liberty timing models. If no parasitics are read only the pin capacitances of the timing models are used in delay calculation. Use the read_spef command to read parasitics from an extractor, or read_sdf to use delays calculated by an external delay calculator. Timing constraints can be entered as TCL commands or read using the read_sdc command. - The units used by OpenSTA for all command arguments and reports are taken from the first Liberty file that is read. Use the set_cmd_units command to override the default units. - Timing Analysis using SDF - A sample command file that reads a library and a Verilog netlist and reports timing checks is shown below. - read_liberty example1_slow.libread_verilog example1.vlink_design topread_sdf example1.sdfcreate_clock -name clk -period 10 {clk1 clk2 clk3}set_input_delay -clock clk 0 {in1 in2}report_checks - This example can be found in examples/sdf_delays.tcl. - Timing Analysis with Multiple Process Corners - An example command script using three process corners and +/-10% min/max derating is shown below. - define_corners wc typ bcread_liberty -corner wc example1_slow.libread_liberty -corner typ example1_typ.libread_liberty -corner bc example1_fast.libread_verilog example1.vlink_design topset_timing_derate -early 0.9set_timing_derate -late 1.1create_clock -name clk -period 10 {clk1 clk2 clk3}set_input_delay -clock clk 0 {in1 in2}report_checks -path_delay min_maxreport_checks -corner typ - This example can be found in examples/spef_parasitics.tcl. Other examples can be found in the examples directory. - Power Analysis - OpenSTA also supports static power analysis with the report_power command. Probabalistic switching activities are propagated from the input ports to determine switching activities for internal pins. - read_liberty sky130hd_tt.libread_verilog gcd_sky130hd.vlink_design gcdread_sdc gcd_sky130hd.sdcread_spef gcd_sky130hd.spefset_power_activity -input -activity 0.1set_power_activity -input_port reset -activity 0report_power - In this example the activity for all inputs is set to 0.1, and then the activity for the reset signal is set to zero because it does not switch during steady state operation. - Group Internal Switching Leakage Total Power Power Power Power (Watts)----------------------------------------------------------------Sequential 3.27e-04 7.87e-05 2.96e-10 4.06e-04 36.4%Combinational 2.34e-04 3.10e-04 6.95e-10 5.43e-04 48.7%Clock 4.68e-05 1.20e-04 2.30e-11 1.67e-04 15.0%Macro 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0%Pad 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0%----------------------------------------------------------------Total 6.07e-04 5.09e-04 1.01e-09 1.12e-03 100.0% 54.4% 45.6% 0.0% - This example can be found in examples/power.tcl. - Gate level simulation results can be used to get a more accurate power estimate. For example, the Icarus verilog simulator can be used to run the the test bench examples/gcd_tb.v for the gcd design in the previous example. + The units used by OpenSTA for all command arguments and reports are taken from the first Liberty file that is read. Use the set_cmd_units command to override the default units. Use the report_units command to see the ccmmand units. + Timing Analysis using SDF + A sample command file that reads a library and a Verilog netlist and reports timing checks is shown below. + read_liberty example1_slow.libread_verilog example1.vlink_design topread_sdf example1.sdfcreate_clock -name clk -period 10 {clk1 clk2 clk3}set_input_delay -clock clk 0 {in1 in2}report_checks + This example can be found in examples/sdf_delays.tcl. + Timing Analysis with Multiple Process Corners + An example command script using three process corners and +/-10% min/max derating is shown below. + read_liberty nangate45_slow.lib.gzread_liberty nangate45_typ.lib.gzread_liberty nangate45_fast.lib.gzread_verilog example1.link_design topset_timing_derate -early 0.9set_timing_derate -late 1.1create_clock -name clk -period 10 {clk1 clk2 clk3}set_input_delay -clock clk 0 {in1 in2}define_scene ss -liberty nangate45_slowdefine_scene tt -liberty nangate45_typdefine_scene ff -liberty nangate45_fast# report all scenesreport_checks -path_delay min_max# report typical scenereport_checks -scene tt + This example can be found in examples/multi_corner.tcl. Other examples can be found in the examples directory. + Timing Analysis with Multiple Corners and Modes + OpenSTA supports multi-corner, multi-mode analysis. Each corner/mode combination is called a “scene”. The SDC constraints in each mode describe a different operating mode, such as mission mode or scan mode. Each corner has min/max Liberty libraries and SPEF parasitics. + A mode named “default” is initially created for SDC commands. It is deleted when a mode is defined with set_mode or read_sdc -mode. Similartly, a named “default” is initially created that is deleted when define_scene is used to define a scene. + An example command script using two process corners two modes is shown below. + read_liberty asap7_small_ff.lib.gzread_liberty asap7_small_ss.lib.gzread_verilog reg1_asap7.vlink_design topread_sdc -mode mode1 mcmm2_mode1.sdcread_sdc -mode mode2 mcmm2_mode2.sdcread_spef -name reg1_ff reg1_asap7.spefread_spef -name reg1_ss reg1_asap7_ss.spefdefine_scene scene1 -mode mode1 -liberty asap7_small_ff -spef reg1_ffdefine_scene scene2 -mode mode2 -liberty asap7_small_ss -spef reg1_ssreport_checks -scenes scene1report_checks -scenes scene2report_checks -group_path_count 4 + 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. + 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 + 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 + LVF skew normal distributions are specified with liberty groups below. + ocv_std_dev_cell_riseocv_std_dev_cell_fallocv_mean_shift_cell_riseocv_mean_shift_cell_fallocv_skewness_cell_riseocv_skewness_cell_fallocv_std_dev_rise_transitionocv_std_dev_fall_transitionocv_skewness_rise_transitionocv_skewness_fall_transitionocv_mean_shift_rise_transitionocv_mean_shift_fall_transitionocv_std_dev_rise_constraintocv_std_dev_fall_constraintocv_skewness_rise_constraintocv_skewness_fall_constraintocv_mean_shift_rise_constraintocv_mean_shift_fall_constraint + + Power Analysis + OpenSTA also supports static power analysis with the report_power command. Probabalistic switching activities are propagated from the input ports to determine switching activities for internal pins. + read_liberty sky130hd_tt.libread_verilog gcd_sky130hd.vlink_design gcdread_sdc gcd_sky130hd.sdcread_spef gcd_sky130hd.spefset_power_activity -input -activity 0.1set_power_activity -input_port reset -activity 0report_power + In this example the activity for all inputs is set to 0.1, and then the activity for the reset signal is set to zero because it does not switch during steady state operation. + Group Internal Switching Leakage Total Power Power Power Power (Watts)----------------------------------------------------------------Sequential 3.27e-04 7.87e-05 2.96e-10 4.06e-04 36.4%Combinational 2.34e-04 3.10e-04 6.95e-10 5.43e-04 48.7%Clock 4.68e-05 1.20e-04 2.30e-11 1.67e-04 15.0%Macro 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0%Pad 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0%----------------------------------------------------------------Total 6.07e-04 5.09e-04 1.01e-09 1.12e-03 100.0% 54.4% 45.6% 0.0% + This example can be found in examples/power.tcl. + Gate level simulation results can be used to get a more accurate power estimate. For example, the Icarus verilog simulator can be used to run the the test bench examples/gcd_tb.v for the gcd design in the previous example. iverilog -o gcd_tb gcd_tb.vvvp gcd_tb - The test bench writes the VCD (Value Change Data) file gcd_sky130hd.vcd which can then be read with the read_vcd command. - read_liberty sky130hd_tt.libread_verilog gcd_sky130hd.vlink_design gcdread_sdc gcd_sky130hd.sdcread_spef gcd_sky130hd.spefread_vcd -scope gcd_tb/gcd1 gcd_sky130hd.vcd.gzreport_power - This example can be found in examples/power_vcd.tcl. - Note that in this simple example design simulation based activities does not significantly change the results. - TCL Interpreter + The test bench writes the VCD (Value Change Data) file gcd_sky130hd.vcd which can then be read with the read_vcd command. + read_liberty sky130hd_tt.libread_verilog gcd_sky130hd.vlink_design gcdread_sdc gcd_sky130hd.sdcread_spef gcd_sky130hd.spefread_vcd -scope gcd_tb/gcd1 gcd_sky130hd.vcd.gzreport_power + This example can be found in examples/power_vcd.tcl. + Note that in this simple example design simulation based activities does not significantly change the results. + TCL Interpreter Keyword arguments to commands may be abbreviated. For example, report_checks -unique is equivalent to the following command. - report_checks -unique_paths_to_endpoint - The help command lists matching commands and their arguments. - > help report*report_annotated_check [-setup] [-hold] [-recovery] [-removal] [-nochange] [-width] [-period] [-max_skew] [-max_lines liness] [-list_annotated]group_path_count [-list_not_annotated] [-constant_arcs]report_annotated_delay [-cell] [-net] [-from_in_ports] [-to_out_ports] [-max_lines liness] [-list_annotated] [-list_not_annotated] [-constant_arcs]report_arrival pinreport_check_types [-violators] [-verbose] [-corner corner] [-format slack_only|end] [-max_delay] [-min_delay] [-recovery] [-removal] [-clock_gating_setup] [-clock_gating_hold] [-max_slew] [-min_slew] [-max_fanout] [-min_fanout] [-max_capacitance] [-min_capacitance [-min_pulse_width] [-min_period] [-max_skew] [-net net] [-digits digits [-no_line_splits] [> filename] [>> filename]report_checks [-from from_list|-rise_from from_list|-fall_from from_list] [-through through_list|-rise_through through_list|-fall_through through_list] [-to to_list|-rise_to to_list|-fall_to to_list] [-unconstrained] [-path_delay min|min_rise|min_fall|max|max_rise|max_fall|min_max] [-corner corner] [-group_path_count path_count] [-endpoint_path_count path_count] [-unique_paths_to_endpoint] [-slack_max slack_max] [-slack_min slack_min] [-sort_by_slack] [-path_group group_name] [-format full|full_clock|full_clock_expanded|short|end|summary]... - Many reporting commands support redirection of the output to a file much like a Unix shell. - report_checks -to out1 > path.logreport_checks -to out2 >> path.log - Debugging Timing - Here are some guidelines for debugging your design if static timing does not report any paths, or does not report the expected paths. - Debugging timing problems generally involves using the following commands to follow the propagation of arrival times from a known arrival downstream to understand why the arrival times are not propagating: - report_edgesreport_arrivalsreport_net - report_edges -from can be used to walk forward and report_edges -to to walk backward in the netlist/timing graph. report_arrivals shows the min/max rise/fall arrival times with respect to each clock that has a path to the pin. report_net shows connections to a net across hierarchy levels. + report_checks -unique_paths_to_endpoint + The help command lists matching commands and their arguments. + > help report*report_annotated_check [-setup] [-hold] [-recovery] [-removal] [-nochange] [-width] [-period] [-max_skew] [-max_lines liness] [-list_annotated]group_path_count [-list_not_annotated] [-constant_arcs]report_annotated_delay [-cell] [-net] [-from_in_ports] [-to_out_ports] [-max_lines liness] [-list_annotated] [-list_not_annotated] [-constant_arcs]report_arrival pinreport_check_types [-violators] [-verbose] [-scene scene] [-format slack_only|end] [-max_delay] [-min_delay] [-recovery] [-removal] [-clock_gating_setup] [-clock_gating_hold] [-max_slew] [-min_slew] [-max_fanout] [-min_fanout] [-max_capacitance] [-min_capacitance [-min_pulse_width] [-min_period] [-max_skew] [-net net] [-digits digits [-no_line_splits] [> filename] [>> filename]report_checks [-from from_list|-rise_from from_list|-fall_from from_list] [-through through_list|-rise_through through_list|-fall_through through_list] [-to to_list|-rise_to to_list|-fall_to to_list] [-unconstrained] [-path_delay min|min_rise|min_fall|max|max_rise|max_fall|min_max] [-scene scene] [-group_path_count path_count] [-endpoint_path_count path_count] [-unique_paths_to_endpoint] [-slack_max slack_max] [-slack_min slack_min] [-sort_by_slack] [-path_group group_name] [-format full|full_clock|full_clock_expanded|short|end|summary]... + Many reporting commands support redirection of the output to a file much like a Unix shell. + report_checks -to out1 > path.logreport_checks -to out2 >> path.log + Debugging Timing + Here are some guidelines for debugging your design if static timing does not report any paths, or does not report the expected paths. + Debugging timing problems generally involves using the following commands to follow the propagation of arrival times from a known arrival downstream to understand why the arrival times are not propagating: + report_edgesreport_arrivalsreport_net + report_edges -from can be used to walk forward and report_edges -to to walk backward in the netlist/timing graph. report_arrivals shows the min/max rise/fall arrival times with respect to each clock that has a path to the pin. report_net shows connections to a net across hierarchy levels. No paths found - The report_checks command only reports paths that are constrained by timing checks or SDC commands such as set_output_delay. If the design has only combinational logic (no registers or latches), there are no timing checks, so no paths are reported. Use the -unconstrained option to report_checks to see unconstrained paths. + The report_checks command only reports paths that are constrained by timing checks or SDC commands such as set_output_delay. If the design has only combinational logic (no registers or latches), there are no timing checks, so no paths are reported. Use the -unconstrained option to report_checks to see unconstrained paths. % report_checks -unconstrained - If the design is sequential (has registers or latches) and no paths are reported, it is likely that there is a problem with the clock propagation. Check the timing at an register in the design with the report_arrivals command. - % report_arrivals r1/CP (clk ^) r 0.00:0.00 f INF:-INF (clk v) r INF:-INF f 5.00:5.00 - In this example the rising edge of the clock "clk" causes the rising arrival min:max time at 0.00, and the falling edge arrives at 5.00. Since the rising edge of the clock causes the rising edge of the register clock pin, the clock path is positive unate. - The clock path should be positive or negative unate. Something is probably wrong with the clock network if it is non-unate. A non-unate clock path will report arrivals similar to the foillowing: + If the design is sequential (has registers or latches) and no paths are reported, it is likely that there is a problem with the clock propagation. Check the timing at an register in the design with the report_arrivals command. + % report_arrivals r1/CP (clk ^) r 0.00:0.00 f INF:-INF (clk v) r INF:-INF f 5.00:5.00 + In this example the rising edge of the clock "clk" causes the rising arrival min:max time at 0.00, and the falling edge arrives at 5.00. Since the rising edge of the clock causes the rising edge of the register clock pin, the clock path is positive unate. + The clock path should be positive or negative unate. Something is probably wrong with the clock network if it is non-unate. A non-unate clock path will report arrivals similar to the foillowing: % report_arrivals r1/CP (clk ^) r 0.00:0.00 f 0.00:0.00 (clk v) r 5.00:5.00 f 5.00:5.00 - Notice that each clock edge causes both rise and fall arrivals at the register clock pin. - If there are no paths to the register clock pin, nothing is printed. Use the report_edges -to command to find the gate driving the clock pin. + Notice that each clock edge causes both rise and fall arrivals at the register clock pin. + If there are no paths to the register clock pin, nothing is printed. Use the report_edges -to command to find the gate driving the clock pin. % report_edges -to r1/CPi1/ZN -> CP wire ^ -> ^ 0.00:0.00 v -> v 0.00:0.00 - This shows that the gate/pin i1/ZN is driving the clock pin. The report_edges -to commond can be used to walk backward or forward through the netlist one gate/net at a time. By checking the arrivals with the report_arrival command you can determine where the path is broken. - No path reported an endpoint - In order for a timing check to be reported, there must be an arrival time at the data pin (the constrained pin) as well as the timing check clock pin. If report_checks -to a register input does not report any paths, check that the input is constrained by a timing check with report_edges -to. + This shows that the gate/pin i1/ZN is driving the clock pin. The report_edges -to commond can be used to walk backward or forward through the netlist one gate/net at a time. By checking the arrivals with the report_arrival command you can determine where the path is broken. + No path reported an endpoint + In order for a timing check to be reported, there must be an arrival time at the data pin (the constrained pin) as well as the timing check clock pin. If report_checks -to a register input does not report any paths, check that the input is constrained by a timing check with report_edges -to. % report_edges -to r1/DCP -> D hold ^ -> ^ -0.04:-0.04 ^ -> v -0.03:-0.03CP -> D setup ^ -> ^ 0.09:0.0 ^ -> v 0.08:0.08in1 -> D wire ^ -> ^ 0.00:0.00 v -> v 0.00:0.00 - This reports the setup and hold checks for the D pin of r1. - Next, check the arrival times at the D and CP pins of the register with report_arrivals. + This reports the setup and hold checks for the D pin of r1. + Next, check the arrival times at the D and CP pins of the register with report_arrivals. % report_arrivals r1/D (clk1 ^) r 1.00:1.00 f 1.00:1.00% report_arrivals r1/CP (clk1 ^) r 0.00:0.00 f INF:-INF (clk1 v) r INF:-INF f 5.00:5.00 - If there are no arrivals on an input port of the design, use the set_input_delay command to specify the arrival times on the port. - Commands + If there are no arrivals on an input port of the design, use the set_input_delay command to specify the arrival times on the port. + Commands - all_clocks + all_clocks - + @@ -6419,15 +6718,15 @@ - all_inputs + all_inputs - [-no_clocks] + [-no_clocks] - -no_clocks + -no_clocks Exclude inputs defined as clock sources. @@ -6440,10 +6739,10 @@ - all_outputs + all_outputs - + @@ -6451,94 +6750,95 @@ + - all_registers + all_registers - [-clock clock_names][-cells | -data_pins | -clock_pins | -async_pins | ‑output_pins][-level_sensitive][-edge_triggered] + [-clock clock_names][-cells | -data_pins | -clock_pins | -async_pins | ‑output_pins][-level_sensitive][-edge_triggered] - -clock clock_names + -clock clock_names - A list of clock names. Only registers clocked by these clocks are returned. + A list of clock names. Only registers clocked by these clocks are returned. - -cells + -cells - Return a list of register instances. + Return a list of register instances. - -data_pins + -data_pins - Return the register data pins. + Return the register data pins. - -clock_pins + -clock_pins - Return the register clock pins. + Return the register clock pins. - -async_pins + -async_pins - Return the register set/clear pins. + Return the register set/clear pins. - -output_pins + -output_pins - Return the register output pins. + Return the register output pins. - -level_sensitive + -level_sensitive - Return level-sensitive latches. + Return level-sensitive latches. - -edge_triggered + -edge_triggered - Return edge-triggered registers. + Return edge-triggered registers. - The all_registers command returns a list of register instances or register pins in the design. Options allow the list of registers to be restricted in various ways. The -clock keyword restrcts the registers to those that are clocked by a set of clocks. The -cells option returns the list of registers or latches (the default). The -‑data_pins, -clock_pins, -async_pins and -output_pins options cause all_registers to return a list of register pins rather than instances. + The all_registers command returns a list of register instances or register pins in the design. Options allow the list of registers to be restricted in various ways. The -clock keyword restrcts the registers to those that are clocked by a set of clocks. The -cells option returns the list of registers or latches (the default). The -‑data_pins, -clock_pins, -async_pins and -output_pins options cause all_registers to return a list of register pins rather than instances. - check_setup + check_setup - [-verbose][-unconstrained_endpoints][-multiple_clock][-no_clock][-no_input_delay][-loops][-generated_clocks][> filename][>> filename] + [-verbose][-unconstrained_endpoints][-multiple_clock][-no_clock][-no_input_delay][-loops][-generated_clocks][> filename][>> filename] - -verbose + -verbose Show offending objects rather than just error counts. @@ -6546,7 +6846,7 @@ - -unconstrained_endpoints + -unconstrained_endpoints Check path endpoints for timing constraints (timing check or set_output_delay). @@ -6554,15 +6854,16 @@ - -multiple_clock + -multiple_clock Check register/latch clock pins for multiple clocks. + - -no_clock + -no_clock Check register/latch clock pins for a clock. @@ -6570,7 +6871,7 @@ - -no_input_delay + -no_input_delay Check for inputs that do not have a set_input_delay command. @@ -6578,7 +6879,7 @@ - -loops + -loops Check for combinational logic loops. @@ -6586,28 +6887,28 @@ - -generated_clocks + -generated_clocks Check that generated clock source pins have been defined as clocks. - The check_setup command performs sanity checks on the design. Individual checks can be performed with the keywords. If no check keywords are specified all checks are performed. Checks that fail are reported as warnings. If no checks fail nothing is reported. The command returns 1 if there are no warnings for use in scripts. + The check_setup command performs sanity checks on the design. Individual checks can be performed with the keywords. If no check keywords are specified all checks are performed. Checks that fail are reported as warnings. If no checks fail nothing is reported. The command returns 1 if there are no warnings for use in scripts. - connect_pin + connect_pin - netport|pin + netport|pin - net + net A net to add connections to. @@ -6615,7 +6916,7 @@ - port + port A port to connect to net. @@ -6623,29 +6924,28 @@ - Pin + Pin A pin to connect to net. - The connect_pin command connects a port or instance pin to a net. + The connect_pin command connects a port or instance pin to a net. - - create_clock + create_clock - -period period[-name clock_name][-waveform edge_list][-add][pin_list] + -period period[-name clock_name][-waveform edge_list][-add][pin_list] - -period period + -period period The clock period. @@ -6653,7 +6953,7 @@ - -name clock_name + -name clock_name The name of the clock. @@ -6661,7 +6961,7 @@ - -waveform edge_list + -waveform edge_list A list of edge rise and fall time. @@ -6669,15 +6969,15 @@ - -add + -add - Add this clock to the clocks on pin_list. + Add this clock to the clocks on pin_list. - pin_list + pin_list A list of pins driven by the clock. @@ -6685,8 +6985,8 @@ The create_clock command defines the waveform of a clock used by the design. - If no pin_list is specified the clock is virtual. A virtual clock can be refered to by name in input arrival and departure time commands but is not attached to any pins in the design. - If no clock name is specified the name of the first pin is used as the clock name. + If no pin_list is specified the clock is virtual. A virtual clock can be refered to by name in input arrival and departure time commands but is not attached to any pins in the design. + If no clock name is specified the name of the first pin is used as the clock name. If a wavform is not specified the clock rises at zero and falls at half the clock period. The waveform is a list with time the clock rises as the first element and the time it falls as the second element. If a clock is already defined on a pin the clock is redefined using the new clock parameters. If multiple clocks drive the same pin, use the -add option to prevent the existing definition from being overwritten. The following command creates a clock with a period of 10 time units that rises at time 0 and falls at 5 time units on the pin named clk1. @@ -6696,18 +6996,17 @@ - - create_generated_clock + create_generated_clock - [-name clock_name]-source master_pin[-master_clock master_clock][-divide_by divisor][-multiply_by multiplier][-duty_cycle duty_cycle][-invert][-edges edge_list][-edge_shift shift_list][-add]pin_list + [-name clock_name]-source master_pin[-master_clock master_clock][-divide_by divisor][-multiply_by multiplier][-duty_cycle duty_cycle][-invert][-edges edge_list][-edge_shift shift_list][-add]pin_list - -name clock_name + -name clock_name The name of the generated clock. @@ -6715,39 +7014,39 @@ - -source master_pin + -source master_pin - A pin or port in the fanout of the master clock that is the source of the generated clock. + A pin or port in the fanout of the master clock that is the source of the generated clock. - -master_clock master_clock + -master_clock master_clock - Use -master_clock to specify which source clock to use when multiple clocks are present on master_pin. + Use -master_clock to specify which source clock to use when multiple clocks are present on master_pin. - -divide_by divisor + -divide_by divisor - Divide the master clock period by divisor. + Divide the master clock period by divisor. - -multiply_by multiplier + -multiply_by multiplier - Multiply the master clock period by multiplier. + Multiply the master clock period by multiplier. - -duty_cycle duty_cycle + -duty_cycle duty_cycle The percent of the period that the generated clock is high (between 0 and 100). @@ -6755,23 +7054,24 @@ - -invert + -invert Invert the master clock. + - -edges edge_list + -edges edge_list - List of master clock edges to use in the generated clock. Edges are numbered from 1. edge_list must be 3 edges long. + List of master clock edges to use in the generated clock. Edges are numbered from 1. edge_list must be 3 edges long. - -edge_shift shift_list + -edge_shift shift_list Not supported. @@ -6779,46 +7079,46 @@ - -add + -add - Add this clock to the existing clocks on pin_list. + Add this clock to the existing clocks on pin_list. - pin_list + pin_list - A list of pins driven by the generated clock. + A list of pins driven by the generated clock. The create_generated_clock command is used to generate a clock from an existing clock definition. It is used to model clock generation circuits such as clock dividers and phase locked loops. The -divide_by, -multiply_by and -edges arguments are mutually exclusive. The -multiply_by option is used to generate a higher frequency clock from the source clock. The period of the generated clock is divided by multiplier. The clock multiplier must be a positive integer. If a duty cycle is specified the generated clock rises at zero and falls at period * duty_cycle / 100. If no duty cycle is specified the source clock edge times are divided by multiplier. - The -divide_by option is used to generate a lower frequency clock from the source clock. The clock divisor must be a positive integer. If the clock divisor is a power of two the source clock period is multiplied by divisor, the clock rise time is the same as the source clock, and the clock fall edge is one half period later. If the clock divisor is not a power of two the source clock waveform edge times are multiplied by divisor. + The -divide_by option is used to generate a lower frequency clock from the source clock. The clock divisor must be a positive integer. If the clock divisor is a power of two the source clock period is multiplied by divisor, the clock rise time is the same as the source clock, and the clock fall edge is one half period later. If the clock divisor is not a power of two the source clock waveform edge times are multiplied by divisor. The -edges option forms the generated clock waveform by selecting edges from the source clock waveform. If the -invert option is specified the waveform derived above is inverted. If a clock is already defined on a pin the clock is redefined using the new clock parameters. If multiple clocks drive the same pin, use the -add option to prevent the existing definition from being overwritten. - In the example show below generates a clock named gclk1 on register output pin r1/Q by dividing it by four. + In the example show below generates a clock named gclk1 on register output pin r1/Q by dividing it by four. create_clock -period 10 -waveform {1 8} clk1create_generated_clock -name gclk1 -source clk1 -divide_by 4 r1/Q The generated clock has a period of 40, rises at time 1 and falls at time 21. In the example shown below the duty cycle is used to define the derived clock waveform. create_generated_clock -name gclk1 -source clk1 -duty_cycle 50 \ -multiply_by 2 r1/Q The generated clock has a period of 5, rises at time .5 and falls at time 3. In the example shown below the first, third and fifth source clock edges are used to define the derived clock waveform. - create_generated_clock -name gclk1 -source clk1 -edges {1 3 5} r1/Q + create_generated_clock -name gclk1 -source clk1 -edges {1 3 5} r1/Q The generated clock has a period of 20, rises at time 1 and falls at time 11. - create_voltage_area + create_voltage_area - [-name name][-coordinate coordinates][-guard_band_x guard_x][-guard_band_y guard_y]cells + [-name name][-coordinate coordinates][-guard_band_x guard_x][-guard_band_y guard_y]cells @@ -6828,10 +7128,10 @@ - current_design + current_design - [design] + [design] @@ -6841,62 +7141,78 @@ - current_instance + current_instance - [instance] + [instance] - - instance + instance - Not supported. + Not supported. - - - - - - - define_corners + + + + + + + define_scene - - corner1 [corner2]... + + -mode mode_name -liberty liberty_files|-liberty_min liberty_min_files -liberty_max liberty_max_files-spef spef_file| -spef_min spef_min_file -spef_max spef_max_file - - - corner + + + mode_name + + + The SDC mode to use. + + + + + liberty_files + + + List of Liberty files to use. - - The name of a delay calculation corner. + + + + spef_file + + + The SPEF parasitics file to use. - Use the define_corners command to define the names of multiple process/temperature/voltage corners. The define_corners command must follow set_operating_conditions -analysis_type and precede any reference to the corner names and can only appear once in a command file. There is no support for re-defining corners. - For analysis type single, each corner has one delay calculation result and early/late path arrivals. For analysis type best_case/worst_case and on_chip_variation, each corner has min/max delay calculation results and early/late path arrivals. + The define_scene command defines a scene for a mode (SDC), liberty files and spef parasitics. Define scenes after reading Liberty libraries and SPEF parasitics.Liberty files are specifiec with the name of the liberty library or the filename of the liberty file. If a filename is used, it must be the same as the filename used to read the library with read_liberty.. + Use get_scenes to find defined scenes. + - delete_clock + delete_clock - [-all] clocks + [-all] clocks - clocks + clocks - A list of clocks to remove. + A list of clocks to remove. @@ -6906,26 +7222,26 @@ - delete_from_list + delete_from_list - list objects + list objects - list + list - A list of objects. + A list of objects. - objects + objects - A list of objects to delete from list. + A list of objects to delete from list. @@ -6935,18 +7251,18 @@ - delete_generated_clock + delete_generated_clock - [-all] clocks + [-all] clocks - clocks + clocks - A list of generated clocks to remove. + A list of generated clocks to remove. @@ -6956,18 +7272,18 @@ - delete_instance + delete_instance - instance + instance - instance + instance - Instance to delete. + Instance to delete. @@ -6975,47 +7291,46 @@ - - delete_net + delete_net - net + net - net + net - Net to delete. + Net to delete. - The network editing command delete_net removes a net from the design. + The network editing command delete_net removes a net from the design. - disconnect_pin + disconnect_pin - netport | pin | -all + netport | pin | -all - net + net - The net to disconnect pins from. + The net to disconnect pins from. - port + port A port to connect to net. @@ -7023,18 +7338,19 @@ - pin + pin A pin to connect to net. + - -all + -all - Disconnect all pins from the net. + Disconnect all pins from the net. @@ -7044,10 +7360,10 @@ - elapsed_run_time + elapsed_run_time - + @@ -7055,107 +7371,106 @@ - - find_timing_paths + find_timing_paths - [-from from_list |-rise_from from_list |-fall_from from_list][-through through_list |-rise_through through_list |-fall_through through_list][-to to_list |-rise_to to_list |-fall_to to_list][-unconstrained][-path_delay min|min_rise|min_fall |max|max_rise|max_fall |min_max][-group_path_count path_count][-endpoint_path_count endpoint_path_count][-unique_paths_to_endpoint][-corner corner][-slack_max max_slack][-slack_min min_slack][-sort_by_slack][-path_group groups] + [-from from_list |-rise_from from_list |-fall_from from_list][-through through_list |-rise_through through_list |-fall_through through_list][-to to_list |-rise_to to_list |-fall_to to_list][-unconstrained][-path_delay min|min_rise|min_fall |max|max_rise|max_fall |min_max][-group_path_count path_count][-endpoint_path_count endpoint_path_count][-unique_paths_to_endpoint][-scene scene][-slack_max max_slack][-slack_min min_slack][-sort_by_slack][-path_group groups] - -from from_list + -from from_list - Return paths from a list of clocks, instances, ports, register clock pins, or latch data pins. + Return paths from a list of clocks, instances, ports, register clock pins, or latch data pins. - -rise_from from_list + -rise_from from_list - Return paths from the rising edge of clocks, instances, ports, register clock pins, or latch data pins. + Return paths from the rising edge of clocks, instances, ports, register clock pins, or latch data pins. - -fall_from from_list + -fall_from from_list - Return paths from the falling edge of clocks, instances, ports, register clock pins, or latch data pins. + Return paths from the falling edge of clocks, instances, ports, register clock pins, or latch data pins. - -through through_list + -through through_list - Return paths through a list of instances, pins or nets. + Return paths through a list of instances, pins or nets. - -rise_through through_list + -rise_through through_list - Return rising paths through a list of instances, pins or nets. + Return rising paths through a list of instances, pins or nets. + - -fall_through through_list + -fall_through through_list - Return falling paths through a list of instances, pins or nets. + Return falling paths through a list of instances, pins or nets. - -to to_list + -to to_list - Return paths to a list of clocks, instances, ports or pins. + Return paths to a list of clocks, instances, ports or pins. - -rise_to to_list + -rise_to to_list - Return rising paths to a list of clocks, instances, ports or pins. + Return rising paths to a list of clocks, instances, ports or pins. - -fall_to to_list + -fall_to to_list - Return falling paths to a list of clocks, instances, ports or pins. + Return falling paths to a list of clocks, instances, ports or pins. - -unconstrained + -unconstrained - Report unconstrained paths also. + Report unconstrained paths also. - -path_delay min + -path_delay min Return min path (hold) checks. - - -path_delay min_rise + -path_delay min_rise Return min path (hold) checks for rising endpoints. @@ -7163,7 +7478,7 @@ - -path_delay min_fall + -path_delay min_fall Return min path (hold) checks for falling endpoints. @@ -7171,7 +7486,7 @@ - -path_delay max + -path_delay max Return max path (setup) checks. @@ -7179,7 +7494,7 @@ - -path_delay max_rise + -path_delay max_rise Return max path (setup) checks for rising endpoints. @@ -7187,7 +7502,7 @@ - -path_delay max_fall + -path_delay max_fall Return max path (setup) checks for falling endpoints. @@ -7195,7 +7510,7 @@ - -path_delay min_max + -path_delay min_max Return max and max path (setup and hold) checks. @@ -7203,23 +7518,23 @@ - -group_path_count path_count + -group_path_count path_count - The number of paths to return in each path group. + The number of paths to return in each path group. - -endpoint_path_count endpoint_path_count + -endpoint_path_count endpoint_path_count - The number of paths to return for each endpoint. + The number of paths to return for each endpoint. - ‑unique_paths_to_endpoint + ‑unique_paths_to_endpoint Return multiple paths to an endpoint that traverse different pins without showing multiple paths with different rise/fall transitions. @@ -7227,7 +7542,7 @@ - -corner corner + -scene scene Return paths for one process corner. @@ -7235,109 +7550,108 @@ - -slack_max max_slack + -slack_max max_slack - Return paths with slack less than max_slack. + Return paths with slack less than max_slack. - -slack_min min_slack + -slack_min min_slack - Return paths with slack greater than min_slack. + Return paths with slack greater than min_slack. - -sort_by_slack + -sort_by_slack - Sort paths by slack rather than slack within path groups. + Sort paths by slack rather than slack within path groups. - -path_group groups + -path_group groups - Return paths in path groups. Paths in all groups are returned if this option is not specified. + Return paths in path groups. Paths in all groups are returned if this option is not specified. - The find_timing_paths command returns a list of path objects for scripting. Use the get_property function to access properties of the paths. + The find_timing_paths command returns a list of path objects for scripting. Use the get_property function to access properties of the paths. - get_cells + get_cells - [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] + [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] - -hierarchical + -hierarchical - Searches hierarchy levels below the current instance for matches. + Searches hierarchy levels below the current instance for matches. - - -hsc separator + -hsc separator - Character to use to separate hierarchical instance names in patterns. + Character to use to separate hierarchical instance names in patterns. - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - -regexp + -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. - -nocase + -nocase - Ignore case when matching. Only valid with –regexp. + Ignore case when matching. Only valid with –regexp. - -quiet + -quiet - Do not warn if no matches are found. + Do not warn if no matches are found. - -of_objects objects + -of_objects objects - The name of a pin or net, a list of pins returned by get_pins, or a list of nets returned by get_nets. The –hierarchical option cannot be used with ‑of_objects. + The name of a pin or net, a list of pins returned by get_pins, or a list of nets returned by get_nets. The –hierarchical option cannot be used with ‑of_objects. - patterns + patterns A list of instance name patterns. @@ -7350,47 +7664,48 @@ - get_clocks + get_clocks - [-regexp][-nocase][-filter expr][-quiet]patterns + [-regexp][-nocase][-filter expr][-quiet]patterns - -regexp + -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. - -nocase + -nocase - Ignore case when matching. Only valid with –regexp. + Ignore case when matching. Only valid with –regexp. + - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - -quiet + -quiet - Do not warn if no matches are found. + Do not warn if no matches are found. - patterns + patterns A list of clock name patterns. @@ -7401,18 +7716,17 @@ - - get_fanin + get_fanin - -to sink_list[-flat][-only_cells][-startpoints_only][-levels level_count][-pin_levels pin_count][-trace_arcs timing|enabled|all] + -to sink_list[-flat][-only_cells][-startpoints_only][-levels level_count][-pin_levels pin_count][-trace_arcs timing|enabled|all] - -to sink_list + -to sink_list List of pins, ports, or nets to find the fanin of. For nets, the fanin of driver pins on the nets are returned. @@ -7420,15 +7734,15 @@ - -flat + -flat - With –flat pins in the fanin at any hierarchy level are returned. Without ‑flat only pins at the same hierarchy level as the sinks are returned. + With –flat pins in the fanin at any hierarchy level are returned. Without ‑flat only pins at the same hierarchy level as the sinks are returned. - -only_cells + -only_cells Return the instances connected to the pins in the fanin. @@ -7436,7 +7750,7 @@ - -startpoints_only + -startpoints_only Only return pins that are startpoints. @@ -7444,7 +7758,7 @@ - -level level_count + -level level_count Only return pins within level_count instance traversals. @@ -7452,7 +7766,7 @@ - -pin_levels pin_count + -pin_levels pin_count Only return pins within pin_count pin traversals. @@ -7460,44 +7774,45 @@ - -trace_arcs timing + -trace_arcs timing - Only trace through timing arcs that are not disabled. + Only trace through timing arcs that are not disabled. - -trace_arcs enabled + -trace_arcs enabled - Only trace through timing arcs that are not disabled. + Only trace through timing arcs that are not disabled. - -trace_arcs all + -trace_arcs all - Trace through all arcs, including disabled ones. + Trace through all arcs, including disabled ones. - The get_fanin command returns traverses the design from sink_list pins, ports or nets backwards and return the fanin pins or instances. + The get_fanin command returns traverses the design from sink_list pins, ports or nets backwards and return the fanin pins or instances. + - get_fanout + get_fanout - -from source_list[-flat][-only_cells][-endpoints_only][-levels level_count][-pin_levels pin_count][-trace_arcs timing|enabled|all] + -from source_list[-flat][-only_cells][-endpoints_only][-levels level_count][-pin_levels pin_count][-trace_arcs timing|enabled|all] - -from source_list + -from source_list List of pins, ports, or nets to find the fanout of. For nets, the fanout of load pins on the nets are returned. @@ -7505,24 +7820,23 @@ - -flat + -flat - With –flat pins in the fanin at any hierarchy level are returned. Without ‑flat only pins at the same hierarchy level as the sinks are returned. + With –flat pins in the fanin at any hierarchy level are returned. Without ‑flat only pins at the same hierarchy level as the sinks are returned. - -only_cells + -only_cells Return the instances connected to the pins in the fanout. - - -endpoints_only + -endpoints_only Only return pins that are endpoints. @@ -7530,7 +7844,7 @@ - -level level_count + -level level_count Only return pins within level_count instance traversals. @@ -7538,7 +7852,7 @@ - -pin_levels pin_count + -pin_levels pin_count Only return pins within pin_count pin traversals. @@ -7546,114 +7860,114 @@ - -trace_arcs timing + -trace_arcs timing - Only trace through timing arcs that are not disabled. + Only trace through timing arcs that are not disabled. - -trace_arcs enabled + -trace_arcs enabled - Only trace through timing arcs that are not disabled. + Only trace through timing arcs that are not disabled. - -trace_arcs all + -trace_arcs all - Trace through all arcs, including disabled ones. + Trace through all arcs, including disabled ones. - The get_fanout command returns traverses the design from source_list pins, ports or nets backwards and return the fanout pins or instances. + The get_fanout command returns traverses the design from source_list pins, ports or nets backwards and return the fanout pins or instances. - get_full_name + get_full_name - object + object - object + object - A library, cell, port, instance, pin or timing arc object. + A library, cell, port, instance, pin or timing arc object. - Return the name of object. Equivalent to [get_property object full_name]. + Return the name of object. Equivalent to [get_property object full_name]. + - get_lib_cells + get_lib_cells - [-of_objects objects][-hsc separator][-filter expr][-regexp][-nocase][-quiet]patterns + [-of_objects objects][-hsc separator][-filter expr][-regexp][-nocase][-quiet]patterns - -of_objects objects + -of_objects objects - A list of instance objects. + A list of instance objects. - -hsc separator + -hsc separator - Character that separates the library name and cell name in patterns. Defaults to ‘/’. + Character that separates the library name and cell name in patterns. Defaults to ‘/’. - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - -regexp + -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. - -nocase + -nocase - Ignore case when matching. Only valid with –regexp. + Ignore case when matching. Only valid with –regexp. - - -quiet + -quiet - Do not warn if no matches are found. + Do not warn if no matches are found. - patterns + patterns A list of library cell name patterns of the form library_name/cell_name. @@ -7666,63 +7980,64 @@ - get_lib_pins + get_lib_pins - [-of_objects objects][-hsc separator][-filter expr][-regexp][-nocase][-quiet]patterns + [-of_objects objects][-hsc separator][-filter expr][-regexp][-nocase][-quiet]patterns - -of_objects objects + -of_objects objects - A list of library cell objects. + A list of library cell objects. - -hsc separator + -hsc separator - Character that separates the library name, cell name and port name in pattern. Defaults to ‘/’. + Character that separates the library name, cell name and port name in pattern. Defaults to ‘/’. + - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - -regexp + -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. - -nocase + -nocase - Ignore case when matching. Only valid with –regexp. + Ignore case when matching. Only valid with –regexp. - -quiet + -quiet - Do not warn if no matches are found. + Do not warn if no matches are found. - patterns + patterns A list of library port name patterns of the form library_name/cell_name/port_name. @@ -7735,117 +8050,117 @@ - get_libs + get_libs - [-filter expr][-regexp][-nocase][-quiet]patterns + [-filter expr][-regexp][-nocase][-quiet]patterns - - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - -regexp + -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. - -nocase + -nocase - Ignore case when matching. Only valid with –regexp. + Ignore case when matching. Only valid with –regexp. - -quiet + -quiet - Do not warn if no matches are found. + Do not warn if no matches are found. - patterns + patterns A list of library name patterns. - The get_libs command returns a list of clocks that match patterns. + The get_libs command returns a list of clocks that match patterns. + - get_nets + get_nets - [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] + [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] - -hierarchical + -hierarchical - Searches hierarchy levels below the current instance for matches. + Searches hierarchy levels below the current instance for matches. - -hsc separator + -hsc separator - Character that separates the library name, cell name and port name in pattern. Defaults to ‘/’. + Character that separates the library name, cell name and port name in pattern. Defaults to ‘/’. - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - -regexp + -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. - -nocase + -nocase - Ignore case when matching. Only valid with –regexp. + Ignore case when matching. Only valid with –regexp. - -quiet + -quiet - Do not warn if no matches are found. + Do not warn if no matches are found. - -of_objects objects + -of_objects objects The name of a pin or instance, a list of pins returned by get_pins, or a list of instances returned by get_cells. The –hierarchical option cannot be used with –of_objects. @@ -7853,97 +8168,98 @@ - patterns + patterns A list of net name patterns. - The get_nets command returns a list of all nets that match patterns. + The get_nets command returns a list of all nets that match patterns. - get_name + get_name - object + object - object + object A library, cell, port, instance, pin or timing arc object. - Return the name of object. Equivalent to [get_property object name]. + Return the name of object. Equivalent to [get_property object name]. + - get_pins + get_pins - [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] + [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] - -hierarchical + -hierarchical - Searches hierarchy levels below the current instance for matches. + Searches hierarchy levels below the current instance for matches. - -hsc separator + -hsc separator - Character that separates the library name, cell name and port name in pattern. Defaults to ‘/’. + Character that separates the library name, cell name and port name in pattern. Defaults to ‘/’. - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - -nocase + -nocase - Ignore case when matching. Only valid with –regexp. + Ignore case when matching. Only valid with –regexp. - -quiet + -quiet - Do not warn if no matches are found. + Do not warn if no matches are found. - -of_objects objects + -of_objects objects - The name of a net or instance, a list of nets returned by get_nets, or a list of instances returned by get_cells. The –hierarchical option cannot be used with –of_objects. + The name of a net or instance, a list of nets returned by get_nets, or a list of instances returned by get_cells. The –hierarchical option cannot be used with –of_objects. - patterns + patterns A list of pin name patterns. @@ -7951,63 +8267,63 @@ The get_pins command returns a list of all instance pins that match patterns. - A useful idiom to find the driver pin for a net is the following. - get_pins -of_objects [get_net net_name] -filter “direction==output” + A useful idiom to find the driver pin for a net is the following. + get_pins -of_objects [get_net net_name] -filter “direction==output” - - get_ports + get_ports - [-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] + [-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] + - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - -regexp + -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. - -nocase + -nocase - Ignore case when matching. Only valid with –regexp. + Ignore case when matching. Only valid with –regexp. - -quiet + -quiet - Do not warn if no matches are found. + Do not warn if no matches are found. - -of_objects objects + -of_objects objects - The name of net or a list of nets returned by get_nets. + The name of net or a list of nets returned by get_nets. - patterns + patterns A list of port name patterns. @@ -8020,31 +8336,31 @@ - get_property + get_property - [-object_type object_type]objectproperty + [-object_type object_type]objectproperty - -object_type object_type + -object_type object_type - The type of object when it is specified as a name.cell|pin|net|port|clock|library|library_cell|library_pin|timing_arc + The type of object when it is specified as a name.cell|pin|net|port|clock|library|library_cell|library_pin|timing_arc - object + object - An object returned by get_cells, get_pins, get_nets, get_ports, get_clocks, get_libs, get_lib_cells, get_lib_pins, or get_timing_arcs, or object name. ‑object_type is required if object is a name. + An object returned by get_cells, get_pins, get_nets, get_ports, get_clocks, get_libs, get_lib_cells, get_lib_pins, or get_timing_arcs, or object name. ‑object_type is required if object is a name. - property + property A property name. @@ -8052,89 +8368,120 @@ The properties for different objects types are shown below. - cell (SDC lib_cell) - base_namefilenamefull_namelibraryname - clock - full_nameis_generatedis_propagatedis_virtualnameperiodsources - edge - delay_max_falldelay_min_falldelay_max_risedelay_min_risefull_namefrom_pinsenseto_pin - instance (SDC cell) - cellfull_nameis_bufferis_clock_gateis_hierarchicalis_inverteris_macrois_memoryliberty_cellnameref_name - liberty_cell (SDC lib_cell) - areabase_namedont_usefilenamefull_nameis_bufferis_inverteris_memorylibraryname - liberty_port (SDC lib_pin) - capacitancedirectiondrive_resistancedrive_resistance_max_falldrive_resistance_max_risedrive_resistance_min_falldrive_resistance_min_risefull_nameintrinsic_delayintrinsic_delay_max_fallintrinsic_delay_max_riseintrinsic_delay_min_fallintrinsic_delay_min_riseis_register_clocklib_cellname - library - filename (Liberty library only)namefull_name - net - full_namename - path (PathEnd) + cell (SDC lib_cell) + base_namefilenamefull_namelibraryname + clock + full_nameis_generatedis_propagatedis_virtualnameperiodsources + edge + delay_max_falldelay_min_falldelay_max_risedelay_min_risefull_namefrom_pinsenseto_pin + instance (SDC cell) + cellfull_nameis_bufferis_clock_gateis_hierarchicalis_inverteris_macrois_memoryliberty_cellnameref_name + liberty_cell (SDC lib_cell) + areabase_namedont_usefilenamefull_nameis_bufferis_inverteris_memorylibraryname + liberty_port (SDC lib_pin) + capacitancedirectiondrive_resistancedrive_resistance_max_falldrive_resistance_max_risedrive_resistance_min_falldrive_resistance_min_risefull_nameintrinsic_delayintrinsic_delay_max_fallintrinsic_delay_max_riseintrinsic_delay_min_fallintrinsic_delay_min_riseis_register_clocklib_cellname + library + filename (Liberty library only)namefull_name + net + full_namename + path (PathEnd) endpointendpoint_clockendpoint_clock_pinslackstartpointstartpoint_clockpoints - pin - activity (activity in transitions per second, duty cycle, origin)slew_max_fallslew_max_riseslew_min_fallslew_min_riseclocksclock_domainsdirectionfull_nameis_hierarchicalis_portis_register_clocklib_pin_namenameslack_maxslack_max_fallslack_max_riseslack_minslack_min_fallslack_min_rise - port - activityslew_max_fallslew_max_riseslew_min_fallslew_min_risedirectionfull_nameliberty_portnameslack_maxslack_max_fallslack_max_riseslack_minslack_min_fallslack_min_rise - point (PathRef) - arrivalpinrequiredslack + pin + activity (activity in transitions per second, duty cycle, origin)origin is one ofglobalset_power_activity -globalinputset_power_activity -inputuserset_power_activity -input_ports -pinsvcdread_vcdsaifread_saifpropagatedpropagated from upstream activitiesclockSDC create_clock or create_generated_clockconstantconstant pins propagated from verilog tie high/low, set_case_analysis, set_logic_one/zero/dc + slew_max_fallslew_max_riseslew_min_fallslew_min_riseclocksclock_domainsdirectionfull_nameis_hierarchicalis_portis_register_clocklib_pin_namenameslack_maxslack_max_fallslack_max_riseslack_minslack_min_fallslack_min_rise + port + activityslew_max_fallslew_max_riseslew_min_fallslew_min_risedirectionfull_nameliberty_portnameslack_maxslack_max_fallslack_max_riseslack_minslack_min_fallslack_min_rise + point (PathRef) + arrivalpinrequiredslack + + + + + + get_scenes + + + [-mode mode_name]scene_name + + + + + mode_name + + + Get the scenes for mode_name. + + + + + scene_name + + + A scene name pattern. + + + + The get_scenes command is used to find the scenes matching a pattern or that use an SDC mode. - get_timing_edges + get_timing_edges - [-from from_pins][-to to_pins][-of_objects objects][-filter expr][patterns] + [-from from_pins][-to to_pins][-of_objects objects][-filter expr][patterns] - -from from_pin + -from from_pin - A list of pins. + A list of pins. - -to to_pin + -to to_pin - A list of pins. + A list of pins. - -of_objects objects + -of_objects objects - A list of instances or library cells. The –from and -to options cannot be used with –of_objects. + A list of instances or library cells. The –from and -to options cannot be used with –of_objects. - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - The get_timing_edges command returns a list of timing edges (arcs) to, from or between pins. The result can be passed to get_property or set_disable_timing. + The get_timing_edges command returns a list of timing edges (arcs) to, from or between pins. The result can be passed to get_property or set_disable_timing. + - group_path + group_path - -name group_name[-weight weight][-critical_range range][-from from_list |-rise_from from_list |-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list |-rise_to to_list |-fall_to to_list][-default] + -name group_name[-weight weight][-critical_range range][-from from_list |-rise_from from_list |-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list |-rise_to to_list |-fall_to to_list][-default] - -name group_name + -name group_name The name of the path group. @@ -8142,7 +8489,7 @@ - -weight weight + -weight weight Not supported. @@ -8150,7 +8497,7 @@ - -critical_range range + -critical_range range Not supported. @@ -8158,216 +8505,215 @@ - -from from_list + -from from_list - Group paths from a list of clocks, instances, ports, register clock pins, or latch data pins. + Group paths from a list of clocks, instances, ports, register clock pins, or latch data pins. - -rise_from from_list + -rise_from from_list - Group paths from the rising edge of clocks, instances, ports, register clock pins, or latch data pins. + Group paths from the rising edge of clocks, instances, ports, register clock pins, or latch data pins. - -fall_from from_list + -fall_from from_list - Group paths from the falling edge of clocks, instances, ports, register clock pins, or latch data pins. + Group paths from the falling edge of clocks, instances, ports, register clock pins, or latch data pins. - -through through_list + -through through_list - Group paths through a list of instances, pins or nets. + Group paths through a list of instances, pins or nets. - -rise_through through_list + -rise_through through_list - Group rising paths through a list of instances, pins or nets. + Group rising paths through a list of instances, pins or nets. - -fall_through through_list + -fall_through through_list - Group falling paths through a list of instances, pins or nets. + Group falling paths through a list of instances, pins or nets. - -to to_list + -to to_list - Group paths to a list of clocks, instances, ports or pins. + Group paths to a list of clocks, instances, ports or pins. - -rise_to to_list + -rise_to to_list - Group rising paths to a list of clocks, instances, ports or pins. + Group rising paths to a list of clocks, instances, ports or pins. - -fall_to to_list + -fall_to to_list - Group falling paths to a list of clocks, instances, port-s or pins. + Group falling paths to a list of clocks, instances, port-s or pins. - - -default + -default - Restore the paths in the path group -from/-to/-through/-to to their default path group. + Restore the paths in the path group -from/-to/-through/-to to their default path group. - The group_path command is used to group paths reported by the report_checks command. See set_false_path for a description of allowed from_list, through_list and to_list objects. + The group_path command is used to group paths reported by the report_checks command. See set_false_path for a description of allowed from_list, through_list and to_list objects. - include + include - [-echo|-e][-verbose|-v]filename[> log_filename][>> log_filename] + [-echo|-e][-verbose|-v]filename[> log_filename][>> log_filename] - -echo|-e + -echo|-e - Print each command before evaluating it. + Print each command before evaluating it. - -verbose|-v + -verbose|-v - Print each command before evaluating it as well as the result it returns. + Print each command before evaluating it as well as the result it returns. - filename + filename - The name of the file containing commands to read. + The name of the file containing commands to read. - > log_filename + > log_filename - Redirect command output to log_filename. + Redirect command output to log_filename. - >> log_filename + >> log_filename - Redirect command output and append log_filename. + Redirect command output and append log_filename. - Read STA/SDC/Tcl commands from filename. - The include command stops and reports any errors encountered while reading a file unless sta_continue_on_error is 1. + Read STA/SDC/Tcl commands from filename. + The include command stops and reports any errors encountered while reading a file unless sta_continue_on_error is 1. - link_design + link_design - [-no_black_boxes][cell_name] + [-no_black_boxes][cell_name] - -no_black_boxes + -no_black_boxes - Do not make empty “black box” cells for instances that reference undefined cells. + Do not make empty “black box” cells for instances that reference undefined cells. - cell_name + cell_name - The top level module/cell name of the design hierarchy to link. + The top level module/cell name of the design hierarchy to link. - Link (elaborate, flatten) the the top level cell cell_name. The design must be linked after reading netlist and library files. The default value of cell_name is the current design. + Link (elaborate, flatten) the the top level cell cell_name. The design must be linked after reading netlist and library files. The default value of cell_name is the current design. The linker creates empty "block box" cells for instances the reference undefined cells when the variable link_create_black_boxes is true. When link_create_black_boxes is false an error is reported and the link fails. The link_design command returns 1 if the link succeeds and 0 if it fails. - - make_instance + make_instance - inst_pathlib_cell + inst_pathlib_cell - inst_path + inst_path A hierarchical instance name. + - lib_cell + lib_cell The library cell of the new instance. - The make_instance command makes an instance of library cell lib_cell. + The make_instance command makes an instance of library cell lib_cell. - make_net + make_net - net_name_list + net_name_list - net_name_list + net_name_list A list of net names. @@ -8380,48 +8726,48 @@ - read_liberty + read_liberty - [-corner corner][-min][-max][-infer_latches]filename + [-corner corner][-min][-max][-infer_latches]filename - -corner corner + -corner corner - Use the library for process corner corner delay calculation. + Use the library for process corner corner delay calculation. - -min + -min - Use library for min delay calculation. + Use library for min delay calculation. - -max + -max - Use library for max delay calculation. + Use library for max delay calculation. - filename + filename - The liberty file name to read. + The liberty file name to read. The read_liberty command reads a Liberty format library file. The first library that is read sets the units used by SDC/TCL commands and reporting. The include_file attribute is supported. - Some Liberty libraries do not include latch groups for cells that are describe transparent latches. In that situation the -infer_latches command flag can be used to infer the latches. The timing arcs required for a latch to be inferred should look like the following: - cell (infered_latch) { pin(D) { direction : input ; timing () { related_pin : "E" ; timing_type : setup_falling ; } timing () { related_pin : "E" ; timing_type : hold_falling ; } } pin(E) { direction : input; } pin(Q) { direction : output ; timing () { related_pin : "D" ; } timing () { related_pin : "E" ; timing_type : rising_edge ; } }} + Some Liberty libraries do not include latch groups for cells that are describe transparent latches. In that situation the -infer_latches command flag can be used to infer the latches. The timing arcs required for a latch to be inferred should look like the following: + cell (infered_latch) { pin(D) { direction : input ; timing () { related_pin : "E" ; timing_type : setup_falling ; } timing () { related_pin : "E" ; timing_type : hold_falling ; } } pin(E) { direction : input; } pin(Q) { direction : output ; timing () { related_pin : "D" ; } timing () { related_pin : "E" ; timing_type : rising_edge ; } }} In this example a positive level-sensitive latch is inferred. Files compressed with gzip are automatically uncompressed. @@ -8429,247 +8775,232 @@ - read_saif + read_saif - [-scope scope]filename + [-scope scope]filename - scope + scope - The SAIF scope of the current design to extract simulation data. Typically the test bench name and design under test instance name. Scope levels are separated with ‘/’. + The SAIF scope of the current design to extract simulation data. Typically the test bench name and design under test instance name. Scope levels are separated with ‘/’. - filename + filename - The name of the SAIF file to read. + The name of the SAIF file to read. - The read_saif command reads a SAIF (Switching Activity Interchange Format) file from a Verilog simulation and extracts pin activities and duty cycles for use in power estimation. Files compressed with gzip are supported. Annotated activities are propagated to the fanout of the annotated pins. + The read_saif command reads a SAIF (Switching Activity Interchange Format) file from a Verilog simulation and extracts pin activities and duty cycles for use in power estimation. Files compressed with gzip are supported. Annotated activities are propagated to the fanout of the annotated pins. + - read_sdc + read_sdc - [-echo]filename + [-mode mode_name][-echo]filename + + + + + mode_name + + + Mode for the SDC commands in the file. - -echo + -echo - Print each command before evaluating it. + Print each command before evaluating it. - filename + filename - SDC command file. + SDC command file. - Read SDC commands from filename. + Read SDC commands from filename. + If the mode does not exist it is created. Multiple SDC files can append commands to a mode by using the -mode_name argument for each one. If no -mode arguement is is used the commands are added to the current mode. The read_sdc command stops and reports any errors encountered while reading a file unless sta_continue_on_error is 1. Files compressed with gzip are automatically uncompressed. - - read_sdf + read_sdf - [-corner corner][-unescaped_dividers]filename + [-scene scene][-unescaped_dividers]filename - -corner corner + scene - Process corner delays to annotate. + Scene delays to annotate. - -unescaped_dividers + -unescaped_dividers - With this option path names in the SDF do not have to escape hierarchy dividers when the path name is escaped. For example, the escaped Verilog name "\inst1/inst2 " can be referenced as "inst1/inst2". The correct SDF name is "inst1\/inst2", since the divider does not represent a change in hierarchy in this case. + With this option path names in the SDF do not have to escape hierarchy dividers when the path name is escaped. For example, the escaped Verilog name "\inst1/inst2 " can be referenced as "inst1/inst2". The correct SDF name is "inst1\/inst2", since the divider does not represent a change in hierarchy in this case. - filename + filename - The name of the SDF file to read. + The name of the SDF file to read. - Read SDF delays from a file. The min and max values in the SDF tuples are used to annotate the delays for corner. The typical values in the SDF tuples are ignored. If multiple corners are defined -corner must be specified. + Read SDF delays from a file. The min and max values in the SDF tuples are used to annotate the delays for corner. The typical values in the SDF tuples are ignored. If multiple scenes are defined -scene must be specified. SDC annotation for mcmm analysis must follow the scene definitions. Files compressed with gzip are automatically uncompressed. INCREMENT is supported as an alias for INCREMENTAL. - The following SDF statements are not supported. + The following SDF statements are not supported. PORTINSTANCE wildcards - read_spef + read_spef - [-min][-max][-path path][-corner corner][-keep_capacitive_coupling][-coupling_reduction_factor factor][-reduce]filename - - - - - -min - - - Annotate parasitics for min delays. - - - - - -max - - - Annotate parasitics for max delays. - - - - - path - - - Hierarchical block instance path to annotate with parasitics. + [-name name][-keep_capacitive_coupling][-coupling_reduction_factor factor][-reduce][-path path]filename - + - -corner corner + name - Annotate parasitics for one process corner. + The name of the SPEF parasitics to use for defining scenes. The default is the base name of filename. - - + - ‑keep_capacitive_coupling + path - Keep coupling capacitors in parasitic networks rather than converting them to grounded capacitors. + Hierarchical block instance path to annotate with parasitics. - + - ‑coupling_reduction_factorfactor + ‑keep_capacitive_coupling - Factor to multiply coupling capacitance by when reducing parasitic networks. The default value is 1.0. + Keep coupling capacitors in parasitic networks rather than converting them to grounded capacitors. - + - -reduce + ‑coupling_reduction_factorfactor - Reduce detailed parasitics and do not save the detailed parastic network. + Factor to multiply coupling capacitance by when reducing parasitic networks. The default value is 1.0. - + - filename + filename - The name of the parasitics file to read. + The name of the parasitics file to read. - The read_spef command reads a file of net parasitics in SPEF format. Use the report_parasitic_annotation command to check for nets that are not annotated. + The read_spef command reads a file of net parasitics in SPEF format. Use the report_parasitic_annotation command to check for nets that are not annotated. Files compressed with gzip are automatically uncompressed. - Separate parasitics can be annotated for corners and min and max paths using the -corner, –min and -max arguments. To use the same parastiics for every corner and for min/max delay calculation read the SPEF without -corner, -min, and -max options. - read_spef spef1 - To use separate parastics for min/max delay, use the -min, and -max options for each SPEF file. - read_spef -min spef1read_spef -max spef2 - To use separate parastics for each corner, use the -corner option for each SPEF file. - read_spef -corner ss spef1read_spef -corner tt spef2read_spef -corner ff spef3 - To use separate parastics for each corner and separate min/max delay calculation, use the -corner option along with the -min, and -max options. - read_spef -corner ss -min spef1read_spef -corner ss -max spef2read_spef -corner ff -min spef3read_spef -corner ff -max spef4 - With the -reduce option, the current delay calculator reduces the parastic network to the appropriate type and deletes the parasitic network. This substantially reduces the memory required to store the parasitics. - Coupling capacitors are multiplied by the –coupling_reduction_factor when a parasitic network is reduced. + Separate min/max parasitics can be annotated for each scene mode/corner. + read_spef -name min spef1read_spef -name max spef2define_scene -mode mode1 -spef_min min -spef_max max + Coupling capacitors are multiplied by the –coupling_reduction_factor when a parasitic network is reduced. The following SPEF constructs are ignored. - *DESIGN_FLOW (all values are ignored)*S slews*D driving cell*I pin capacitances (library cell capacitances are used instead)*Q r_net load poles*K r_net load residues - If the SPEF file contains triplet values the first value is used. - Parasitic networks (DSPEF) can be annotated on hierarchical blocks using the -path argument to specify the instance path to the block. Parasitic networks in the higher level netlist are stitched together at the hierarchical pins of the blocks. + *DESIGN_FLOW (all values are ignored)*S slews*D driving cell*I pin capacitances (library cell capacitances are used instead)*Q r_net load poles*K r_net load residues + If the SPEF file contains triplet values the first value is used. + Parasitic networks (DSPEF) can be annotated on hierarchical blocks using the -path argument to specify the instance path to the block. Parasitic networks in the higher level netlist are stitched together at the hierarchical pins of the blocks. - read_vcd + read_vcd - [-scope scope]filename + [-scope scope][-mode mode_name]filename + + + + + scope + + + The VCD scope of the current design to extract simulation data. Typically the test bench name and design under test instance name. Scope levels are separated with ‘/’. - scope + mode_name - The VCD scope of the current design to extract simulation data. Typically the test bench name and design under test instance name. Scope levels are separated with ‘/’. + Mode to annotate activities. - filename + filename - The name of the VCD file to read. + The name of the VCD file to read. - The read_vcd command reads a VCD (Value Change Dump) file from a Verilog simulation and extracts pin activities and duty cycles for use in power estimation. Files compressed with gzip are supported. Annotated activities are propagated to the fanout of the annotated pins. + The read_vcd command reads a VCD (Value Change Dump) file from a Verilog simulation and extracts pin activities and duty cycles for use in power estimation. Files compressed with gzip are supported. Annotated activities are propagated to the fanout of the annotated pins. - read_verilog + read_verilog - filename + filename - filename + filename - The name of the verilog file to read. + The name of the verilog file to read. - The read_verilog command reads a gate level verilog netlist. After all verilog netlist and Liberty libraries are read the design must be linked with the link_design command. - Verilog 2001 module port declaratations are supported. An example is shown below. + The read_verilog command reads a gate level verilog netlist. After all verilog netlist and Liberty libraries are read the design must be linked with the link_design command. + Verilog 2001 module port declaratations are supported. An example is shown below. module top (input in1, in2, clk1, clk2, clk3, output out); Files compressed with gzip are automatically uncompressed. @@ -8677,699 +9008,715 @@ - replace_cell + replace_cell - instance_listreplacement_cell + instance_listreplacement_cell + - instance_list + instance_list - A list of instances to swap the cell. + A list of instances to swap the cell. - replacement_cell + replacement_cell - The replacement lib cell. + The replacement lib cell. - The replace_cell command changes the cell of an instance. The replacement cell must have the same port list (number, name, and order) as the instance's existing cell for the replacement to be successful. + The replace_cell command changes the cell of an instance. The replacement cell must have the same port list (number, name, and order) as the instance's existing cell for the replacement to be successful. - - replace_activity_annotation + replace_activity_annotation - [-report_unannotated][-report_annotated] + [-report_unannotated][-report_annotated] - -report_unannotated + -report_unannotated - Report unannotated pins. + Report unannotated pins. - -report_unannotated + -report_unannotated - Report annotated pins. + Report annotated pins. - Report a summary of pins that are annotated by read_vcd, read_saif or set_power_activity. Sequential internal pins and hierarchical pins are ignored. + Report a summary of pins that are annotated by read_vcd, read_saif or set_power_activity. Sequential internal pins and hierarchical pins are ignored. - report_annotated_check + report_annotated_check - [-setup][-hold][-recovery][-removal][-nochange][-width][-period][-max_skew][-max_line lines][-report_annotated][-report_unannotated][-constant_arcs] + [-setup][-hold][-recovery][-removal][-nochange][-width][-period][-max_skew][-max_line lines][-report_annotated][-report_unannotated][-constant_arcs] - -setup + -setup - Report annotated setup checks. + Report annotated setup checks. - -hold + -hold - Report annotated hold checks. + Report annotated hold checks. - -recovery + -recovery - Report annotated recovery checks. + Report annotated recovery checks. - -removal + -removal - Report annotated removal checks. + Report annotated removal checks. - -nochange + -nochange - Report annotated nochange checks. + Report annotated nochange checks. - -width + -width - Report annotated width checks. + Report annotated width checks. - -period + -period - Report annotated period checks. + Report annotated period checks. - -max_skew + -max_skew - Report annotated max skew checks. + Report annotated max skew checks. + - -max_line lines + -max_line lines - Maximum number of lines listed by the report_annotated and ‑report_unannotated options. + Maximum number of lines listed by the report_annotated and ‑report_unannotated options. - -report_annotated + -report_annotated - Report annotated timing arcs. + Report annotated timing arcs. - -report_unannotated + -report_unannotated - Report unannotated timing arcs. + Report unannotated timing arcs. - - -constant_arcs + -constant_arcs - Report separate annotation counts for arcs disabled by logic constants (set_logic_one, set_logic_zero). + Report separate annotation counts for arcs disabled by logic constants (set_logic_one, set_logic_zero). - The report_annotated_check command reports a summary of SDF timing check annotation. The -report_annotated and report_annotated options can be used to list arcs that are annotated or not annotated. + The report_annotated_check command reports a summary of SDF timing check annotation. The -report_annotated and report_annotated options can be used to list arcs that are annotated or not annotated. - report_annotated_delay + report_annotated_delay - [-cell][-net][-from_in_ports][-to_out_ports][-max_lines lines][-report_annotated][-report_unannotated][-constant_arcs] + [-cell][-net][-from_in_ports][-to_out_ports][-max_lines lines][-report_annotated][-report_unannotated][-constant_arcs] - -cell + -cell - Report annotated cell delays. + Report annotated cell delays. - -net + -net - Report annotated internal net delays. + Report annotated internal net delays. - -from_in_ports + -from_in_ports - Report annotated delays from input ports. + Report annotated delays from input ports. - -to_out_ports + -to_out_ports - Report annotated delays to output ports. + Report annotated delays to output ports. - -max_lines lines + -max_lines lines - Maximum number of lines listed by the report_annotated and ‑report_unannotated options. + Maximum number of lines listed by the report_annotated and ‑report_unannotated options. - -report_annotated + -report_annotated - Report annotated timing arcs. + Report annotated timing arcs. - -report_unannotated + -report_unannotated - Report unannotated timing arcs. + Report unannotated timing arcs. - -constant_arcs + -constant_arcs Report separate annotation counts for arcs disabled by logic constants (set_logic_one, set_logic_zero). - The report_annotated_delay command reports a summary of SDF delay annotation. Without the ‑from_in_ports and –to_out_ports options arcs to and from top level ports are not reported. The ‑report_annotated and report_unannotated options can be used to list arcs that are annotated or not annotated. + The report_annotated_delay command reports a summary of SDF delay annotation. Without the ‑from_in_ports and –to_out_ports options arcs to and from top level ports are not reported. The ‑report_annotated and report_unannotated options can be used to list arcs that are annotated or not annotated. - report_checks + report_checks - [-from from_list |-rise_from from_list |-fall_from from_list][-through through_list |-rise_through through_list |-fall_through through_list][-to to_list |-rise_to to_list |-fall_to to_list][-unconstrained][-path_delay min|min_rise|min_fall |max|max_rise|max_fall |min_max][-group_path_count path_count][-endpoint_path_count endpoint_path_count][-unique_paths_to_endpoint][-unique_edges_to_endpoint][-corner corner][-slack_max max_slack][-slack_min min_slack][-sort_by_slack][-path_group groups][-format end|full|short|summary |full_clock|full_clock_expanded |json][-fields fields][-digits digits][-no_line_split][> filename][>> filename] + [-from from_list |-rise_from from_list |-fall_from from_list][-through through_list |-rise_through through_list |-fall_through through_list][-to to_list |-rise_to to_list |-fall_to to_list][-unconstrained][-path_delay min|min_rise|min_fall |max|max_rise|max_fall |min_max][-group_path_count path_count][-endpoint_path_count endpoint_path_count][-unique_paths_to_endpoint][-unique_edges_to_endpoint][-scenes scenes][-slack_max max_slack][-slack_min min_slack][-sort_by_slack][-path_group groups][-format end|full|short|summary |full_clock|full_clock_expanded |json][-fields fields][-digits digits][-no_line_split][> filename][>> filename] - -from from_list + -from from_list - Report paths from a list of clocks, instances, ports, register clock pins, or latch data pins. + Report paths from a list of clocks, instances, ports, register clock pins, or latch data pins. - -rise_from from_list + -rise_from from_list - Report paths from the rising edge of clocks, instances, ports, register clock pins, or latch data pins. + Report paths from the rising edge of clocks, instances, ports, register clock pins, or latch data pins. - -fall_from from_list + -fall_from from_list - Report paths from the falling edge of clocks, instances, ports, register clock pins, or latch data pins. + Report paths from the falling edge of clocks, instances, ports, register clock pins, or latch data pins. - -through through_list + -through through_list - Report paths through a list of instances, pins or nets. + Report paths through a list of instances, pins or nets. - -rise_through through_list + -rise_through through_list - Report rising paths through a list of instances, pins or nets. + Report rising paths through a list of instances, pins or nets. - -fall_through through_list + -fall_through through_list - Report falling paths through a list of instances, pins or nets. + Report falling paths through a list of instances, pins or nets. - -to to_list + -to to_list - Report paths to a list of clocks, instances, ports or pins. + Report paths to a list of clocks, instances, ports or pins. - -rise_to to_list + -rise_to to_list - Report rising paths to a list of clocks, instances, ports or pins. + Report rising paths to a list of clocks, instances, ports or pins. - -fall_to to_list + -fall_to to_list - Report falling paths to a list of clocks, instances, ports or pins. + Report falling paths to a list of clocks, instances, ports or pins. - -unconstrained + -unconstrained - Report unconstrained paths also. The unconstrained path group is not reported without this option. + Report unconstrained paths also. The unconstrained path group is not reported without this option. - -path_delay min + -path_delay min - Report min path (hold) checks. + Report min path (hold) checks. - -path_delay min_rise + -path_delay min_rise - Report min path (hold) checks for rising endpoints. + Report min path (hold) checks for rising endpoints. - -path_delay min_fall + -path_delay min_fall - Report min path (hold) checks for falling endpoints. + Report min path (hold) checks for falling endpoints. - -path_delay max + -path_delay max - Report max path (setup) checks. + Report max path (setup) checks. - -path_delay max_rise + -path_delay max_rise - Report max path (setup) checks for rising endpoints. + Report max path (setup) checks for rising endpoints. - -path_delay max_fall + -path_delay max_fall - Report max path (setup) checks for falling endpoints. + Report max path (setup) checks for falling endpoints. - -path_delay min_max + -path_delay min_max - Report max and max path (setup and hold) checks. + Report max and max path (setup and hold) checks. - -group_path_count path_count + -group_path_count path_count - The number of paths to report in each path group. The default is 1. + The number of paths to report in each path group. The default is 1. - -endpoint_path_count endpoint_path_count + -endpoint_path_count endpoint_path_count - The number of paths to report for each endpoint. The default is 1. + The number of paths to report for each endpoint. The default is 1. - ‑unique_paths_to_endpoint + ‑unique_paths_to_endpoint - When multiple paths to an endpoint are specified with ‑endpoint_path_count, many of the paths may differ only in the rise/fall edges of the pins in the paths. With this option only the worst path through the set of pins is reported. + When multiple paths to an endpoint are specified with ‑endpoint_path_count, many of the paths may differ only in the rise/fall edges of the pins in the paths. With this option only the worst path through the set of pins is reported. - ‑unique_edges_to_endpoint + ‑unique_edges_to_endpoint - When multiple paths to an endpoint are specified with ‑endpoint_path_count, conditional timing arcs result in paths that through the same pins and rise/fall edges. With this option only the worst path through the set of pins and rise/fall edges is reported. + When multiple paths to an endpoint are specified with ‑endpoint_path_count, conditional timing arcs result in paths that through the same pins and rise/fall edges. With this option only the worst path through the set of pins and rise/fall edges is reported. - -corner corner + scenes - Report paths for one process corner. The default is to report paths for all process corners. + Report paths for one process corner. The default is to report paths for all process corners. + - -slack_max max_slack + max_slack - Only report paths with less slack than max_slack. + Only report paths with less slack than max_slack. - - -slack_min min_slack + min_slack - Only report paths with more slack than min_slack. + Only report paths with more slack than min_slack. - -sort_by_slack + -sort_by_slack - Sort paths by slack rather than slack grouped by path group. + Sort paths by slack rather than slack grouped by path group. - -path_group groups + groups - List of path groups to report. The default is to report all path groups. + List of path groups to report. The default is to report all path groups. - -format end + -format end - Report path ends in one line with delay, required time and slack. + Report path ends in one line with delay, required time and slack. - -format full + -format full - Report path start and end points and the path. This is the default path type. + Report path start and end points and the path. This is the default path type. - -format full_clock + -format full_clock - Report path start and end points, the path, and the source and and target clock paths. + Report path start and end points, the path, and the source and and target clock paths. - -format full_clock_expanded + -format full_clock_expanded - Report path start and end points, the path, and the source and and target clock paths. If the clock is generated and propagated, the path from the clock source pin is also reported. + Report path start and end points, the path, and the source and and target clock paths. If the clock is generated and propagated, the path from the clock source pin is also reported. - -format short + -format short - Report only path start and end points. + Report only path start and end points. - -format summary + -format summary - Report only path ends with delay. + Report only path ends with delay. - -format json + -format json - Report in json format. -fields is ignored. + Report in json format. -fields is ignored. - -fields fields + fields - List of capacitance|slew|input_pins|hierarchical_pins|nets|fanout|src_attr + List of capacitance|slew|input_pins|hierarchical_pins|nets|fanout|src_attr|variance - -digits digits + digits - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - -no_line_splits + -no_line_splits - Do not split long lines into multiple lines. + Do not split long lines into multiple lines. - The report_checks command reports paths in the design. Paths are reported in groups by capture clock, unclocked path delays, gated clocks and unconstrained. - See set_false_path for a description of allowed from_list, through_list and to_list objects. + The report_checks command reports paths in the design. Paths are reported in groups by capture clock, unclocked path delays, gated clocks and unconstrained. + See set_false_path for a description of allowed from_list, through_list and to_list objects. - report_check_types + report_check_types - [-violators][-verbose][-format slack_only|end][-max_delay][-min_delay][-recovery][-removal][-clock_gating_setup][-clock_gating_hold][-max_slew][-min_slew][-min_pulse_width][-min_period][-digits digits][-no_split_lines][> filename][>> filename] + [-scenes scenes][-violators][-verbose][-fields fields][-format slack_only|end][-max_delay][-min_delay][-recovery][-removal][-clock_gating_setup][-clock_gating_hold][-max_slew][-min_slew][-min_pulse_width][-min_period][-digits digits][-no_split_lines][> filename][>> filename] + + + + + scenes + + + Report checks for some scenes. The default value is all scenes. - -violators + -violators - Report all violated timing and design rule constraints. + Report all violated timing and design rule constraints. - -verbose + -verbose - Use a verbose output format. + Use a verbose output format. - -format slack_only + -format slack_only - Report the minimum slack for each timing check. + Report the minimum slack for each timing check. - -format end + -format end - Report the endpoint for each check. + Report the endpoint for each check. - -max_delay + fields - Report setup and max delay path delay constraints. + List of capacitance|slew|input_pins|hierarchical_pins|nets|fanout|src_attr|variance - -min_delay + -max_delay - Report hold and min delay path delay constraints. + Report setup and max delay path delay constraints. - -recovery + -min_delay - Report asynchronous recovery checks. + Report hold and min delay path delay constraints. - -removal + -recovery - Report asynchronous removal checks. + Report asynchronous recovery checks. - -clock_gating_setup + -removal - Report gated clock enable setup checks. + Report asynchronous removal checks. - -clock_gating_hold + -clock_gating_setup - Report gated clock hold setup checks. + Report gated clock enable setup checks. - -max_slew + -clock_gating_hold - Report max transition design rule checks. + Report gated clock hold setup checks. + - -max_skew + -max_slew - Report max skew design rule checks. + Report max transition design rule checks. - -min_pulse_width + -max_skew - Report min pulse width design rule checks. + Report max skew design rule checks. + + + + + -min_pulse_width + + + Report min pulse width design rule checks. - - -min_period + -min_period - Report min period design rule checks. + Report min period design rule checks. - -min_slew + -min_slew - Report min slew design rule checks. + Report min slew design rule checks. - -digits digits + -digits digits - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - -no_split_lines + -no_split_lines - Do not split long lines into multiple lines. + Do not split long lines into multiple lines. @@ -9379,84 +9726,92 @@ - report_clock_latency + report_clock_latency - [-clock clocks][-include_internal_latency][-digits digits] + [-clocks clocks][-scenes scenes][-include_internal_latency][-digits digits] + + + + + clocks + + + The clocks to report. The default value is all c - -clock clocks + scenes - The clocks to report. + Report clocks for scenes. The default value is all clocks in scenes modes. - -include_internal_latency + -include_internal_latency - Include internal clock latency from liberty min/max_clock_tree_path timing groups. + Include internal clock latency from liberty min/max_clock_tree_path timing groups. - -digits digits + digits - The number of digits to report for delays. + The number of digits to report for delays. - Report the clock network latency. + Report the clock network latency. - report_clock_min_period + report_clock_min_period - [-clocks clocks][-include_port_paths] + [-clocks clocks][-scenes scenes][-include_port_paths] - -clocks clocks + clocks - The clocks to report. + The clocks to report. - -include_port_paths + -include_port_paths - Include paths from input port and to output ports. + Include paths from input port and to output ports. - Report the minimum period and maximum frequency for clocks. If the -clocks argument is not specified all clocks are reported. The minimum period is determined by examining the smallest slack paths between registers the rising edges of the clock or between falling edges of the clock. Paths between different clocks, different clock edges of the same clock, level sensitive latches, or paths constrained by set_multicycle_path, set_max_path are not considered. + Report the minimum period and maximum frequency for clocks. If the -clocks argument is not specified all clocks are reported. The minimum period is determined by examining the smallest slack paths between registers the rising edges of the clock or between falling edges of the clock. Paths between different clocks, different clock edges of the same clock, level sensitive latches, or paths constrained by set_multicycle_path, set_max_path are not considered. - report_clock_properties + report_clock_properties - [clock_names] + [clock_names] - clock_names + clock_names - List of clock names to report. + List of clock names to report. @@ -9464,114 +9819,122 @@ - - report_clock_skew + report_clock_skew - [-setup|-hold][-clock clocks][-include_internal_latency][-digits digits] + [-setup|-hold][-clocks clocks][-scenes scenes][-include_internal_latency][-digits digits] + + + + + -setup + + + Report skew for setup checks. - -setup + -hold - Report skew for setup checks. + Report skew for hold checks. - -hold + clocks - Report skew for hold checks. + The clocks to report. The default value is all clocks in scenes modes. - -clock clocks + scenes - The clocks to report. + Report clocks for scenes. The default value is all scenes. - -include_internal_latency + -include_internal_latency - Include internal clock latency from liberty min/max_clock_tree_path timing groups. + Include internal clock latency from liberty min/max_clock_tree_path timing groups. - -digits digits + -digits digits - The number of digits to report for delays. + The number of digits to report for delays. - Report the maximum difference in clock arrival between every source and target register that has a path between the source and target registers. + Report the maximum difference in clock arrival between every source and target register that has a path between the source and target registers. - report_dcalc + report_dcalc - [-from from_pin][-to to_pin][-corner corner][-min][-max][-digits digits][> filename][>> filename] + [-from from_pin][-to to_pin][-scene scene][-min][-max][-digits digits][> filename][>> filename] - -from from_pin + from_pin - Report delay calculations for timing arcs from instance input pin from_pin. + Report delay calculations for timing arcs from instance input pin from_pin. - -to to_pin + to_pin - Report delay calculations for timing arcs to instance output pin to_pin. + Report delay calculations for timing arcs to instance output pin to_pin. + - -corner corner + scene - Report paths for process corner. The -corner keyword is required if more than one process corner is defined. + Report paths for process scene. The -scene keyword is required if more than one process corner is defined. - -min + -min - Report delay calculation for min delays. + Report delay calculation for min delays. - -max + -max - Report delay calculation for max delays. + Report delay calculation for max delays. - -digits digits + -digits digits - The number of digits after the decimal point to report. The default is sta_report_default_digits. + The number of digits after the decimal point to report. The default is sta_report_default_digits. @@ -9579,64 +9942,80 @@ - - report_disabled_edges + report_disabled_edges - + The report_disabled_edges command reports disabled timing arcs along with the reason they are disabled. Each disabled timing arc is reported as the instance name along with the from and to ports of the arc. The disable reason is shown next. Arcs that are disabled with set_disable_timing are reported with constraint as the reason. Arcs that are disabled by constants are reported with constant as the reason along with the constant instance pin and value. Arcs that are disabled to break combinational feedback loops are reported with loop as the reason. - > report_disabled_edgesu1 A B constant B=0 + > report_disabled_edgesu1 A B constant B=0 - report_edges + report_edges - [-from from_pin][-to to_pin] + [-from from_pin][-to to_pin][-report_variation][-digits digits] + + + + + from_pin + + + Report edges/timing arcs from pin from_pin. + + + + + to_pin + + + Report edges/timing arcs to pin to_pin. - -from from_pin + -report_variation - Report edges/timing arcs from pin from_pin. + - -to to_pin + digits - Report edges/timing arcs to pin to_pin. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - Report the edges/timing arcs and their delays in the timing graph from/to/between pins. + Report the edges/timing arcs and their delays in the timing graph from/to/between pins. + - report_instance + report_instance - instance_path[> filename][>> filename] + instance_path[> filename][>> filename] - instance_path + instance_path - Hierarchical path to an instance. + Hierarchical path to an instance. @@ -9646,307 +10025,280 @@ - report_lib_cell + report_lib_cell - cell_name[> filename][>> filename] + cell_name[> filename][>> filename] - cell_name + cell_name - The name of a library cell. + The name of a library cell. - Describe the liberty library cell cell_name. + Describe the liberty library cell cell_name. - report_net + report_net - [-digits digits]net_path[> filename][>> filename] + [-digits digits]net_path[> filename][>> filename] - - -digits digits + digits - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - net_path + net_path - Hierarchical path to a net. + Hierarchical path to a net. - Report the connections and capacitance of a net. + Report the connections and capacitance of a net. - report_parasitic_annotation + report_parasitic_annotation - [-report_unannotated][> filename][>> filename] + [-report_unannotated][> filename][>> filename] - -report_unannotated + -report_unannotated - Report unannotated and partially annotated nets. + Report unannotated and partially annotated nets. - Report SPEF parasitic annotation completeness. + Report SPEF parasitic annotation completeness. - report_power + report_power - [-instances instances][-highest_power_instances count][-digits digits][> filename][>> filename] + [-instances instances][-highest_power_instances count][-digits digits][> filename][>> filename] + - -instances instances + -instances instances - Report the power for each instance of instances. If the instance is hierarchical the total power for the instances inside the hierarchical instance is reported. + Report the power for each instance of instances. If the instance is hierarchical the total power for the instances inside the hierarchical instance is reported. - -highest_power_instances count + -highest_power_instances count - Report the power for the count highest power instances. + Report the power for the count highest power instances. - -digits digits + -digits digits - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - The report_power command uses static power analysis based on propagated or annotated pin activities in the circuit using Liberty power models. The internal, switching, leakage and total power are reported. Design power is reported separately for combinational, sequential, macro and pad groups. Power values are reported in watts. - The read_vcd or read_saif commands can be used to read activities from a file based on simulation. If no simulation activities are available, the set_power_activity command should be used to set the activity of input ports or pins in the design. The default input activity and duty for inputs are 0.1 and 0.5 respectively. The activities are propagated from annotated input ports or pins through gates and used in the power calculations. - Group Internal Switching Leakage Total Power Power Power Power----------------------------------------------------------------Sequential 3.29e-06 3.41e-08 2.37e-07 3.56e-06 92.4%Combinational 1.86e-07 3.31e-08 7.51e-08 2.94e-07 7.6%Macro 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0%Pad 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0%---------------------------------------------------------------Total 3.48e-06 6.72e-08 3.12e-07 3.86e-06 100.0% 90.2% 1.7% 8.1% - - - - - - report_pulse_width_checks - - - [-verbose][-digits digits][-no_line_splits][pins][> filename][>> filename] - - - - - -verbose - - - Use a verbose output format. - - - - - -digits digits - - - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - - - - - -no_line_splits + The report_power command uses static power analysis based on propagated or annotated pin activities in the circuit using Liberty power models. The internal, switching, leakage and total power are reported. Design power is reported separately for combinational, sequential, macro and pad groups. Power values are reported in watts. + The read_vcd or read_saif commands can be used to read activities from a file based on simulation. If no simulation activities are available, the set_power_activity command should be used to set the activity of input ports or pins in the design. The default input activity and duty for inputs are 0.1 and 0.5 respectively. The activities are propagated from annotated input ports or pins through gates and used in the power calculations. + Group Internal Switching Leakage Total Power Power Power Power----------------------------------------------------------------Sequential 3.29e-06 3.41e-08 2.37e-07 3.56e-06 92.4%Combinational 1.86e-07 3.31e-08 7.51e-08 2.94e-07 7.6%Macro 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0%Pad 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0%---------------------------------------------------------------Total 3.48e-06 6.72e-08 3.12e-07 3.86e-06 100.0% 90.2% 1.7% 8.1% + + + + + + report_slews - - + + [-scenes scenes][-report_variation][-digits digits]pin - - - pins + + + scenes - - List of pins or ports to report. + + Report slews for process for scenes process corners. - - The report_pulse_width_checks command reports min pulse width checks for pins in the clock network. If pins is not specified all clock network pins are reported. - - - - - report_slews + + -report_variation - - [-corner corner]pin + + Report SSTA distribution parameters. - -corner corner + -digits digits - Report paths for process corner. The -corner keyword is required if more than one process corner is defined. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - pin + pin - + - Report the slews at pin + Report the slews at pin + - report_tns + report_tns - [-min][-max][-digits digits] + [-min][-max][-digits digits] - -max + -max - Report the total max/setup slack. + Report the total max/setup slack. - -min + -min - Report the total min/hold slack. + Report the total min/hold slack. - -digits digits + -digits digits - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - Report the total negative slack. + Report the total negative slack. - report_units + report_units - + - Report the units used for command arguments and reporting. + Report the units used for command arguments and reporting. report_units time 1ns capacitance 1pF resistance 1kohm voltage 1v current 1A power 1pW distance 1um - report_wns + report_wns - [-min][-max][-digits digits] + [-min][-max][-digits digits] - -max + -max - Report the worst max/setup slack. + Report the worst max/setup slack. - -min + -min - Report the worst min/hold slack. + Report the worst min/hold slack. - -digits digits + -digits digits - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - Report the worst negative slack. If the worst slack is positive, zero is reported. + Report the worst negative slack. If the worst slack is positive, zero is reported. - report_worst_slack + report_worst_slack - [-min][-max][-digits digits] + [-min][-max][-digits digits] + - -max + -max - Report the worst max/setup slack. + Report the worst max/setup slack. - -min + -min - Report the worst min/hold slack. + Report the worst min/hold slack. - -digits digits + -digits digits - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. @@ -9954,117 +10306,117 @@ - - set_assigned_check + set_assigned_check - -setup|-hold|-recovery|-removal[-rise][-fall][-corner corner][-min][-max][-from from_pins][-to to_pins][-clock rise|fall][-cond sdf_cond][-worst]margin + -setup|-hold|-recovery|-removal[-rise][-fall][-scene scene][-min][-max][-from from_pins][-to to_pins][-clock rise|fall][-cond sdf_cond][-worst]margin - -setup + -setup - Annotate setup timing checks. + Annotate setup timing checks. - -hold + -hold - Annotate hold timing checks. + Annotate hold timing checks. - -recovery + -recovery - Annotate recovery timing checks. + Annotate recovery timing checks. - -removal + -removal - Annotate removal timing checks. + Annotate removal timing checks. - -rise + -rise - Annotate rising delays. + Annotate rising delays. - -fall + -fall - Annotate falling delays. + Annotate falling delays. - -corner corner + scene - The name of a process corner. The -corner keyword is required if more than one process corner is defined. + The name of a scene. The -scene keyword is required if more than one scene is defined. - -min + -min - Annotate the minimum value of the process corner. + Annotate the minimum value of the process corner. - -max + -max - Annotate the maximum value of the process corner. + Annotate the maximum value of the process corner. - -from from_pins + from_pins - A list of pins for the clock. + A list of pins for the clock. - -to to_pins + to_pins - A list of pins for the data. + A list of pins for the data. + - -clock rise|fall + -clock rise|fall - The timing check clock pin transition. + The timing check clock pin transition. - margin + margin - The timing check margin. + The timing check margin. @@ -10072,164 +10424,162 @@ - - set_assigned_delay + set_assigned_delay - -cell|-net[-rise][-fall][-corner corner][-min][-max][-from from_pins][-to to_pins]delay + -cell|-net[-rise][-fall][-scene scene][-min][-max][-from from_pins][-to to_pins]delay - -cell + -cell - Annotate the delays between two pins on an instance. + Annotate the delays between two pins on an instance. - -net + -net - Annotate the delays between two pins on a net. + Annotate the delays between two pins on a net. - -rise + -rise - Annotate the rising delays. + Annotate the rising delays. - -fall + -fall - Annotate the falling delays. + Annotate the falling delays. - -corner corner + scene - The name of a process corner. The -corner keyword is required if more than one process corner is defined. + The name of a scene. The -scene keyword is required if more than one scene is defined. - -min + -min - Annotate the minimum delays. + Annotate the minimum delays. - -max + -max - Annotate the maximum delays. + Annotate the maximum delays. - -from from_pins + from_pins - A list of pins. + A list of pins. - -to to_pins + to_pins - A list of pins. + A list of pins. - delay + delay - The delay between from_pins and to_pins. + The delay between from_pins and to_pins. The set_assigned_delay command is used to annotate the delays between two pins on an instance or net. The annotated delay overrides the calculated delay. This command is an interactive way to back-annotate delays like an SDF file. - Use the -corner keyword to specify a process corner. The -corner keyword is required if more than one process corner is defined. + - set_assigned_transition + set_assigned_transition - [-rise][-fall][-corner corner][-min][-max]slewpin_list + [-rise][-fall][-scene scene][-min][-max]slewpin_list - - -rise + -rise - Annotate the rising transition. + Annotate the rising transition. - -fall + -fall - Annotate the falling transition. + Annotate the falling transition. - -corner corner + scene - Annotate delays for process corner. + Annotate delays for scene. - -min + -min - Annotate the minimum transition time. + Annotate the minimum transition time. - -max + -max - Annotate the maximum transition time. + Annotate the maximum transition time. - slew + slew - The pin transition time. + The pin transition time. - pin_list + pin_list - A list of pins. + A list of pins. @@ -10239,89 +10589,89 @@ - set_case_analysis + set_case_analysis - 0|1|zero|one|rise|rising|fall|fallingport_or_pin_list + 0|1|zero|one|rise|rising|fall|fallingport_or_pin_list - port_or_pin_list + port_or_pin_list - A list of ports or pins. + A list of ports or pins. The set_case_analysis command sets the signal on a port or pin to a constant logic value. No paths are propagated from constant pins. Constant values set with the set_case_analysis command are propagated through downstream gates. - Conditional timing arcs with mode groups are controlled by logic values on the instance pins. + Conditional timing arcs with mode groups are controlled by logic values on the instance pins. - set_clock_gating_check + set_clock_gating_check - [-setup setup_time][-hold hold_time][-rise][-fall][-high][-low][objects] + [-setup setup_time][-hold hold_time][-rise][-fall][-high][-low][objects] + - -setup setup_time + -setup setup_time - Clock enable setup margin. + Clock enable setup margin. - -hold hold_time + -hold hold_time - Clock enable hold margin. + Clock enable hold margin. - -rise + -rise - The setup/hold margin is for the rising edge of the clock enable. + The setup/hold margin is for the rising edge of the clock enable. - -fall + -fall - The setup/hold margin is for the falling edge of the clock enable. + The setup/hold margin is for the falling edge of the clock enable. - - -high + -high - The gating clock is active high (pin and instance objects only). + The gating clock is active high (pin and instance objects only). - -low + -low - The gating clock is active low (pin and instance objects only). + The gating clock is active low (pin and instance objects only). - objects + objects - A list of clocks, instances, pins or ports. + A list of clocks, instances, pins or ports. @@ -10335,197 +10685,197 @@ - set_clock_groups + set_clock_groups - [-name name][-logically_exclusive][-physically_exclusive][-asynchronous][-allow_paths]-group clocks + [-name name][-logically_exclusive][-physically_exclusive][-asynchronous][-allow_paths]-group clocks - -name name + -name name - The clock group name. + The clock group name. - -logically_exclusive + -logically_exclusive - The clocks in different groups do not interact logically but can be physically present on the same chip. Paths between clock groups are considered for noise analysis. + The clocks in different groups do not interact logically but can be physically present on the same chip. Paths between clock groups are considered for noise analysis. + - -physically_exclusive + -physically_exclusive - The clocks in different groups cannot be present at the same time on a chip. Paths between clock groups are not considered for noise analysis. + The clocks in different groups cannot be present at the same time on a chip. Paths between clock groups are not considered for noise analysis. - -asynchronous + -asynchronous - The clock groups are asynchronous. Paths between clock groups are considered for noise analysis. + The clock groups are asynchronous. Paths between clock groups are considered for noise analysis. - -allow_paths + -allow_paths - + - clocks + clocks - A list of clocks in the group. + A list of clocks in the group. - The set_clock_groups command is used to define groups of clocks that interact with each other. Clocks in different groups do not interact and paths between them are not reported. Use a –group argument for each clock group. + The set_clock_groups command is used to define groups of clocks that interact with each other. Clocks in different groups do not interact and paths between them are not reported. Use a –group argument for each clock group. - set_clock_latency + set_clock_latency - [-source][-clock clock][-rise][-fall][-min][-max]delayobjects + [-source][-clock clock][-rise][-fall][-min][-max]delayobjects - -source + -source - The latency is at the clock source. + The latency is at the clock source. - -clock clock + -clock clock - If multiple clocks are defined at a pin this use this option to specify the latency for a specific clock. + If multiple clocks are defined at a pin this use this option to specify the latency for a specific clock. - -rise + -rise - The latency is for the rising edge of the clock. + The latency is for the rising edge of the clock. - -fall + -fall - The latency is for the falling edge of the clock. + The latency is for the falling edge of the clock. - -min + -min - delay is the minimum latency. + delay is the minimum latency. - -max + -max - delay is the maximum latency. + delay is the maximum latency. - delay + delay - Clock source or insertion delay. + Clock source or insertion delay. - objects + objects - A list of clocks, pins or ports. + A list of clocks, pins or ports. - The set_clock_latency command describes expected delays of the clock tree when analyzing a design using ideal clocks. Use the -source option to specify latency at the clock source, also known as insertion delay. Source latency is delay in the clock tree that is external to the design or a clock tree internal to an instance that implements a complex logic function. + The set_clock_latency command describes expected delays of the clock tree when anxsalyzing a design using ideal clocks. Use the -source option to specify latency at the clock source, also known as insertion delay. Source latency is delay in the clock tree that is external to the design or a clock tree internal to an instance that implements a complex logic function.set_clock_latency removes propagated clock properties for the clocks and pins objects. - set_clock_transition + set_clock_transition - [-rise][-fall][-min][-max]transitionclocks + [-rise][-fall][-min][-max]transitionclocks - -rise + -rise - Set the transition time for the rising edge of the clock. + Set the transition time for the rising edge of the clock. - -fall + -fall - Set the transition time for the falling edge of the clock. + Set the transition time for the falling edge of the clock. - - -min + -min - Set the min transition time. + Set the min transition time. - -max + -max - Set the min transition time. + Set the min transition time. - transition + transition - Clock transition time (slew). + Clock transition time (slew). - clocks + clocks - A list of clocks. + A list of clocks. @@ -10535,152 +10885,153 @@ - set_clock_uncertainty + set_clock_uncertainty - [-from|-rise_from|-fall_from from_clock][-to|-rise_to|-fall_to to_clock][-rise][-fall][-setup][-hold]uncertainty[objects] + [-from|-rise_from|-fall_from from_clock][-to|-rise_to|-fall_to to_clock][-rise][-fall][-setup][-hold]uncertainty[objects] - -from from_clock + -from from_clock - Inter-clock uncertainty source clock. + Inter-clock uncertainty source clock. - -to to_clock + -to to_clock - Inter-clock uncertainty target clock. + Inter-clock uncertainty target clock. - -rise + -rise - Inter-clock target clock rise edge, alternative to ‑rise_to.Inter-clock target clock rise edge, alternative to ‑rise_to. + Inter-clock target clock rise edge, alternative to ‑rise_to.Inter-clock target clock rise edge, alternative to ‑rise_to. - -fall + -fall - Inter-clock target clock rise edge, alternative to ‑fall_to. + Inter-clock target clock rise edge, alternative to ‑fall_to. - -setup + -setup - uncertainty is for setup checks. + uncertainty is for setup checks. + - -hold + -hold - uncertainty is for hold checks. + uncertainty is for hold checks. - uncertainty + uncertainty - Clock uncertainty. + Clock uncertainty. - objects + objects - A list of clocks, ports or pins. + A list of clocks, ports or pins. - The set_clock_uncertainty command specifies the uncertainty or jitter in a clock. The uncertainty for a clock can be specified on its source pin or port, or the clock itself. - set_clock_uncertainty .1 [get_clock clk1] - Inter-clock uncertainty between the source and target clocks of timing checks is specified with the ‑from|‑rise_from|-fall_from andto|‑rise_to|-fall_to arguments . - set_clock_uncertainty -from [get_clock clk1] -to [get_clocks clk2] .1 - The following commands are equivalent. - set_clock_uncertainty -from [get_clock clk1] -rise_to [get_clocks clk2] .1set_clock_uncertainty -from [get_clock clk1] -to [get_clocks clk2] -rise .1 + The set_clock_uncertainty command specifies the uncertainty or jitter in a clock. The uncertainty for a clock can be specified on its source pin or port, or the clock itself. + set_clock_uncertainty .1 [get_clock clk1] + Inter-clock uncertainty between the source and target clocks of timing checks is specified with the ‑from|‑rise_from|-fall_from andto|‑rise_to|-fall_to arguments . + set_clock_uncertainty -from [get_clock clk1] -to [get_clocks clk2] .1 + The following commands are equivalent. + set_clock_uncertainty -from [get_clock clk1] -rise_to [get_clocks clk2] .1set_clock_uncertainty -from [get_clock clk1] -to [get_clocks clk2] -rise .1 - set_cmd_units + set_cmd_units - [-capacitance cap_unit][-resistance res_unit][-time time_unit][-voltage voltage_unit][-current current_unit][-power power_unit][-distance distance_unit] + [-capacitance cap_unit][-resistance res_unit][-time time_unit][-voltage voltage_unit][-current current_unit][-power power_unit][-distance distance_unit] - -capacitance cap_unit + -capacitance cap_unit - The capacitance scale factor followed by 'f'. + The capacitance scale factor followed by 'f'. - -resistance res_unit + -resistance res_unit - The resistance scale factor followed by 'ohm'. + The resistance scale factor followed by 'ohm'. - -time time_unit + -time time_unit - The time scale factor followed by 's'. + The time scale factor followed by 's'. - -voltage voltage_unit + -voltage voltage_unit - The voltage scale factor followed by 'v'. + The voltage scale factor followed by 'v'. - -current current_unit + -current current_unit - The current scale factor followed by 'A'. + The current scale factor followed by 'A'. - -power power_unit + -power power_unit - The power scale factor followed by 'w'. + The power scale factor followed by 'w'. - -distance distance_unit + -distance distance_unit - The distance scale factor followed by 'm'. + The distance scale factor followed by 'm'. - The set_cmd_units command is used to change the units used by the STA command interpreter when parsing commands and reporting results. The default units are the units specified in the first Liberty library file that is read. + The set_cmd_units command is used to change the units used by the STA command interpreter when parsing commands and reporting results. The default units are the units specified in the first Liberty library file that is read. Units are specified as a scale factor followed by a unit name. The scale factors are as follows. M 1E+6k 1E+3m 1E-3u 1E-6n 1E-9p 1E-12f 1E-15 An example of the set_units command is shown below. @@ -10688,61 +11039,60 @@ - - set_data_check + set_data_check - [-from|-rise_from|-fall_from from_pin][-to|-rise_to|-fall_to to_pin][-setup][-hold][-clock clock]margin + [-from|-rise_from|-fall_from from_pin][-to|-rise_to|-fall_to to_pin][-setup][-hold][-clock clock]margin - -from from_pin + -from from_pin - A pin used as the timing check reference. + A pin used as the timing check reference. - -to to_pin + -to to_pin - A pin that the setup/hold check is applied to. + A pin that the setup/hold check is applied to. - -setup + -setup - Add a setup timing check. + Add a setup timing check. - -hold + -hold - Add a hold timing check. + Add a hold timing check. - -clock clock + -clock clock - The setup/hold check clock. + The setup/hold check clock. - margin + margin - The setup or hold time margin. + The setup or hold time margin. @@ -10752,121 +11102,121 @@ - set_disable_inferred_clock_gating + set_disable_inferred_clock_gating - objects + objects - objects + objects - A list of clock gating instances, clock gating pins, or clock enable pins. + A list of clock gating instances, clock gating pins, or clock enable pins. - The set_disable_inferred_clock_gating command disables clock gating checks on a clock gating instance, clock gating pin, or clock gating enable pin. + The set_disable_inferred_clock_gating command disables clock gating checks on a clock gating instance, clock gating pin, or clock gating enable pin. - set_disable_timing + set_disable_timing - [-from from_port][-to to_port]objects + [-from from_port][-to to_port]objects - -from from_port + -from from_port - + - -to to_port + -to to_port - + - objects + objects - A list of instances, ports, pins, cells, cell/port, or library/cell/port. + A list of instances, ports, pins, cells, cell/port, or library/cell/port. The set_disable_timing command is used to disable paths though pins in the design. There are many different forms of the command depending on the objects specified in objects. - All timing paths though an instance are disabled when objects contains an instance. Timing checks in the instance are not disabled. - set_disable_timing u2 + All timing paths though an instance are disabled when objects contains an instance. Timing checks in the instance are not disabled. + set_disable_timing u2 The -from and -to options can be used to restrict the disabled path to those from, to or between specific pins on the instance. set_disable_timing -from A u2set_disable_timing -to Z u2set_disable_timing -from A -to Z u2 A list of top level ports or instance pins can also be disabled. set_disable_timing u2/Zset_disable_timing in1 Timing paths though all instances of a library cell in the design can be disabled by naming the cell using a hierarchy separator between the library and cell name. Paths from or to a cell port can be disabled with the -from and -to options or a port name after library and cell names. - set_disable_timing liberty1/snl_bufx2set_disable_timing -from A liberty1/snl_bufxset_disable_timing -to Z liberty1/snl_bufxset_disable_timing liberty1/snl_bufx2/A + set_disable_timing liberty1/snl_bufx2set_disable_timing -from A liberty1/snl_bufxset_disable_timing -to Z liberty1/snl_bufxset_disable_timing liberty1/snl_bufx2/A - set_drive + set_drive - [-rise][-fall][-max][-min]resistanceports + [-rise][-fall][-max][-min]resistanceports - -rise + -rise - Set the drive rise resistance. + Set the drive rise resistance. - -fall + -fall - Set the drive fall resistance. + Set the drive fall resistance. - -max + -max - Set the maximum resistance. + Set the maximum resistance. - -min + -min - Set the minimum resistance. + Set the minimum resistance. - resistance + resistance - The external drive resistance. + The external drive resistance. - ports + ports A list of ports. @@ -10877,98 +11227,98 @@ - - set_driving_cell + set_driving_cell - [-lib_cell cell_name][-library library][-rise][-fall][-min][-max][-pin pin][-from_pin from_pin][-input_transition_rise trans_rise][-input_transition_fall trans_fall]ports + [-lib_cell cell_name][-library library][-rise][-fall][-min][-max][-pin pin][-from_pin from_pin][-input_transition_rise trans_rise][-input_transition_fall trans_fall]ports - -lib_cell cell_name + -lib_cell cell_name - The driving cell. + The driving cell. - -library library + -library library - The driving cell library. + The driving cell library. - -rise + -rise - Set the driving cell for a rising edge. + Set the driving cell for a rising edge. + - -fall + -fall - Set the driving cell for a falling edge. + Set the driving cell for a falling edge. - -max + -max - Set the driving cell for max delays. + Set the driving cell for max delays. - -min + -min - Set the driving cell for min delays. + Set the driving cell for min delays. - -pin pin + -pin pin - The output port of the driving cell. + The output port of the driving cell. - -from_pin from_pin + -from_pin from_pin - Use timing arcs from from_pin to the output pin. + Use timing arcs from from_pin to the output pin. - -input_transition_rise trans_rise + -input_transition_rise trans_rise - The transition time for a rising input at from_pin. + The transition time for a rising input at from_pin. - -input_transition_fall trans_fall + -input_transition_fall trans_fall - The transition time for a falling input at from_pin. + The transition time for a falling input at from_pin. - ports + ports A list of ports. @@ -10979,50 +11329,50 @@ - - set_false_path + set_false_path - [-setup][-hold][-rise][-fall][-from from_list][-rise_from from_list][-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list][-rise_to to_list][-fall_to to_list][-reset_path] + [-setup][-hold][-rise][-fall][-from from_list][-rise_from from_list][-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list][-rise_to to_list][-fall_to to_list][-reset_path] - -setup + -setup - Apply to setup checks. + Apply to setup checks. - -hold + -hold - Apply to hold checks. + Apply to hold checks. - -rise + -rise - Apply to rising path edges. + Apply to rising path edges. - -fall + -fall - Apply to falling path edges. + Apply to falling path edges. + - -reset_path + -reset_path Remove any matching set_false_path, set_multicycle_path, set_max_delay, set_min_delay exceptions first. @@ -11030,7 +11380,7 @@ - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -11038,7 +11388,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -11046,7 +11396,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -11060,13 +11410,12 @@ - - set_fanout_load + set_fanout_load - fanoutport_list + fanoutport_list @@ -11076,18 +11425,18 @@ - set_hierarchy_separator + set_hierarchy_separator - separator + separator - separator + separator - Character used to separate hierarchical names. + Character used to separate hierarchical names. @@ -11097,10 +11446,10 @@ - set_ideal_latency + set_ideal_latency - [-rise] [-fall] [-min] [-max] delay objects + [-rise] [-fall] [-min] [-max] delay objects @@ -11110,10 +11459,10 @@ - set_ideal_network + set_ideal_network - [-no_propagation] objects + [-no_propagation] objects @@ -11123,188 +11472,186 @@ - set_ideal_transition + set_ideal_transition - [-rise] [-fall] [-min] [-max] transition_time objects + [-rise] [-fall] [-min] [-max] transition_time objects - The set_ideal_transition command is parsed but ignored. + The set_ideal_transition command is parsed but ignored. - set_input_delay + set_input_delay - [-rise][-fall][-max][-min][-clock clock][-clock_fall][-reference_pin ref_pin][-source_latency_included][-network_latency_included][-add_delay]delayport_pin_list + [-rise][-fall][-max][-min][-clock clock][-clock_fall][-reference_pin ref_pin][-source_latency_included][-network_latency_included][-add_delay]delayport_pin_list - -rise + -rise - Set the arrival time for the rising edge of the input. + Set the arrival time for the rising edge of the input. - -fall + -fall - Set the arrival time for the falling edge of the input. + Set the arrival time for the falling edge of the input. - - -max + -max - Set the maximum arrival time. + Set the maximum arrival time. - -min + -min - Set the minimum arrival time. + Set the minimum arrival time. - -clock clock + -clock clock - The arrival time is from clock. + The arrival time is from clock. - -clock_fall + -clock_fall - The arrival time is from the falling edge of clock. + The arrival time is from the falling edge of clock. - -reference_pin ref_pin + -reference_pin ref_pin - The arrival time is with respect to the clock that arrives at ref_pin. + The arrival time is with respect to the clock that arrives at ref_pin. - -source_latency_included + -source_latency_included - D no add the clock source latency (insertion delay) to the delay value. + D no add the clock source latency (insertion delay) to the delay value. - -network_latency_included + -network_latency_included - Do not add the clock latency to the delay value when the clock is ideal. + Do not add the clock latency to the delay value when the clock is ideal. - -add_delay + -add_delay - Add this arrival to any existing arrivals. + Add this arrival to any existing arrivals. - delay + delay - The arrival time after clock. + The arrival time after clock. - pin_port_list + pin_port_list - A list of pins or ports. + A list of pins or ports. - The set_input_delay command is used to specify the arrival time of an input signal. - The following command sets the min, max, rise and fall times on the in1 input port 1.0 time units after the rising edge of clk1. - set_input_delay -clock clk1 1.0 [get_ports in1] - Use multiple commands with the -add_delay option to specify separate arrival times for min, max, rise and fall times or multiple clocks. For example, the following specifies separate arrival times with respect to clocks clk1 and clk2. - set_input_delay -clock clk1 1.0 [get_ports in1]set_input_delay -add_delay -clock clk2 2.0 [get_ports in1] - The –reference_pin option is used to specify an arrival time with respect to the arrival on a pin in the clock network. For propagated clocks, the input arrival time is relative to the clock arrival time at the reference pin (the clock source latency and network latency from the clock source to the reference pin). For ideal clocks, input arrival time is relative to the reference pin clock source latency. With the -clock_fall flag the arrival time is relative to the falling transition at the reference pin. If no clocks arrive at the reference pin the set_input_delay command is ignored. If no -clock is specified the arrival time is with respect to all clocks that arrive at the reference pin. The -source_latency_included and -network_latency_included options cannot be used with -reference_pin. - Paths from inputs that do not have an arrival time defined by set_input_delay are not reported. Set the sta_input_port_default_clock variable to 1 to report paths from inputs without a set_input_delay. + The set_input_delay command is used to specify the arrival time of an input signal. + The following command sets the min, max, rise and fall times on the in1 input port 1.0 time units after the rising edge of clk1. + set_input_delay -clock clk1 1.0 [get_ports in1] + Use multiple commands with the -add_delay option to specify separate arrival times for min, max, rise and fall times or multiple clocks. For example, the following specifies separate arrival times with respect to clocks clk1 and clk2. + set_input_delay -clock clk1 1.0 [get_ports in1]set_input_delay -add_delay -clock clk2 2.0 [get_ports in1] + The –reference_pin option is used to specify an arrival time with respect to the arrival on a pin in the clock network. For propagated clocks, the input arrival time is relative to the clock arrival time at the reference pin (the clock source latency and network latency from the clock source to the reference pin). For ideal clocks, input arrival time is relative to the reference pin clock source latency. With the -clock_fall flag the arrival time is relative to the falling transition at the reference pin. If no clocks arrive at the reference pin the set_input_delay command is ignored. If no -clock is specified the arrival time is with respect to all clocks that arrive at the reference pin. The -source_latency_included and -network_latency_included options cannot be used with -reference_pin. + Paths from inputs that do not have an arrival time defined by set_input_delay are not reported. Set the sta_input_port_default_clock variable to 1 to report paths from inputs without a set_input_delay. - - set_input_transition + set_input_transition - [-rise][-fall][-max][-min]transitionport_list + [-rise][-fall][-max][-min]transitionport_list - -rise + -rise - Set the rising edge transition. + Set the rising edge transition. - -fall + -fall - Set the falling edge transition. + Set the falling edge transition. - -max + -max - Set the minimum transition time. + Set the minimum transition time. - -min + -min - Set the maximum transition time. + Set the maximum transition time. - transition + transition - The transition time (slew). + The transition time (slew). - port_list + port_list - A list of ports. + A list of ports. @@ -11314,10 +11661,10 @@ - set_level_shifter_strategy + set_level_shifter_strategy - [-rule rule_type] + [-rule rule_type] @@ -11325,12 +11672,13 @@ + - set_level_shifter_threshold + set_level_shifter_threshold - [-voltage voltage] + [-voltage voltage] @@ -11340,56 +11688,55 @@ - set_load + set_load - [-rise][-fall][-max][-min][-subtract_pin_load][-pin_load][-wire_load]capacitanceobjects + [-rise][-fall][-max][-min][-subtract_pin_load][-pin_load][-wire_load]capacitanceobjects - -rise + -rise - Set the external port rising capacitance (ports only). + Set the external port rising capacitance (ports only). - -fall + -fall - Set the external port falling capacitance (ports only). + Set the external port falling capacitance (ports only). - - -max + -max - Set the max capacitance. + Set the max capacitance. - -min + -min - Set the min capacitance. + Set the min capacitance. - -subtract_pin_load + -subtract_pin_load - Subtract the capacitance of all instance pins connected to the net from capacitance (nets only). If the resulting capacitance is negative, zero is used. Pin capacitances are ignored by delay calculation when this option is used. + Subtract the capacitance of all instance pins connected to the net from capacitance (nets only). If the resulting capacitance is negative, zero is used. Pin capacitances are ignored by delay calculation when this option is used. - -pin_load + -pin_load capacitance is external instance pin capacitance (ports only). @@ -11397,7 +11744,7 @@ - -wire_load + -wire_load capacitance is external wire capacitance (ports only). @@ -11405,7 +11752,7 @@ - capacitance + capacitance The capacitance, in library capacitance units. @@ -11413,34 +11760,34 @@ - objects + objects A list of nets or ports. - The set_load command annotates wire capacitance on a net or external capacitance on a port. There are four different uses for the set_load commanc: - set_load -wire_load port external port wire capacitanceset_load -pin_load port external port pin capacitanceset_load port same as -pin_loadset_load net net wire capacitance - External port capacitance can be annotated separately with the -pin_load and ‑wire_load options. Without the -pin_load and -wire_load options pin capacitance is annotated. - When annotating net wire capacitance with the -subtract_pin_load option the capacitance of all instance pins connected to the net is subtracted from capacitance. Setting the capacitance on a net overrides SPEF parasitics for delay calculation. + The set_load command annotates wire capacitance on a net or external capacitance on a port. There are four different uses for the set_load commanc: + set_load -wire_load port external port wire capacitanceset_load -pin_load port external port pin capacitanceset_load port same as -pin_loadset_load net net wire capacitance + External port capacitance can be annotated separately with the -pin_load and ‑wire_load options. Without the -pin_load and -wire_load options pin capacitance is annotated. + When annotating net wire capacitance with the -subtract_pin_load option the capacitance of all instance pins connected to the net is subtracted from capacitance. Setting the capacitance on a net overrides SPEF parasitics for delay calculation. - set_logic_dc + set_logic_dc - port_list + port_list - port_pin_list + port_pin_list - List of ports or pins. + List of ports or pins. @@ -11450,61 +11797,60 @@ - set_logic_one + set_logic_one - port_list + port_list - port_pin_list + port_pin_list - List of ports or pins. + List of ports or pins. - Set a port or pin to a constant logic one value. No paths are propagated from constant pins. Constant values set with the set_logic_one command are not propagated through downstream gates. + Set a port or pin to a constant logic one value. No paths are propagated from constant pins. Constant values set with the set_logic_one command are not propagated through downstream gates. - - set_logic_zero + set_logic_zero - port_list + port_list - port_pin_list + port_pin_list - List of ports or pins. + List of ports or pins. - Set a port or pin to a constant logic zero value. No paths are propagated from constant pins. Constant values set with the set_logic_zero command are not propagated through downstream gates. + Set a port or pin to a constant logic zero value. No paths are propagated from constant pins. Constant values set with the set_logic_zero command are not propagated through downstream gates. - set_max_area + set_max_area - area + area - area + area - + @@ -11514,26 +11860,27 @@ - set_max_capacitance + set_max_capacitance - capacitanceobjects + capacitanceobjects - capacitance + capacitance - + + - objects + objects - List of ports or cells. + List of ports or cells. @@ -11543,32 +11890,31 @@ - set_max_delay + set_max_delay - [-rise][-fall][-from from_list][-rise_from from_list][-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list][-rise_to to_list][-fall_to to_list][-ignore_clock_latency][-probe][-reset_path]delay + [-rise][-fall][-from from_list][-rise_from from_list][-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list][-rise_to to_list][-fall_to to_list][-ignore_clock_latency][-probe][-reset_path]delay - -rise + -rise - Set max delay for rising paths. + Set max delay for rising paths. - -fall + -fall - Set max delay for falling paths. + Set max delay for falling paths. - - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -11576,7 +11922,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -11584,7 +11930,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -11592,7 +11938,7 @@ - -ignore_clock_latency + -ignore_clock_latency Ignore clock latency at the source and target registers. @@ -11600,15 +11946,15 @@ - -probe + -probe - Do not break paths at internal pins (non startpoints). + Do not break paths at internal pins (non startpoints). - -reset_path + -reset_path Remove any matching set_false_path, set_multicycle_path, set_max_delay, set_min_delay exceptions first. @@ -11616,7 +11962,7 @@ - delay + delay The maximum delay. @@ -11624,16 +11970,16 @@ The set_max_delay command constrains the maximum delay through combinational logic paths. See set_false_path for a description of allowed from_list, through_list and to_list objects. If the to_list ends at a timing check the setup/hold time is included in the path delay. - When the -ignore_clock_latency option is used clock latency at the source and destination of the path delay is ignored. The constraint is reported in the default path group (**default**) rather than the clock path group when the path ends at a timing check. + When the -ignore_clock_latency option is used clock latency at the source and destination of the path delay is ignored. The constraint is reported in the default path group (**default**) rather than the clock path group when the path ends at a timing check. - set_max_dynamic_power + set_max_dynamic_power - power [unit] + power [unit] @@ -11643,26 +11989,26 @@ - set_max_fanout + set_max_fanout - fanoutobjects + fanoutobjects - fanout + fanout - + - objects + objects - List of ports or cells. + List of ports or cells. @@ -11672,10 +12018,10 @@ - set_max_leakage_power + set_max_leakage_power - power [unit] + power [unit] @@ -11685,92 +12031,92 @@ - set_max_time_borrow + set_max_time_borrow - delayobjects + delayobjects - - delay + delay - The maximum time the latches can borrow. + The maximum time the latches can borrow. - objects + objects - List of clocks, instances or pins. + List of clocks, instances or pins. - The set_max_time_borrow command specifies the maximum amount of time that latches can borrow. Time borrowing is the time that a data input to a transparent latch arrives after the latch opens. + The set_max_time_borrow command specifies the maximum amount of time that latches can borrow. Time borrowing is the time that a data input to a transparent latch arrives after the latch opens. - set_max_transition + set_max_transition - [-data_path][-clock_path][-rise][-fall]transitionobjects + [-data_path][-clock_path][-rise][-fall]transitionobjects - -data_path + -data_path - Set the max slew for data paths. + Set the max slew for data paths. - -clock_path + -clock_path - Set the max slew for clock paths. + Set the max slew for clock paths. + - -rise + -rise - Set the max slew for rising paths. + Set the max slew for rising paths. - -fall + -fall - Set the max slew for falling paths. + Set the max slew for falling paths. - transition + transition - The maximum slew/transition time. + The maximum slew/transition time. - objects + objects - List of clocks, ports or designs. + List of clocks, ports or designs. - The set_max_transition command is specifies the maximum transition time (slew) design rule checked by the report_check_types –max_transition command. + The set_max_transition command is specifies the maximum transition time (slew) design rule checked by the report_check_types –max_transition command. If specified for a design, the default maximum transition is set for the design. If specified for a clock, the maximum transition is applied to all pins in the clock domain. The –clock_path option restricts the maximum transition to clocks in clock paths. The -data_path option restricts the maximum transition to clocks data paths. The –clock_path, -data_path, -rise and –fall options only apply to clock objects. @@ -11778,26 +12124,26 @@ - set_min_capacitance + set_min_capacitance - capacitanceobjects + capacitanceobjects - capacitance + capacitance - Minimum capacitance. + Minimum capacitance. - objects + objects - List of ports or cells. + List of ports or cells. @@ -11805,34 +12151,34 @@ - - set_min_delay + set_min_delay - [-rise][-fall][-from from_list][-rise_from from_list][-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list][-rise_to to_list][-fall_to to_list][-ignore_clock_latency][-probe][-reset_path]delay + [-rise][-fall][-from from_list][-rise_from from_list][-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list][-rise_to to_list][-fall_to to_list][-ignore_clock_latency][-probe][-reset_path]delay - -rise + -rise - Set min delay for rising paths. + Set min delay for rising paths. + - -fall + -fall - Set min delay for falling paths. + Set min delay for falling paths. - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -11840,7 +12186,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -11848,7 +12194,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -11856,7 +12202,7 @@ - -ignore_clock_latency + -ignore_clock_latency Ignore clock latency at the source and target registers. @@ -11864,15 +12210,15 @@ - -probe + -probe - Do not break paths at internal pins (non startpoints). + Do not break paths at internal pins (non startpoints). - -reset_path + -reset_path Remove any matching set_false_path, set_multicycle_path, set_max_delay, set_min_delay exceptions first. @@ -11880,107 +12226,120 @@ - delay + delay - The minimum delay. + The minimum delay. The set_min_delay command constrains the minimum delay through combinational logic. See set_false_path for a description of allowed from_list, through_list and to_list objects. If the to_list ends at a timing check the setup/hold time is included in the path delay. - When the -ignore_clock_latency option is used clock latency at the source and destination of the path delay is ignored. The constraint is reported in the default path group (**default**) rather than the clock path group when the path ends at a timing check. + When the -ignore_clock_latency option is used clock latency at the source and destination of the path delay is ignored. The constraint is reported in the default path group (**default**) rather than the clock path group when the path ends at a timing check. - - set_min_pulse_width + set_min_pulse_width - [-high][-low]min_widthobjects + [-high][-low]min_widthobjects - -high + -high - Set the minimum high pulse width. + Set the minimum high pulse width. - -low + -low - Set the minimum low pulse width. + Set the minimum low pulse width. - min_width + min_width - + - objects + objects - List of pins, instances or clocks. + List of pins, instances or clocks. If -low and -high are not specified the minimum width applies to both high and low pulses. + + + + + + set_mode + + + mode_name + + + + The the mode for SDC c ommands in the TCL interpreter. If mode mode_name does not exist, it is created. When modes are created the default mode is deleted. + - set_multicycle_path + set_multicycle_path - [-setup][-hold][-rise][-fall][-start][-end][-from from_list][-rise_from from_list][-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list][-rise_to to_list][-fall_to to_list][-reset_path]path_multiplier + [-setup][-hold][-rise][-fall][-start][-end][-from from_list][-rise_from from_list][-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list][-rise_to to_list][-fall_to to_list][-reset_path]path_multiplier - -setup + -setup - Set cycle count for setup checks. + Set cycle count for setup checks. - -hold + -hold - Set cycle count for hold checks. + Set cycle count for hold checks. - -rise + -rise - Set cycle count for rising path edges. + Set cycle count for rising path edges. - -fall + -fall - Set cycle count for falling path edges. + Set cycle count for falling path edges. - -start + -start Multiply the source clock period by period_multiplier. @@ -11988,16 +12347,15 @@ - -end + -end Multiply the target clock period by period_multiplier. - - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -12005,7 +12363,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -12013,7 +12371,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -12021,7 +12379,7 @@ - -reset_path + -reset_path Remove any matching set_false_path, set_multicycle_path, set_max_delay, set_min_delay exceptions first. @@ -12029,7 +12387,7 @@ - path_multiplier + path_multiplier The number of clock periods to add to the path required time. @@ -12040,323 +12398,325 @@ + - set_operating_conditions + set_operating_conditions - [-analysis_type single|bc_wc|on_chip_variation][-library lib][condition][-min min_condition][-max max_condition][-min_library min_lib][-max_library max_lib] + [-analysis_type single|bc_wc|on_chip_variation][-library lib][condition][-min min_condition][-max max_condition][-min_library min_lib][-max_library max_lib] - -analysis_type single + -analysis_type single - Use one operating condition for min and max paths. + Use one operating condition for min and max paths. - -analysis_type bc_wc + -analysis_type bc_wc - Best case, worst case analysis. Setup checks use max_condition for clock and data paths. Hold checks use the min_condition for clock and data paths. + Best case, worst case analysis. Setup checks use max_condition for clock and data paths. Hold checks use the min_condition for clock and data paths. - ‑analysis_type on_chip_variation + ‑analysis_type on_chip_variation - The min and max operating conditions represent variations on the chip that can occur simultaneously. Setup checks use max_condition for data paths and min_condition for clock paths. Hold checks use min_condition for data paths and max_condition for clock paths. This is the default analysis type. + The min and max operating conditions represent variations on the chip that can occur simultaneously. Setup checks use max_condition for data paths and min_condition for clock paths. Hold checks use min_condition for data paths and max_condition for clock paths. This is the default analysis type. - -library lib + -library lib - The name of the library that contains condition. + The name of the library that contains condition. - condition + condition - The operating condition for analysis type single. + The operating condition for analysis type single. - -min min_condition + -min min_condition - The operating condition to use for min paths and hold checks. + The operating condition to use for min paths and hold checks. - -max max_condition + -max max_condition - The operating condition to use for max paths and setup checks. + The operating condition to use for max paths and setup checks. - -min_library min_lib + -min_library min_lib - The name of the library that contains min_condition. + The name of the library that contains min_condition. - -max_library max_lib + -max_library max_lib - The name of the library that contains max_condition. + The name of the library that contains max_condition. - The set_operating_conditions command is used to specify the type of analysis performed and the operating conditions used to derate library data. + The set_operating_conditions command is used to specify the type of analysis performed and the operating conditions used to derate library data. - set_output_delay + set_output_delay - [-rise][-fall][-max][-min][-clock clock][-clock_fall][-reference_pin ref_pin][-source_latency_included][-network_latency_included][-add_delay]delayport_pin_list + [-rise][-fall][-max][-min][-clock clock][-clock_fall][-reference_pin ref_pin][-source_latency_included][-network_latency_included][-add_delay]delayport_pin_list + - -rise + -rise - Set the output delay for the rising edge of the input. + Set the output delay for the rising edge of the input. - -fall + -fall - Set the output delay for the falling edge of the input. + Set the output delay for the falling edge of the input. - -max + -max - Set the maximum output delay. + Set the maximum output delay. - -min + -min - Set the minimum output delay. + Set the minimum output delay. - -clock clock + -clock clock - The external check is to clock. The default clock edge is rising. + The external check is to clock. The default clock edge is rising. - -clock_fall + -clock_fall - The external check is to the falling edge of clock. + The external check is to the falling edge of clock. - -reference_pin ref_pin + -reference_pin ref_pin - The external check is clocked by the clock that arrives at ref_pin. + The external check is clocked by the clock that arrives at ref_pin. - -add_delay + -add_delay - Add this output delay to any existing output delays. + Add this output delay to any existing output delays. - delay + delay - The external delay to the check clocked by clock. + The external delay to the check clocked by clock. - pin_port_list + pin_port_list - A list of pins or ports. + A list of pins or ports. - The set_output_delay command is used to specify the external delay to a setup/hold check on an output port or internal pin that is clocked by clock. Unless the -add_delay option is specified any existing output delays are replaced. - The –reference_pin option is used to specify a timing check with respect to the arrival on a pin in the clock network. For propagated clocks, the timing check is relative to the clock arrival time at the reference pin (the clock source latency and network latency from the clock source to the reference pin). For ideal clocks, the timing check is relative to the reference pin clock source latency. With the -clock_fall flag the timing check is relative to the falling edge of the reference pin. If no clocks arrive at the reference pin the set_output_delay command is ignored. If no -clock is specified the timing check is with respect to all clocks that arrive at the reference pin. The -source_latency_included and -network_latency_included options cannot be used with -reference_pin. + The set_output_delay command is used to specify the external delay to a setup/hold check on an output port or internal pin that is clocked by clock. Unless the -add_delay option is specified any existing output delays are replaced. + The –reference_pin option is used to specify a timing check with respect to the arrival on a pin in the clock network. For propagated clocks, the timing check is relative to the clock arrival time at the reference pin (the clock source latency and network latency from the clock source to the reference pin). For ideal clocks, the timing check is relative to the reference pin clock source latency. With the -clock_fall flag the timing check is relative to the falling edge of the reference pin. If no clocks arrive at the reference pin the set_output_delay command is ignored. If no -clock is specified the timing check is with respect to all clocks that arrive at the reference pin. The -source_latency_included and -network_latency_included options cannot be used with -reference_pin. - set_port_fanout_number + set_port_fanout_number - [-min][-max]fanoutports + [-min][-max]fanoutports - -min + -min - Set the min fanout. + Set the min fanout. - -max + -max - Set the max fanout. + Set the max fanout. - fanout + fanout - The external fanout of the ports. + The external fanout of the ports. - port_list + port_list - A list of ports. + A list of ports. - Set the external fanout for ports. + Set the external fanout for ports. - set_power_activity + set_power_activity - [-global][-input][-input_ports ports][-pins pins][-activity activity | -density density][-duty duty][-clock clock] + [-global][-input][-input_ports ports][-pins pins][-activity activity | -density density][-duty duty][-clock clock] - -global + -global - Set the activity/duty for all non-clock pins. + Set the activity/duty for all non-clock pins. - -input + -input - Set the default input port activity/duty. + Set the default input port activity/duty. - -input_ports input_ports + -input_ports input_ports - Set the input port activity/duty. + Set the input port activity/duty. - -pins pins + -pins pins - Set the pin activity/duty. + Set the pin activity/duty. - -activity activity + -activity activity - The activity, or number of transitions per clock cycle. If clock is not specified the clock with the minimum period is used. If no clocks are defined an error is reported. + The activity, or number of transitions per clock cycle. If clock is not specified the clock with the minimum period is used. If no clocks are defined an error is reported. - -density density + -density density - Transitions per library time unit. + Transitions per library time unit. - -duty duty + -duty duty - The duty, or probability the signal is high (0 <= duty <= 1.0). Defaults to 0.5. + The duty, or probability the signal is high (0 <= duty <= 1.0). Defaults to 0.5. - -clock clock + -clock clock - The clock to use for the period with -activity. This option is ignored if -density is used. + The clock to use for the period with -activity. This option is ignored if -density is used. - The set_power_activity command is used to set the activity and duty used for power analysis globally or for input ports or pins in the design. - The default input activity for inputs is 0.1 transitions per minimum clock period if a clock is defined or 0.0 if there are no clocks defined. The default input duty is 0.5. This is equivalent to the following command: - set_power_activity -input -activity 0.1 -duty 0.5 + The set_power_activity command is used to set the activity and duty used for power analysis globally or for input ports or pins in the design. + The default input activity for inputs is 0.1 transitions per minimum clock period if a clock is defined or 0.0 if there are no clocks defined. The default input duty is 0.5. This is equivalent to the following command: + set_power_activity -input -activity 0.1 -duty 0.5 - set_propagated_clock + set_propagated_clock - objects + objects - objects + objects - A list of clocks, ports or pins. + A list of clocks, ports or pins. @@ -12364,61 +12724,62 @@ + - set_pvt + set_pvt - [-min][-max][-process process][-voltage voltage] - [-temperature temperature]instances + [-min][-max][-process process][-voltage voltage] + [-temperature temperature]instances - -min + -min - Set the PVT values for max delays. + Set the PVT values for max delays. - -max + -max - Set the PVT values for min delays. + Set the PVT values for min delays. - -process process + -process process - A process value (float). + A process value (float). - -voltage voltage + -voltage voltage - A voltage value (float). + A voltage value (float). - -temperature temperature + -temperature temperature - A temperature value (float). + A temperature value (float). - instances + instances - A list instances. + A list instances. @@ -12426,226 +12787,225 @@ - - set_sense + set_sense - [-type clock|data][-positive][-negative][-pulse pulse_type][-stop_propagation][-clock clocks]pins + [-type clock|data][-positive][-negative][-pulse pulse_type][-stop_propagation][-clock clocks]pins - -type clock + -type clock - Set the sense for clock paths. + Set the sense for clock paths. - -type data + -type data - Set the sense for data paths (not supported). + Set the sense for data paths (not supported). - -positive + -positive - The clock sense is positive unate. + The clock sense is positive unate. - -negative + -negative - The clock sense is negative unate. + The clock sense is negative unate. + - -pulse pulse_type + -pulse pulse_type - rise_triggered_high_pulserise_triggered_low_pulsefall_triggered_high_pulsefall_triggered_low_pulseNot supported. + rise_triggered_high_pulserise_triggered_low_pulsefall_triggered_high_pulsefall_triggered_low_pulseNot supported. - -stop_propagation + -stop_propagation - Stop propagating clocks at pins. + Stop propagating clocks at pins. - clocks + clocks - A list of clocks to apply the sense. + A list of clocks to apply the sense. - pins + pins - A list of pins. + A list of pins. - The set_sense command is used to modify the propagation of a clock signal. The clock sense is set with the ‑positive and –negative flags. Use the –stop_propagation flag to stop the clock from propagating beyond a pin. The –positive, -negative, -stop_propagation, and –pulse options are mutually exclusive. If the –clock option is not used the command applies to all clocks that traverse pins. The –pulse option is currently not supported. + The set_sense command is used to modify the propagation of a clock signal. The clock sense is set with the ‑positive and –negative flags. Use the –stop_propagation flag to stop the clock from propagating beyond a pin. The –positive, -negative, -stop_propagation, and –pulse options are mutually exclusive. If the –clock option is not used the command applies to all clocks that traverse pins. The –pulse option is currently not supported. - - set_timing_derate + set_timing_derate - [-rise][-fall][-early][-late][-clock][-data][-net_delay][-cell_delay][-cell_check]derate[objects] + [-rise][-fall][-early][-late][-clock][-data][-net_delay][-cell_delay][-cell_check]derate[objects] - -rise + -rise - Set the derating for rising delays. + Set the derating for rising delays. - -fall + -fall - Set the derating for falling delays. + Set the derating for falling delays. - -early + -early - Derate early (min) paths. + Derate early (min) paths. - -late + -late - Derate late (max) paths. + Derate late (max) paths. - -clock + -clock - Derate paths in the clock network. + Derate paths in the clock network. - -data + -data - Derate data paths. + Derate data paths. - -net_delay + -net_delay - Derate net (interconnect) delays. + Derate net (interconnect) delays. + - -cell_delay + -cell_delay - Derate cell delays. + Derate cell delays. - -cell_check + -cell_check - Derate cell timing check margins. + Derate cell timing check margins. - derate + derate - The derating factor to apply to delays. + The derating factor to apply to delays. - objects + objects - A list of instances, library cells, or nets. + A list of instances, library cells, or nets. The set_timing_derate command is used to derate delay calculation results used by the STA. If the –early and –late flags are omitted the both min and max paths are derated. If the –clock and –data flags are not used the derating both clock and data paths are derated. - Use the unset_timing_derate command to remove all derating factors. + Use the unset_timing_derate command to remove all derating factors. - set_resistance + set_resistance - [-max][-min]resistancenets + [-max][-min]resistancenets - - -min + -min - The resistance for minimum path delay calculation. + The resistance for minimum path delay calculation. - -max + -max - The resistance for maximum path delay calculation. + The resistance for maximum path delay calculation. - resistance + resistance - The net resistance. + The net resistance. - nets + nets - A list of nets. + A list of nets. @@ -12655,75 +13015,76 @@ - set_units + set_units - [-capacitance cap_unit][-resistance res_unit][-time time_unit][-voltage voltage_unit][-current current_unit][-power power_unit][-distance distance_unit] + [-capacitance cap_unit][-resistance res_unit][-time time_unit][-voltage voltage_unit][-current current_unit][-power power_unit][-distance distance_unit] - -capacitance cap_unit + -capacitance cap_unit - The capacitance scale factor followed by 'f'. + The capacitance scale factor followed by 'f'. - -resistance res_unit + -resistance res_unit - The resistance scale factor followed by 'ohm'. + The resistance scale factor followed by 'ohm'. - -time time_unit + -time time_unit - The time scale factor followed by 's'. + The time scale factor followed by 's'. + - -voltage voltage_unit + -voltage voltage_unit - The voltage scale factor followed by 'v'. + The voltage scale factor followed by 'v'. - -current current_unit + -current current_unit - The current scale factor followed by 'A'. + The current scale factor followed by 'A'. - -power power_unit + -power power_unit - The power scale factor followed by 'w'. + The power scale factor followed by 'w'. - The set_units command is used to check the units used by the STA command interpreter when parsing commands and reporting results. If the current units differ from the set_unit value a warning is printed. Use the set_cmd_units command to change the command units. + The set_units command is used to check the units used by the STA command interpreter when parsing commands and reporting results. If the current units differ from the set_unit value a warning is printed. Use the set_cmd_units command to change the command units. Units are specified as a scale factor followed by a unit name. The scale factors are as follows. - M 1E+6k 1E+3m 1E-3u 1E-6n 1E-9p 1E-12f 1E-15 + M 1E+6k 1E+3m 1E-3u 1E-6n 1E-9p 1E-12f 1E-15 An example of the set_units command is shown below. - set_units -time ns -capacitance pF -current mA -voltage V -resistance kOhm + set_units -time ns -capacitance pF -current mA -voltage V -resistance kOhm - set_wire_load_min_block_size + set_wire_load_min_block_size - size + size @@ -12733,34 +13094,34 @@ - set_wire_load_mode + set_wire_load_mode - top|enclosed|segmented + top|enclosed|segmented - top + top - + - enclosed + enclosed - + - segmented + segmented - + @@ -12770,50 +13131,51 @@ - set_wire_load_model + set_wire_load_model - -name model_name[-library library][-max][-min][objects] + -name model_name[-library library][-max][-min][objects] + - -name model_name + -name model_name - The name of a wire load model. + The name of a wire load model. - -library library + -library library - Library to look for model_name. + Library to look for model_name. - -max + -max - The wire load model is for maximum path delays. + The wire load model is for maximum path delays. - -min + -min - The wire load model is for minimum path delays. + The wire load model is for minimum path delays. - objects + objects - Not supported. + Not supported. @@ -12823,51 +13185,50 @@ - set_wire_load_selection_group + set_wire_load_selection_group - [-library library][-max][-min]group_name[objects] + [-library library][-max][-min]group_name[objects] - library + library - Library to look for group_name. + Library to look for group_name. - - -max + -max - The wire load selection is for maximum path delays. + The wire load selection is for maximum path delays. - -min + -min - The wire load selection is for minimum path delays. + The wire load selection is for minimum path delays. - group_name + group_name - A wire load selection group name. + A wire load selection group name. - objects + objects - Not supported. + Not supported. @@ -12877,68 +13238,68 @@ - suppress_msg + suppress_msg - msg_ids + msg_ids - msg_ids + msg_ids - A list of error/warning message IDs to suppress. + A list of error/warning message IDs to suppress. - The suppress_msg command suppresses specified error/warning messages by ID. The list of message IDs can be found in doc/messages.txt. + The suppress_msg command suppresses specified error/warning messages by ID. The list of message IDs can be found in doc/messages.txt. - unset_case_analysis + unset_case_analysis - port_or_pin_list + port_or_pin_list - port_or_pin_list + port_or_pin_list - A list of ports or pins. + A list of ports or pins. - The unset_case_analysis command removes the constant values defined by the set_case_analysis command. + The unset_case_analysis command removes the constant values defined by the set_case_analysis command. - unset_clock_latency + unset_clock_latency - [-source]objects + [-source]objects - -source + -source - Specifies source clock latency (clock insertion delay). + Specifies source clock latency (clock insertion delay). - objects + objects - A list of clocks, pins or ports. + A list of clocks, pins or ports. @@ -12948,18 +13309,18 @@ - unset_clock_transition + unset_clock_transition - clocks + clocks - clocks + clocks - A list of clocks. + A list of clocks. @@ -12967,185 +13328,184 @@ - - unset_clock_uncertainty + unset_clock_uncertainty - [-from|-rise_from|-fall_from from_clock][-to|-rise_to|-fall_to to_clock][-rise][-fall][-setup][-hold][objects] + [-from|-rise_from|-fall_from from_clock][-to|-rise_to|-fall_to to_clock][-rise][-fall][-setup][-hold][objects] - -from from_clock + -from from_clock - + - -to to_clock + -to to_clock - + - -rise + -rise - The uncertainty is for the rising edge of the clock. + The uncertainty is for the rising edge of the clock. - -fall + -fall - The uncertainty is for the falling edge of the clock. + The uncertainty is for the falling edge of the clock. - -setup + -setup - uncertainty is the setup check uncertainty. + uncertainty is the setup check uncertainty. - -hold + -hold - uncertainty is the hold uncertainty. + uncertainty is the hold uncertainty. - uncertainty + uncertainty - Clock uncertainty. + Clock uncertainty. - objects + objects - A list of clocks, ports or pins. + A list of clocks, ports or pins. - The unset_clock_uncertainty command removes clock uncertainty defined with the set_clock_uncertainty command. + The unset_clock_uncertainty command removes clock uncertainty defined with the set_clock_uncertainty command. - unset_data_check + unset_data_check - [-from|-rise_from|-fall_from from_object][-to|-rise_to|-fall_to to_object][-setup][-hold][-clock clock] + [-from|-rise_from|-fall_from from_object][-to|-rise_to|-fall_to to_object][-setup][-hold][-clock clock] - -from from_object + -from from_object - A pin used as the timing check reference. + A pin used as the timing check reference. - -to to_object + -to to_object - A pin that the setup/hold check is applied to. + A pin that the setup/hold check is applied to. - -setup + -setup - Add a setup timing check. + Add a setup timing check. - -hold + -hold - Add a hold timing check. + Add a hold timing check. - clock + clock - The setup/hold check clock. + The setup/hold check clock. - The unset_clock_transition command removes a setup or hold check defined by the set_data_check command. + The unset_clock_transition command removes a setup or hold check defined by the set_data_check command. - unset_disable_inferred_clock_gating + unset_disable_inferred_clock_gating - objects + objects - objects + objects - A list of clock gating instances, clock gating pins, or clock enable pins. + A list of clock gating instances, clock gating pins, or clock enable pins. - The unset_disable_inferred_clock_gating command removes a previous set_disable_inferred_clock_gating command. + The unset_disable_inferred_clock_gating command removes a previous set_disable_inferred_clock_gating command. - unset_disable_timing + unset_disable_timing - [-from from_port][-to to_port]objects + [-from from_port][-to to_port]objects - from_port + from_port - + - to_port + to_port - + - objects + objects A list of instances, ports, pins, cells or [library/]cell/port. @@ -13156,69 +13516,69 @@ + - unset_input_delay + unset_input_delay - [-rise][-fall][-max][-min][-clock clock][-clock_fall]port_pin_list + [-rise][-fall][-max][-min][-clock clock][-clock_fall]port_pin_list - -rise + -rise - Unset the arrival time for the rising edge of the input. + Unset the arrival time for the rising edge of the input. - -fall + -fall - Unset the arrival time for the falling edge of the input. + Unset the arrival time for the falling edge of the input. - -max + -max - Unset the minimum arrival time. + Unset the minimum arrival time. - -min + -min - Unset the maximum arrival time. + Unset the maximum arrival time. - clock + clock - Unset the arrival time from clock. + Unset the arrival time from clock. - -clock_fall + -clock_fall - Unset the arrival time from the falling edge of clock + Unset the arrival time from the falling edge of clock - - pin_port_list + pin_port_list - A list of pins or ports. + A list of pins or ports. @@ -13228,66 +13588,67 @@ - unset_output_delay + unset_output_delay - [-rise][-fall][-max][-min][-clock clock][-clock_fall]port_pin_list + [-rise][-fall][-max][-min][-clock clock][-clock_fall]port_pin_list - -rise + -rise - This is the arrival time for the rising edge of the input. + This is the arrival time for the rising edge of the input. - -fall + -fall - This is the arrival time for the falling edge of the input. + This is the arrival time for the falling edge of the input. - -max + -max - This is the minimum arrival time. + This is the minimum arrival time. - -min + -min - This is the maximum arrival time. + This is the maximum arrival time. - clock + clock - The arrival time is from this clock. + The arrival time is from this clock. + - -clock_fall + -clock_fall - The arrival time is from the falling edge of clock + The arrival time is from the falling edge of clock - pin_port_list + pin_port_list - A list of pins or ports. + A list of pins or ports. @@ -13297,48 +13658,47 @@ - unset_path_exceptions + unset_path_exceptions - [-setup][-hold][-rise][-fall][-from|-rise_from|-fall_from from][-through|-rise_through|-fall_through through][-to|-rise_to|-fall_to to] + [-setup][-hold][-rise][-fall][-from|-rise_from|-fall_from from][-through|-rise_through|-fall_through through][-to|-rise_to|-fall_to to] - -setup + -setup - Unset path exceptions for setup checks. + Unset path exceptions for setup checks. - -hold + -hold - Unset path exceptions for hold checks. + Unset path exceptions for hold checks. - -rise + -rise - Unset path exceptions for rising path edges. + Unset path exceptions for rising path edges. - - -fall + -fall - Unset path exceptions for falling path edges. + Unset path exceptions for falling path edges. - -from from + -from from A list of clocks, instances, ports or pins. @@ -13346,7 +13706,7 @@ - -through through + -through through A list of instances, pins or nets. @@ -13354,7 +13714,7 @@ - -to to + -to to A list of clocks, instances, ports or pins. @@ -13362,74 +13722,75 @@ The unset_path_exceptions command removes any matching set_false_path, set_multicycle_path, set_max_delay, and set_min_delay exceptions. - + - unset_power_activity + unset_power_activity - [-global][-input][-input_ports ports][-pins pins] + [-global][-input][-input_ports ports][-pins pins] - -global + -global - Set the activity/duty for all non-clock pins. + Set the activity/duty for all non-clock pins. - -input + -input - Set the default input port activity/duty. + Set the default input port activity/duty. - -input_ports input_ports + -input_ports input_ports - Set the input port activity/duty. + Set the input port activity/duty. + - -pins pins + -pins pins - Set the pin activity/duty. + Set the pin activity/duty. - -activity activity + -activity activity - The activity, or number of transitions per clock cycle. If clock is not specified the clock with the minimum period is used. If no clocks are defined an error is reported. + The activity, or number of transitions per clock cycle. If clock is not specified the clock with the minimum period is used. If no clocks are defined an error is reported. - The unset_power_activity_command is used to undo the effects of the set_power_activity command. + The unset_power_activity_command is used to undo the effects of the set_power_activity command. - unset_propagated_clock + unset_propagated_clock - objects + objects - objects + objects A list of clocks, ports or pins. @@ -13442,45 +13803,44 @@ - unset_timing_derate + unset_timing_derate - + - Remove all derating factors set with the set_timing_derate command. + Remove all derating factors set with the set_timing_derate command. - - unsuppress_msg + unsuppress_msg - msg_ids + msg_ids - msg_ids + msg_ids - A list of error/warning message IDs to unsuppress. + A list of error/warning message IDs to unsuppress. - The unsuppress_msg command removes suppressions for the specified error/warning messages by ID. The list of message IDs can be found in doc/messages.txt. + The unsuppress_msg command removes suppressions for the specified error/warning messages by ID. The list of message IDs can be found in doc/messages.txt. - user_run_time + user_run_time - + @@ -13490,145 +13850,147 @@ - with_output_to_variable + with_output_to_variable - var { commands } + var { commands } - var + var - The name of a variable to save the output of commands to. + The name of a variable to save the output of commands to. - commands + commands - TCL commands that the output will be redirected from. + TCL commands that the output will be redirected from. - The with_output_to_variable command redirects the output of TCL commands to a variable. + The with_output_to_variable command redirects the output of TCL commands to a variable. + - write_path_spice + write_path_spice - -path_args path_args-spice_directory spice_directory-lib_subckt_file lib_subckts_file-model_file model_file-power power-ground ground[-simulator hspice|ngspice|xyce] + -path_args path_args-spice_file spice_file-lib_subckt_file lib_subckts_file-model_file model_file-power power-ground ground[-simulator hspice|ngspice|xyce] - path_args + path_args - -from|-through|-to arguments as in report_checks. + -from|-through|-to arguments as in report_checks. - spice_directory + spice_file - Directory for spice to write output files. + Directory and path prefix for spice output files. - lib_subckts_file + lib_subckts_file - Cell transistor level subckts. + Cell transistor level subckts. - model_file + model_file - Transistor model definitions .included by spice_file. + Transistor model definitions .included by spice_file. - power + power - Voltage supply name in voltage_map of the default liberty library. + Voltage supply name in voltage_map of the default liberty library. - ground + ground - Ground supply name in voltage_map of the default liberty library. + Ground supply name in voltage_map of the default liberty library. - -simulator + -simulator - Simulator that will read the spice netlist. + Simulator that will read the spice netlist. - The write_path_spice command writes a spice netlist for timing paths. Use path_args to specify -from/-through/-to as arguments to the find_timing_paths command. For each path, a spice netlist and the subckts referenced by the path are written in spice_directory. The spice netlist is written in path_<id>.sp and subckt file is path_<id>.subckt. - The spice netlists used by the path are written to subckt_file, which spice_file .includes. The device models used by the spice subckt netlists in model_file are also .included in spice_file. Power and ground names are specified with the -power and -ground arguments. The spice netlist includes a piecewise linear voltage source at the input and .measure statement for each gate delay and pin slew. - Example command: - write_path_spice -path_args {-from "in0" -to "out1" -unconstrained} \ -spice_directory $result_dir \ -lib_subckt_file "write_spice1.subckt" \ -model_file "write_spice1.models" \ -power VDD -ground VSS - When the simulator is hspice, .measure statements will be added to the spice netlist. - When the simulator is Xyce, the .print statement selects the CSV format and writes the waveform data to a file name path_<id>.csv so the results can be used by gnuplot. + The write_path_spice command writes a spice netlist for timing paths. Use path_args to specify -from/-through/-to as arguments to the find_timing_paths command. For each path, a spice netlist and the subckts referenced by the path are written in spice_directory. The spice netlist is written in path_<id>.sp and subckt file is path_<id>.subckt. + The spice netlists used by the path are written to subckt_file, which spice_file .includes. The device models used by the spice subckt netlists in model_file are also .included in spice_file. Power and ground names are specified with the -power and -ground arguments. The spice netlist includes a piecewise linear voltage source at the input and .measure statement for each gate delay and pin slew. + Example command: + write_path_spice -path_args {-from "in0" -to "out1" -unconstrained} \ -spice_directory $result_dir \ -lib_subckt_file "write_spice1.subckt" \ -model_file "write_spice1.models" \ -power VDD -ground VSS + When the simulator is hspice, .measure statements will be added to the spice netlist. + When the simulator is Xyce, the .print statement selects the CSV format and writes the waveform data to a file name path_<id>.csv so the results can be used by gnuplot. - write_sdc + write_sdc - [-digits digits][-gzip][-no_timestamp]filename + [-digits digits][-gzip][-no_timestamp]filename + - digits + digits - The number of digits after the decimal point to report. The default is 4. + The number of digits after the decimal point to report. The default is 4. - -gzip + -gzip - Compress the SDC with gzip. + Compress the SDC with gzip. - -no_timestamp + -no_timestamp - Do not include a time and date in the SDC file. + Do not include a time and date in the SDC file. - filename + filename - The name of the file to write the constraints to. + The name of the file to write the constraints to. @@ -13638,295 +14000,282 @@ - write_sdf + write_sdf - [-corner corner][-divider /|.][-include_typ][-digits digits][-gzip][-no_timestamp][-no_version]filename + [-scene scene][-divider /|.][-include_typ][-digits digits][-gzip][-no_timestamp][-no_version]filename - corner + scene - Write delays for corner. + Write delays for scene. - -divider + -divider - Divider to use between hierarchy levels in pin and instance names. + Divider to use between hierarchy levels in pin and instance names. - - -include_typ + -include_typ - Include a 'typ' value in the SDF triple that is the average of min and max delays to satisfy some Verilog simulators that require three values in the delay triples. + Include a 'typ' value in the SDF triple that is the average of min and max delays to satisfy some Verilog simulators that require three values in the delay triples. - -digits digits + -digits digits - The number of digits after the decimal point to report. The default is 4. + The number of digits after the decimal point to report. The default is 4. - -gzip + -gzip - Compress the SDF using gzip. + Compress the SDF using gzip. - -no_timestamp + -no_timestamp - Do not write a DATE statement. + Do not write a DATE statement. - -no_version + -no_version - Do not write a VERSION statement. + Do not write a VERSION statement. - filename + filename - The SDF filename to write. + The SDF filename to write. - Write the delay calculation delays for the design in SDF format to filename. If -corner is not specified the min/max delays are across all corners. With -corner the min/max delays for corner are written. The SDF TIMESCALE is same as the time_unit in the first liberty file read. + Write the delay calculation delays for the design in SDF format to filename. If -corner is not specified the min/max delays are across all corners. With -corner the min/max delays for corner are written. The SDF TIMESCALE is same as the time_unit in the first liberty file read. - write_timing_model + write_timing_model - [-library_name lib_name][-cell_name cell_name] - [-corner corner]filename + [-library_name lib_name][-cell_name cell_name] + [-scene scene]filename + - -library_name lib_name + lib_name - The name to use for the liberty library. Defaults to cell_name. + The name to use for the liberty library. Defaults to cell_name. - -cell_name cell_name + cell_name - The name to use for the liberty cell. Defaults to the top level module name. + The name to use for the liberty cell. Defaults to the top level module name. - -corner corner + scene - The process corner to use for extracting the model. + The scene to use for extracting the model. - filename + filename - Filename for the liberty timing model. + Filename for the liberty timing model. - The write_timing_model command constructs a liberty timing model for the current design and writes it to filename. cell_name defaults to the cell name of the top level block in the design. - The SDC used to extract the block should include the clock definitions. If the block contains a clock network set_propagated_clock should be used so the clock delays are included in the timing model. The following SDC commands are ignored when building the timing model. - set_input_delayset_output_delayset_loadset_timing_derate - Using set_input_transition with the slew from the block context will be used will improve the match between the timing model and the block netlist. Paths defined on clocks that are defined on internal pins are ignored because the model has no way to include the clock definition. - The resulting timing model can be used in a hierarchical timing flow as a replacement for the block to speed up timing analysis. This hierarchical timing methodology does not handle timing exceptions that originate or terminate inside the block. The timing model includes: - combinational paths between inputs and outputssetup and hold timing constraints on inputsclock to output timing paths - Resistance of long wires on inputs and outputs of the block cannot be modeled in Liberty. To reduce inaccuracies from wire resistance in technologies with resistive wires place buffers on inputs and ouputs. + The write_timing_model command constructs a liberty timing model for the current design and writes it to filename. cell_name defaults to the cell name of the top level block in the design. + The SDC used to extract the block should include the clock definitions. If the block contains a clock network set_propagated_clock should be used so the clock delays are included in the timing model. The following SDC commands are ignored when building the timing model. + set_input_delayset_output_delayset_loadset_timing_derate + Using set_input_transition with the slew from the block context will be used will improve the match between the timing model and the block netlist. Paths defined on clocks that are defined on internal pins are ignored because the model has no way to include the clock definition. + The resulting timing model can be used in a hierarchical timing flow as a replacement for the block to speed up timing analysis. This hierarchical timing methodology does not handle timing exceptions that originate or terminate inside the block. The timing model includes: + combinational paths between inputs and outputssetup and hold timing constraints on inputsclock to output timing paths + Resistance of long wires on inputs and outputs of the block cannot be modeled in Liberty. To reduce inaccuracies from wire resistance in technologies with resistive wires place buffers on inputs and ouputs. The extracted timing model setup/hold checks are scalar (no input slew dependence). Delay timing arcs are load dependent but do not include input slew dependency. - write_verilog + write_verilog - [-include_pwr_gnd][-remove_cells lib_cells]filename + [-include_pwr_gnd][-remove_cells lib_cells]filename - -include_pwr_gnd + -include_pwr_gnd - Include power and ground pins on instances. + Include power and ground pins on instances. - -remove_cells lib_cells + -remove_cells lib_cells - Liberty cells to remove from the Verilog netlist. Use get_lib_cells, a list of cells names, or a cell name with wildcards. + Liberty cells to remove from the Verilog netlist. Use get_lib_cells, a list of cells names, or a cell name with wildcards. - filename + filename - Filename for the liberty library. + Filename for the liberty library. - The write_verilog command writes a Verilog netlist to filename. Instances are always sorted so the results are reproducible across operating systems. Use -remove_cells to remove instances of lib_cells from the netlist. - Filter Expressions - The get_cells, get_pins, get_ports and get_timing_edges functions support filtering the returned objects by property values. Supported filter expressions are shown below. + The write_verilog command writes a Verilog netlist to filename. Use -sort to sort the instances so the results are reproducible across operating systems. Use -remove_cells to remove instances of lib_cells from the netlist. + Filter Expressions + The get_cells, get_pins, get_ports and get_timing_edges functions support filtering the returned objects by property values. Supported filter expressions are shown below. - property + property - Return objects with property value equal to 1. + Return objects with property value equal to 1. - property==value + property==value - Return objects with property value equal to value. + Return objects with property value equal to value. - property=~pattern + property=~pattern - Return objects with property value that matches pattern. + Return objects with property value that matches pattern. - property!=value + property!=value - Return objects with property value not equal to value. + Return objects with property value not equal to value. - property!~value + property!~value - Return objects with property value that does not match pattern. + Return objects with property value that does not match pattern. - expr1&&expr2 + expr1&&expr2 - Return objects with expr1 and expr2. expr1 and expr2 are one of the first three property value forms shown above. + Return objects with expr1 and expr2. expr1 and expr2 are one of the first three property value forms shown above. - - expr1||expr2 + expr1||expr2 - Return objects with expr1 or expr2. expr1 and expr2 are one of the first three property value forms shown above. + Return objects with expr1 or expr2. expr1 and expr2 are one of the first three property value forms shown above. - Where property is a property supported by the get_property command. Note that if there are spaces in the expression it must be enclosed in quotes so that it is a single argument. - Variables + Where property is a property supported by the get_property command. Note that if there are spaces in the expression it must be enclosed in quotes so that it is a single argument. + Variables - hierarchy_separator + hierarchy_separator - Any character. + Any character. The hierarchy_separator separates instance names in a hierarchical instance, net, or pin name. The default value is '/'. - - - - - - sta_bidirect_net_paths_enabled - - - 0|1 - - - - When set to 0, paths from bidirectional (inout) ports back through nets are disabled. When set to 1, paths from bidirectional paths from the net back into the instance are enabled. The default value is 0. - sta_continue_on_error + sta_continue_on_error - 0|1 + 0|1 - The include and read_sdc commands stop and report any errors encountered while reading a file unless sta_continue_on_error is 1. The default value is 0. + The include and read_sdc commands stop and report any errors encountered while reading a file unless sta_continue_on_error is 1. The default value is 0. - sta_crpr_mode + sta_crpr_mode - same_pin|same_transition + same_pin|same_transition - When the data and clock paths of a timing check overlap (see sta_crpr_enabled), pessimism is removed independent of whether of the path rise/fall transitions. When sta_crpr_mode is same_transition, the pessimism is only removed if the path rise/fall transitions are the same. The default value is same_pin. + When the data and clock paths of a timing check overlap (see sta_crpr_enabled), pessimism is removed independent of whether of the path rise/fall transitions. When sta_crpr_mode is same_transition, the pessimism is only removed if the path rise/fall transitions are the same. The default value is same_pin. + - sta_cond_default_arcs_enabled + sta_cond_default_arcs_enabled - 0|1 + 0|1 @@ -13936,49 +14285,49 @@ - sta_crpr_enabled + sta_crpr_enabled - 0|1 + 0|1 - During min/max timing analysis for on_chip_variation the data and clock paths may overlap. For a setup check the maximum path delays are used for the data and the minimum path delays are used for the clock. Because the gates cannot simultaneously have minimum and maximum delays the timing check slack is pessimistic. This pessimism is known as Common Reconvergent Pessimism Removal, or “CRPR”. Enabling CRPR slows down the analysis. The default value is 1. + During min/max timing analysis for on_chip_variation the data and clock paths may overlap. For a setup check the maximum path delays are used for the data and the minimum path delays are used for the clock. Because the gates cannot simultaneously have minimum and maximum delays the timing check slack is pessimistic. This pessimism is known as Common Reconvergent Pessimism Removal, or “CRPR”. Enabling CRPR slows down the analysis. The default value is 1. - sta_dynamic_loop_breaking + sta_dynamic_loop_breaking - 0|1 + 0|1 - When sta_dynamic_loop_breaking is 0, combinational logic loops are disabled by disabling a timing arc that closes the loop. When sta_dynamic_loop_breaking is 1, all paths around the loop are reported. The default value is 0. + When sta_dynamic_loop_breaking is 0, combinational logic loops are disabled by disabling a timing arc that closes the loop. When sta_dynamic_loop_breaking is 1, all paths around the loop are reported. The default value is 0. - sta_gated_clock_checks_enabled + sta_gated_clock_checks_enabled - 0|1 + 0|1 - When sta_gated_clock_checks_enabled is 1, clock gating setup and hold timing checks are checked. The default value is 1. + When sta_gated_clock_checks_enabled is 1, clock gating setup and hold timing checks are checked. The default value is 1. - sta_input_port_default_clock + sta_input_port_default_clock - 0|1 + 0|1 @@ -13988,10 +14337,10 @@ - sta_internal_bidirect_instance_paths_enabled + sta_internal_bidirect_instance_paths_enabled - 0|1 + 0|1 @@ -14001,27 +14350,40 @@ - sta_pocv_enabled + sta_pocv_mode - 0|1 + scalar|normal|skew_normal + + + + Enable parametric on chip variation using statistical timing analysis. The default value is scalar. + + + + + + sta_pocv_quartile + + + quartile - Enable parametric on chip variation using statistical timing analysis. The default value is 0. + The target quantile of a delay probability distribution (confidence level).The default value is 3 standard deviations, or sigma. - sta_propagate_all_clocks + sta_propagate_all_clocks - 0|1 + 0|1 - All clocks defined after sta_propagate_all_clocks is set to 1 are propagated. If it is set before any clocks are defined it has the same effect as + All clocks defined after sta_propagate_all_clocks is set to 1 are propagated. If it is set before any clocks are defined it has the same effect as set_propagated_clock [all_clocks] After all clocks have been defined. The default value is 0. @@ -14029,37 +14391,36 @@ - sta_propagate_gated_clock_enable + sta_propagate_gated_clock_enable - 0|1 + 0|1 - When set to 1, paths of gated clock enables are propagated through the clock gating instances. If the gated clock controls sequential elements setting sta_propagate_gated_clock_enable to 0 prevents spurious paths from the clock enable. The default value is 1. + When set to 1, paths of gated clock enables are propagated through the clock gating instances. If the gated clock controls sequential elements setting sta_propagate_gated_clock_enable to 0 prevents spurious paths from the clock enable. The default value is 1. - sta_recovery_removal_checks_enabled + sta_recovery_removal_checks_enabled - 0|1 + 0|1 - When sta_recovery_removal_checks_enabled is 0, recovery and removal timing checks are disabled. The default value is 1. + When sta_recovery_removal_checks_enabled is 0, recovery and removal timing checks are disabled. The default value is 1. - - sta_report_default_digits + sta_report_default_digits - integer + integer @@ -14069,10 +14430,10 @@ - sta_preset_clear_arcs_enabled + sta_preset_clear_arcs_enabled - 0|1 + 0|1 @@ -14102,184 +14463,186 @@ - Alphabetical Index + Alphabetical Index - all_clocks6 - all_inputs6 - all_outputs6 - all_registers6 - check_setup7 - Command Line Arguments1 - Commands6 - connect_pin7 - create_generated_clock9 - create_voltage_area10 - current_design10 - current_instance10 - define_corners11 - delete_clock11 - delete_from_list11 - delete_generated_clock11 - delete_instance11 - delete_net12 - disconnect_pin12 - elapsed_run_time12 - Example Command Scripts1 - Filter Expressions80 - find_timing_paths13 - get_cells14 - get_clocks15 - get_fanin16 - get_fanout16 - get_full_name17 - get_lib_pins18 - get_libs18 - get_name20 - get_nets19 - get_pins20 - get_ports21 - get_property21 - get_timing_edges24 - group_path25 - hierarchy_separator80 - include26 - link_design26 - make_instance26 - make_net27 - Power Analysis2 - read_liberty27 - read_saif28 - read_sdc28 - read_sdf28 - read_spef29 - read_vcd31 - read_verilog31 - redirection4 - replace_activity_annotation31 - replace_cell31 - report_annotated_check32 - report_annotated_delay33 - report_check_types36 - report_checks34 - report_clock_latency37 - report_clock_min_period38 - report_clock_properties38 - report_clock_skew38 - report_dcalc39 - report_disabled_edges39 - report_edges39 - report_instance40 - report_lib_cell40 - report_net40 - report_parasitic_annotation40 - report_power41 - report_pulse_width_checks41 - report_slews42 - report_tns42 - report_units42 - report_wns43 - report_worst_slack43 - set_assigned_check43 - set_assigned_delay44 - set_assigned_transition45 - set_case_analysis46 - set_clock_gating_check46 - set_clock_groups47 - set_clock_latency47 - set_clock_transition48 - set_clock_uncertainty49 - set_cmd_units50 - set_data_check51 - set_disable_inferred_clock_gating51 - set_disable_timing51 - set_drive52 - set_driving_cell53 - set_false_path54 - set_fanout_load55 - set_hierarchy_separator55 - set_ideal_latency55 - set_ideal_network55 - set_ideal_transition55 - set_input_delay55 - set_input_transition57 - set_level_shifter_strategy57 - set_level_shifter_threshold57 - set_load57 - set_logic_dc58 - set_logic_one58 - set_logic_zero59 - set_max_area59 - set_max_capacitance59 - set_max_delay59 - set_max_dynamic_power60 - set_max_fanout60 - set_max_leakage_power60 - set_max_time_borrow60 - set_max_transition61 - set_min_capacitance61 - set_min_delay62 - set_min_pulse_width62 - set_multicycle_path63 - set_operating_conditions64 - set_output_delay65 - set_port_fanout_number66 - set_power_activity66 - set_propagated_clock67 - set_pvt67 - set_resistance69 - set_sense68 - set_timing_derate69 - set_units70 - set_wire_load_min_block_size71 - set_wire_load_mode71 - set_wire_load_model71 - set_wire_load_selection_group71 - SPEF30 - sta_bidirect_net_paths_enabled80 - sta_cond_default_arcs_enabled81 - sta_continue_on_error80 - sta_crpr_enabled81 - sta_crpr_mode81 - sta_dynamic_loop_breaking81 - sta_gated_clock_checks_enabled81 - sta_input_port_default_clock81 - sta_internal_bidirect_instance_paths_enabled81 - sta_pocv_enabled82 - sta_preset_clear_arcs_enabled82 - sta_propagate_all_clocks82 - sta_propagate_gated_clock_enable82 - sta_recovery_removal_checks_enabled82 - sta_report_default_digits82 - suppress_msg72 - TCL Interpreter3 - Timing Analysis using SDF2 - Timing Analysis with Multiple Process Corners2 - unset_case_analysis72 - unset_clock_latency72 - unset_clock_transition72 - unset_clock_uncertainty73 - unset_data_check73 - unset_disable_inferred_clock_gating74 - unset_disable_timing74 - unset_input_delay74 - unset_output_delay75 - unset_path_exceptions75 - unset_propagated_clock76 - unset_timing_derate76 - unsuppress_msg76 - user_run_time76 - Variables80 - verilog netlist31 - with_output_to_variable76 - write_path_spice77 - write_sdc77 - write_sdf78 - write_timing_model78 - write_verilog79 + all_clocks7 + all_inputs7 + all_outputs8 + all_registers8 + check_setup9 + Command Line Arguments1 + Commands7 + connect_pin9 + create_generated_clock11 + create_voltage_area12 + current_design12 + current_instance13 + define_scene13 + delete_clock13 + delete_from_list13 + delete_generated_clock14 + delete_instance14 + delete_net14 + disconnect_pin14 + elapsed_run_time14 + Example Command Scripts1 + Filter Expressions84 + find_timing_paths15 + get_cells17 + get_clocks17 + get_fanin18 + get_fanout19 + get_full_name19 + get_lib_pins20 + get_libs21 + get_name22 + get_nets22 + get_pins23 + get_ports23 + get_property24 + get_scenes28 + get_timing_edges28 + group_path29 + hierarchy_separator85 + include30 + link_design30 + make_instance30 + make_net31 + Power Analysis3 + read_liberty31 + read_saif32 + read_sdc33 + read_sdf33 + read_spef34 + read_vcd35 + read_verilog35 + redirection5 + replace_activity_annotation36 + replace_cell35 + report_annotated_check36 + report_annotated_delay37 + report_check_types41 + report_checks38 + report_clock_latency42 + report_clock_min_period42 + report_clock_properties43 + report_clock_skew43 + report_dcalc43 + report_disabled_edges44 + report_edges44 + report_instance44 + report_lib_cell44 + report_net45 + report_parasitic_annotation45 + report_power45 + report_slews46 + report_tns46 + report_units46 + report_wns47 + report_worst_slack47 + set_assigned_check48 + set_assigned_delay49 + set_assigned_transition49 + set_case_analysis50 + set_clock_gating_check50 + set_clock_groups51 + set_clock_latency52 + set_clock_transition52 + set_clock_uncertainty53 + set_cmd_units54 + set_data_check55 + set_disable_inferred_clock_gating55 + set_disable_timing55 + set_drive56 + set_driving_cell57 + set_false_path58 + set_fanout_load59 + set_hierarchy_separator59 + set_ideal_latency59 + set_ideal_network59 + set_ideal_transition59 + set_input_delay59 + set_input_transition61 + set_level_shifter_strategy61 + set_level_shifter_threshold61 + set_load61 + set_logic_dc62 + set_logic_one62 + set_logic_zero63 + set_max_area63 + set_max_capacitance63 + set_max_delay63 + set_max_dynamic_power64 + set_max_fanout64 + set_max_leakage_power64 + set_max_time_borrow64 + set_max_transition65 + set_min_capacitance65 + set_min_delay66 + set_min_pulse_width67 + set_mode67 + set_multicycle_path67 + set_operating_conditions68 + set_output_delay69 + set_port_fanout_number70 + set_power_activity70 + set_propagated_clock71 + set_pvt71 + set_resistance73 + set_sense72 + set_timing_derate73 + set_units74 + set_wire_load_min_block_size75 + set_wire_load_mode75 + set_wire_load_model75 + set_wire_load_selection_group75 + SPEF34 + sta_cond_default_arcs_enabled85 + sta_continue_on_error85 + sta_crpr_enabled85 + sta_crpr_mode85 + sta_dynamic_loop_breaking85 + sta_gated_clock_checks_enabled85 + sta_input_port_default_clock86 + sta_internal_bidirect_instance_paths_enabled86 + sta_pocv_enabled86 + sta_preset_clear_arcs_enabled87 + sta_propagate_all_clocks86 + sta_propagate_gated_clock_enable86 + sta_recovery_removal_checks_enabled86 + sta_report_default_digits86 + suppress_msg76 + TCL Interpreter5 + Timing Analysis using SDF2 + Timing Analysis with Multiple Corners and Modes3 + Timing Analysis with Multiple Process Corners2 + unset_case_analysis76 + unset_clock_latency76 + unset_clock_transition76 + unset_clock_uncertainty77 + unset_data_check77 + unset_disable_inferred_clock_gating78 + unset_disable_timing78 + unset_input_delay78 + unset_output_delay79 + unset_path_exceptions79 + unset_power_activity80 + unset_propagated_clock80 + unset_timing_derate80 + unsuppress_msg81 + user_run_time81 + Variables85 + verilog netlist35 + with_output_to_variable81 + write_path_spice81 + write_sdc82 + write_sdf82 + write_timing_model83 + write_verilog84 - - Version 2.6.0, Sep 23, 2024Copyright (c) 2024, Parallax Software, Inc. + + Version 3.0.0, Mar 7, 2026Copyright (c) 2026, Parallax Software, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. diff --git a/doc/OpenSTA.pdf b/doc/OpenSTA.pdf index 62964a595..745f20950 100644 Binary files a/doc/OpenSTA.pdf and b/doc/OpenSTA.pdf differ diff --git a/doc/StaApi.txt b/doc/StaApi.txt index 30a16a9c6..e48eff981 100644 --- a/doc/StaApi.txt +++ b/doc/StaApi.txt @@ -108,9 +108,6 @@ in a class derived from Sta. Because the components refer to each other, Sta::updateComponentsState() must be called to notify the components if any of them are changed after creation. -The file liberty/LibertyExt.cc contains an example that shows how the -liberty reader is replaced with a custom one on the Sta object. - Units ----- @@ -291,7 +288,7 @@ in this arc set: S r -> Z f The liberty file reader can be customized to read attributes that are -not used by the STA. See liberty/LibertyExt.cc for an example. +not used by the STA. Graph ----- @@ -323,28 +320,6 @@ net have wire edges between the pin vertices. Timing arc sets in the leaf instance timing models have corresponding edges in the graph between pins on the instance. -The Graph class constructor option slew_tr_count is used to prevent -the grpah from reserving memory to store slews. Similarly, if the -have_arc_delays option is false no memory is reserved for storing arc -delay values. This is useful if an external delay calculator is used -to annotate delays on the graph. In this case the Graph functions -arcDelay and wireDelay should be overloaded to return delay values -stored outside of the STA. - -A graph with no slews or delays is constructed using: - - Graph(this, 0, false, ap_count); - -A graph with one slew for rising and falling edges is constructed using: - - Graph(this, 1, true, ap_count); - -A graph with separate rising and falling slews (the default) is -constructed using: - - Graph(this, 2, true, ap_count); - - SDC --- diff --git a/etc/FindMessages.tcl b/etc/FindMessages.tcl index bf60754bf..40f00d292 100755 --- a/etc/FindMessages.tcl +++ b/etc/FindMessages.tcl @@ -61,7 +61,7 @@ foreach subdir $subdirs { set files [glob -nocomplain [file join $subdir "*.{cc,hh,yy,ll,i}"]] set files_c [concat $files_c $files] } -set warn_regexp_c {(?:(?:->critical|->warn|->fileWarn|->error|->fileError|libWarn|libError| warn)\(|tclArgError\(interp,\s*)([0-9]+),.*(".+")} +set warn_regexp_c {(?:(?:->critical|->warn|->fileWarn|->error|->fileError|criticalError|warn|error)\(|tclArgError\(interp,\s*)([0-9]+),.*(".+")} set files_tcl {} foreach subdir $subdirs { diff --git a/examples/asap7_small_ff.lib.gz b/examples/asap7_small_ff.lib.gz new file mode 100644 index 000000000..3bd6e823f Binary files /dev/null and b/examples/asap7_small_ff.lib.gz differ diff --git a/examples/asap7_small_ss.lib.gz b/examples/asap7_small_ss.lib.gz new file mode 100644 index 000000000..8041fb4ae Binary files /dev/null and b/examples/asap7_small_ss.lib.gz differ diff --git a/examples/mcmm2_mode1.sdc b/examples/mcmm2_mode1.sdc new file mode 100644 index 000000000..d84bb5793 --- /dev/null +++ b/examples/mcmm2_mode1.sdc @@ -0,0 +1,2 @@ +create_clock -name m1_clk -period 1000 {clk1 clk2 clk3} +set_input_delay -clock m1_clk 100 {in1 in2} diff --git a/examples/mcmm2_mode2.sdc b/examples/mcmm2_mode2.sdc new file mode 100644 index 000000000..1eee9b1fd --- /dev/null +++ b/examples/mcmm2_mode2.sdc @@ -0,0 +1,2 @@ +create_clock -name m2_clk -period 500 {clk1 clk3} +set_output_delay -clock m2_clk 100 out diff --git a/examples/mcmm3.tcl b/examples/mcmm3.tcl new file mode 100644 index 000000000..a846c2e22 --- /dev/null +++ b/examples/mcmm3.tcl @@ -0,0 +1,18 @@ +# mmcm reg1 parasitics +read_liberty asap7_small_ff.lib.gz +read_liberty asap7_small_ss.lib.gz +read_verilog reg1_asap7.v +link_design top + +read_sdc -mode mode1 mcmm2_mode1.sdc +read_sdc -mode mode2 mcmm2_mode2.sdc + +read_spef -name reg1_ff reg1_asap7.spef +read_spef -name reg1_ss reg1_asap7_ss.spef + +define_scene scene1 -mode mode1 -liberty asap7_small_ff -spef reg1_ff +define_scene scene2 -mode mode2 -liberty asap7_small_ss -spef reg1_ss + +report_checks -scenes scene1 +report_checks -scenes scene2 +report_checks -group_path_count 4 diff --git a/examples/multi_corner.tcl b/examples/multi_corner.tcl index 589ca9b71..fa5fab183 100644 --- a/examples/multi_corner.tcl +++ b/examples/multi_corner.tcl @@ -1,15 +1,19 @@ -# 3 corners with +/- 10% derating example -define_corners ss tt ff -read_liberty -corner ss nangate45_slow.lib.gz -read_liberty -corner tt nangate45_typ.lib.gz -read_liberty -corner ff nangate45_fast.lib.gz +# 3 liberty corners with +/- 10% derating example +read_liberty nangate45_slow.lib.gz +read_liberty nangate45_typ.lib.gz +read_liberty nangate45_fast.lib.gz read_verilog example1.v link_design top set_timing_derate -early 0.9 set_timing_derate -late 1.1 create_clock -name clk -period 10 {clk1 clk2 clk3} set_input_delay -clock clk 0 {in1 in2} -# report all corners + +define_scene ss -liberty NangateOpenCellLibrary_slow +define_scene tt -liberty NangateOpenCellLibrary +define_scene ff -liberty NangateOpenCellLibrary_fast + +# report all scenes report_checks -path_delay min_max -# report typical corner -report_checks -corner tt +# report typical scene +report_checks -scene tt diff --git a/examples/reg1_asap7.spef b/examples/reg1_asap7.spef new file mode 100644 index 000000000..14bc4d6b9 --- /dev/null +++ b/examples/reg1_asap7.spef @@ -0,0 +1,135 @@ +*SPEF "IEEE 1481-1998" +*DESIGN "reg1" +*DATE "Fri Nov 20 13:23:00 2002" +*VENDOR "Parallax Software, Inc" +*PROGRAM "Handjob" +*VERSION "1.0.1c" +*DESIGN_FLOW "MISSING_NETS" +*DIVIDER / +*DELIMITER : +*BUS_DELIMITER [ ] +*T_UNIT 1.0 PS +*C_UNIT 1.0 FF +*R_UNIT 1.0 KOHM +*L_UNIT 1.0 UH + +*POWER_NETS VDD +*GROUND_NETS VSS + +*PORTS +in1 I +in2 I +clk1 I +clk2 I +clk3 I +out O + +*D_NET in1 13.4 +*CONN +*P in1 I +*I r1:D I *L .0036 +*CAP +1 in1 6.7 +2 r1:D 6.7 +*RES +3 in1 r1:D 2.42 +*END + +*D_NET in2 13.4 +*CONN +*P in2 I +*I r2:D I *L .0036 +*CAP +1 in2 6.7 +2 r2:D 6.7 +*RES +3 in2 r2:D 2.42 +*END + +*D_NET clk1 13.4 +*CONN +*P clk1 I +*I r1:CLK I *L .0036 +*CAP +1 clk1 6.7 +2 r1:CLK 6.7 +*RES +3 clk1 r1:CLK 2.42 +*END + +*D_NET clk2 13.4 +*CONN +*P clk2 I +*I r2:CLK I *L .0036 +*CAP +1 clk2 6.7 +2 r2:CLK 6.7 +*RES +3 clk2 r2:CLK 2.42 +*END + +*D_NET clk3 13.4 +*CONN +*P clk3 I +*I r3:CLK I *L .0036 +*CAP +1 clk3 6.7 +2 r3:CLK 6.7 +*RES +3 clk3 r3:CLK 2.42 +*END + +*D_NET r1q 13.4 +*CONN +*I r1:Q O +*I u2:A I *L .0086 +*CAP +1 r1:Q 6.7 +2 u2:A 6.7 +*RES +3 r1:Q u2:A 2.42 +*END + +*D_NET r2q 13.4 +*CONN +*I r2:Q O +*I u1:A I *L .0086 +*CAP +1 r2:Q 6.7 +2 u1:A 6.7 +*RES +3 r2:Q u1:A 2.42 +*END + +*D_NET u1z 13.4 +*CONN +*I u1:Y O +*I u2:B I *L .0086 +*CAP +1 u1:Y 6.7 +2 u2:B 6.7 +*RES +3 u1:Y u2:B 2.42 +*END + +*D_NET u2z 13.4 +*CONN +*I u2:Y O +*I r3:D I *L .0086 +*CAP +1 u2:Y 6.7 +2 r3:D 6.7 +*RES +3 u2:Y r3:D 2.42 +*END + +*D_NET out 13.4 +*CONN +*I r3:Q O +*P out O +*CAP +1 r3:Q 6.7 +2 out 6.7 +*RES +3 r3:Q out 2.42 +*END diff --git a/examples/reg1_asap7.v b/examples/reg1_asap7.v new file mode 100644 index 000000000..5eb10b466 --- /dev/null +++ b/examples/reg1_asap7.v @@ -0,0 +1,11 @@ +module top (in1, in2, clk1, clk2, clk3, out); + input in1, in2, clk1, clk2, clk3; + output out; + wire r1q, r2q, u1z, u2z; + + DFFHQx4_ASAP7_75t_R r1 (.D(in1), .CLK(clk1), .Q(r1q)); + DFFHQx4_ASAP7_75t_R r2 (.D(in2), .CLK(clk2), .Q(r2q)); + BUFx2_ASAP7_75t_R u1 (.A(r2q), .Y(u1z)); + AND2x2_ASAP7_75t_R u2 (.A(r1q), .B(u1z), .Y(u2z)); + DFFHQx4_ASAP7_75t_R r3 (.D(u2z), .CLK(clk3), .Q(out)); +endmodule // top diff --git a/examples/reg1_asap7_ss.spef b/examples/reg1_asap7_ss.spef new file mode 100644 index 000000000..fe49a5e5a --- /dev/null +++ b/examples/reg1_asap7_ss.spef @@ -0,0 +1,135 @@ +*SPEF "IEEE 1481-1998" +*DESIGN "reg1" +*DATE "Fri Nov 20 13:23:00 2002" +*VENDOR "Parallax Software, Inc" +*PROGRAM "Handjob" +*VERSION "1.0.1c" +*DESIGN_FLOW "MISSING_NETS" +*DIVIDER / +*DELIMITER : +*BUS_DELIMITER [ ] +*T_UNIT 1.0 PS +*C_UNIT 1.0 FF +*R_UNIT 1.0 KOHM +*L_UNIT 1.0 UH + +*POWER_NETS VDD +*GROUND_NETS VSS + +*PORTS +in1 I +in2 I +clk1 I +clk2 I +clk3 I +out O + +*D_NET in1 13.4 +*CONN +*P in1 I +*I r1:D I *L .0036 +*CAP +1 in1 8.1 +2 r1:D 8.1 +*RES +3 in1 r1:D 2.7 +*END + +*D_NET in2 13.4 +*CONN +*P in2 I +*I r2:D I *L .0036 +*CAP +1 in2 8.1 +2 r2:D 8.1 +*RES +3 in2 r2:D 2.7 +*END + +*D_NET clk1 13.4 +*CONN +*P clk1 I +*I r1:CLK I *L .0036 +*CAP +1 clk1 8.1 +2 r1:CLK 8.1 +*RES +3 clk1 r1:CLK 2.7 +*END + +*D_NET clk2 13.4 +*CONN +*P clk2 I +*I r2:CLK I *L .0036 +*CAP +1 clk2 8.1 +2 r2:CLK 8.1 +*RES +3 clk2 r2:CLK 2.7 +*END + +*D_NET clk3 13.4 +*CONN +*P clk3 I +*I r3:CLK I *L .0036 +*CAP +1 clk3 8.1 +2 r3:CLK 8.1 +*RES +3 clk3 r3:CLK 2.7 +*END + +*D_NET r1q 13.4 +*CONN +*I r1:Q O +*I u2:A I *L .0086 +*CAP +1 r1:Q 8.1 +2 u2:A 8.1 +*RES +3 r1:Q u2:A 2.7 +*END + +*D_NET r2q 13.4 +*CONN +*I r2:Q O +*I u1:A I *L .0086 +*CAP +1 r2:Q 8.1 +2 u1:A 8.1 +*RES +3 r2:Q u1:A 2.7 +*END + +*D_NET u1z 13.4 +*CONN +*I u1:Y O +*I u2:B I *L .0086 +*CAP +1 u1:Y 8.1 +2 u2:B 8.1 +*RES +3 u1:Y u2:B 2.7 +*END + +*D_NET u2z 13.4 +*CONN +*I u2:Y O +*I r3:D I *L .0086 +*CAP +1 u2:Y 8.1 +2 r3:D 8.1 +*RES +3 u2:Y r3:D 2.7 +*END + +*D_NET out 13.4 +*CONN +*I r3:Q O +*P out O +*CAP +1 r3:Q 8.1 +2 out 8.1 +*RES +3 r3:Q out 2.7 +*END diff --git a/graph/DelayFloat.cc b/graph/DelayFloat.cc deleted file mode 100644 index a9a90dcf9..000000000 --- a/graph/DelayFloat.cc +++ /dev/null @@ -1,199 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#include "Delay.hh" - -#include "StaConfig.hh" -#include "Fuzzy.hh" -#include "Units.hh" -#include "StaState.hh" - -// Non-SSTA compilation. -#if !SSTA - -namespace sta { - -static Delay delay_init_values[MinMax::index_count]; - -void -initDelayConstants() -{ - delay_init_values[MinMax::minIndex()] = MinMax::min()->initValue(); - delay_init_values[MinMax::maxIndex()] = MinMax::max()->initValue(); -} - -const char * -delayAsString(const Delay &delay, - const StaState *sta) -{ - return delayAsString(delay, sta, sta->units()->timeUnit()->digits()); -} - -const char * -delayAsString(const Delay &delay, - const StaState *sta, - int digits) -{ - return sta->units()->timeUnit()->asString(delay, digits); -} - -const char * -delayAsString(const Delay &delay, - const EarlyLate *, - const StaState *sta, - int digits) -{ - const Unit *unit = sta->units()->timeUnit(); - return unit->asString(delay, digits); -} - -const Delay & -delayInitValue(const MinMax *min_max) -{ - return delay_init_values[min_max->index()]; -} - -bool -delayIsInitValue(const Delay &delay, - const MinMax *min_max) -{ - return fuzzyEqual(delay, min_max->initValue()); -} - -bool -delayZero(const Delay &delay) -{ - return fuzzyZero(delay); -} - -bool -delayInf(const Delay &delay) -{ - return fuzzyInf(delay); -} - -bool -delayEqual(const Delay &delay1, - const Delay &delay2) -{ - return fuzzyEqual(delay1, delay2); -} - -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const StaState *) -{ - return fuzzyLess(delay1, delay2); -} - -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *) -{ - if (min_max == MinMax::max()) - return fuzzyLess(delay1, delay2); - else - return fuzzyGreater(delay1, delay2); -} - -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - const StaState *) -{ - return fuzzyLessEqual(delay1, delay2); -} - -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *) -{ - if (min_max == MinMax::max()) - return fuzzyLessEqual(delay1, delay2); - else - return fuzzyGreaterEqual(delay1, delay2); -} - -bool -delayGreater(const Delay &delay1, - const Delay &delay2, - const StaState *) -{ - return fuzzyGreater(delay1, delay2); -} - -bool -delayGreater(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *) -{ - if (min_max == MinMax::max()) - return fuzzyGreater(delay1, delay2); - else - return fuzzyLess(delay1, delay2); -} - -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const StaState *) -{ - return fuzzyGreaterEqual(delay1, delay2); -} - -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *) -{ - if (min_max == MinMax::max()) - return fuzzyGreaterEqual(delay1, delay2); - else - return fuzzyLessEqual(delay1, delay2); -} - -Delay -delayRemove(const Delay &delay1, - const Delay &delay2) -{ - return delay1 - delay2; -} - -float -delayRatio(const Delay &delay1, - const Delay &delay2) -{ - return delay1 / delay2; -} - -} // namespace - -#endif // !SSTA diff --git a/graph/DelayNormal1.cc b/graph/DelayNormal1.cc deleted file mode 100644 index d50db11f5..000000000 --- a/graph/DelayNormal1.cc +++ /dev/null @@ -1,483 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#include "Delay.hh" - -#include // sqrt - -#include "StaConfig.hh" -#include "Error.hh" -#include "StringUtil.hh" -#include "Fuzzy.hh" -#include "Units.hh" -#include "StaState.hh" -#include "Variables.hh" - -// SSTA compilation. -#if (SSTA == 1) - -namespace sta { - -inline float -square(float x) -{ - return x * x; -} - -static Delay delay_init_values[MinMax::index_count]; - -void -initDelayConstants() -{ - delay_init_values[MinMax::minIndex()] = MinMax::min()->initValue(); - delay_init_values[MinMax::maxIndex()] = MinMax::max()->initValue(); -} - -const Delay & -delayInitValue(const MinMax *min_max) -{ - return delay_init_values[min_max->index()]; -} - -Delay::Delay() : - mean_(0.0), - sigma2_(0.0) -{ -} - -Delay::Delay(const Delay &delay) : - mean_(delay.mean_), - sigma2_(delay.sigma2_) -{ -} - -Delay::Delay(const DelayDbl &delay) : - mean_(delay.mean_), - sigma2_(delay.sigma2_) -{ -} - -Delay::Delay(float mean) : - mean_(mean), - sigma2_(0.0) -{ -} - -Delay::Delay(float mean, - float sigma2) : - mean_(mean), - sigma2_(sigma2) -{ -} - -float -Delay::sigma() const -{ - if (sigma2_ < 0.0) - // Sigma is negative for crpr to offset sigmas in the common - // clock path. - return -sqrt(-sigma2_); - else - return sqrt(sigma2_); -} - -float -Delay::sigma2() const -{ - return sigma2_; -} - -void -Delay::operator=(const Delay &delay) -{ - mean_ = delay.mean_; - sigma2_ = delay.sigma2_; -} - -void -Delay::operator=(float delay) -{ - mean_ = delay; - sigma2_ = 0.0; -} - -void -Delay::operator+=(const Delay &delay) -{ - mean_ += delay.mean_; - sigma2_ += delay.sigma2_; -} - -void -Delay::operator+=(float delay) -{ - mean_ += delay; -} - -Delay -Delay::operator+(const Delay &delay) const -{ - return Delay(mean_ + delay.mean_, - sigma2_ + delay.sigma2_); -} - -Delay -Delay::operator+(float delay) const -{ - return Delay(mean_ + delay, sigma2_); -} - -Delay -Delay::operator-(const Delay &delay) const -{ - return Delay(mean_ - delay.mean_, - sigma2_ + delay.sigma2_); -} - -Delay -Delay::operator-(float delay) const -{ - return Delay(mean_ - delay, sigma2_); -} - -Delay -Delay::operator-() const -{ - return Delay(-mean_, sigma2_); -} - -void -Delay::operator-=(float delay) -{ - mean_ -= delay; -} - -void -Delay::operator-=(const Delay &delay) -{ - mean_ -= delay.mean_; - sigma2_ += delay.sigma2_; -} - -bool -Delay::operator==(const Delay &delay) const -{ - return delayEqual(*this, delay); -} - -//////////////////////////////////////////////////////////////// - -DelayDbl::DelayDbl() : - mean_(0.0), - sigma2_(0.0) -{ -} - -void -DelayDbl::operator=(float delay) -{ - mean_ = delay; - sigma2_ = 0.0; -} - -void -DelayDbl::operator+=(const Delay &delay) -{ - mean_ += delay.mean_; - sigma2_ += delay.sigma2_; -} - -void -DelayDbl::operator-=(const Delay &delay) -{ - mean_ -= delay.mean_; - sigma2_ += delay.sigma2_; -} - -//////////////////////////////////////////////////////////////// - -Delay -makeDelay(float delay, - float sigma, - float) -{ - return Delay(delay, square(sigma)); -} - -Delay -makeDelay2(float delay, - float sigma2, - float ) -{ - return Delay(delay, sigma2); -} - -float -delayAsFloat(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta) -{ - if (sta->variables()->pocvEnabled()) { - if (early_late == EarlyLate::early()) - return delay.mean() - delay.sigma() * sta->sigmaFactor(); - else if (early_late == EarlyLate::late()) - return delay.mean() + delay.sigma() * sta->sigmaFactor(); - else - sta->report()->critical(1020, "unknown early/late value."); - } - return delay.mean(); -} - -float -delaySigma2(const Delay &delay, - const EarlyLate *) -{ - return delay.sigma2(); -} - -const char * -delayAsString(const Delay &delay, - const StaState *sta) -{ - return delayAsString(delay, sta, sta->units()->timeUnit()->digits()); -} - -const char * -delayAsString(const Delay &delay, - const StaState *sta, - int digits) -{ - const Unit *unit = sta->units()->timeUnit(); - if (sta->variables()->pocvEnabled()) { - float sigma = delay.sigma(); - return stringPrintTmp("%s[%s]", - unit->asString(delay.mean(), digits), - unit->asString(sigma, digits)); - } - else - return unit->asString(delay.mean(), digits); -} - -const char * -delayAsString(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta, - int digits) -{ - float mean_sigma = delayAsFloat(delay, early_late, sta); - return sta->units()->timeUnit()->asString(mean_sigma, digits); -} - -bool -delayIsInitValue(const Delay &delay, - const MinMax *min_max) -{ - return fuzzyEqual(delay.mean(), min_max->initValue()) - && delay.sigma2() == 0.0; -} - -bool -delayZero(const Delay &delay) -{ - return fuzzyZero(delay.mean()) - && fuzzyZero(delay.sigma2()); -} - -bool -delayInf(const Delay &delay) -{ - return fuzzyInf(delay.mean()); -} - -bool -delayEqual(const Delay &delay1, - const Delay &delay2) -{ - return fuzzyEqual(delay1.mean(), delay2.mean()) - && fuzzyEqual(delay1.sigma2(), delay2.sigma2()); -} - -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const StaState *sta) -{ - return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), - delayAsFloat(delay2, EarlyLate::early(), sta)); -} - -bool -delayLess(const Delay &delay1, - float delay2, - const StaState *sta) -{ - return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), - delay2); -} - -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta) -{ - if (min_max == MinMax::max()) - return delayLess(delay1, delay2, sta); - else - return delayGreater(delay1, delay2, sta); -} - -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta) -{ - return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), - delayAsFloat(delay2, EarlyLate::early(), sta)); -} - -bool -delayLessEqual(const Delay &delay1, - float delay2, - const StaState *sta) -{ - return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), - delay2); -} - -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta) -{ - if (min_max == MinMax::max()) - return delayLessEqual(delay1, delay2, sta); - else - return delayGreaterEqual(delay1, delay2, sta); -} - -bool -delayGreater(const Delay &delay1, - const Delay &delay2, - const StaState *sta) - -{ - return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), - delayAsFloat(delay2, EarlyLate::late(), sta)); -} - -bool -delayGreater(const Delay &delay1, - float delay2, - const StaState *sta) -{ - return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), - delay2); -} - -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta) -{ - return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), - delayAsFloat(delay2, EarlyLate::late(), sta)); -} - -bool -delayGreaterEqual(const Delay &delay1, - float delay2, - const StaState *sta) -{ - return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), - delay2); -} - -bool -delayGreater(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta) -{ - if (min_max == MinMax::max()) - return delayGreater(delay1, delay2, sta); - else - return delayLess(delay1, delay2, sta); -} - -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta) -{ - if (min_max == MinMax::max()) - return delayGreaterEqual(delay1, delay2, sta); - else - return delayLessEqual(delay1, delay2, sta); -} - -Delay -delayRemove(const Delay &delay1, - const Delay &delay2) -{ - return Delay(delay1.mean() - delay2.mean(), - delay1.sigma2() - delay2.sigma2()); -} - -float -delayRatio(const Delay &delay1, - const Delay &delay2) -{ - return delay1.mean() / delay2.mean(); -} - -Delay -operator+(float delay1, - const Delay &delay2) -{ - return Delay(delay1 + delay2.mean(), - delay2.sigma2()); -} - -Delay -operator/(float delay1, - const Delay &delay2) -{ - return Delay(delay1 / delay2.mean(), - delay2.sigma2()); -} - -Delay -operator*(const Delay &delay1, - float delay2) -{ - return Delay(delay1.mean() * delay2, - delay1.sigma2() * delay2 * delay2); -} - -} // namespace - -#endif // (SSTA == 1) diff --git a/graph/DelayNormal2.cc b/graph/DelayNormal2.cc deleted file mode 100644 index 9d445559d..000000000 --- a/graph/DelayNormal2.cc +++ /dev/null @@ -1,517 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#include "Delay.hh" - -#include // sqrt - -#include "StaConfig.hh" -#include "Error.hh" -#include "StringUtil.hh" -#include "Fuzzy.hh" -#include "Units.hh" -#include "StaState.hh" - -// SSTA compilation. -#if (SSTA == 2) - -namespace sta { - -inline float -square(float x) -{ - return x * x; -} - -static Delay delay_init_values[MinMax::index_count]; - -void -initDelayConstants() -{ - delay_init_values[MinMax::minIndex()] = MinMax::min()->initValue(); - delay_init_values[MinMax::maxIndex()] = MinMax::max()->initValue(); -} - -const Delay & -delayInitValue(const MinMax *min_max) -{ - return delay_init_values[min_max->index()]; -} - -Delay::Delay() : - mean_(0.0), - sigma2_{0.0, 0.0} -{ -} - -Delay::Delay(const Delay &delay) : - mean_(delay.mean_) -{ - sigma2_[EarlyLate::earlyIndex()] = delay.sigma2_[EarlyLate::earlyIndex()]; - sigma2_[EarlyLate::lateIndex()] = delay.sigma2_[EarlyLate::lateIndex()]; -} - -Delay::Delay(const DelayDbl &delay) : - mean_(delay.mean_) -{ - sigma2_[EarlyLate::earlyIndex()] = delay.sigma2_[EarlyLate::earlyIndex()]; - sigma2_[EarlyLate::lateIndex()] = delay.sigma2_[EarlyLate::lateIndex()]; -} - -Delay::Delay(float mean) : - mean_(mean), - sigma2_{0.0, 0.0} -{ -} - -Delay::Delay(float mean, - float sigma2_early, - float sigma2_late) : - mean_(mean), - sigma2_{sigma2_early, sigma2_late} -{ -} - -float -Delay::sigma(const EarlyLate *early_late) const -{ - float sigma = sigma2_[early_late->index()]; - if (sigma < 0.0) - // Sigma is negative for crpr to offset sigmas in the common - // clock path. - return -sqrt(-sigma); - else - return sqrt(sigma); -} - -float -Delay::sigma2(const EarlyLate *early_late) const -{ - return sigma2_[early_late->index()]; -} - -float -Delay::sigma2Early() const -{ - return sigma2_[early_index]; -} - -float -Delay::sigma2Late() const -{ - return sigma2_[late_index]; -} - -void -Delay::operator=(const Delay &delay) -{ - mean_ = delay.mean_; - sigma2_[early_index] = delay.sigma2_[early_index]; - sigma2_[late_index] = delay.sigma2_[late_index]; -} - -void -Delay::operator=(float delay) -{ - mean_ = delay; - sigma2_[early_index] = 0.0; - sigma2_[late_index] = 0.0; -} - -void -Delay::operator+=(const Delay &delay) -{ - mean_ += delay.mean_; - sigma2_[early_index] += delay.sigma2_[early_index]; - sigma2_[late_index] += delay.sigma2_[late_index]; -} - -void -Delay::operator+=(float delay) -{ - mean_ += delay; -} - -Delay -Delay::operator+(const Delay &delay) const -{ - return Delay(mean_ + delay.mean_, - sigma2_[early_index] + delay.sigma2_[early_index], - sigma2_[late_index] + delay.sigma2_[late_index]); -} - -Delay -Delay::operator+(float delay) const -{ - return Delay(mean_ + delay, sigma2_[early_index], sigma2_[late_index]); -} - -Delay -Delay::operator-(const Delay &delay) const -{ - return Delay(mean_ - delay.mean_, - sigma2_[early_index] + delay.sigma2_[late_index], - sigma2_[late_index] + delay.sigma2_[early_index]); -} - -Delay -Delay::operator-(float delay) const -{ - return Delay(mean_ - delay, sigma2_[early_index], sigma2_[late_index]); -} - -Delay -Delay::operator-() const -{ - return Delay(-mean_, sigma2_[late_index], sigma2_[early_index]); -} - -void -Delay::operator-=(float delay) -{ - mean_ -= delay; -} - -void -Delay::operator-=(const Delay &delay) -{ - mean_ -= delay.mean_; - sigma2_[early_index] += delay.sigma2_[early_index]; - sigma2_[late_index] += delay.sigma2_[late_index]; -} - -bool -Delay::operator==(const Delay &delay) const -{ - return mean_ == delay.mean_ - && sigma2_[early_index] == delay.sigma2_[late_index] - && sigma2_[late_index] == delay.sigma2_[early_index]; -} - -//////////////////////////////////////////////////////////////// - -DelayDbl::DelayDbl() : - mean_(0.0), - sigma2_{0.0, 0.0} -{ -} - -void -DelayDbl::operator=(float delay) -{ - mean_ = delay; - sigma2_[early_index] = 0.0; - sigma2_[late_index] = 0.0; -} - -void -DelayDbl::operator+=(const Delay &delay) -{ - mean_ += delay.mean_; - sigma2_[early_index] += delay.sigma2_[early_index]; - sigma2_[late_index] += delay.sigma2_[late_index]; -} - -void -DelayDbl::operator-=(const Delay &delay) -{ - mean_ -= delay.mean_; - sigma2_[early_index] += delay.sigma2_[early_index]; - sigma2_[late_index] += delay.sigma2_[late_index]; -} - -//////////////////////////////////////////////////////////////// - -Delay -makeDelay(float delay, - float sigma_early, - float sigma_late) -{ - return Delay(delay, square(sigma_early), square(sigma_late)); -} - -Delay -makeDelay2(float delay, - float sigma2_early, - float sigma2_late) -{ - return Delay(delay, sigma2_early, sigma2_late); -} - -bool -delayIsInitValue(const Delay &delay, - const MinMax *min_max) -{ - return fuzzyEqual(delay.mean(), min_max->initValue()) - && fuzzyZero(delay.sigma2Early()) - && fuzzyZero(delay.sigma2Late()); -} - -bool -delayZero(const Delay &delay) -{ - return fuzzyZero(delay.mean()) - && fuzzyZero(delay.sigma2Early()) - && fuzzyZero(delay.sigma2Late()); -} - -bool -delayInf(const Delay &delay) -{ - return fuzzyInf(delay.mean()); -} - -bool -delayEqual(const Delay &delay1, - const Delay &delay2) -{ - return fuzzyEqual(delay1.mean(), delay2.mean()) - && fuzzyEqual(delay1.sigma2Early(), delay2.sigma2Early()) - && fuzzyEqual(delay1.sigma2Late(), delay2.sigma2Late()); -} - -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const StaState *sta) -{ - return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), - delayAsFloat(delay2, EarlyLate::early(), sta)); -} - -bool -delayLess(const Delay &delay1, - float delay2, - const StaState *sta) -{ - return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), - delay2); -} - -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta) -{ - if (min_max == MinMax::max()) - return delayLess(delay1, delay2, sta); - else - return delayGreater(delay1, delay2, sta); -} - -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta) -{ - return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), - delayAsFloat(delay2, EarlyLate::early(), sta)); -} - -bool -delayLessEqual(const Delay &delay1, - float delay2, - const StaState *sta) -{ - return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), - delay2); -} - -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta) -{ - if (min_max == MinMax::max()) - return delayLessEqual(delay1, delay2, sta); - else - return delayGreaterEqual(delay1, delay2, sta); -} - -bool -delayGreater(const Delay &delay1, - const Delay &delay2, - const StaState *sta) -{ - return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), - delayAsFloat(delay2, EarlyLate::late(), sta)); -} - -bool -delayGreater(const Delay &delay1, - float delay2, - const StaState *sta) -{ - return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), - delayAsFloat(delay2, EarlyLate::late(), sta)); -} - -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta) -{ - return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), - delayAsFloat(delay2, EarlyLate::late(), sta)); -} - -bool -delayGreaterEqual(const Delay &delay1, - float delay2, - const StaState *sta) -{ - return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), - delay2); -} - -bool -delayGreater(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta) -{ - if (min_max == MinMax::max()) - return delayGreater(delay1, delay2, sta); - else - return delayLess(delay1, delay2, sta); -} - -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta) -{ - if (min_max == MinMax::max()) - return delayGreaterEqual(delay1, delay2, sta); - else - return delayLessEqual(delay1, delay2, sta); -} - -float -delayAsFloat(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta) -{ - if (sta->pocvEnabled()) { - if (early_late == EarlyLate::early()) - return delay.mean() - delay.sigma(early_late) * sta->sigmaFactor(); - else if (early_late == EarlyLate::late()) - return delay.mean() + delay.sigma(early_late) * sta->sigmaFactor(); - else - sta->report()->critical(1030, "unknown early/late value."); - } - return delay.mean(); -} - -float -delaySigma2(const Delay &delay, - const EarlyLate *early_late) -{ - return delay.sigma2(early_late); -} - -const char * -delayAsString(const Delay &delay, - const StaState *sta) -{ - return delayAsString(delay, sta, sta->units()->timeUnit()->digits()); -} - -const char * -delayAsString(const Delay &delay, - const StaState *sta, - int digits) -{ - const Unit *unit = sta->units()->timeUnit(); - if (sta->pocvEnabled()) { - float sigma_early = delay.sigma(EarlyLate::early()); - float sigma_late = delay.sigma(EarlyLate::late()); - return stringPrintTmp("%s[%s:%s]", - unit->asString(delay.mean(), digits), - unit->asString(sigma_early, digits), - unit->asString(sigma_late, digits)); - } - else - return unit->asString(delay.mean(), digits); -} - -const char * -delayAsString(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta, - int digits) -{ - float mean_sigma = delayAsFloat(delay, early_late, sta); - return sta->units()->timeUnit()->asString(mean_sigma, digits); -} - -Delay -delayRemove(const Delay &delay1, - const Delay &delay2) -{ - return Delay(delay1.mean() - delay2.mean(), - delay1.sigma2Early() - delay2.sigma2Early(), - delay1.sigma2Late() - delay2.sigma2Late()); -} - -float -delayRatio(const Delay &delay1, - const Delay &delay2) -{ - return delay1.mean() / delay2.mean(); -} - -Delay -operator+(float delay1, - const Delay &delay2) -{ - return Delay(delay1 + delay2.mean(), - delay2.sigma2Early(), - delay2.sigma2Late()); -} - -Delay -operator/(float delay1, - const Delay &delay2) -{ - return Delay(delay1 / delay2.mean(), - delay2.sigma2Early(), - delay2.sigma2Late()); -} - -Delay -operator*(const Delay &delay1, - float delay2) -{ - return Delay(delay1.mean() * delay2, - delay1.sigma2Early() * delay2 * delay2, - delay1.sigma2Late() * delay2 * delay2); -} - -} // namespace - -#endif // (SSTA == 2) diff --git a/graph/Graph.cc b/graph/Graph.cc index 713fa2e56..74996842e 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,23 +24,22 @@ #include "Graph.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" -#include "Stats.hh" +#include "FuncExpr.hh" +#include "Liberty.hh" #include "MinMax.hh" #include "Mutex.hh" -#include "Transition.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" -#include "Liberty.hh" -#include "PortDirection.hh" #include "Network.hh" -#include "DcalcAnalysisPt.hh" -#include "FuncExpr.hh" +#include "PortDirection.hh" +#include "Stats.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Transition.hh" +#include "Variables.hh" namespace sta { -using std::string; - //////////////////////////////////////////////////////////////// // // Graph @@ -48,15 +47,11 @@ using std::string; //////////////////////////////////////////////////////////////// Graph::Graph(StaState *sta, - int slew_rf_count, - DcalcAPIndex ap_count) : + DcalcAPIndex ap_count) : StaState(sta), - vertices_(nullptr), - edges_(nullptr), - slew_rf_count_(slew_rf_count), ap_count_(ap_count), - period_check_annotations_(nullptr), - reg_clk_vertices_(new VertexSet(graph_)) + period_check_annotations_(network_), + reg_clk_vertices_(makeVertexSet(this)) { // For the benifit of reg_clk_vertices_ that references graph_. graph_ = this; @@ -68,7 +63,6 @@ Graph::~Graph() delete edges_; vertices_->clear(); delete vertices_; - delete reg_clk_vertices_; removePeriodCheckAnnotations(); } @@ -105,12 +99,12 @@ class FindNetDrvrLoadCounts : public PinVisitor { public: FindNetDrvrLoadCounts(Pin *drvr_pin, - PinSet &visited_drvrs, - int &drvr_count, - int &bidirect_count, - int &load_count, - const Network *network); - virtual void operator()(const Pin *pin); + PinSet &visited_drvrs, + int &drvr_count, + int &bidirect_count, + int &load_count, + const Network *network); + void operator()(const Pin *pin) override; protected: Pin *drvr_pin_; @@ -122,11 +116,11 @@ class FindNetDrvrLoadCounts : public PinVisitor }; FindNetDrvrLoadCounts::FindNetDrvrLoadCounts(Pin *drvr_pin, - PinSet &visited_drvrs, - int &drvr_count, - int &bidirect_count, - int &load_count, - const Network *network) : + PinSet &visited_drvrs, + int &drvr_count, + int &bidirect_count, + int &load_count, + const Network *network) : drvr_pin_(drvr_pin), visited_drvrs_(visited_drvrs), drvr_count_(drvr_count), @@ -186,48 +180,48 @@ Graph::makePinInstanceEdges(const Pin *pin) void Graph::makePortInstanceEdges(const Instance *inst, - LibertyCell *cell, - LibertyPort *from_to_port) + LibertyCell *cell, + LibertyPort *from_to_port) { for (TimingArcSet *arc_set : cell->timingArcSets()) { LibertyPort *from_port = arc_set->from(); LibertyPort *to_port = arc_set->to(); if ((from_to_port == nullptr - || from_port == from_to_port - || to_port == from_to_port) - && from_port) { + || from_port == from_to_port + || to_port == from_to_port) + && from_port) { Pin *from_pin = network_->findPin(inst, from_port); Pin *to_pin = network_->findPin(inst, to_port); if (from_pin && to_pin) { - Vertex *from_vertex, *from_bidirect_drvr_vertex; - Vertex *to_vertex, *to_bidirect_drvr_vertex; - pinVertices(from_pin, from_vertex, from_bidirect_drvr_vertex); - pinVertices(to_pin, to_vertex, to_bidirect_drvr_vertex); - // From pin and/or to pin can be bidirect. - // For combinational arcs edge is to driver. - // For timing checks edge is to load. - // Vertices can be missing from the graph if the pins - // are power or ground. - if (from_vertex) { + Vertex *from_vertex, *from_bidirect_drvr_vertex; + Vertex *to_vertex, *to_bidirect_drvr_vertex; + pinVertices(from_pin, from_vertex, from_bidirect_drvr_vertex); + pinVertices(to_pin, to_vertex, to_bidirect_drvr_vertex); + // From pin and/or to pin can be bidirect. + // For combinational arcs edge is to driver. + // For timing checks edge is to load. + // Vertices can be missing from the graph if the pins + // are power or ground. + if (from_vertex) { const TimingRole *role = arc_set->role(); - bool is_check = role->isTimingCheckBetween(); - if (to_bidirect_drvr_vertex && !is_check) - makeEdge(from_vertex, to_bidirect_drvr_vertex, arc_set); - else if (to_vertex) { - makeEdge(from_vertex, to_vertex, arc_set); - if (is_check) { - to_vertex->setHasChecks(true); - from_vertex->setIsCheckClk(true); - } - } - if (from_bidirect_drvr_vertex && to_vertex) { - // Internal path from bidirect output back into the - // instance. - Edge *edge = makeEdge(from_bidirect_drvr_vertex, to_vertex, - arc_set); - edge->setIsBidirectInstPath(true); - } - } + bool is_check = role->isTimingCheckBetween(); + if (to_bidirect_drvr_vertex && !is_check) + makeEdge(from_vertex, to_bidirect_drvr_vertex, arc_set); + else if (to_vertex) { + makeEdge(from_vertex, to_vertex, arc_set); + if (is_check) { + to_vertex->setHasChecks(true); + from_vertex->setIsCheckClk(true); + } + } + if (from_bidirect_drvr_vertex && to_vertex) { + // Internal path from bidirect output back into the + // instance. + Edge *edge = makeEdge(from_bidirect_drvr_vertex, to_vertex, + arc_set); + edge->setIsBidirectInstPath(true); + } + } } } } @@ -248,13 +242,13 @@ Graph::makeWireEdges() void Graph::makeInstDrvrWireEdges(const Instance *inst, - PinSet &visited_drvrs) + PinSet &visited_drvrs) { InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); if (network_->isDriver(pin) - && !visited_drvrs.hasKey(pin)) + && !visited_drvrs.contains(pin)) makeWireEdgesFromPin(pin, visited_drvrs); } delete pin_iter; @@ -276,7 +270,7 @@ Graph::makeWireEdgesFromPin(const Pin *drvr_pin) void Graph::makeWireEdgesFromPin(const Pin *drvr_pin, - PinSet &visited_drvrs) + PinSet &visited_drvrs) { // Find all drivers and loads on the net to avoid N*M run time // for large fanin/fanout nets. @@ -287,7 +281,7 @@ Graph::makeWireEdgesFromPin(const Pin *drvr_pin, if (isIsolatedNet(drvrs, loads)) { for (auto drvr_pin : drvrs) { visited_drvrs.insert(drvr_pin); - debugPrint(debug_, "graph", 1, "ignoring isolated driver %s", + debugPrint(debug_, "graph", 1, "ignoring isolated driver {}", network_->pathName(drvr_pin)); } return; @@ -296,7 +290,7 @@ Graph::makeWireEdgesFromPin(const Pin *drvr_pin, for (auto drvr_pin : drvrs) { for (auto load_pin : loads) { if (drvr_pin != load_pin) - makeWireEdge(drvr_pin, load_pin); + makeWireEdge(drvr_pin, load_pin); } } } @@ -337,7 +331,7 @@ Graph::makeWireEdgesToPin(const Pin *to_pin) if (drvrs) { for (auto drvr : *drvrs) { if (drvr != to_pin) - makeWireEdge(drvr, to_pin); + makeWireEdge(drvr, to_pin); } } } @@ -346,11 +340,10 @@ class MakeEdgesThruHierPin : public HierPinThruVisitor { public: MakeEdgesThruHierPin(Graph *graph); + void visit(const Pin *drvr, + const Pin *load) override; private: - virtual void visit(const Pin *drvr, - const Pin *load); - Graph *graph_; }; @@ -362,7 +355,7 @@ MakeEdgesThruHierPin::MakeEdgesThruHierPin(Graph *graph) : void MakeEdgesThruHierPin::visit(const Pin *drvr, - const Pin *load) + const Pin *load) { graph_->makeWireEdge(drvr, load); } @@ -376,7 +369,7 @@ Graph::makeWireEdgesThruPin(const Pin *hpin) void Graph::makeWireEdge(const Pin *from_pin, - const Pin *to_pin) + const Pin *to_pin) { TimingArcSet *arc_set = TimingArcSet::wireTimingArcSet(); Vertex *from_vertex, *from_bidirect_drvr_vertex; @@ -391,6 +384,13 @@ Graph::makeWireEdge(const Pin *from_pin, } } +void +Graph::makeSceneAfter() +{ + ap_count_ = dcalcAnalysisPtCount(); + initSlews(); +} + //////////////////////////////////////////////////////////////// Vertex * @@ -414,8 +414,8 @@ Graph::makePinVertices(Pin *pin) void Graph::makePinVertices(Pin *pin, - Vertex *&vertex, - Vertex *&bidir_drvr_vertex) + Vertex *&vertex, + Vertex *&bidir_drvr_vertex) { PortDirection *dir = network_->direction(pin); if (!dir->isPowerGround()) { @@ -433,26 +433,26 @@ Graph::makePinVertices(Pin *pin, Vertex * Graph::makeVertex(Pin *pin, - bool is_bidirect_drvr, - bool is_reg_clk) + bool is_bidirect_drvr, + bool is_reg_clk) { Vertex *vertex = vertices_->make(); vertex->init(pin, is_bidirect_drvr, is_reg_clk); initSlews(vertex); if (is_reg_clk) - reg_clk_vertices_->insert(vertex); + reg_clk_vertices_.insert(vertex); return vertex; } void Graph::pinVertices(const Pin *pin, - // Return values. - Vertex *&vertex, - Vertex *&bidirect_drvr_vertex) const + // Return values. + Vertex *&vertex, + Vertex *&bidirect_drvr_vertex) const { vertex = Graph::vertex(network_->vertexId(pin)); if (network_->direction(pin)->isBidirect()) - bidirect_drvr_vertex = pin_bidirect_drvr_vertex_map_.findKey(pin); + bidirect_drvr_vertex = findKey(pin_bidirect_drvr_vertex_map_, pin); else bidirect_drvr_vertex = nullptr; } @@ -461,7 +461,7 @@ Vertex * Graph::pinDrvrVertex(const Pin *pin) const { if (network_->direction(pin)->isBidirect()) - return pin_bidirect_drvr_vertex_map_.findKey(pin); + return findKey(pin_bidirect_drvr_vertex_map_, pin); else return Graph::vertex(network_->vertexId(pin)); } @@ -476,11 +476,11 @@ void Graph::deleteVertex(Vertex *vertex) { if (vertex->isRegClk()) - reg_clk_vertices_->erase(vertex); + reg_clk_vertices_.erase(vertex); Pin *pin = vertex->pin_; if (vertex->isBidirectDriver()) pin_bidirect_drvr_vertex_map_.erase(pin_bidirect_drvr_vertex_map_ - .find(pin)); + .find(pin)); else network_->setVertexId(pin, vertex_id_null); // Delete edges to vertex. @@ -513,7 +513,7 @@ Graph::hasFaninOne(Vertex *vertex) const void Graph::deleteInEdge(Vertex *vertex, - Edge *edge) + Edge *edge) { EdgeId edge_id = id(edge); EdgeId prev = 0; @@ -529,7 +529,7 @@ Graph::deleteInEdge(Vertex *vertex, void Graph::deleteOutEdge(Vertex *vertex, - Edge *edge) + Edge *edge) { EdgeId next = edge->vertex_out_next_; EdgeId prev = edge->vertex_out_prev_; @@ -574,46 +574,32 @@ Graph::gateEdgeArc(const Pin *in_pin, //////////////////////////////////////////////////////////////// -Path * -Graph::makePaths(Vertex *vertex, - uint32_t count) -{ - Path *paths = new Path[count]; - vertex->setPaths(paths); - return paths; -} - -Path * -Graph::paths(const Vertex *vertex) const -{ - return vertex->paths(); -} - -void -Graph::deletePaths(Vertex *vertex) -{ - vertex->setPaths(nullptr); - vertex->tag_group_index_ = tag_group_index_max; -} - -//////////////////////////////////////////////////////////////// - -const Slew & +Slew Graph::slew(const Vertex *vertex, const RiseFall *rf, DcalcAPIndex ap_index) { - if (slew_rf_count_) { - const Slew *slews = vertex->slews(); - size_t slew_index = (slew_rf_count_ == 1) - ? ap_index - : ap_index*slew_rf_count_+rf->index(); + size_t slew_index = ap_index * RiseFall::index_count + rf->index(); + const float *slews_flt = vertex->slewsFloat(); + if (variables_->pocvEnabled()) { + const Slew *slews = reinterpret_cast(slews_flt); return slews[slew_index]; } - else { - static Slew slew(0.0); - return slew; + else + return slews_flt[slew_index]; +} + +Slew +Graph::slew(const Vertex *vertex, + size_t index) +{ + const float *slews_flt = vertex->slewsFloat(); + if (variables_->pocvEnabled()) { + const Slew *slews = reinterpret_cast(slews_flt); + return slews[index]; } + else + return slews_flt[index]; } void @@ -622,18 +608,15 @@ Graph::setSlew(Vertex *vertex, DcalcAPIndex ap_index, const Slew &slew) { - if (slew_rf_count_) { + size_t slew_index = ap_index * RiseFall::index_count + rf->index(); + if (variables_->pocvEnabled()) { Slew *slews = vertex->slews(); - if (slews == nullptr) { - int slew_count = slew_rf_count_ * ap_count_; - slews = new Slew[slew_count]; - vertex->setSlews(slews); - } - size_t slew_index = (slew_rf_count_ == 1) - ? ap_index - : ap_index*slew_rf_count_+rf->index(); slews[slew_index] = slew; } + else { + float *slews_flt = vertex->slewsFloat(); + slews_flt[slew_index] = slew.mean(); + } } //////////////////////////////////////////////////////////////// @@ -652,8 +635,8 @@ Graph::id(const Edge *edge) const Edge * Graph::makeEdge(Vertex *from, - Vertex *to, - TimingArcSet *arc_set) + Vertex *to, + TimingArcSet *arc_set) { Edge *edge = edges_->make(); edge->init(id(from), id(to), arc_set); @@ -687,69 +670,93 @@ Graph::deleteEdge(Edge *edge) ArcDelay Graph::arcDelay(const Edge *edge, - const TimingArc *arc, - DcalcAPIndex ap_index) const + const TimingArc *arc, + DcalcAPIndex ap_index) const { - ArcDelay *delays = edge->arcDelays(); size_t index = arc->index() * ap_count_ + ap_index; - return delays[index]; + if (variables_->pocvEnabled()) { + const ArcDelay *delays = reinterpret_cast(edge->arcDelays()); + return delays[index]; + } + else { + const float *delays = edge->arcDelays(); + return delays[index]; + } } void Graph::setArcDelay(Edge *edge, - const TimingArc *arc, - DcalcAPIndex ap_index, - ArcDelay delay) + const TimingArc *arc, + DcalcAPIndex ap_index, + const ArcDelay &delay) { - ArcDelay *arc_delays = edge->arcDelays(); size_t index = arc->index() * ap_count_ + ap_index; - arc_delays[index] = delay; + if (variables_->pocvEnabled()) { + ArcDelay *delays = reinterpret_cast(edge->arcDelays()); + delays[index] = delay; + } + else { + float *delays = edge->arcDelays(); + delays[index] = delay.mean(); + } } -const ArcDelay & +ArcDelay Graph::wireArcDelay(const Edge *edge, - const RiseFall *rf, - DcalcAPIndex ap_index) + const RiseFall *rf, + DcalcAPIndex ap_index) { - ArcDelay *delays = edge->arcDelays(); size_t index = rf->index() * ap_count_ + ap_index; - return delays[index]; + if (variables_->pocvEnabled()) { + const ArcDelay *delays = reinterpret_cast(edge->arcDelays()); + return delays[index]; + } + else { + const float *delays = edge->arcDelays(); + return delays[index]; + } } void Graph::setWireArcDelay(Edge *edge, - const RiseFall *rf, - DcalcAPIndex ap_index, - const ArcDelay &delay) + const RiseFall *rf, + DcalcAPIndex ap_index, + const ArcDelay &delay) { - ArcDelay *delays = edge->arcDelays(); size_t index = rf->index() * ap_count_ + ap_index; - delays[index] = delay; + if (variables_->pocvEnabled()) { + ArcDelay *delays = reinterpret_cast(edge->arcDelays()); + delays[index] = delay; + } + else { + float *delays = edge->arcDelays(); + delays[index] = delay.mean(); + } } //////////////////////////////////////////////////////////////// bool Graph::arcDelayAnnotated(const Edge *edge, - const TimingArc *arc, - DcalcAPIndex ap_index) const + const TimingArc *arc, + DcalcAPIndex ap_index) const { return edge->arcDelayAnnotated(arc, ap_index, ap_count_); } void Graph::setArcDelayAnnotated(Edge *edge, - const TimingArc *arc, - DcalcAPIndex ap_index, - bool annotated) + const TimingArc *arc, + DcalcAPIndex ap_index, + bool annotated) { - return edge->setArcDelayAnnotated(arc, ap_index, ap_count_, annotated); + edge->setArcDelayAnnotated(arc, ap_index, ap_count_, annotated); } bool Graph::wireDelayAnnotated(const Edge *edge, - const RiseFall *rf, - DcalcAPIndex ap_index) const + const RiseFall *rf, + DcalcAPIndex ap_index) const { int arc_index = TimingArcSet::wireArcIndex(rf); TimingArc *arc = TimingArcSet::wireTimingArcSet()->findTimingArc(arc_index); @@ -758,13 +765,13 @@ Graph::wireDelayAnnotated(const Edge *edge, void Graph::setWireDelayAnnotated(Edge *edge, - const RiseFall *rf, - DcalcAPIndex ap_index, - bool annotated) + const RiseFall *rf, + DcalcAPIndex ap_index, + bool annotated) { int arc_index = TimingArcSet::wireArcIndex(rf); TimingArc *arc = TimingArcSet::wireTimingArcSet()->findTimingArc(arc_index); - return edge->setArcDelayAnnotated(arc, ap_index, ap_count_, annotated); + edge->setArcDelayAnnotated(arc, ap_index, ap_count_, annotated); } void @@ -808,16 +815,20 @@ void Graph::initSlews(Vertex *vertex) { size_t slew_count = slewCount(); - Slew *slews = new Slew[slew_count]; - vertex->setSlews(slews); - for (size_t i = 0; i < slew_count; i++) - slews[i] = 0.0; + if (variables_->pocvEnabled()) { + float *slews = reinterpret_cast(new Slew[slew_count]{}); + vertex->setSlews(slews); + } + else { + float *slews = new float[slew_count]{}; + vertex->setSlews(slews); + } } size_t Graph::slewCount() { - return slew_rf_count_ * ap_count_; + return RiseFall::index_count * ap_count_; } void @@ -825,23 +836,14 @@ Graph::initArcDelays(Edge *edge) { size_t arc_count = edge->timingArcSet()->arcCount(); size_t delay_count = arc_count * ap_count_; - ArcDelay *arc_delays = new ArcDelay[delay_count]; - edge->setArcDelays(arc_delays); - for (size_t i = 0; i < delay_count; i++) - arc_delays[i] = 0.0; -} - -bool -Graph::delayAnnotated(Edge *edge) -{ - TimingArcSet *arc_set = edge->timingArcSet(); - for (TimingArc *arc : arc_set->arcs()) { - for (DcalcAPIndex ap_index = 0; ap_index < ap_count_; ap_index++) { - if (!arcDelayAnnotated(edge, arc, ap_index)) - return false; - } + if (variables_->pocvEnabled()) { + float *delays = reinterpret_cast(new ArcDelay[delay_count]{}); + edge->setArcDelays(delays); + } + else { + float *delays = new float[delay_count]{}; + edge->setArcDelays(delays); } - return true; } //////////////////////////////////////////////////////////////// @@ -873,10 +875,10 @@ Graph::minPulseWidthArc(Vertex *vertex, void Graph::minPeriodArc(Vertex *vertex, - const RiseFall *rf, - // Return values. - Edge *&edge, - TimingArc *&arc) + const RiseFall *rf, + // Return values. + Edge *&edge, + TimingArc *&arc) { VertexOutEdgeIterator edge_iter(vertex, this); while (edge_iter.hasNext()) { @@ -899,36 +901,32 @@ Graph::minPeriodArc(Vertex *vertex, void Graph::periodCheckAnnotation(const Pin *pin, - DcalcAPIndex ap_index, - // Return values. - float &period, - bool &exists) + DcalcAPIndex ap_index, + // Return values. + float &period, + bool &exists) { exists = false; - if (period_check_annotations_) { - float *periods = period_check_annotations_->findKey(pin); - if (periods) { - period = periods[ap_index]; - if (period >= 0.0) - exists = true; - } + float *periods = findKey(period_check_annotations_, pin); + if (periods) { + period = periods[ap_index]; + if (period >= 0.0) + exists = true; } } void Graph::setPeriodCheckAnnotation(const Pin *pin, - DcalcAPIndex ap_index, - float period) + DcalcAPIndex ap_index, + float period) { - if (period_check_annotations_ == nullptr) - period_check_annotations_ = new PeriodCheckAnnotations(network_); - float *periods = period_check_annotations_->findKey(pin); + float *periods = findKey(period_check_annotations_, pin); if (periods == nullptr) { periods = new float[ap_count_]; // Use negative (illegal) period values to indicate unannotated checks. for (int i = 0; i < ap_count_; i++) periods[i] = -1; - (*period_check_annotations_)[pin] = periods; + period_check_annotations_[pin] = periods; } periods[ap_index] = period; } @@ -936,12 +934,9 @@ Graph::setPeriodCheckAnnotation(const Pin *pin, void Graph::removePeriodCheckAnnotations() { - if (period_check_annotations_) { - for (const auto [pin, periods] : *period_check_annotations_) - delete [] periods; - delete period_check_annotations_; - period_check_annotations_ = nullptr; - } + for (auto& [pin, periods] : period_check_annotations_) + delete [] periods; + period_check_annotations_.clear(); } void @@ -974,8 +969,8 @@ Vertex::Vertex() void Vertex::init(Pin *pin, - bool is_bidirect_drvr, - bool is_reg_clk) + bool is_bidirect_drvr, + bool is_reg_clk) { pin_ = pin; is_reg_clk_ = is_reg_clk; @@ -986,16 +981,13 @@ Vertex::init(Pin *pin, paths_ = nullptr; tag_group_index_ = tag_group_index_max; slew_annotated_ = false; - sim_value_ = unsigned(LogicValue::unknown); - is_disabled_constraint_ = false; - is_gated_clk_enable_ = false; has_checks_ = false; is_check_clk_ = false; - is_constrained_ = false; has_downstream_clk_pin_ = false; level_ = 0; visited1_ = false; visited2_ = false; + has_sim_value_ = false; bfs_in_queue_ = 0; } @@ -1019,12 +1011,12 @@ Vertex::setObjectIdx(ObjectIdx idx) object_idx_ = idx; } -string +std::string Vertex::to_string(const StaState *sta) const { const Network *network = sta->sdcNetwork(); if (network->direction(pin_)->isBidirect()) { - string str = network->pathName(pin_); + std::string str(network->pathName(pin_)); str += ' '; str += is_bidirect_drvr_ ? "driver" : "load"; return str; @@ -1033,11 +1025,10 @@ Vertex::to_string(const StaState *sta) const return network->pathName(pin_); } -const char * +std::string Vertex::name(const Network *network) const { - string name = to_string(network); - return makeTmpString(name); + return to_string(network); } bool @@ -1046,15 +1037,15 @@ Vertex::isDriver(const Network *network) const PortDirection *dir = network->direction(pin_); bool top_level_port = network->isTopLevelPort(pin_); return ((top_level_port - && (dir->isInput() - || (dir->isBidirect() - && is_bidirect_drvr_))) - || (!top_level_port - && (dir->isOutput() - || dir->isTristate() - || (dir->isBidirect() - && is_bidirect_drvr_) - || dir->isInternal()))); + && (dir->isInput() + || (dir->isBidirect() + && is_bidirect_drvr_))) + || (!top_level_port + && (dir->isOutput() + || dir->isTristate() + || (dir->isBidirect() + && is_bidirect_drvr_) + || dir->isInternal()))); } void @@ -1076,17 +1067,23 @@ Vertex::setVisited2(bool visited) } void -Vertex::setSlews(Slew *slews) +Vertex::setSlews(float *slews) { delete [] slews_; slews_ = slews; } +void +Vertex::setHasSimValue(bool has_sim) +{ + has_sim_value_ = has_sim; +} + bool Vertex::slewAnnotated(const RiseFall *rf, - const MinMax *min_max) const + const MinMax *min_max) const { - int index = min_max->index() * transitionCount() + rf->index(); + int index = min_max->index() * RiseFall::index_count+ rf->index(); return ((1 << index) & slew_annotated_) != 0; } @@ -1098,14 +1095,14 @@ Vertex::slewAnnotated() const void Vertex::setSlewAnnotated(bool annotated, - const RiseFall *rf, - DcalcAPIndex ap_index) + const RiseFall *rf, + DcalcAPIndex ap_index) { // Track rise/fall/min/max annotations separately, but after that // only rise/fall. if (ap_index > 1) ap_index = 0; - int index = ap_index * transitionCount() + rf->index(); + int index = ap_index * RiseFall::index_count + rf->index(); if (annotated) slew_annotated_ |= (1 << index); else @@ -1130,37 +1127,28 @@ Vertex::setTagGroupIndex(TagGroupIndex tag_index) tag_group_index_ = tag_index; } -void -Vertex::setPaths(Path *paths) +Path * +Vertex::makePaths(uint32_t count) { delete [] paths_; + Path *paths = new Path[count]; paths_ = paths; -} - -LogicValue -Vertex::simValue() const -{ - return static_cast(sim_value_); + return paths; } void -Vertex::setSimValue(LogicValue value) -{ - sim_value_ = unsigned(value); -} - -bool -Vertex::isConstant() const +Vertex::setPaths(Path *paths) { - LogicValue value = static_cast(sim_value_); - return value == LogicValue::zero - || value == LogicValue::one; + delete [] paths_; + paths_ = paths; } void -Vertex::setIsDisabledConstraint(bool disabled) +Vertex::deletePaths() { - is_disabled_constraint_ = disabled; + delete [] paths_; + paths_ = nullptr; + tag_group_index_ = tag_group_index_max; } bool @@ -1187,18 +1175,6 @@ Vertex::setIsCheckClk(bool is_check_clk) is_check_clk_ = is_check_clk; } -void -Vertex::setIsGatedClkEnable(bool enable) -{ - is_gated_clk_enable_ = enable; -} - -void -Vertex::setIsConstrained(bool constrained) -{ - is_constrained_ = constrained; -} - void Vertex::setHasDownstreamClkPin(bool has_clk_pin) { @@ -1208,17 +1184,17 @@ Vertex::setHasDownstreamClkPin(bool has_clk_pin) bool Vertex::bfsInQueue(BfsIndex index) const { - return (bfs_in_queue_ >> unsigned(index)) & 1; + return (bfs_in_queue_ >> static_cast(index)) & 1; } void Vertex::setBfsInQueue(BfsIndex index, - bool value) + bool value) { if (value) - bfs_in_queue_ |= 1 << int(index); + bfs_in_queue_ |= 1 << static_cast(index); else - bfs_in_queue_ &= ~(1 << int(index)); + bfs_in_queue_ &= ~(1 << static_cast(index)); } //////////////////////////////////////////////////////////////// @@ -1235,8 +1211,8 @@ Edge::Edge() void Edge::init(VertexId from, - VertexId to, - TimingArcSet *arc_set) + VertexId to, + TimingArcSet *arc_set) { from_ = from; to_ = to; @@ -1251,10 +1227,9 @@ Edge::init(VertexId from, arc_delay_annotated_is_bits_ = true; arc_delay_annotated_.bits_ = 0; delay_annotation_is_incremental_ = false; - sim_timing_sense_ = unsigned(TimingSense::unknown); - is_disabled_constraint_ = false; - is_disabled_cond_ = false; is_disabled_loop_ = false; + has_sim_sense_ = false; + has_disabled_cond_ = false; } Edge::~Edge() @@ -1279,13 +1254,15 @@ Edge::setObjectIdx(ObjectIdx idx) object_idx_ = idx; } -string +std::string Edge::to_string(const StaState *sta) const { const Graph *graph = sta->graph(); - string str = from(graph)->to_string(sta); + std::string str = from(graph)->to_string(sta); str += " -> "; str += to(graph)->to_string(sta); + str += " "; + str += role()->to_string(); FuncExpr *when = arc_set_->cond(); if (when) { str += " "; @@ -1301,10 +1278,10 @@ Edge::setTimingArcSet(TimingArcSet *set) } void -Edge::setArcDelays(ArcDelay *arc_delays) +Edge::setArcDelays(float *delays) { delete [] arc_delays_; - arc_delays_ = arc_delays; + arc_delays_ = delays; } bool @@ -1377,71 +1354,41 @@ Edge::isWire() const { return arc_set_->role()->isWire(); } - + TimingSense Edge::sense() const { return arc_set_->sense(); } - -TimingSense -Edge::simTimingSense() const -{ - return static_cast(sim_timing_sense_); -} - -void -Edge::setSimTimingSense(TimingSense sense) -{ - sim_timing_sense_ = unsigned(sense); -} - -bool -Edge::isDisabledConstraint() const -{ - const TimingRole *role = arc_set_->role(); - bool is_wire = role->isWire(); - return is_disabled_constraint_ - || arc_set_->isDisabledConstraint() - // set_disable_timing cell does not disable timing checks. - || (!(role->isTimingCheck() || is_wire) - && arc_set_->libertyCell()->isDisabledConstraint()) - || (!is_wire - && arc_set_->from()->isDisabledConstraint()) - || (!is_wire - && arc_set_->to()->isDisabledConstraint()); -} - - void -Edge::setIsDisabledConstraint(bool disabled) +Edge::setIsDisabledLoop(bool disabled) { - is_disabled_constraint_ = disabled; + is_disabled_loop_ = disabled; } void -Edge::setIsDisabledCond(bool disabled) +Edge::setIsBidirectInstPath(bool is_bidir) { - is_disabled_cond_ = disabled; + is_bidirect_inst_path_ = is_bidir; } void -Edge::setIsDisabledLoop(bool disabled) +Edge::setIsBidirectNetPath(bool is_bidir) { - is_disabled_loop_ = disabled; + is_bidirect_net_path_ = is_bidir; } void -Edge::setIsBidirectInstPath(bool is_bidir) +Edge::setHasSimSense(bool has_sense) { - is_bidirect_inst_path_ = is_bidir; + has_sim_sense_ = has_sense; } void -Edge::setIsBidirectNetPath(bool is_bidir) +Edge::setHasDisabledCond(bool has_disabled) { - is_bidirect_net_path_ = is_bidir; + has_disabled_cond_ = has_disabled; } //////////////////////////////////////////////////////////////// @@ -1450,10 +1397,7 @@ VertexIterator::VertexIterator(Graph *graph) : graph_(graph), network_(graph->network()), top_inst_(network_->topInstance()), - inst_iter_(network_->leafInstanceIterator()), - pin_iter_(nullptr), - vertex_(nullptr), - bidir_vertex_(nullptr) + inst_iter_(network_->leafInstanceIterator()) { if (inst_iter_) findNext(); @@ -1483,7 +1427,7 @@ VertexIterator::findNextPin() Pin *pin = pin_iter_->next(); vertex_ = graph_->vertex(network_->vertexId(pin)); bidir_vertex_ = network_->direction(pin)->isBidirect() - ? graph_->pin_bidirect_drvr_vertex_map_.findKey(pin) + ? findKey(graph_->pin_bidirect_drvr_vertex_map_, pin) : nullptr; if (vertex_ || bidir_vertex_) return true; @@ -1518,14 +1462,14 @@ VertexIterator::findNext() } VertexInEdgeIterator::VertexInEdgeIterator(Vertex *vertex, - const Graph *graph) : + const Graph *graph) : next_(graph->edge(vertex->in_edges_)), graph_(graph) { } VertexInEdgeIterator::VertexInEdgeIterator(VertexId vertex_id, - const Graph *graph) : + const Graph *graph) : next_(graph->edge(graph->vertex(vertex_id)->in_edges_)), graph_(graph) { @@ -1541,7 +1485,7 @@ VertexInEdgeIterator::next() } VertexOutEdgeIterator::VertexOutEdgeIterator(Vertex *vertex, - const Graph *graph) : + const Graph *graph) : next_(graph->edge(vertex->out_edges_)), graph_(graph) { @@ -1562,9 +1506,9 @@ class FindEdgesThruHierPinVisitor : public HierPinThruVisitor { public: FindEdgesThruHierPinVisitor(EdgeSet &edges, - Graph *graph); - virtual void visit(const Pin *drvr, - const Pin *load); + Graph *graph); + void visit(const Pin *drvr, + const Pin *load) override; protected: EdgeSet &edges_; @@ -1572,7 +1516,7 @@ class FindEdgesThruHierPinVisitor : public HierPinThruVisitor }; FindEdgesThruHierPinVisitor::FindEdgesThruHierPinVisitor(EdgeSet &edges, - Graph *graph) : + Graph *graph) : HierPinThruVisitor(), edges_(edges), graph_(graph) @@ -1581,7 +1525,7 @@ FindEdgesThruHierPinVisitor::FindEdgesThruHierPinVisitor(EdgeSet &edges, void FindEdgesThruHierPinVisitor::visit(const Pin *drvr, - const Pin *load) + const Pin *load) { Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr); Vertex *load_vertex = graph_->pinLoadVertex(load); @@ -1595,12 +1539,24 @@ FindEdgesThruHierPinVisitor::visit(const Pin *drvr, } EdgesThruHierPinIterator::EdgesThruHierPinIterator(const Pin *hpin, - Network *network, - Graph *graph) + Network *network, + Graph *graph) { FindEdgesThruHierPinVisitor visitor(edges_, graph); visitDrvrLoadsThruHierPin(hpin, network, &visitor); - edge_iter_.init(edges_); + edge_iter_ = edges_.begin(); +} + +bool +EdgesThruHierPinIterator::hasNext() +{ + return edge_iter_ != edges_.end(); +} + +Edge * +EdgesThruHierPinIterator::next() +{ + return *edge_iter_++; } //////////////////////////////////////////////////////////////// @@ -1617,9 +1573,5 @@ VertexIdLess::operator()(const Vertex *vertex1, return graph_->id(vertex1) < graph_->id(vertex2); } -VertexSet::VertexSet(Graph *&graph) : - Set(VertexIdLess(graph)) -{ -} -} // namespace +} // namespace sta diff --git a/graph/Graph.i b/graph/Graph.i index 032c056a6..d6fb17697 100644 --- a/graph/Graph.i +++ b/graph/Graph.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,18 +22,17 @@ // // This notice may not be removed or altered from any source distribution. -%module graph - %{ -#include "Graph.hh" +#include "Clock.hh" #include "FuncExpr.hh" -#include "TimingRole.hh" +#include "Graph.hh" #include "Liberty.hh" #include "Network.hh" -#include "Clock.hh" -#include "Corner.hh" +#include "Scene.hh" #include "Search.hh" +#include "Sdc.hh" #include "Sta.hh" +#include "TimingRole.hh" using namespace sta; @@ -92,22 +91,22 @@ vertex_iterator() void set_arc_delay(Edge *edge, - TimingArc *arc, - const Corner *corner, - const MinMaxAll *min_max, - float delay) + TimingArc *arc, + const Scene *scene, + const MinMaxAll *min_max, + float delay) { - Sta::sta()->setArcDelay(edge, arc, corner, min_max, delay); + Sta::sta()->setArcDelay(edge, arc, scene, min_max, delay); } void set_annotated_slew(Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - const RiseFallBoth *rf, - float slew) + const Scene *scene, + const MinMaxAll *min_max, + const RiseFallBoth *rf, + float slew) { - Sta::sta()->setAnnotatedSlew(vertex, corner, min_max, rf, slew); + Sta::sta()->setAnnotatedSlew(vertex, scene, min_max, rf, slew); } // Remove all delay and slew annotations. @@ -131,21 +130,24 @@ bool is_bidirect_driver() { return self->isBidirectDriver(); } int level() { return Sta::sta()->vertexLevel(self); } int tag_group_index() { return self->tagGroupIndex(); } -Slew -slew(const RiseFall *rf, +float +slew(const RiseFallBoth *rf, const MinMax *min_max) { Sta *sta = Sta::sta(); - return sta->vertexSlew(self, rf, min_max); + return delayAsFloat(sta->slew(self, rf, sta->scenes(), min_max), min_max, sta); } -Slew -slew_corner(const RiseFall *rf, - const Corner *corner, - const MinMax *min_max) +std::string +slew_scenes_string(const RiseFallBoth *rf, + const SceneSeq scenes, + const MinMax *min_max, + bool report_variance, + int digits) { Sta *sta = Sta::sta(); - return sta->vertexSlew(self, rf, corner, min_max); + Slew slew = sta->slew(self, rf, scenes, min_max); + return delayAsString(slew, min_max, report_variance, digits, sta); } VertexOutEdgeIterator * @@ -160,230 +162,116 @@ in_edge_iterator() return new VertexInEdgeIterator(self, Sta::sta()->graph()); } -FloatSeq -arrivals_clk(const RiseFall *rf, - Clock *clk, - const RiseFall *clk_rf) -{ - Sta *sta = Sta::sta(); - FloatSeq arrivals; - const ClockEdge *clk_edge = nullptr; - if (clk) - clk_edge = clk->edge(clk_rf); - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - arrivals.push_back(delayAsFloat(sta->vertexArrival(self, rf, clk_edge, - path_ap, nullptr))); - } - return arrivals; -} - -float -arrival(const MinMax *min_max) -{ - Sta *sta = Sta::sta(); - return delayAsFloat(sta->vertexArrival(self, min_max)); -} - -StringSeq -arrivals_clk_delays(const RiseFall *rf, - Clock *clk, - const RiseFall *clk_rf, - int digits) -{ - Sta *sta = Sta::sta(); - StringSeq arrivals; - const ClockEdge *clk_edge = nullptr; - if (clk) - clk_edge = clk->edge(clk_rf); - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - arrivals.push_back(delayAsString(sta->vertexArrival(self, rf, clk_edge, - path_ap, nullptr), - sta, digits)); - } - return arrivals; -} - -FloatSeq -requireds_clk(const RiseFall *rf, - Clock *clk, - const RiseFall *clk_rf) -{ - Sta *sta = Sta::sta(); - FloatSeq reqs; - const ClockEdge *clk_edge = nullptr; - if (clk) - clk_edge = clk->edge(clk_rf); - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - reqs.push_back(delayAsFloat(sta->vertexRequired(self, rf, clk_edge, - path_ap))); - } - return reqs; -} - -StringSeq -requireds_clk_delays(const RiseFall *rf, - Clock *clk, - const RiseFall *clk_rf, - int digits) -{ - Sta *sta = Sta::sta(); - StringSeq reqs; - const ClockEdge *clk_edge = nullptr; - if (clk) - clk_edge = clk->edge(clk_rf); - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - reqs.push_back(delayAsString(sta->vertexRequired(self, rf, clk_edge, path_ap), - sta, digits)); - } - return reqs; -} - -Slack -slack(MinMax *min_max) -{ - Sta *sta = Sta::sta(); - return sta->vertexSlack(self, min_max); -} - -FloatSeq -slacks(RiseFall *rf) -{ - Sta *sta = Sta::sta(); - FloatSeq slacks; - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - slacks.push_back(delayAsFloat(sta->vertexSlack(self, rf, path_ap))); - } - return slacks; -} - -// Slack with respect to a clock rise/fall edge. -FloatSeq -slacks_clk(const RiseFall *rf, - Clock *clk, - const RiseFall *clk_rf) -{ - Sta *sta = Sta::sta(); - FloatSeq slacks; - const ClockEdge *clk_edge = nullptr; - if (clk) - clk_edge = clk->edge(clk_rf); - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - slacks.push_back(delayAsFloat(sta->vertexSlack(self, rf, clk_edge, - path_ap))); - } - return slacks; -} - -StringSeq -slacks_clk_delays(const RiseFall *rf, - Clock *clk, - const RiseFall *clk_rf, - int digits) -{ - Sta *sta = Sta::sta(); - StringSeq slacks; - const ClockEdge *clk_edge = nullptr; - if (clk) - clk_edge = clk->edge(clk_rf); - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - slacks.push_back(delayAsString(sta->vertexSlack(self, rf, clk_edge, - path_ap), - sta, digits)); - } - return slacks; -} - VertexPathIterator * path_iterator(const RiseFall *rf, - const MinMax *min_max) -{ - return Sta::sta()->vertexPathIterator(self, rf, min_max); -} - -bool -has_downstream_clk_pin() + const MinMax *min_max) { - return self->hasDownstreamClkPin(); + return new VertexPathIterator(self, rf, min_max, Sta::sta()); } -bool -is_clock() -{ - Sta *sta = Sta::sta(); - Search *search = sta->search(); - return search->isClock(self); -} - -bool is_disabled_constraint() { return self->isDisabledConstraint(); } - } // Vertex methods %extend Edge { +std::string to_string() { return self->to_string(Sta::sta()); }; Vertex *from() { return self->from(Sta::sta()->graph()); } Vertex *to() { return self->to(Sta::sta()->graph()); } Pin *from_pin() { return self->from(Sta::sta()->graph())->pin(); } Pin *to_pin() { return self->to(Sta::sta()->graph())->pin(); } const TimingRole *role() { return self->role(); } -const char *sense() { return to_string(self->sense()); } +const char *sense() { return to_string(self->sense()).c_str(); } TimingArcSeq & timing_arcs() { return self->timingArcSet()->arcs(); } bool is_disabled_loop() { return Sta::sta()->isDisabledLoop(self); } -bool is_disabled_constraint() { return Sta::sta()->isDisabledConstraint(self);} -bool is_disabled_constant() { return Sta::sta()->isDisabledConstant(self); } + +bool is_disabled_constraint() +{ + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + return sta->isDisabledConstraint(self, sdc); +} + +bool is_disabled_constant() +{ + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + return sta->isDisabledConstant(self, mode); +} + bool is_disabled_cond_default() { return Sta::sta()->isDisabledCondDefault(self); } + PinSet -disabled_constant_pins() { return Sta::sta()->disabledConstantPins(self); } +disabled_constant_pins() +{ + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + return sta->disabledConstantPins(self, mode); +} + bool is_disabled_bidirect_inst_path() { return Sta::sta()->isDisabledBidirectInstPath(self); } -bool is_disabled_bidirect_net_path() -{ return Sta::sta()->isDisabledBidirectNetPath(self); } + bool is_disabled_preset_clear() { return Sta::sta()->isDisabledPresetClr(self); } + const char * -sim_timing_sense(){return to_string(Sta::sta()->simTimingSense(self));} +sim_timing_sense() +{ + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + return to_string(sta->simTimingSense(self, mode)).c_str(); +} FloatSeq arc_delays(TimingArc *arc) { Sta *sta = Sta::sta(); FloatSeq delays; - for (auto dcalc_ap : sta->corners()->dcalcAnalysisPts()) - delays.push_back(delayAsFloat(sta->arcDelay(self, arc, dcalc_ap))); + for (Scene *scene : sta->scenes()) { + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + delays.push_back(delayAsFloat(sta->arcDelay(self, arc, ap_index), min_max, sta)); + } + } return delays; } StringSeq arc_delay_strings(TimingArc *arc, - int digits) + bool report_variance, + int digits) { Sta *sta = Sta::sta(); StringSeq delays; - for (auto dcalc_ap : sta->corners()->dcalcAnalysisPts()) - delays.push_back(delayAsString(sta->arcDelay(self, arc, dcalc_ap), - sta, digits)); + for (Scene *scene : sta->scenes()) { + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + delays.push_back(delayAsString(sta->arcDelay(self, arc, ap_index), + min_max, report_variance, digits, sta)); + } + } return delays; } bool delay_annotated(TimingArc *arc, - const Corner *corner, - const MinMax *min_max) + const Scene *scene, + const MinMax *min_max) { - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - return Sta::sta()->arcDelayAnnotated(self, arc, dcalc_ap); + return Sta::sta()->arcDelayAnnotated(self, arc, scene, min_max); } float arc_delay(TimingArc *arc, - const Corner *corner, - const MinMax *min_max) + const Scene *scene, + const MinMax *min_max) { - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - return delayAsFloat(Sta::sta()->arcDelay(self, arc, dcalc_ap)); + Sta *sta = Sta::sta(); + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + return delayAsFloat(Sta::sta()->arcDelay(self, arc, ap_index), min_max, sta); } -string +std::string cond() { FuncExpr *cond = self->timingArcSet()->cond(); @@ -396,16 +284,16 @@ cond() const char * mode_name() { - return self->timingArcSet()->modeName(); + return self->timingArcSet()->modeName().c_str(); } const char * mode_value() { - return self->timingArcSet()->modeValue(); + return self->timingArcSet()->modeValue().c_str(); } -const char * +std::string latch_d_to_q_en() { if (self->role() == TimingRole::latchDtoQ()) { @@ -421,9 +309,7 @@ latch_d_to_q_en() const RiseFall *enable_rf; lib_cell->latchEnable(d_q_set, enable_port, enable_func, enable_rf); if (enable_port) - return stringPrintTmp("%s %s", - enable_port->name(), - enable_rf->to_string().c_str()); + return sta::format("{} {}", enable_port->name(), enable_rf->shortName()); } return ""; } diff --git a/graph/Graph.tcl b/graph/Graph.tcl index 331fcc731..3d04bf682 100644 --- a/graph/Graph.tcl +++ b/graph/Graph.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -26,51 +26,64 @@ namespace eval sta { -define_cmd_args "report_edges" {[-from from_pin] [-to to_pin]} +define_cmd_args "report_edges" {[-from from_pin] [-to to_pin]\ + [-digits digits] [-report_variance]} proc report_edges { args } { - parse_key_args "report_edges" args keys {-from -to} flags {} + global sta_report_default_digits + + parse_key_args "report_edges" args keys {-from -to -digits} flags {-report_variance} check_argc_eq0 "report_edges" $args + if [info exists keys(-digits)] { + set digits $keys(-digits) + check_positive_integer "-digits" $digits + } else { + set digits $sta_report_default_digits + } + + set report_variance [info exists flags(-report_variance)] + if { [info exists keys(-from)] && [info exists keys(-to)] } { set from_pin [get_port_pin_error "from_pin" $keys(-from)] set to_pin [get_port_pin_error "to_pin" $keys(-to)] foreach from_vertex [$from_pin vertices] { foreach to_vertex [$to_pin vertices] { - report_edges_between_ $from_vertex $to_vertex + report_edges_between_ $from_vertex $to_vertex $digits $report_variance } } } elseif [info exists keys(-from)] { set from_pin [get_port_pin_error "from_pin" $keys(-from)] foreach from_vertex [$from_pin vertices] { report_edges_ $from_vertex out_edge_iterator \ - vertex_port_name vertex_path_name + vertex_port_name vertex_path_name $digits $report_variance } } elseif [info exists keys(-to)] { set to_pin [get_port_pin_error "to_pin" $keys(-to)] foreach to_vertex [$to_pin vertices] { report_edges_ $to_vertex in_edge_iterator \ - vertex_path_name vertex_port_name + vertex_path_name vertex_port_name $digits $report_variance } } } -proc report_edges_between_ { from_vertex to_vertex } { +proc report_edges_between_ { from_vertex to_vertex digits report_variance } { set iter [$from_vertex out_edge_iterator] while {[$iter has_next]} { set edge [$iter next] if { [$edge to] == $to_vertex } { if { [$edge role] == "wire" } { - report_edge_ $edge vertex_path_name vertex_path_name + report_edge_ $edge vertex_path_name vertex_path_name $digits $report_variance } else { - report_edge_ $edge vertex_port_name vertex_port_name + report_edge_ $edge vertex_port_name vertex_port_name $digits $report_variance } } } $iter finish } -proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc } { +proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc \ + digits report_variance } { # First report edges internal to the device. set device_header 0 set iter [$vertex $iter_proc] @@ -78,13 +91,13 @@ proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc } { set edge [$iter next] if { [$edge role] != "wire" } { if { !$device_header } { - set pin [$vertex pin] - if { ![$pin is_top_level_port] } { - set inst [$pin instance] - } - set device_header 1 + set pin [$vertex pin] + if { ![$pin is_top_level_port] } { + set inst [$pin instance] + } + set device_header 1 } - report_edge_ $edge vertex_port_name vertex_port_name + report_edge_ $edge vertex_port_name vertex_port_name $digits $report_variance } } $iter finish @@ -94,15 +107,14 @@ proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc } { while {[$iter has_next]} { set edge [$iter next] if { [$edge role] == "wire" } { - report_edge_ $edge $wire_from_name_proc $wire_to_name_proc + report_edge_ $edge $wire_from_name_proc $wire_to_name_proc $digits $report_variance } } $iter finish } -proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc } { - global sta_report_default_digits - +proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc \ + digits report_variance } { set latch_enable [$edge latch_d_to_q_en] if { $latch_enable != "" } { set latch_enable " enable $latch_enable" @@ -125,7 +137,7 @@ proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc } { } foreach arc [$edge timing_arcs] { - set delays [$edge arc_delay_strings $arc $sta_report_default_digits] + set delays [$edge arc_delay_strings $arc $report_variance $digits] set delays_fmt [format_delays $delays] set disable_reason "" if { [timing_arc_disabled $edge $arc] } { @@ -135,18 +147,6 @@ proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc } { } } -# Separate list elements with colons. -proc format_times { values digits } { - set result "" - foreach value $values { - if { $result != "" } { - append result ":" - } - append result [format_time $value $digits] - } - return $result -} - # Separate delay list elements with colons. proc format_delays { values } { set result "" @@ -180,10 +180,6 @@ proc edge_disable_reason { edge } { if { $disables != "" } { append disables ", " } append disables "bidirect instance path" } - if [$edge is_disabled_bidirect_net_path] { - if { $disables != "" } { append disables ", " } - append disables "bidirect net path" - } if { [$edge is_disabled_preset_clear] } { if { $disables != "" } { append disables ", " } append disables "sta_preset_clear_arcs_enabled" @@ -252,7 +248,7 @@ proc_redirect report_disabled_edges { set to_port_name [get_name [$to_pin port]] set cond [$edge cond] if { $cond != "" } { - set when " when: $cond" + set when " when: $cond" } else { set when "" } @@ -295,28 +291,6 @@ proc edge_disable_reason_verbose { edge } { return $disables } -################################################################ - -define_cmd_args "report_slews" {[-corner corner] pin} - -proc report_slews { args } { - global sta_report_default_digits - - parse_key_args "report_slews" args keys {-corner} flags {} - check_argc_eq1 "report_slews" $args - - set corner [parse_corner_or_all keys] - set pin [get_port_pin_error "pin" [lindex $args 0]] - set digits $sta_report_default_digits - foreach vertex [$pin vertices] { - if { $corner == "NULL" } { - report_line "[vertex_path_name $vertex] [rise_short_name] [format_time [$vertex slew rise min] $digits]:[format_time [$vertex slew rise max] $digits] [fall_short_name] [format_time [$vertex slew fall min] $digits]:[format_time [$vertex slew fall max] $digits]" - } else { - report_line "[vertex_path_name $vertex] [rise_short_name] [format_time [$vertex slew_corner rise $corner min] $digits]:[format_time [$vertex slew_corner rise $corner max] $digits] [fall_short_name] [format_time [$vertex slew_corner fall $corner min] $digits]:[format_time [$vertex slew_corner fall $corner max] $digits]" - } - } -} - proc vertex_path_name { vertex } { set pin [$vertex pin] set pin_name [get_full_name $pin] @@ -348,8 +322,8 @@ proc hier_pins_crossed_by_edge { edge } { set to_pins [hier_pins_above [[$edge to] pin]] foreach p $to_pins { report_line [$p path_name] } while { [llength $from_pins] > 0 \ - && [llength $to_pins] > 0 \ - && [lindex $from_pins 0] == [lindex $to_pins 0] } { + && [llength $to_pins] > 0 \ + && [lindex $from_pins 0] == [lindex $to_pins 0] } { set from_pins [lrange $from_pins 1 end] set to_pins [lrange $to_pins 1 end] } @@ -367,9 +341,9 @@ proc hier_pins_above { pin } { while {[$parent_pin_iter has_next]} { set parent_pin [$parent_pin_iter next] if {[$parent_pin net] == $net} { - set pins_above [concat [list $parent_pin] $pins_above] - set found 1 - break + set pins_above [concat [list $parent_pin] $pins_above] + set found 1 + break } } $parent_pin_iter finish diff --git a/graph/GraphCmp.cc b/graph/GraphCmp.cc index 4001fb821..8ad693dfd 100644 --- a/graph/GraphCmp.cc +++ b/graph/GraphCmp.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,11 +22,13 @@ // // This notice may not be removed or altered from any source distribution. -#include "StringUtil.hh" +#include "GraphCmp.hh" + +#include "ContainerHelpers.hh" +#include "Graph.hh" #include "Network.hh" #include "NetworkCmp.hh" -#include "Graph.hh" -#include "GraphCmp.hh" +#include "StringUtil.hh" namespace sta { @@ -37,7 +39,7 @@ VertexNameLess::VertexNameLess(Network *network) : bool VertexNameLess::operator()(const Vertex *vertex1, - const Vertex *vertex2) + const Vertex *vertex2) { return network_->pathNameLess(vertex1->pin(), vertex2->pin()); } @@ -45,7 +47,7 @@ VertexNameLess::operator()(const Vertex *vertex1, //////////////////////////////////////////////////////////////// EdgeLess::EdgeLess(const Network *network, - Graph *&graph) : + Graph *&graph) : pin_less_(network), graph_(graph) { @@ -53,7 +55,7 @@ EdgeLess::EdgeLess(const Network *network, bool EdgeLess::operator()(const Edge *edge1, - const Edge *edge2) const + const Edge *edge2) const { const Pin *from1 = edge1->from(graph_)->pin(); const Pin *from2 = edge2->from(graph_)->pin(); @@ -61,15 +63,15 @@ EdgeLess::operator()(const Edge *edge1, const Pin *to2 = edge2->to(graph_)->pin(); return pin_less_(from1, from2) || (from1 == from2 - && pin_less_(to1, to2)); + && pin_less_(to1, to2)); } void sortEdges(EdgeSeq *edges, - Network *network, - Graph *graph) + Network *network, + Graph *graph) { sort(edges, EdgeLess(network, graph)); } -} +} // namespace sta diff --git a/include/sta/ArcDelayCalc.hh b/include/sta/ArcDelayCalc.hh index aaf0bf056..eacad4454 100644 --- a/include/sta/ArcDelayCalc.hh +++ b/include/sta/ArcDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,33 +24,33 @@ #pragma once +#include #include +#include #include -#include -#include "MinMax.hh" +#include "Delay.hh" +#include "GraphClass.hh" #include "LibertyClass.hh" -#include "TimingArc.hh" -#include "TableModel.hh" +#include "MinMax.hh" #include "NetworkClass.hh" -#include "GraphClass.hh" -#include "Delay.hh" #include "ParasiticsClass.hh" #include "StaState.hh" +#include "TableModel.hh" +#include "TimingArc.hh" namespace sta { -class Corner; +class Scene; class Parasitic; -class DcalcAnalysisPt; class MultiDrvrNet; class ArcDcalcArg; -typedef std::vector ArcDcalcArgPtrSeq; -typedef std::vector ArcDcalcArgSeq; +using ArcDcalcArgPtrSeq = std::vector; +using ArcDcalcArgSeq = std::vector; // Driver load pin -> index in driver loads. -typedef std::map LoadPinIndexMap; +using LoadPinIndexMap = std::map; // Arguments for gate delay calculation delay/slew at one driver pin // through one timing arc at one delay calc analysis point. @@ -63,14 +63,14 @@ public: const Pin *drvr_pin, Edge *edge, const TimingArc *arc, - const Slew in_slew, + Slew in_slew, float load_cap, const Parasitic *parasitic); ArcDcalcArg(const Pin *in_pin, const Pin *drvr_pin, Edge *edge, const TimingArc *arc, - float in_delay); + float input_delay); const Pin *inPin() const { return in_pin_; } const RiseFall *inEdge() const; const Pin *drvrPin() const { return drvr_pin_; } @@ -81,7 +81,7 @@ public: const Net *drvrNet(const Network *network) const; Edge *edge() const { return edge_; } const TimingArc *arc() const { return arc_; } - Slew inSlew() const { return in_slew_; } + const Slew &inSlew() const { return in_slew_; } float inSlewFlt() const; void setInSlew(Slew in_slew); const Parasitic *parasitic() const { return parasitic_; } @@ -104,12 +104,12 @@ protected: ArcDcalcArg -makeArcDcalcArg(const char *inst_name, - const char *in_port_name, - const char *in_rf_name, - const char *drvr_port_name, - const char *drvr_rf_name, - const char *input_delay_str, +makeArcDcalcArg(std::string_view inst_name, + std::string_view in_port_name, + std::string_view in_rf_name, + std::string_view drvr_port_name, + std::string_view drvr_rf_name, + std::string_view input_delay_str, const StaState *sta); // Arc delay calc result. @@ -120,15 +120,15 @@ public: ArcDcalcResult(size_t load_count); void setLoadCount(size_t load_count); ArcDelay &gateDelay() { return gate_delay_; } - void setGateDelay(ArcDelay gate_delay); + void setGateDelay(const ArcDelay &gate_delay); Slew &drvrSlew() { return drvr_slew_; } - void setDrvrSlew(Slew drvr_slew); - ArcDelay wireDelay(size_t load_idx) const; + void setDrvrSlew(const Slew &drvr_slew); + const ArcDelay &wireDelay(size_t load_idx) const; void setWireDelay(size_t load_idx, - ArcDelay wire_delay); - Slew loadSlew(size_t load_idx) const; + const ArcDelay &wire_delay); + const Slew &loadSlew(size_t load_idx) const; void setLoadSlew(size_t load_idx, - Slew load_slew); + const Slew &load_slew); protected: ArcDelay gate_delay_; @@ -138,8 +138,7 @@ protected: std::vector load_slews_; }; -typedef std::vector ArcDcalcArgSeq; -typedef std::vector ArcDcalcResultSeq; +using ArcDcalcResultSeq = std::vector; // Delay calculator class hierarchy. // ArcDelayCalc @@ -160,34 +159,37 @@ typedef std::vector ArcDcalcResultSeq; class ArcDelayCalc : public StaState { public: - explicit ArcDelayCalc(StaState *sta); - virtual ~ArcDelayCalc() {} + ArcDelayCalc(StaState *sta); virtual ArcDelayCalc *copy() = 0; - virtual const char *name() const = 0; + virtual std::string_view name() const = 0; // Find the parasitic for drvr_pin that is acceptable to the delay // calculator by probing parasitics_. virtual Parasitic *findParasitic(const Pin *drvr_pin, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) = 0; + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) = 0; virtual bool reduceSupported() const = 0; // Reduce parasitic_network to a representation acceptable to the delay calculator. virtual Parasitic *reduceParasitic(const Parasitic *parasitic_network, const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) = 0; + const Scene *scene, + const MinMax *min_max) = 0; // Reduce parasitic_network to a representation acceptable to the delay calculator - // for one or more corners and min/max rise/fall. - // Null corner means reduce all corners. + // for one or more scenes and min/max rise/fall. + // Null scene means reduce all scenes. virtual void reduceParasitic(const Parasitic *parasitic_network, const Net *net, - const Corner *corner, + const Scene *scene, const MinMaxAll *min_max) = 0; // Set the in_slew, load_cap, parasitic for gates. virtual void setDcalcArgParasiticSlew(ArcDcalcArg &gate, - const DcalcAnalysisPt *dcalc_ap) = 0; + const Scene *scene, + const MinMax *min_max) = 0; virtual void setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates, - const DcalcAnalysisPt *dcalc_ap) = 0; + const Scene *scene, + const MinMax *min_max) = 0; // Find the wire delays and slews for an input port without a driving cell. // This call primarily initializes the load delay/slew iterator. virtual ArcDcalcResult inputPortDelay(const Pin *port_pin, @@ -195,7 +197,8 @@ public: const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) = 0; + const Scene *scene, + const MinMax *min_max) = 0; // Find the delay and slew for arc driving drvr_pin. virtual ArcDcalcResult gateDelay(const Pin *drvr_pin, @@ -205,23 +208,26 @@ public: float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) = 0; + const Scene *scene, + const MinMax *min_max) = 0; // deprecated 2024-02-27 virtual void gateDelay(const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *parasitic, - float related_out_cap, - const Pvt *pvt, - const DcalcAnalysisPt *dcalc_ap, - // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew) __attribute__ ((deprecated)); + const Slew &in_slew, + float load_cap, + const Parasitic *parasitic, + float related_out_cap, + const Pvt *pvt, + const Scene *scene, + const MinMax *min_max, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) __attribute__ ((deprecated)); // Find gate delays and slews for parallel gates. virtual ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) = 0; + const Scene *scene, + const MinMax *min_max) = 0; // Find the delay for a timing check arc given the arc's // from/clock, to/data slews and related output pin parasitic. @@ -230,7 +236,8 @@ public: const Slew &from_slew, const Slew &to_slew, float related_out_cap, - const DcalcAnalysisPt *dcalc_ap) = 0; + const Scene *scene, + const MinMax *min_max) = 0; // Report delay and slew calculation. virtual std::string reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, @@ -238,18 +245,20 @@ public: float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) = 0; // Report timing check delay calculation. virtual std::string reportCheckDelay(const Pin *check_pin, const TimingArc *arc, const Slew &from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, const Slew &to_slew, float related_out_cap, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) = 0; virtual void finishDrvrPin() = 0; }; -} // namespace +} // namespace sta diff --git a/include/sta/Bdd.hh b/include/sta/Bdd.hh index b0497d259..834696fcb 100644 --- a/include/sta/Bdd.hh +++ b/include/sta/Bdd.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,22 +26,22 @@ #include -#include "StaState.hh" #include "LibertyClass.hh" +#include "StaState.hh" struct DdNode; struct DdManager; namespace sta { -typedef std::map BddPortVarMap; -typedef std::map BddVarIdxPortMap; +using BddPortVarMap = std::map; +using BddVarIdxPortMap = std::map; class Bdd : public StaState { public: Bdd(const StaState *sta); - ~Bdd(); + ~Bdd() override; DdNode *funcBdd(const FuncExpr *expr); DdNode *findNode(const LibertyPort *port); const LibertyPort *nodePort(DdNode *node); @@ -58,4 +58,4 @@ private: BddVarIdxPortMap bdd_var_idx_port_map_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Bfs.hh b/include/sta/Bfs.hh index c95fc5cb0..5c11ab1ce 100644 --- a/include/sta/Bfs.hh +++ b/include/sta/Bfs.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,12 +25,12 @@ #pragma once #include +#include -#include "Iterator.hh" -#include "Set.hh" #include "GraphClass.hh" -#include "VertexVisitor.hh" +#include "Iterator.hh" #include "StaState.hh" +#include "VertexVisitor.hh" namespace sta { @@ -39,7 +39,7 @@ class BfsFwdIterator; class BfsBkwdIterator; // LevelQueue is a vector of vertex vectors indexed by logic level. -typedef Vector LevelQueue; +using LevelQueue = std::vector; // Abstract base class for forward and backward breadth first search iterators. // Visit all of the vertices at a level before moving to the next. @@ -50,36 +50,36 @@ typedef Vector LevelQueue; // 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, Iterator +class BfsIterator : public StaState, + public Iterator { public: - virtual ~BfsIterator(); // Make sure that the BFS queue is deep enough for the max logic level. void ensureSize(); // Reset to virgin state. void clear(); - bool empty() const; + [[nodiscard]] bool empty() const; // Enqueue a vertex to search from. void enqueue(Vertex *vertex); // Enqueue vertices adjacent to a vertex. void enqueueAdjacentVertices(Vertex *vertex); - void enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred); - void enqueueAdjacentVertices(Vertex *vertex, - Level to_level); + virtual void enqueueAdjacentVertices(Vertex *vertex, + const Mode *mode); virtual void enqueueAdjacentVertices(Vertex *vertex, SearchPred *search_pred, - Level to_level) = 0; - bool inQueue(Vertex *vertex); + 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. void deleteVertexBefore(Vertex *vertex); void remove(Vertex *vertex); void reportEntries() const; - virtual bool hasNext(); + bool hasNext() override; bool hasNext(Level to_level); - virtual Vertex *next(); + Vertex *next() override; // Apply visitor to all vertices in the queue in level order. // Returns the number of vertices that are visited. @@ -130,18 +130,20 @@ public: BfsFwdIterator(BfsIndex bfs_index, SearchPred *search_pred, StaState *sta); - virtual ~BfsFwdIterator(); - virtual void enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred, - Level to_level); + ~BfsFwdIterator() override; + void enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred) override; + void enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred, + const Mode *mode) override; using BfsIterator::enqueueAdjacentVertices; protected: - virtual bool levelLessOrEqual(Level level1, - Level level2) const; - virtual bool levelLess(Level level1, - Level level2) const; - virtual void incrLevel(Level &level) const; + bool levelLessOrEqual(Level level1, + Level level2) const override; + bool levelLess(Level level1, + Level level2) const override; + void incrLevel(Level &level) const override; }; class BfsBkwdIterator : public BfsIterator @@ -150,18 +152,20 @@ public: BfsBkwdIterator(BfsIndex bfs_index, SearchPred *search_pred, StaState *sta); - virtual ~BfsBkwdIterator(); - virtual void enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred, - Level to_level); + ~BfsBkwdIterator() override; + void enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred) override; + void enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred, + const Mode *mode) override; using BfsIterator::enqueueAdjacentVertices; protected: - virtual bool levelLessOrEqual(Level level1, - Level level2) const; - virtual bool levelLess(Level level1, - Level level2) const; - virtual void incrLevel(Level &level) const; + bool levelLessOrEqual(Level level1, + Level level2) const override; + bool levelLess(Level level1, + Level level2) const override; + void incrLevel(Level &level) const override; }; -} // namespace +} // namespace sta diff --git a/include/sta/BoundedHeap.hh b/include/sta/BoundedHeap.hh new file mode 100644 index 000000000..6ae23ce06 --- /dev/null +++ b/include/sta/BoundedHeap.hh @@ -0,0 +1,262 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include +#include +#include +#include + +namespace sta { + +// BoundedHeap: A container that maintains the top N elements using a min-heap. +// This provides O(log n) insertion when the heap is full, O(1) when not full, +// and O(n log n) extraction of all elements. Useful for maintaining top K +// elements without storing all elements. +// +// The heap maintains the "worst" (minimum according to Compare) element at +// the root, so new elements that are better than the worst can replace it. +// For example, with Compare = std::greater, this maintains the N largest +// values (greater values are "better"). +// +// Template parameters: +// T: The element type +// Compare: Comparison function object type (default: std::less) +// For top N largest, use std::greater +// For top N smallest, use std::less +template > +class BoundedHeap { +public: + using value_type = T; + using size_type = size_t; + using const_reference = const T&; + using compare_type = Compare; + + // Constructors + explicit BoundedHeap(size_type max_size, + const Compare& comp = Compare()) : + max_size_(max_size), + comp_(comp), + min_heap_comp_(comp) + { + } + + // Copy constructor + BoundedHeap(const BoundedHeap& other) : + heap_(other.heap_), + max_size_(other.max_size_), + comp_(other.comp_), + min_heap_comp_(other.comp_) + {} + + // Assignment operator + BoundedHeap& operator=(const BoundedHeap& other) + { + if (this != &other) { + heap_ = other.heap_; + max_size_ = other.max_size_; + comp_ = other.comp_; + min_heap_comp_ = MinHeapCompare(other.comp_); + } + return *this; + } + + // Move constructor + BoundedHeap(BoundedHeap&& other) noexcept : + heap_(std::move(other.heap_)), + max_size_(other.max_size_), + comp_(std::move(other.comp_)), + min_heap_comp_(comp_) + {} + + // Move assignment operator + BoundedHeap& operator=(BoundedHeap&& other) noexcept + { + if (this != &other) { + heap_ = std::move(other.heap_); + max_size_ = other.max_size_; + comp_ = std::move(other.comp_); + min_heap_comp_ = MinHeapCompare(comp_); + } + return *this; + } + + void + setMaxSize(size_t max_size) + { + max_size_ = max_size; + } + + void + reserve(size_t size) + { + heap_.reserve(size); + } + + // Insert an element into the heap. + // If the heap is not full, the element is added. + // If the heap is full and the new element is better than the worst element, + // the worst element is replaced. Otherwise, the element is ignored. + // Returns (inserted, displaced): inserted is true if the element was inserted, + // displaced is set when an element was pushed out (caller must handle ownership). + std::pair> + insert(const T& value) { + if (heap_.size() < max_size_) { + heap_.push_back(value); + std::push_heap(heap_.begin(), heap_.end(), min_heap_comp_); + return {true, std::nullopt}; + } + else if (!heap_.empty()) { + // When keeping N worst (smallest) values: if new value is smaller than worst, + // we should keep it and remove the largest element to make room. + // If new value is larger than worst, we reject it (already have worse values). + // comp_(value, worst) is true when value < worst (value is smaller/worse) + if (comp_(value, heap_.front())) { + // New value is smaller than worst - find and replace the largest element + auto max_it = std::max_element(heap_.begin(), heap_.end(), comp_); + T displaced = std::move(*max_it); + *max_it = value; + // Rebuild heap since we modified an internal element + std::make_heap(heap_.begin(), heap_.end(), min_heap_comp_); + return {true, std::move(displaced)}; + } + // Otherwise, new value is >= worst, so we already have worse values - reject it + } + return {false, std::nullopt}; + } + + // Insert an element using move semantics + std::pair> + insert(T&& value) + { + if (heap_.size() < max_size_) { + heap_.push_back(std::move(value)); + std::push_heap(heap_.begin(), heap_.end(), min_heap_comp_); + return {true, std::nullopt}; + } + else if (!heap_.empty()) { + // When keeping N worst (smallest) values: if new value is smaller than worst, + // we should keep it and remove the largest element to make room. + // If new value is larger than worst, we reject it (already have worse values). + // comp_(value, worst) is true when value < worst (value is smaller/worse) + if (comp_(value, heap_.front())) { + // New value is smaller than worst - find and replace the largest element + auto max_it = std::max_element(heap_.begin(), heap_.end(), comp_); + T displaced = std::move(*max_it); + *max_it = std::move(value); + // Rebuild heap since we modified an internal element + std::make_heap(heap_.begin(), heap_.end(), min_heap_comp_); + return {true, std::move(displaced)}; + } + // Otherwise, new value is >= worst, so we already have worse values - reject it + } + return {false, std::nullopt}; + } + + // Extract all elements sorted from best to worst. + // This destroys the heap structure but preserves the elements. + std::vector extract() + { + // Convert heap to sorted vector (best to worst) + std::sort_heap(heap_.begin(), heap_.end(), min_heap_comp_); + std::vector result = std::move(heap_); + heap_.clear(); + return result; + } + + // Extract all elements sorted from best to worst (const version). + // Creates a copy since we can't modify the heap. + std::vector contents() const + { + std::vector temp_heap = heap_; + std::sort_heap(temp_heap.begin(), temp_heap.end(), min_heap_comp_); + return temp_heap; + } + + // Get the worst element (the one that would be replaced next). + // Requires !empty() + const_reference worst() const + { + return heap_.front(); + } + + // Check if the heap is empty + bool empty() const + { + return heap_.empty(); + } + + // Get the current number of elements in the heap + size_type size() const + { + return heap_.size(); + } + + // Get the maximum size of the heap + size_type max_size() const + { + return max_size_; + } + + // Check if the heap is full + bool full() const + { + return heap_.size() >= max_size_; + } + + // Clear all elements from the heap + void clear() + { + heap_.clear(); + } + + // Get the comparison function + Compare compare() const + { + return comp_; + } + +private: + std::vector heap_; + size_type max_size_; + Compare comp_; + + // Helper comparator for min-heap: we want the worst element at root + // so we can easily remove it when adding better elements. + // This is the inverse of the user's comparison. + struct MinHeapCompare + { + Compare comp_; + explicit MinHeapCompare(const Compare& c) : comp_(c) {} + bool operator()(const T& a, const T& b) const { + return comp_(a, b); // comp = less puts largest at root (worst) + } + }; + + MinHeapCompare min_heap_comp_; +}; + +} // namespace sta + diff --git a/include/sta/CircuitSim.hh b/include/sta/CircuitSim.hh index 201710dbd..8e366d095 100644 --- a/include/sta/CircuitSim.hh +++ b/include/sta/CircuitSim.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -28,4 +28,4 @@ namespace sta { enum class CircuitSim { hspice, ngspice, xyce }; -} // namespace +} // namespace sta diff --git a/include/sta/ClkNetwork.hh b/include/sta/ClkNetwork.hh index 62d89a781..493446c8d 100644 --- a/include/sta/ClkNetwork.hh +++ b/include/sta/ClkNetwork.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,40 +24,43 @@ #pragma once -#include "Map.hh" -#include "Set.hh" -#include "StaState.hh" -#include "NetworkClass.hh" +#include + #include "GraphClass.hh" +#include "NetworkClass.hh" #include "SdcClass.hh" +#include "StaState.hh" namespace sta { -typedef Map PinClksMap; -typedef Map ClkPinsMap; +using PinClksMap = std::map; +using ClkPinsMap = std::map; class Sta; // Find clock network pins. -// This is not as reliable as Search::isClock but is much cheaper. class ClkNetwork : public StaState { public: - ClkNetwork(StaState *sta); - ~ClkNetwork(); + ClkNetwork(Mode *mode, + StaState *sta); + ~ClkNetwork() override; void ensureClkNetwork(); void clear(); bool isClock(const Pin *pin) const; + bool isClock(const Vertex *vertex) const; bool isClock(const Net *net) const; bool isIdealClock(const Pin *pin) const; + bool isIdealClock(const Vertex *vertex) const; bool isPropagatedClock(const Pin *pin) const; - const ClockSet *clocks(const Pin *pin); - const ClockSet *idealClocks(const Pin *pin); + const ClockSet *clocks(const Pin *pin) const; + const ClockSet *clocks(const Vertex *vertex) const; + const ClockSet *idealClocks(const Pin *pin) const; const PinSet *pins(const Clock *clk); void clkPinsInvalid(); float idealClkSlew(const Pin *pin, const RiseFall *rf, - const MinMax *min_max); + const MinMax *min_max) const; protected: void deletePinBefore(const Pin *pin); @@ -66,11 +69,13 @@ protected: friend class Sta; private: + Mode *mode_; + void findClkPins(); void findClkPins(bool ideal_only, - PinClksMap &clk_pin_map); + PinClksMap &pin_clks_map); - bool clk_pins_valid_; + bool clk_pins_valid_{false}; // pin -> clks PinClksMap pin_clks_map_; // pin -> ideal clks @@ -79,4 +84,4 @@ private: ClkPinsMap clk_pins_map_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Clock.hh b/include/sta/Clock.hh index 2d8e01a99..08f4392cf 100644 --- a/include/sta/Clock.hh +++ b/include/sta/Clock.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,21 +24,24 @@ #pragma once +#include +#include + +#include "GraphClass.hh" #include "MinMax.hh" #include "RiseFallMinMax.hh" #include "SdcClass.hh" #include "SdcCmdComment.hh" -#include "GraphClass.hh" namespace sta { -typedef Map ClkHpinEdgeMap; +using ClkHpinEdgeMap = std::map; class Clock : public SdcCmdComment { public: ~Clock(); - const char *name() const { return name_; } + const std::string &name() const { return name_; } float period() const { return period_; } // Virtual clocks have no pins. bool isVirtual() const; @@ -54,8 +57,7 @@ public: const Pin *defaultPin() const; bool addToPins() const { return add_to_pins_; } void setAddToPins(bool add_to_pins); - FloatSeq *waveform() { return waveform_; } - const FloatSeq *waveform() const { return waveform_; } + const FloatSeq &waveform() const { return waveform_; } ClockEdge *edge(const RiseFall *rf) const; int index() const { return index_; } bool isPropagated() const { return is_propagated_; } @@ -63,40 +65,40 @@ public: bool isIdeal() const { return !is_propagated_; } // Ideal clock slew. void slew(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &slew, - bool &exists) const; + const MinMax *min_max, + // Return values. + float &slew, + bool &exists) const; // Return zero (default) if no slew exists. float slew(const RiseFall *rf, - const MinMax *min_max) const; + const MinMax *min_max) const; void setSlew(const RiseFall *rf, - const MinMax *min_max, - float slew); + const MinMax *min_max, + float slew); void setSlew(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew); + const MinMaxAll *min_max, + float slew); void removeSlew(); const RiseFallMinMax &slews() const { return slews_; } void setSlewLimit(const RiseFallBoth *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - float slew); + PathClkOrData clk_data, + const MinMax *min_max, + float slew); void slewLimit(const RiseFall *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - // Return values. - float &slew, - bool &exists) const; - ClockUncertainties *uncertainties() const { return uncertainties_; } + PathClkOrData clk_data, + const MinMax *min_max, + // Return values. + float &slew, + bool &exists) const; + const ClockUncertainties &uncertainties() const { return uncertainties_; } void uncertainty(const SetupHold *setup_hold, - // Return values. - float &uncertainty, - bool &exists) const; + // Return values. + float &uncertainty, + bool &exists) const; void setUncertainty(const SetupHoldAll *setup_hold, - float uncertainty); + float uncertainty); void setUncertainty(const SetupHold *setup_hold, - float uncertainty); + float uncertainty); void removeUncertainty(const SetupHoldAll *setup_hold); void setPeriod(float period); @@ -117,81 +119,81 @@ public: int multiplyBy() const { return multiply_by_; } float dutyCycle() const { return duty_cycle_; } bool invert() const { return invert_; } - IntSeq *edges() const { return edges_; } - FloatSeq *edgeShifts() const { return edge_shifts_; } + const IntSeq &edges() const { return edges_; } + const FloatSeq &edgeShifts() const { return edge_shifts_; } const RiseFall *masterClkEdgeTr(const RiseFall *rf) const; bool combinational() const { return combinational_; } bool isDivideByOneCombinational() const; bool generatedUpToDate() const; void srcPinVertices(VertexSet &src_vertices, - const Network *network, - Graph *graph); + const Network *network, + Graph *graph); // True if the generated clock waveform is up to date. bool waveformValid() const { return waveform_valid_; } void waveformInvalid(); protected: // Private to Sdc::makeClock. - Clock(const char *name, - int index, + Clock(std::string_view name, + int index, const Network *network); - void initClk(PinSet *pins, - bool add_to_pins, - float period, - FloatSeq *waveform, - const char *comment, - const Network *network); - void initGeneratedClk(PinSet *pins, - bool add_to_pins, - Pin *src_pin, - Clock *master_clk, - int divide_by, - int multiply_by, - float duty_cycle, - bool invert, - bool combinational, - IntSeq *edges, - FloatSeq *edge_shifts, - bool is_propagated, - const char *comment, - const Network *network); - void setPins(PinSet *pins, - const Network *network); + void initClk(const PinSet &pins, + bool add_to_pins, + float period, + const FloatSeq &waveform, + std::string_view comment, + const Network *network); + void initGeneratedClk(const PinSet &pins, + bool add_to_pins, + Pin *src_pin, + Clock *master_clk, + int divide_by, + int multiply_by, + float duty_cycle, + bool invert, + bool combinational, + const IntSeq &edges, + const FloatSeq &edge_shifts, + bool is_propagated, + std::string_view comment, + const Network *network); + void setPins(const PinSet &pins, + const Network *network); void setMasterClk(Clock *master); void makeClkEdges(); void setClkEdgeTimes(); void setClkEdgeTime(const RiseFall *rf); void generateScaledClk(const Clock *src_clk, - float scale); + float scale); void generateEdgesClk(const Clock *src_clk); - const char *name_; + std::string name_; PinSet pins_; - bool add_to_pins_; + bool add_to_pins_{false}; // Hierarchical pins in pins_ become driver pins through the pin. PinSet leaf_pins_; - float period_; - FloatSeq *waveform_; - bool waveform_valid_; + float period_{0.0}; + FloatSeq waveform_; + bool waveform_valid_{false}; const int index_; - ClockEdge **clk_edges_; - bool is_propagated_; + std::array clk_edges_; + bool is_propagated_{false}; RiseFallMinMax slews_; RiseFallMinMax slew_limits_[path_clk_or_data_count]; - ClockUncertainties *uncertainties_; - bool is_generated_; + ClockUncertainties uncertainties_; + bool is_generated_{false}; // Generated clock variables. - Pin *src_pin_; - Clock *master_clk_; + Pin *src_pin_{nullptr}; + Clock *master_clk_{nullptr}; // True if the master clock is infered rather than specified by command. - bool master_clk_infered_; - int divide_by_; - int multiply_by_; - float duty_cycle_; - bool invert_; - bool combinational_; - IntSeq *edges_; - FloatSeq *edge_shifts_; + bool master_clk_infered_{false}; + int divide_by_{0}; + int multiply_by_{0}; + float duty_cycle_{0}; + bool invert_{false}; + bool combinational_{false}; + IntSeq edges_; + FloatSeq edge_shifts_; private: friend class Sdc; @@ -202,10 +204,9 @@ class ClockEdge { public: Clock *clock() const { return clock_; } - ~ClockEdge(); const RiseFall *transition() const { return rf_; } float time() const { return time_; } - const char *name() const { return name_; } + const std::string &name() const { return name_; } int index() const { return index_; } ClockEdge *opposite() const; // Pulse width if this is the leading edge of the pulse. @@ -219,8 +220,8 @@ private: Clock *clock_; const RiseFall *rf_; - const char *name_; - float time_; + std::string name_; + float time_{0.0}; int index_; }; @@ -229,16 +230,19 @@ clkCmp(const Clock *clk1, const Clock *clk2); int clkEdgeCmp(const ClockEdge *clk_edge1, - const ClockEdge *clk_edge2); + const ClockEdge *clk_edge2); bool clkEdgeLess(const ClockEdge *clk_edge1, - const ClockEdge *clk_edge2); + const ClockEdge *clk_edge2); class ClockNameLess { public: bool operator()(const Clock *clk1, - const Clock *clk2); + const Clock *clk2) const + { + return clk1->name() < clk2->name(); + } }; //////////////////////////////////////////////////////////////// @@ -247,24 +251,24 @@ class InterClockUncertainty { public: InterClockUncertainty(const Clock *src, - const Clock *target); + const Clock *target); const Clock *src() const { return src_; } const Clock *target() const { return target_; } void uncertainty(const RiseFall *src_rf, - const RiseFall *tgt_rf, - const SetupHold *setup_hold, - // Return values. - float &uncertainty, - bool &exists) const; + const RiseFall *tgt_rf, + const SetupHold *setup_hold, + // Return values. + float &uncertainty, + bool &exists) const; void setUncertainty(const RiseFallBoth *src_rf, - const RiseFallBoth *tgt_rf, - const SetupHoldAll *setup_hold, - float uncertainty); + const RiseFallBoth *tgt_rf, + const SetupHoldAll *setup_hold, + float uncertainty); void removeUncertainty(const RiseFallBoth *src_rf, - const RiseFallBoth *tgt_rf, - const SetupHoldAll *setup_hold); + const RiseFallBoth *tgt_rf, + const SetupHoldAll *setup_hold); const RiseFallMinMax *uncertainties(const RiseFall *src_rf) const; - bool empty() const; + [[nodiscard]] bool empty() const; private: const Clock *src_; @@ -276,17 +280,7 @@ class InterClockUncertaintyLess { public: bool operator()(const InterClockUncertainty *inter1, - const InterClockUncertainty *inter2) const; -}; - -class ClkNameLess -{ -public: - bool operator()(const Clock *clk1, - const Clock *clk2) const - { - return stringLess(clk1->name(), clk2->name()); - } + const InterClockUncertainty *inter2) const; }; ClockSeq @@ -297,4 +291,4 @@ compare(const ClockSet *set1, bool isPowerOfTwo(int i); -} // namespace +} // namespace sta diff --git a/include/sta/ClockGatingCheck.hh b/include/sta/ClockGatingCheck.hh index e87cdb749..f460472f8 100644 --- a/include/sta/ClockGatingCheck.hh +++ b/include/sta/ClockGatingCheck.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,22 +24,21 @@ #pragma once -#include "SdcClass.hh" #include "RiseFallMinMax.hh" +#include "SdcClass.hh" namespace sta { class ClockGatingCheck { public: - ClockGatingCheck(); RiseFallMinMax *margins() { return &margins_; } void setActiveValue(LogicValue value); LogicValue activeValue() const { return active_value_; } private: RiseFallMinMax margins_; - LogicValue active_value_; + LogicValue active_value_{LogicValue::unknown}; }; -} // namespace +} // namespace sta diff --git a/include/sta/ClockGroups.hh b/include/sta/ClockGroups.hh index 509440344..7a3540b10 100644 --- a/include/sta/ClockGroups.hh +++ b/include/sta/ClockGroups.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,23 +24,25 @@ #pragma once -#include "SdcCmdComment.hh" +#include + #include "SdcClass.hh" +#include "SdcCmdComment.hh" namespace sta { class ClockGroups : public SdcCmdComment { public: - ClockGroups(const char *name, - bool logically_exclusive, - bool physically_exclusive, - bool asynchronous, - bool allow_paths, - const char *comment); + ClockGroups(std::string_view name, + bool logically_exclusive, + bool physically_exclusive, + bool asynchronous, + bool allow_paths, + std::string_view comment); ~ClockGroups(); void makeClockGroup(ClockSet *clks); - const char *name() const { return name_; } + const std::string &name() const { return name_; } ClockGroupSet *groups() { return &groups_; } bool logicallyExclusive() const { return logically_exclusive_; } bool physicallyExclusive() const { return physically_exclusive_; } @@ -49,7 +51,7 @@ public: void removeClock(Clock *clk); private: - const char *name_; + std::string name_; bool logically_exclusive_; bool physically_exclusive_; bool asynchronous_; @@ -57,4 +59,4 @@ private: ClockGroupSet groups_; }; -} // namespace +} // namespace sta diff --git a/include/sta/ClockInsertion.hh b/include/sta/ClockInsertion.hh index 1496dedae..a33423b38 100644 --- a/include/sta/ClockInsertion.hh +++ b/include/sta/ClockInsertion.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,8 +26,8 @@ #include "MinMax.hh" #include "NetworkClass.hh" -#include "SdcClass.hh" #include "RiseFallMinMax.hh" +#include "SdcClass.hh" #include "Transition.hh" namespace sta { @@ -39,16 +39,16 @@ public: const Clock *clock() const { return clk_; } const Pin *pin() const { return pin_; } float delay(const RiseFall *rf, const MinMax *min_max, - const EarlyLate *early_late); + const EarlyLate *early_late); void delay(const RiseFall *rf, const MinMax *min_max, - const EarlyLate *early_late, - // Return values. - float &insertion, bool &exists); + const EarlyLate *early_late, + // Return values. + float &insertion, bool &exists); RiseFallMinMax *delays(const EarlyLate *early_late); void setDelay(const RiseFall *rf, const MinMax *min_max, - const EarlyLate *early_late, float delay); + const EarlyLate *early_late, float delay); void setDelay(const RiseFallBoth *rf, const MinMaxAll *min_max, - const EarlyLateAll *early_late, float delay); + const EarlyLateAll *early_late, float delay); void setDelays(RiseFallMinMax *delays); private: @@ -57,4 +57,4 @@ private: RiseFallMinMax delays_[EarlyLate::index_count]; }; -} // namespace +} // namespace sta diff --git a/include/sta/ClockLatency.hh b/include/sta/ClockLatency.hh index 9e05680b0..54c4dff2c 100644 --- a/include/sta/ClockLatency.hh +++ b/include/sta/ClockLatency.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,9 +26,9 @@ #include "MinMax.hh" #include "NetworkClass.hh" -#include "Transition.hh" -#include "SdcClass.hh" #include "RiseFallMinMax.hh" +#include "SdcClass.hh" +#include "Transition.hh" namespace sta { @@ -36,23 +36,23 @@ class ClockLatency { public: ClockLatency(const Clock *clk, - const Pin *pin); + const Pin *pin); const Clock *clock() const { return clk_; } const Pin *pin() const { return pin_; } float delay(const RiseFall *rf, - const MinMax *min_max); + const MinMax *min_max); void delay(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &latency, - bool &exists); + const MinMax *min_max, + // Return values. + float &latency, + bool &exists); RiseFallMinMax *delays(); void setDelay(const RiseFall *rf, - const MinMax *min_max, - float delay); + const MinMax *min_max, + float delay); void setDelay(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float delay); + const MinMaxAll *min_max, + float delay); void setDelays(RiseFallMinMax *delays); private: @@ -61,4 +61,4 @@ private: RiseFallMinMax delays_; }; -} // namespace +} // namespace sta diff --git a/include/sta/ConcreteLibrary.hh b/include/sta/ConcreteLibrary.hh index d4eb6ff89..75018056e 100644 --- a/include/sta/ConcreteLibrary.hh +++ b/include/sta/ConcreteLibrary.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,11 +25,12 @@ #pragma once #include +#include +#include +#include -#include "Vector.hh" -#include "Map.hh" -#include "StringUtil.hh" #include "NetworkClass.hh" +#include "StringUtil.hh" // The classes defined in this file are a contrete implementation of // the library API. They can be used by a reader to construct classes @@ -45,48 +46,46 @@ class PatternMatch; class LibertyCell; class LibertyPort; -typedef Map ConcreteCellMap; -typedef Vector ConcretePortSeq; -typedef Map ConcretePortMap; -typedef ConcreteCellMap::ConstIterator ConcreteLibraryCellIterator; -typedef ConcretePortSeq::ConstIterator ConcreteCellPortIterator; -typedef ConcretePortSeq::ConstIterator ConcretePortMemberIterator; +using ConcreteCellMap = std::map>; +using ConcretePortSeq = std::vector; +using ConcretePortMap = std::map>; +using ConcreteLibraryCellIterator = MapIterator; +using ConcreteCellPortIterator = VectorIterator; +using ConcretePortMemberIterator = VectorIterator; class ConcreteLibrary { public: - explicit ConcreteLibrary(const char *name, - const char *filename, - bool is_liberty); + ConcreteLibrary(std::string_view name, + std::string_view filename, + bool is_liberty); virtual ~ConcreteLibrary(); - const char *name() const { return name_.c_str(); } - void setName(const char *name); + const std::string &name() const { return name_; } ObjectId id() const { return id_; } bool isLiberty() const { return is_liberty_; } - const char *filename() const { return filename_.c_str(); } + const std::string &filename() const { return filename_; } void addCell(ConcreteCell *cell); - ConcreteCell *makeCell(const char *name, - bool is_leaf, - const char *filename); + ConcreteCell *makeCell(std::string_view name, + bool is_leaf, + std::string_view filename); void deleteCell(ConcreteCell *cell); ConcreteLibraryCellIterator *cellIterator() const; - ConcreteCell *findCell(const char *name) const; + ConcreteCell *findCell(std::string_view name) const; CellSeq findCellsMatching(const PatternMatch *pattern) const; char busBrktLeft() const { return bus_brkt_left_; } char busBrktRight() const { return bus_brkt_right_; } void setBusBrkts(char left, - char right); + char right); protected: - void renameCell(ConcreteCell *cell, - const char *cell_name); + void removeCell(ConcreteCell *cell); std::string name_; ObjectId id_; std::string filename_; bool is_liberty_; - char bus_brkt_left_; - char bus_brkt_right_; + char bus_brkt_left_{'['}; + char bus_brkt_right_{']'}; ConcreteCellMap cell_map_; private: @@ -98,77 +97,77 @@ class ConcreteCell public: // Use ConcreteLibrary::deleteCell. virtual ~ConcreteCell(); - const char *name() const { return name_.c_str(); } + const std::string &name() const { return name_; } ObjectId id() const { return id_; } - const char *filename() const { return filename_.c_str(); } + const std::string &filename() const { return filename_; } ConcreteLibrary *library() const { return library_; } LibertyCell *libertyCell() const { return liberty_cell_; } void setLibertyCell(LibertyCell *cell); void *extCell() const { return ext_cell_; } void setExtCell(void *ext_cell); int portBitCount() const { return port_bit_count_; } - ConcretePort *findPort(const char *name) const; + ConcretePort *findPort(std::string_view name) const; PortSeq findPortsMatching(const PatternMatch *pattern) const; ConcreteCellPortIterator *portIterator() const; ConcreteCellPortBitIterator *portBitIterator() const; bool isLeaf() const { return is_leaf_; } void setIsLeaf(bool is_leaf); - void setAttribute(const std::string &key, - const std::string &value); - std::string getAttribute(const std::string &key) const; + void setAttribute(std::string_view key, + std::string_view value); + std::string getAttribute(std::string_view key) const; const AttributeMap &attributeMap() const { return attribute_map_; } // Cell acts as port factory. - ConcretePort *makePort(const char *name); + ConcretePort *makePort(std::string_view name); // Bus port. - ConcretePort *makeBusPort(const char *name, - int from_index, - int to_index); + ConcretePort *makeBusPort(std::string_view name, + int from_index, + int to_index); // Bundle port. - ConcretePort *makeBundlePort(const char *name, - ConcretePortSeq *members); + ConcretePort *makeBundlePort(std::string_view name, + ConcretePortSeq *members); // Group previously defined bus bit ports together. - void groupBusPorts(const char bus_brkt_left, - const char bus_brkt_right, - std::function port_msb_first); + void groupBusPorts(char bus_brkt_left, + char bus_brkt_right, + const std::function &port_msb_first); size_t portCount() const; - void setName(const char *name); - void addPort(ConcretePort *port); + void setName(std::string_view name); + virtual void addPort(ConcretePort *port); void addPortBit(ConcretePort *port); protected: - ConcreteCell(const char *name, - const char *filename, + ConcreteCell(std::string_view name, + std::string_view filename, bool is_leaf, ConcreteLibrary *library); - ConcretePort *makeBusPort(const char *name, - int from_index, - int to_index, - ConcretePortSeq *members); + ConcretePort *makeBusPort(std::string_view name, + int from_index, + int to_index, + ConcretePortSeq *members); void makeBusPortBits(ConcretePort *bus_port, - const char *name, - int from_index, - int to_index); + std::string_view bus_name, + int from_index, + int to_index); // Bus port bit (internal to makeBusPortBits). - ConcretePort *makePort(const char *bit_name, - int bit_index); + ConcretePort *makePort(std::string_view bit_name, + int bit_index); void makeBusPortBit(ConcretePort *bus_port, - const char *name, - int index); + std::string_view bus_name, + int index); std::string name_; ObjectId id_; // Filename is optional. std::string filename_; ConcreteLibrary *library_; - LibertyCell *liberty_cell_; + LibertyCell *liberty_cell_{nullptr}; // External application cell. - void *ext_cell_; + void *ext_cell_{nullptr}; // Non-bus and bus ports (but no expanded bus bit ports). ConcretePortSeq ports_; ConcretePortMap port_map_; // Port bit count (expanded buses). - int port_bit_count_; + int port_bit_count_{0}; bool is_leaf_; AttributeMap attribute_map_; @@ -181,9 +180,9 @@ class ConcretePort { public: virtual ~ConcretePort(); - const char *name() const { return name_.c_str(); } + const std::string &name() const { return name_; } ObjectId id() const { return id_; } - const char *busName() const; + std::string busName() const; Cell *cell() const; ConcreteLibrary *library() const { return cell_->library(); } PortDirection *direction() const { return direction_; } @@ -231,22 +230,22 @@ public: protected: // Constructors for factory in cell class. - ConcretePort(const char *name, + ConcretePort(std::string_view name, bool is_bus, - int from_index, - int to_index, - bool is_bundle, - ConcretePortSeq *member_ports, + int from_index, + int to_index, + bool is_bundle, + ConcretePortSeq *member_ports, ConcreteCell *cell); std::string name_; ObjectId id_; ConcreteCell *cell_; PortDirection *direction_; - LibertyPort *liberty_port_; + LibertyPort *liberty_port_{nullptr}; // External application port. - void *ext_port_; - int pin_index_; + void *ext_port_{nullptr}; + int pin_index_{-1}; bool is_bundle_; bool is_bus_; int from_index_; @@ -254,7 +253,7 @@ protected: // Expanded bus bit ports (ordered by from_index_ to to_index_) // or bundle member ports. ConcretePortSeq *member_ports_; - ConcretePort *bundle_port_; + ConcretePort *bundle_port_{nullptr}; private: friend class ConcreteCell; @@ -263,16 +262,17 @@ private: class ConcreteCellPortBitIterator : public Iterator { public: - explicit ConcreteCellPortBitIterator(const ConcreteCell *cell); - virtual bool hasNext(); - virtual ConcretePort *next(); + ConcreteCellPortBitIterator(const ConcreteCell *cell); + bool hasNext() override; + ConcretePort *next() override; private: void findNext(); - ConcretePortSeq::ConstIterator port_iter_; - ConcretePortMemberIterator *member_iter_; - ConcretePort *next_; + const ConcretePortSeq &ports_; + ConcretePortSeq::const_iterator port_iter_; + ConcretePortMemberIterator *member_iter_{nullptr}; + ConcretePort *next_{nullptr}; }; -} // Namespace +} // namespace sta diff --git a/include/sta/ConcreteNetwork.hh b/include/sta/ConcreteNetwork.hh index 162598735..593c444c3 100644 --- a/include/sta/ConcreteNetwork.hh +++ b/include/sta/ConcreteNetwork.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,12 +25,14 @@ #pragma once #include +#include +#include +#include +#include -#include "Map.hh" -#include "Set.hh" -#include "StringUtil.hh" -#include "Network.hh" #include "LibertyClass.hh" +#include "Network.hh" +#include "StringUtil.hh" namespace sta { @@ -45,16 +47,14 @@ class ConcretePort; class ConcreteBindingTbl; class ConcreteLibertyLibraryIterator; -typedef Vector ConcreteLibrarySeq; -typedef Map ConcreteLibraryMap; -typedef ConcreteLibrarySeq::ConstIterator ConcreteLibraryIterator; -typedef Map ConcreteInstanceChildMap; -typedef Map ConcreteInstanceNetMap; -typedef Vector ConcreteNetSeq; -typedef Vector ConcretePinSeq; -typedef Map CellNetworkViewMap; -typedef Set ConcreteNetSet; +using ConcreteLibrarySeq = std::vector; +using ConcreteLibraryMap = std::map>; +using ConcreteInstanceChildMap = std::map>; +using ConcreteInstanceNetMap = std::map>; +using ConcreteNetSeq = std::vector; +using ConcretePinSeq = std::vector; +using CellNetworkViewMap = std::map; +using ConcreteNetSet = std::set; // This adapter implements the network api for the concrete network. // A superset of the Network api methods are implemented in the interface. @@ -62,28 +62,28 @@ class ConcreteNetwork : public NetworkReader { public: ConcreteNetwork(); - ~ConcreteNetwork(); + ~ConcreteNetwork() override; void clear() override; - bool linkNetwork(const char *top_cell_name, + bool linkNetwork(std::string_view top_cell_name, bool make_black_boxes, Report *report) override; Instance *topInstance() const override; - const char *name(const Library *library) const override; + std::string name(const Library *library) const override; ObjectId id(const Library *library) const override; LibraryIterator *libraryIterator() const override; LibertyLibraryIterator *libertyLibraryIterator() const override; - Library *findLibrary(const char *name) override; - LibertyLibrary *findLiberty(const char *name) override; + Library *findLibrary(std::string_view name) override; + LibertyLibrary *findLiberty(std::string_view name) override; Cell *findCell(const Library *library, - const char *name) const override; - Cell *findAnyCell(const char *name) override; + std::string_view name) const override; + Cell *findAnyCell(std::string_view name) override; CellSeq findCellsMatching(const Library *library, const PatternMatch *pattern) const override; - const char *name(const Cell *cell) const override; + std::string name(const Cell *cell) const override; std::string getAttribute(const Cell *cell, - const std::string &key) const override; + std::string_view key) const override; const AttributeMap &attributeMap(const Cell *cell) const override; ObjectId id(const Cell *cell) const override; Library *library(const Cell *cell) const override; @@ -91,15 +91,15 @@ public: const LibertyCell *libertyCell(const Cell *cell) const override; Cell *cell(LibertyCell *cell) const override; const Cell *cell(const LibertyCell *cell) const override; - const char *filename(const Cell *cell) override; + std::string_view filename(const Cell *cell) const override; Port *findPort(const Cell *cell, - const char *name) const override; + std::string_view name) const override; bool isLeaf(const Cell *cell) const override; CellPortIterator *portIterator(const Cell *cell) const override; CellPortBitIterator *portBitIterator(const Cell *cell) const override; int portBitCount(const Cell *cell) const override; - const char *name(const Port *port) const override; + std::string name(const Port *port) const override; ObjectId id(const Port *port) const override; Cell *cell(const Port *port) const override; LibertyPort *libertyPort(const Port *port) const override; @@ -109,7 +109,7 @@ public: bool isBus(const Port *port) const override; int size(const Port *port) const override; - const char *busName(const Port *port) const override; + std::string busName(const Port *port) const override; Port *findBusBit(const Port *port, int index) const override; int fromIndex(const Port *port) const override; @@ -118,18 +118,18 @@ public: int index) const override; PortMemberIterator *memberIterator(const Port *port) const override; - const char *name(const Instance *instance) const override; + std::string name(const Instance *instance) const override; std::string getAttribute(const Instance *inst, - const std::string &key) const override; + std::string_view key) const override; const AttributeMap &attributeMap(const Instance *inst) const override; ObjectId id(const Instance *instance) const override; Cell *cell(const Instance *instance) const override; Instance *parent(const Instance *instance) const override; bool isLeaf(const Instance *instance) const override; Instance *findChild(const Instance *parent, - const char *name) const override; + std::string_view name) const override; Pin *findPin(const Instance *instance, - const char *port_name) const override; + std::string_view port_name) const override; Pin *findPin(const Instance *instance, const Port *port) const override; @@ -154,10 +154,10 @@ public: Net *net(const Term *term) const override; Pin *pin(const Term *term) const override; - const char *name(const Net *net) const override; + std::string name(const Net *net) const override; ObjectId id(const Net *net) const override; Net *findNet(const Instance *instance, - const char *net_name) const override; + std::string_view net_name) const override; void findInstNetsMatching(const Instance *instance, const PatternMatch *pattern, NetSeq &matches) const override; @@ -172,47 +172,47 @@ public: ConstantPinIterator *constantPinIterator() override; void addConstantNet(Net *net, - LogicValue value) override; + LogicValue value) override; // Edit methods. - Library *makeLibrary(const char *name, - const char *filename) override; - LibertyLibrary *makeLibertyLibrary(const char *name, - const char *filename) override; + Library *makeLibrary(std::string_view name, + std::string_view filename) override; + LibertyLibrary *makeLibertyLibrary(std::string_view name, + std::string_view filename) override; void deleteLibrary(Library *library) override; Cell *makeCell(Library *library, - const char *name, + std::string_view name, bool is_leaf, - const char *filename) override; + std::string_view filename) override; void deleteCell(Cell *cell) override; void setName(Cell *cell, - const char *name) override; + std::string_view name) override; void setIsLeaf(Cell *cell, bool is_leaf) override; void setAttribute(Cell *cell, - const std::string &key, - const std::string &value) override; + std::string_view key, + std::string_view value) override; Port *makePort(Cell *cell, - const char *name) override; + std::string_view name) override; Port *makeBusPort(Cell *cell, - const char *name, + std::string_view name, int from_index, int to_index) override; void groupBusPorts(Cell *cell, - std::function port_msb_first) override; + std::function port_msb_first) override; Port *makeBundlePort(Cell *cell, - const char *name, + std::string_view name, PortSeq *members) override; void setDirection(Port *port, PortDirection *dir) override; // For NetworkEdit. Instance *makeInstance(LibertyCell *cell, - const char *name, + std::string_view name, Instance *parent) override; void makePins(Instance *inst) override; // For linking. Instance *makeInstance(Cell *cell, - const char *name, + std::string_view name, Instance *parent) override; void replaceCell(Instance *inst, Cell *cell) override; @@ -224,11 +224,11 @@ public: LibertyPort *port, Net *net) override; void setAttribute(Instance *inst, - const std::string &key, - const std::string &value) override; + std::string_view key, + std::string_view value) override; void disconnectPin(Pin *pin) override; void deletePin(Pin *pin) override; - Net *makeNet(const char *name, + Net *makeNet(std::string_view name, Instance *parent) override; void deleteNet(Net *net) override; @@ -263,25 +263,28 @@ public: using Network::isLeaf; protected: + void clearImpl(); + void deleteCellNetworkViewsImpl(); + void deleteInstanceImpl(Instance *inst); void addLibrary(ConcreteLibrary *library); - void setName(const char *name); + void setName(std::string_view name); void clearConstantNets(); void visitConnectedPins(const Net *net, PinVisitor &visitor, NetSet &visited_nets) const override; Instance *makeConcreteInstance(ConcreteCell *cell, - const char *name, - Instance *parent); + std::string_view name, + Instance *parent); void disconnectNetPin(ConcreteNet *cnet, - ConcretePin *cpin); + ConcretePin *cpin); void connectNetPin(ConcreteNet *cnet, - ConcretePin *cpin); + ConcretePin *cpin); // Cell lookup search order sequence. ConcreteLibrarySeq library_seq_; ConcreteLibraryMap library_map_; - Instance *top_instance_; - NetSet constant_nets_[2]; // LogicValue::zero/one + Instance *top_instance_{nullptr}; + NetSet constant_nets_[2]{NetSet(this), NetSet(this)}; // LogicValue::zero/one LinkNetworkFunc link_func_; CellNetworkViewMap cell_network_view_map_; static ObjectId object_id_; @@ -293,47 +296,47 @@ private: class ConcreteInstance { public: - const char *name() const { return name_; } + const std::string &name() const { return name_; } ObjectId id() const { return id_; } Cell *cell() const; ConcreteInstance *parent() const { return parent_; } - ConcretePin *findPin(const char *port_name) const; + ConcretePin *findPin(std::string_view port_name) const; ConcretePin *findPin(const Port *port) const; - ConcreteNet *findNet(const char *net_name) const; + ConcreteNet *findNet(std::string_view net_name) const; void findNetsMatching(const PatternMatch *pattern, NetSeq &matches) const; InstanceNetIterator *netIterator() const; - Instance *findChild(const char *name) const; + Instance *findChild(std::string_view name) const; InstanceChildIterator *childIterator() const; - void setAttribute(const std::string &key, - const std::string &value); - std::string getAttribute(const std::string &key) const; + void setAttribute(std::string_view key, + std::string_view value); + std::string getAttribute(std::string_view key) const; const AttributeMap &attributeMap() const { return attribute_map_; } void addChild(ConcreteInstance *child); void deleteChild(ConcreteInstance *child); void addPin(ConcretePin *pin); void deletePin(ConcretePin *pin); void addNet(ConcreteNet *net); - void addNet(const char *name, - ConcreteNet *net); + void addNet(std::string_view name, + ConcreteNet *net); void deleteNet(ConcreteNet *net); void setCell(ConcreteCell *cell); void initPins(); protected: - ConcreteInstance(const char *name, - ConcreteCell *cell, + ConcreteInstance(std::string_view name, + ConcreteCell *cell, ConcreteInstance *parent); ~ConcreteInstance(); - const char *name_; + std::string name_; ObjectId id_; ConcreteCell *cell_; ConcreteInstance *parent_; // Array of pins indexed by pin->port->index(). ConcretePinSeq pins_; - ConcreteInstanceChildMap *children_; - ConcreteInstanceNetMap *nets_; + ConcreteInstanceChildMap *children_{nullptr}; + ConcreteInstanceNetMap *nets_{nullptr}; AttributeMap attribute_map_; private: @@ -344,7 +347,7 @@ private: class ConcretePin { public: - const char *name() const; + const std::string &name() const; ConcreteInstance *instance() const { return instance_; } ConcreteNet *net() const { return net_; } ConcretePort *port() const { return port_; } @@ -354,20 +357,20 @@ public: void setVertexId(VertexId id); protected: - ~ConcretePin() {} + ~ConcretePin() = default; ConcretePin(ConcreteInstance *instance, - ConcretePort *port, - ConcreteNet *net); + ConcretePort *port, + ConcreteNet *net); ConcreteInstance *instance_; ConcretePort *port_; ConcreteNet *net_; - ConcreteTerm *term_; + ConcreteTerm *term_{nullptr}; ObjectId id_; // Doubly linked list of net pins. - ConcretePin *net_next_; - ConcretePin *net_prev_; - VertexId vertex_id_; + ConcretePin *net_next_{nullptr}; + ConcretePin *net_prev_{nullptr}; + VertexId vertex_id_{vertex_id_null}; private: friend class ConcreteNetwork; @@ -378,21 +381,21 @@ private: class ConcreteTerm { public: - const char *name() const; + const std::string &name() const; ObjectId id() const { return id_; } ConcreteNet *net() const { return net_; } ConcretePin *pin() const { return pin_; } protected: - ~ConcreteTerm() {} + ~ConcreteTerm() = default; ConcreteTerm(ConcretePin *pin, - ConcreteNet *net); + ConcreteNet *net); ConcretePin *pin_; ConcreteNet *net_; ObjectId id_; // Linked list of net terms. - ConcreteTerm *net_next_; + ConcreteTerm *net_next_{nullptr}; private: friend class ConcreteNetwork; @@ -403,7 +406,7 @@ private: class ConcreteNet { public: - const char *name() const { return name_; } + const std::string &name() const { return name_; } ObjectId id() const { return id_; } ConcreteInstance *instance() const { return instance_; } void addPin(ConcretePin *pin); @@ -414,23 +417,22 @@ public: ConcreteNet *mergedInto() { return merged_into_; } protected: - ConcreteNet(const char *name, - ConcreteInstance *instance); - ~ConcreteNet(); - const char *name_; + ConcreteNet(std::string_view name, + ConcreteInstance *instance); + std::string name_; ObjectId id_; ConcreteInstance *instance_; // Pointer to head of linked list of pins. - ConcretePin *pins_; + ConcretePin *pins_{nullptr}; // Pointer to head of linked list of terminals. // These terminals correspond to the pins attached to the instance that // contains this net in the hierarchy level above. - ConcreteTerm *terms_; - ConcreteNet *merged_into_; + ConcreteTerm *terms_{nullptr}; + ConcreteNet *merged_into_{nullptr}; friend class ConcreteNetwork; friend class ConcreteNetTermIterator; friend class ConcreteNetPinIterator; }; -} // namespace +} // namespace sta diff --git a/include/sta/ContainerHelpers.hh b/include/sta/ContainerHelpers.hh new file mode 100644 index 000000000..9b1a447a4 --- /dev/null +++ b/include/sta/ContainerHelpers.hh @@ -0,0 +1,460 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include // for std::declval +#include + +namespace sta { + +// C++ kung foo courtesy of chat gtp. + +// ------------------------------------------------------------ +// 1. Sequence containers (vector, list, deque, …) +// ------------------------------------------------------------ +template +requires std::is_pointer_v +void +deleteContents(Container& c) +{ + for (auto ptr : c) + delete ptr; + c.clear(); +} + +template +requires std::is_pointer_v +void +deleteContents(Container *c) +{ + for (auto ptr : *c) + delete ptr; + c->clear(); +} + +// ------------------------------------------------------------ +// 2. Maps (map, unordered_map) +// ------------------------------------------------------------ +template +requires std::is_pointer_v +void +deleteContents(Map& m) +{ + for (auto& kv : m) + delete kv.second; + m.clear(); +} + +template +requires std::is_pointer_v +void +deleteContents(Map *m) +{ + for (auto& kv : *m) + delete kv.second; + m->clear(); +} + +// ------------------------------------------------------------ +// 3. Sets (set, unordered_set) +// ------------------------------------------------------------ +template +requires (std::is_pointer_v && + requires { typename Set::mapped_type; } && + !std::is_same_v) +void +deleteContents(Set& s) +{ + for (auto ptr : s) + delete ptr; + s.clear(); +} + +//////////////////////////////////////////////////////////////// + +// detect whether container has mapped_type +template +struct has_mapped_type : std::false_type {}; + +template +struct has_mapped_type> + : std::true_type {}; + +// handle pointer types +template +struct has_mapped_type : has_mapped_type {}; + +// return-type chooser: use struct, NOT alias template +template::value> +struct find_return; + +// pointer to map +template +struct find_return +{ + using type = C::mapped_type; +}; + +// pointer to set +template +struct find_return +{ + using type = C::key_type; +}; + +// map ref +template +struct find_return +{ + using type = C::mapped_type; +}; + +// set ref +template +struct find_return +{ + using type = C::key_type; +}; + + +// Find a pointer value in a reference to a contaiiner of pointers. +// Return nullptr if not found. +template +auto +findKey(const AssocContainer& c, + const typename AssocContainer::key_type& key) + -> find_return::type +{ + using ReturnType = find_return::type; + + static_assert(std::is_pointer_v, + "findKey requires pointer types"); + + auto it = c.find(key); + if (it == c.end()) + return nullptr; + + if constexpr (has_mapped_type::value) + return it->second; // map + else + return *it; // set +} + +// Find a pointer value in a reference to a map that uses strings as keys. +// Return nullptr if not found. +template +auto +findStringKey(const AssocContainer& c, + std::string_view key) + -> find_return::type +{ + using ReturnType = find_return::type; + + static_assert(std::is_pointer_v, + "findStringKey requires pointer types"); + + auto it = c.find(key); + if (it == c.end()) + return nullptr; + else + return it->second; +} + +//////////////////////////////////////////////////////////////// + +// Find a value reference in a container. Returns reference to the value if found, +// otherwise returns reference to a static empty value of the same type. +template +auto +findKeyValue(const AssocContainer& c, + const typename AssocContainer::key_type& key) + -> const find_return::type & +{ + auto it = c.find(key); + if (it != c.end()) { + if constexpr (has_mapped_type::value) + return it->second; + else + return *it; + } + static const typename find_return::type empty{}; + return empty; +} + +// Find a value reference in a reference to a contaiiner of objects. +// Return exists. +template +void +findKeyValue(const AssocContainer& c, + const typename AssocContainer::key_type& key, + typename find_return::type &value, + bool &exists) +{ + auto it = c.find(key); + if (it == c.end()) { + exists = false; + return; + } + + if constexpr (has_mapped_type::value) { + // map + value = it->second; + exists = true; + } + else { + // set + value = *it; + exists = true; + } +} + +// Find a value reference in a pointer to a contaiiner of objects. +// Return exists. +template +void +findKeyValue(const AssocContainer *c, + const typename AssocContainer::key_type& key, + typename find_return::type &value, + bool &exists) +{ + auto it = c->find(key); + if (it == c->end()) { + exists = false; + return; + } + + if constexpr (has_mapped_type::value) { + // map + value = it->second; + exists = true; + } + else { + // set + value = *it; + exists = true; + } +} + +//////////////////////////////////////////////////////////////// + +// Find a value pointer in a reference to a contaiiner of objects. +// Return nullptr if not found. +template +auto +findKeyValuePtr(AssocContainer& c, + const typename AssocContainer::key_type& key) + -> find_return::type* +{ + auto it = c.find(key); + if (it == c.end()) + return nullptr; + + if constexpr (has_mapped_type::value) + // map + return &it->second; + else + // sett + return *it; +} + +// Find a value pointer in a reference to a contaiiner of objects. +// Return nullptr if not found. +template +auto +findKeyValuePtr(const AssocContainer& c, + const typename AssocContainer::key_type& key) + -> find_return::type const* +{ + auto it = c.find(key); + if (it == c.end()) + return nullptr; + + if constexpr (has_mapped_type::value) + // map + return &it->second; + else + // set + return *it; +} + +// Find a pointer to a value in a reference to a contaiiner of objects +// using std::string as the key. +// Return nullptr if not found. +template +auto +findStringValuePtr(AssocContainer& c, + std::string_view key) + -> find_return::type* +{ + auto it = c.find(key); + if (it == c.end()) + return nullptr; + else + return &it->second; +} + +// Find a const pointer to a value in a const reference to a contaiiner objects +// using std::string as the key. +// Return nullptr if not found. +template +auto +findStringValuePtr(const AssocContainer& c, + std::string_view key) + -> find_return::type const* +{ + auto it = c.find(key); + if (it == c.end()) + return nullptr; + else + return &it->second; +} + +//////////////////////////////////////////////////////////////// + +// Determine if two std::set's intersect. +// Returns true if there is at least one common element. +template +bool +intersects(const Set &set1, + const Set &set2, + typename Set::key_compare key_less) +{ + auto iter1 = set1.begin(); + auto end1 = set1.end(); + auto iter2 = set2.begin(); + auto end2 = set2.end(); + + while (iter1 != end1 && iter2 != end2) { + if (key_less(*iter1, *iter2)) + iter1++; + else if (key_less(*iter2, *iter1)) + iter2++; + else + return true; + } + return false; +} + +// Determine if two std::set's intersect (pointer version). +// Returns true if there is at least one common element. +template +bool +intersects(const Set *set1, + const Set *set2, + typename Set::key_compare key_less) +{ + if (set1 && set2) { + auto iter1 = set1->begin(); + auto end1 = set1->end(); + auto iter2 = set2->begin(); + auto end2 = set2->end(); + + while (iter1 != end1 && iter2 != end2) { + if (key_less(*iter1, *iter2)) + iter1++; + else if (key_less(*iter2, *iter1)) + iter2++; + else + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////// + +// Compare set contents. +template +int +compare(const Set *set1, + const Set *set2, + typename Set::key_compare key_less) +{ + size_t size1 = set1 ? set1->size() : 0; + size_t size2 = set2 ? set2->size() : 0; + if (size1 == size2) { + if (set1 == nullptr || set2 == nullptr) { + // Both are null or empty, so they're equal + return 0; + } + auto iter1 = set1->begin(); + auto iter2 = set2->begin(); + auto end1 = set1->end(); + auto end2 = set2->end(); + while (iter1 != end1 && iter2 != end2) { + if (key_less(*iter1, *iter2)) + return -1; + else if (key_less(*iter2, *iter1)) + return 1; + ++iter1; + ++iter2; + } + // Sets are equal. + return 0; + } + else + return (size1 > size2) ? 1 : -1; +} + +//////////////////////////////////////////////////////////////// + +// Sort functions that do not require begin()/end() range. + +// reference arg +template> +requires std::predicate, + std::ranges::range_reference_t> +void +sort(Range& r, + const Comp &comp = Comp{}) +{ + std::sort(std::ranges::begin(r), std::ranges::end(r), comp); +} + + +// pointer arg +template> +requires std::ranges::random_access_range && + std::predicate, + std::ranges::range_reference_t> +void +sort(Range* r, + const Comp &comp = Comp{}) +{ + std::sort(std::ranges::begin(*r), std::ranges::end(*r), comp); +} + +} // namespace sta diff --git a/include/sta/Corner.hh b/include/sta/Corner.hh deleted file mode 100644 index bee765d00..000000000 --- a/include/sta/Corner.hh +++ /dev/null @@ -1,139 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include "MinMax.hh" -#include "Vector.hh" -#include "StringSet.hh" -#include "GraphClass.hh" -#include "SearchClass.hh" -#include "StaState.hh" - -namespace sta { - -class ParasiticAnalysisPt; -class DcalcAnalysisPt; -class PathAnalysisPt; -class Corner; -class Corners; -class LibertyLibrary; - -typedef Vector CornerSeq; -typedef Map CornerMap; -typedef Vector ParasiticAnalysisPtSeq; -typedef Vector DcalcAnalysisPtSeq; -typedef Vector PathAnalysisPtSeq; -typedef Vector LibertySeq; - -class Corners : public StaState -{ -public: - explicit Corners(StaState *sta); - ~Corners(); - void clear(); - int count() const; - void copy(Corners *corners); - bool multiCorner() const; - Corner *findCorner(const char *corner); - Corner *findCorner(int corner_index); - void makeCorners(StringSet *corner_names); - void analysisTypeChanged(); - void operatingConditionsChanged(); - - // Make one parasitic analysis points. - void makeParasiticAnalysisPts(bool per_corner); - int parasiticAnalysisPtCount() const; - ParasiticAnalysisPtSeq ¶siticAnalysisPts(); - - DcalcAPIndex dcalcAnalysisPtCount() const; - DcalcAnalysisPtSeq &dcalcAnalysisPts(); - const DcalcAnalysisPtSeq &dcalcAnalysisPts() const; - - PathAPIndex pathAnalysisPtCount() const; - PathAnalysisPt *findPathAnalysisPt(PathAPIndex path_index) const; - PathAnalysisPtSeq &pathAnalysisPts(); - const PathAnalysisPtSeq &pathAnalysisPts() const; - CornerSeq &corners() { return corners_; } - // Iterators for range iteration. - // for (auto corner : *sta->corners()) {} - CornerSeq::iterator begin() { return corners_.begin(); } - CornerSeq::iterator end() { return corners_.end(); } - -protected: - void makeAnalysisPts(); - void makeDcalcAnalysisPts(Corner *corner); - DcalcAnalysisPt *makeDcalcAnalysisPt(Corner *corner, - const MinMax *min_max, - const MinMax *check_clk_slew_min_max); - void makePathAnalysisPts(Corner *corner); - void makePathAnalysisPts(Corner *corner, - bool swap_clk_min_max, - DcalcAnalysisPt *dcalc_ap_min, - DcalcAnalysisPt *dcalc_ap_max); - -private: - CornerMap corner_map_; - CornerSeq corners_; - ParasiticAnalysisPtSeq parasitic_analysis_pts_; - DcalcAnalysisPtSeq dcalc_analysis_pts_; - PathAnalysisPtSeq path_analysis_pts_; -}; - -class Corner -{ -public: - Corner(const char *name, - int index); - const char *name() const { return name_.c_str(); } - int index() const { return index_; } - ParasiticAnalysisPt *findParasiticAnalysisPt(const MinMax *min_max) const; - int parasiticAnalysisPtcount(); - DcalcAnalysisPt *findDcalcAnalysisPt(const MinMax *min_max) const; - PathAnalysisPt *findPathAnalysisPt(const MinMax *min_max) const; - void addLiberty(LibertyLibrary *lib, - const MinMax *min_max); - const LibertySeq &libertyLibraries(const MinMax *min_max) const; - int libertyIndex(const MinMax *min_max) const; - -protected: - void setParasiticAnalysisPtcount(int ap_count); - void setParasiticAP(ParasiticAnalysisPt *path_ap, - int mm_index); - void setDcalcAnalysisPtcount(DcalcAPIndex ap_count); - void addDcalcAP(DcalcAnalysisPt *dcalc_ap); - void addPathAP(PathAnalysisPt *path_ap); - -private: - std::string name_; - int index_; - ParasiticAnalysisPtSeq parasitic_analysis_pts_; - DcalcAnalysisPtSeq dcalc_analysis_pts_; - PathAnalysisPtSeq path_analysis_pts_; - LibertySeq liberty_[MinMax::index_count]; - - friend class Corners; -}; - -} // namespace diff --git a/include/sta/CycleAccting.hh b/include/sta/CycleAccting.hh index 3cc5a0452..8eb369070 100644 --- a/include/sta/CycleAccting.hh +++ b/include/sta/CycleAccting.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,11 +24,12 @@ #pragma once -#include "UnorderedSet.hh" +#include + #include "MinMax.hh" -#include "TimingRole.hh" -#include "StaState.hh" #include "SdcClass.hh" +#include "StaState.hh" +#include "TimingRole.hh" namespace sta { @@ -42,17 +43,19 @@ class CycleAcctingEqual { public: bool operator()(const CycleAccting *acct1, - const CycleAccting *acct2) const; + const CycleAccting *acct2) const; }; class CycleAcctingLess { public: bool operator()(const CycleAccting *acct1, - const CycleAccting *acct2) const; + const CycleAccting *acct2) const; }; -typedef UnorderedSet CycleAcctingSet; +using CycleAcctingSet = std::unordered_set; class CycleAcctings { @@ -63,7 +66,7 @@ public: // Find the cycle accounting info for paths that start at src clock // edge and end at target clock edge. CycleAccting *cycleAccting(const ClockEdge *src, - const ClockEdge *tgt); + const ClockEdge *tgt); void reportClkToClkMaxCycleWarnings(Report *report); private: @@ -75,7 +78,7 @@ class CycleAccting { public: CycleAccting(const ClockEdge *src, - const ClockEdge *tgt); + const ClockEdge *tgt); // Fill in required times. void findDelays(StaState *sta); // Find delays when source clk edge is the default arrival clock edge @@ -92,26 +95,26 @@ public: private: void setHoldAccting(int src_cycle, - int tgt_cycle, - float delay, - float req); + int tgt_cycle, + float delay, + float req); void setAccting(const TimingRole *role, - int src_cycle, - int tgt_cycle, - float delay, - float req); + int src_cycle, + int tgt_cycle, + float delay, + float req); void setSetupAccting(int src_cycle, - int tgt_cycle, - float delay, - float req); + int tgt_cycle, + float delay, + float req); void setDefaultSetupAccting(int src_cycle, - int tgt_cycle, - float delay, - float req); + int tgt_cycle, + float delay, + float req); void setDefaultHoldAccting(int src_cycle, - int tgt_cycle, - float delay, - float req); + int tgt_cycle, + float delay, + float req); int firstCycle(const ClockEdge *clk_edge) const; const ClockEdge *src_; @@ -124,7 +127,7 @@ private: int src_cycle_[TimingRole::index_max + 1]; // Target clock cycle offset. int tgt_cycle_[TimingRole::index_max + 1]; - bool max_cycles_exceeded_; + bool max_cycles_exceeded_{false}; }; -} // namespace +} // namespace sta diff --git a/include/sta/DataCheck.hh b/include/sta/DataCheck.hh index 3e55f4a86..e14c6331e 100644 --- a/include/sta/DataCheck.hh +++ b/include/sta/DataCheck.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,12 +24,12 @@ #pragma once -#include "MinMax.hh" #include "LibertyClass.hh" +#include "MinMax.hh" #include "NetworkClass.hh" #include "NetworkCmp.hh" -#include "SdcClass.hh" #include "RiseFallMinMax.hh" +#include "SdcClass.hh" namespace sta { @@ -37,29 +37,29 @@ class DataCheck { public: DataCheck(Pin *from, - Pin *to, - Clock *clk); + Pin *to, + Clock *clk); Pin *from() const { return from_; } Pin *to() const { return to_; } Clock *clk() const { return clk_; } void margin(const RiseFall *from_rf, - const RiseFall *to_rf, - const SetupHold *setup_hold, - // Return values. - float &margin, - bool &exists) const; + const RiseFall *to_rf, + const SetupHold *setup_hold, + // Return values. + float &margin, + bool &exists) const; void setMargin(const RiseFallBoth *from_rf, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold, - float margin); + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold, + float margin); void removeMargin(const RiseFallBoth *from_rf, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold); - bool empty() const; + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold); + [[nodiscard]] bool empty() const; void marginIsOneValue(const SetupHold *setup_hold, - // Return values. - float &value, - bool &one_value) const; + // Return values. + float &value, + bool &one_value) const; private: Pin *from_; @@ -73,10 +73,10 @@ class DataCheckLess public: DataCheckLess(const Network *network); bool operator()(const DataCheck *check1, - const DataCheck *check2) const; + const DataCheck *check2) const; private: const Network *network_; }; -} // namespace +} // namespace sta diff --git a/include/sta/DcalcAnalysisPt.hh b/include/sta/DcalcAnalysisPt.hh deleted file mode 100644 index 76af5fe8b..000000000 --- a/include/sta/DcalcAnalysisPt.hh +++ /dev/null @@ -1,81 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include "Iterator.hh" -#include "MinMax.hh" -#include "LibertyClass.hh" -#include "SdcClass.hh" -#include "ParasiticsClass.hh" -#include "GraphClass.hh" -#include "StaState.hh" - -namespace sta { - -class Corner; - -// Delay calculation analysis point. -// This collects all of the parameters used to find one set of -// delay calculation results. -class DcalcAnalysisPt -{ -public: - DcalcAnalysisPt(Corner *corner, - DcalcAPIndex index, - const OperatingConditions *op_cond, - const MinMax *min_max, - const MinMax *check_clk_slew_min_max); - Corner *corner() const { return corner_; } - // Which of the delay_count results this analysis point corresponds to. - DcalcAPIndex index() const { return index_; } - // Slew index of timing check data. - DcalcAPIndex checkDataSlewIndex() const { return index_; } - // Slew index of timing check clock. - DcalcAPIndex checkClkSlewIndex() const { return check_clk_slew_index_; } - // Slew min/max of timing check clock. - const MinMax *checkClkSlewMinMax() const { return check_clk_slew_min_max_; } - // Constraint min/max values to use. - const MinMax *constraintMinMax() const { return min_max_; } - // Constraints::operatingCondition(cnst_min_max_) - const OperatingConditions *operatingConditions() const { return op_cond_; } - void setOperatingConditions(const OperatingConditions *op_cond); - // Delay merging min/max operator (for wires). - const MinMax *delayMinMax() const { return min_max_; } - // Merge min/max slews across timing arcs. - const MinMax *slewMinMax() const { return min_max_; } - ParasiticAnalysisPt *parasiticAnalysisPt() const; - void setCheckClkSlewIndex(DcalcAPIndex index); - int libertyIndex() const; - -private: - Corner *corner_; - DcalcAPIndex index_; - DcalcAPIndex check_clk_slew_index_; - const OperatingConditions *op_cond_; - const MinMax *min_max_; - const MinMax *check_clk_slew_min_max_; -}; - -} // namespace diff --git a/include/sta/Debug.hh b/include/sta/Debug.hh index 0f3923aed..876009a40 100644 --- a/include/sta/Debug.hh +++ b/include/sta/Debug.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,10 +24,13 @@ #pragma once -#include +#include #include +#include +#include -#include "Map.hh" +#include "Format.hh" +#include "Report.hh" #include "StringUtil.hh" namespace sta { @@ -35,40 +38,42 @@ namespace sta { class Report; class Pin; -typedef Map DebugMap; +using DebugMap = std::map>; class Debug { public: - explicit Debug(Report *report); - ~Debug(); - int level(const char *what); - void setLevel(const char *what, - int level); - bool check(const char *what, - int level) const; + Debug(Report *report); + int level(std::string_view what); + void setLevel(std::string_view what, + int level); + bool check(std::string_view what, + int level) const; int statsLevel() const { return stats_level_; } - void reportLine(const char *what, - const char *fmt, - ...) - __attribute__((format (printf, 3, 4))); + template + void report(std::string_view what, + std::string_view fmt, + Args &&...args) + { + std::string msg = sta::format("{}: {}", what, + sta::formatRuntime(fmt, std::forward(args)...)); + std::unique_lock lock(buffer_lock_); + report_->reportLine(msg); + } protected: Report *report_; std::mutex buffer_lock_; - bool debug_on_; - DebugMap *debug_map_; - int stats_level_; + bool debug_on_{false}; + DebugMap debug_map_; + int stats_level_{0}; }; // Inlining a varargs function would eval the args, which can // be expensive, so use a macro. -// Note that "##__VA_ARGS__" is a gcc extension to support zero arguments (no comma). -// clang -Wno-gnu-zero-variadic-macro-arguments suppresses the warning. -// c++20 has "__VA_OPT__" to deal with the zero arg case so this is temporary. -#define debugPrint(debug, what, level, ...) \ +#define debugPrint(debug, what, level, fmt, ...) \ if (debug->check(what, level)) { \ - debug->reportLine(what, ##__VA_ARGS__); \ + debug->report(what, fmt __VA_OPT__(,) __VA_ARGS__); \ } -} // namespace +} // namespace sta diff --git a/include/sta/Delay.hh b/include/sta/Delay.hh index 236158c92..6a6984bdf 100644 --- a/include/sta/Delay.hh +++ b/include/sta/Delay.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,27 +24,336 @@ #pragma once -#include "StaConfig.hh" +#include +#include -// IWYU pragma: begin_exports -#if (SSTA == 1) - // Delays are Normal PDFs. - #include "DelayNormal1.hh" -#elif (SSTA == 2) - // Delays are Normal PDFs with early/late sigma. - #include "DelayNormal2.hh" -#else - // Delays are floats. - #include "DelayFloat.hh" -#endif -// IWYU pragma: end_exports +#include "MinMax.hh" +#include "StaConfig.hh" namespace sta { -typedef Delay ArcDelay; -typedef Delay Slew; -typedef Delay Arrival; -typedef Delay Required; -typedef Delay Slack; +class StaState; + +class Delay +{ +public: + Delay() noexcept; + Delay(float mean) noexcept; + Delay(float mean, + // std_dev^2 + float std_dev2) noexcept; + Delay(float mean, + float mean_shift, + // std_dev^2 + float std_dev2, + float skewness) noexcept; + void setValues(float mean, + float mean_shift, + float std_dev2, + float skewnes); + float mean() const { return values_[0]; } + void setMean(float mean); + float meanShift() const { return values_[1]; } + void setMeanShift(float mean_shift); + float stdDev() const; + // std_dev ^ 2 + float stdDev2() const { return values_[2]; } + void setStdDev(float std_dev); + float skewness() const { return values_[3]; } + void setSkewness(float skewness); + + Delay &operator=(float delay); + // This allows applications that do not support statistical timing + // to treat Delays as floats without explicitly converting with + // delayAsFloat. + operator float() const { return mean(); } + +private: + std::array values_; +}; + +// Delay with doubles for accumulating Delays. +// Only a subset of operations are required for DelayDbl. +class DelayDbl +{ +public: + DelayDbl() noexcept; + DelayDbl(double mean) noexcept; + double mean() const { return values_[0]; } + void setMean(double mean); + double meanShift() const { return values_[1]; } + // std_dev ^ 2 + double stdDev2() const { return values_[2]; } + double stdDev() const; + double skewness() const { return values_[3]; } + void setValues(double mean, + double mean_shift, + double std_dev2, + double skewnes); + + DelayDbl &operator=(double delay); + +private: + std::array values_; +}; + +using ArcDelay = Delay; +using Slew = Delay; +using Arrival = Delay; +using Required = Delay; +using Slack = Delay; + +const Delay delay_zero(0.0); + +class DelayOps +{ +public: + virtual ~DelayOps() = default; + virtual float stdDev2(const Delay &delay, + const EarlyLate *early_late) const = 0; + virtual float asFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) const = 0; + virtual double asFloat(const DelayDbl &delay, + const EarlyLate *early_late, + const StaState *sta) const = 0; + virtual bool isZero(const Delay &delay) const = 0; + virtual bool isInf(const Delay &delay) const = 0; + virtual bool equal(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const = 0; + virtual bool less(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const = 0; + virtual bool less(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *sta) const = 0; + virtual bool lessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const = 0; + virtual bool greater(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const = 0; + virtual bool greaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const = 0; + virtual Delay sum(const Delay &delay1, + const Delay &delay2) const = 0; + virtual Delay sum(const Delay &delay1, + float delay2) const = 0; + virtual Delay diff(const Delay &delay1, + const Delay &delay2) const = 0; + virtual Delay diff(const Delay &delay1, + float delay2) const = 0; + virtual Delay diff(float delay1, + const Delay &delay2) const = 0; + virtual void incr(Delay &delay1, + const Delay &delay2) const = 0; + virtual void incr(DelayDbl &delay1, + const Delay &delay2) const = 0; + virtual void decr(Delay &delay1, + const Delay &delay2) const = 0; + virtual void decr(DelayDbl &delay1, + const Delay &delay2) const = 0; + virtual Delay product(const Delay &delay1, + float delay2) const = 0; + virtual Delay div(float delay1, + const Delay &delay2) const = 0; + virtual std::string asStringVariance(const Delay &delay, + int digits, + const StaState *sta) const = 0; + +}; + +void +initDelayConstants(); + +inline float +square(float x) +{ + return x * x; +} + +inline double +square(double x) +{ + return x * x; +} + +inline float +cube(float x) +{ + return x * x * x; +} + +inline double +cube(double x) +{ + return x * x * x; +} + +Delay +makeDelay(float mean, + float mean_shift, + float std_dev, + float skewness); +Delay +makeDelay(float mean, + float std_dev); +Delay +makeDelay2(float mean, + float std_dev); +void +delaySetMean(Delay &delay, + float mean); + +// early_late == late +std::string +delayAsString(const Delay &delay, + const StaState *sta); +// early_late == late +std::string +delayAsString(const Delay &delay, + int digits, + const StaState *sta); +std::string +delayAsString(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta); +std::string +delayAsString(const Delay &delay, + const EarlyLate *early_late, + int digits, + const StaState *sta); +std::string +delayAsString(const Delay &delay, + const EarlyLate *early_late, + bool report_variance, + int digits, + const StaState *sta); + +float +delayAsFloat(const Delay &delay); +float +delayAsFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta); +float +delayAsFloat(const DelayDbl &delay, + const EarlyLate *early_late, + const StaState *sta); + +Delay +delayDblAsDelay(DelayDbl &delay); + +Delay +delaySum(const Delay &delay1, + const Delay &delay2, + const StaState *sta); +Delay +delaySum(const Delay &delay1, + float delay2, + const StaState *sta); +Delay +delayDiff(const Delay &delay1, + const Delay &delay2, + const StaState *sta); +Delay +delayDiff(const Delay &delay1, + float delay2, + const StaState *sta); +Delay +delayDiff(float delay1, + const Delay &delay2, + const StaState *sta); +void +delayIncr(Delay &delay1, + const Delay &delay2, + const StaState *sta); +void +delayIncr(DelayDbl &delay1, + const Delay &delay2, + const StaState *sta); +void +delayIncr(Delay &delay1, + float delay2, + const StaState *sta); +void +delayDecr(Delay &delay1, + const Delay &delay2, + const StaState *sta); +void +delayDecr(DelayDbl &delay1, + const Delay &delay2, + const StaState *sta); +Delay +delayProduct(const Delay &delay1, + float delay2, + const StaState *sta); +Delay +delayDiv(float delay1, + const Delay &delay2, + const StaState *sta); + +const Delay & +delayInitValue(const MinMax *min_max); +bool +delayIsInitValue(const Delay &delay, + const MinMax *min_max); +bool +delayZero(const Delay &delay, + const StaState *sta); +bool +delayInf(const Delay &delay, + const StaState *sta); +bool +delayEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta); +bool +delayLess(const Delay &delay1, + const Delay &delay2, + const StaState *sta); +bool +delayLess(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *sta); +bool +delayLess(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); +bool +delayLessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta); +bool +delayLessEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); +bool +delayGreater(const Delay &delay1, + const Delay &delay2, + const StaState *sta); +bool +delayGreaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta); +bool +delayGreaterEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); +bool +delayGreater(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); + +// delay1-delay2 subtracting sigma instead of addiing. +Delay +delayRemove(const Delay &delay1, + const Delay &delay2); -} // namespace +} // namespace sta diff --git a/include/sta/DelayCalc.hh b/include/sta/DelayCalc.hh index ff1c96295..051279762 100644 --- a/include/sta/DelayCalc.hh +++ b/include/sta/DelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,24 +24,27 @@ #pragma once -#include "StringSeq.hh" +#include +#include + +#include "StringUtil.hh" namespace sta { class ArcDelayCalc; class StaState; -typedef ArcDelayCalc *(*MakeArcDelayCalc)(StaState *sta); +using MakeArcDelayCalc = ArcDelayCalc *(*)(StaState *sta); // Register builtin delay calculators. void registerDelayCalcs(); // Register a delay calculator for the set_delay_calc command. void -registerDelayCalc(const char *name, - MakeArcDelayCalc maker); +registerDelayCalc(std::string_view name, + MakeArcDelayCalc maker); bool -isDelayCalcName(const char *name); +isDelayCalcName(std::string_view name); StringSeq delayCalcNames(); void @@ -49,7 +52,7 @@ deleteDelayCalcs(); // Make a registered delay calculator by name. ArcDelayCalc * -makeDelayCalc(const char *name, - StaState *sta); +makeDelayCalc(std::string_view name, + StaState *sta); -} // namespace +} // namespace sta diff --git a/include/sta/DelayFloat.hh b/include/sta/DelayFloat.hh deleted file mode 100644 index 88aeca964..000000000 --- a/include/sta/DelayFloat.hh +++ /dev/null @@ -1,152 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include "MinMax.hh" - -// Delay values defined as floats. - -namespace sta { - -class StaState; - -typedef float Delay; -// Delay double for accumulating Delays. -typedef double DelayDbl; - -const Delay delay_zero = 0.0; - -void -initDelayConstants(); - -const char * -delayAsString(const Delay &delay, - const StaState *sta); -const char * -delayAsString(const Delay &delay, - const StaState *sta, - int digits); -const char * -delayAsString(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta, - int digits); - -inline Delay -makeDelay(float delay, - float, - float) -{ - return delay; -} - -inline Delay -makeDelay2(float delay, - float, - float) -{ - return delay; -} - -inline float -delayAsFloat(const Delay &delay) -{ - return delay; -} - -// mean late+/early- sigma -inline float -delayAsFloat(const Delay &delay, - const EarlyLate *, - const StaState *) -{ - return delay; -} - -inline float -delaySigma2(const Delay &, - const EarlyLate *) -{ - return 0.0; -} - -const Delay & -delayInitValue(const MinMax *min_max); -bool -delayIsInitValue(const Delay &delay, - const MinMax *min_max); -bool -delayZero(const Delay &delay); -bool -delayInf(const Delay &delay); -bool -delayEqual(const Delay &delay1, - const Delay &delay2); -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const StaState *sta); -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta); -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); -bool -delayGreater(const Delay &delay1, - const Delay &delay2, - const StaState *sta); -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta); -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); -bool -delayGreater(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); - -// delay1-delay2 subtracting sigma instead of addiing. -Delay -delayRemove(const Delay &delay1, - const Delay &delay2); -float -delayRatio(const Delay &delay1, - const Delay &delay2); - -} // namespace diff --git a/include/sta/DelayNormal.hh b/include/sta/DelayNormal.hh new file mode 100644 index 000000000..8b94725e4 --- /dev/null +++ b/include/sta/DelayNormal.hh @@ -0,0 +1,90 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include "Delay.hh" + +namespace sta { + +class DelayOpsNormal : public DelayOps +{ +public: + float stdDev2(const Delay &delay, + const EarlyLate *early_late) const override; + float asFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) const override; + double asFloat(const DelayDbl &delay, + const EarlyLate *early_late, + const StaState *sta) const override; + + bool isZero(const Delay &delay) const override; + bool isInf(const Delay &delay) const override; + bool equal(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool less(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool less(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *sta) const override; + bool lessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool greater(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool greaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + Delay sum(const Delay &delay1, + const Delay &delay2) const override; + Delay sum(const Delay &delay1, + float delay2) const override; + Delay diff(const Delay &delay1, + const Delay &delay2) const override; + Delay diff(const Delay &delay1, + float delay2) const override; + Delay diff(float delay1, + const Delay &delay2) const override; + void incr(Delay &delay1, + const Delay &delay2) const override; + void incr(DelayDbl &delay1, + const Delay &delay2) const override; + void decr(Delay &delay1, + const Delay &delay2) const override; + void decr(DelayDbl &delay1, + const Delay &delay2) const override; + Delay product(const Delay &delay1, + float delay2) const override; + Delay div(float delay1, + const Delay &delay2) const override; + std::string asStringVariance(const Delay &delay, + int digits, + const StaState *sta) const override; +}; + +} // namespace sta diff --git a/include/sta/DelayNormal1.hh b/include/sta/DelayNormal1.hh deleted file mode 100644 index e722bc4ef..000000000 --- a/include/sta/DelayNormal1.hh +++ /dev/null @@ -1,203 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include "MinMax.hh" - -namespace sta { - -class Delay; -class DelayDbl; -class StaState; - -// Normal distribution with std deviation. -class Delay -{ -public: - Delay(); - Delay(const Delay &delay); - Delay(const DelayDbl &delay); - Delay(float mean); - Delay(float mean, - float sigma2); - float mean() const { return mean_; } - float sigma() const; - // sigma^2 - float sigma2() const; - void operator=(const Delay &delay); - void operator=(float delay); - void operator+=(const Delay &delay); - void operator+=(float delay); - Delay operator+(const Delay &delay) const; - Delay operator+(float delay) const; - Delay operator-(const Delay &delay) const; - Delay operator-(float delay) const; - Delay operator-() const; - void operator-=(float delay); - void operator-=(const Delay &delay); - bool operator==(const Delay &delay) const; - -private: - float mean_; - // Sigma^2 - float sigma2_; - - friend class DelayDbl; -}; - -// Dwlay with doubles for accumulating delays. -class DelayDbl -{ -public: - DelayDbl(); - float mean() const { return mean_; } - float sigma() const; - // sigma^2 - float sigma2() const; - void operator=(float delay); - void operator+=(const Delay &delay); - void operator-=(const Delay &delay); - -private: - double mean_; - // Sigma^2 - double sigma2_; - - friend class Delay; -}; - -const Delay delay_zero(0.0); - -void -initDelayConstants(); - -const char * -delayAsString(const Delay &delay, - const StaState *sta); -const char * -delayAsString(const Delay &delay, - const StaState *sta, - int digits); -const char * -delayAsString(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta, - int digits); - -Delay -makeDelay(float delay, - float sigma_early, - float sigma_late); - -Delay -makeDelay2(float delay, - // sigma^2 - float sigma_early, - float sigma_late); - -inline float -delayAsFloat(const Delay &delay) -{ - return delay.mean(); -} - -// mean late+/early- sigma -float -delayAsFloat(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta); -float -delaySigma2(const Delay &delay, - const EarlyLate *early_late); -const Delay & -delayInitValue(const MinMax *min_max); -bool -delayIsInitValue(const Delay &delay, - const MinMax *min_max); -bool -delayZero(const Delay &delay); -bool -delayInf(const Delay &delay); -bool -delayEqual(const Delay &delay1, - const Delay &delay2); -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const StaState *sta); -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta); -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); -bool -delayGreater(const Delay &delay1, - const Delay &delay2, - const StaState *sta); -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta); -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); -bool -delayGreater(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); - -// delay1-delay2 subtracting sigma instead of addiing. -Delay delayRemove(const Delay &delay1, - const Delay &delay2); -float -delayRatio(const Delay &delay1, - const Delay &delay2); - -// Most non-operator functions on Delay are not defined as member -// functions so they can be defined on floats, where there is no class -// to define them. - -Delay operator+(float delay1, - const Delay &delay2); -// Used for parallel gate delay calc. -Delay operator/(float delay1, - const Delay &delay2); -// Used for parallel gate delay calc. -Delay operator*(const Delay &delay1, - float delay2); - -} // namespace diff --git a/include/sta/DelayNormal2.hh b/include/sta/DelayNormal2.hh deleted file mode 100644 index 10e7a9655..000000000 --- a/include/sta/DelayNormal2.hh +++ /dev/null @@ -1,214 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include "MinMax.hh" - -namespace sta { - -class Delay; -class DelayDbl; -class StaState; - -// Normal distribution with early(left)/late(right) std deviations. -class Delay -{ -public: - Delay(); - Delay(const Delay &delay); - Delay(const DelayDbl &delay); - Delay(float mean); - Delay(float mean, - float sigma2_early, - float sigma2_late); - float mean() const { return mean_; } - float sigma(const EarlyLate *early_late) const; - // sigma^2 - float sigma2(const EarlyLate *early_late) const; - float sigma2Early() const; - float sigma2Late() const; - void operator=(const Delay &delay); - void operator=(float delay); - void operator+=(const Delay &delay); - void operator+=(float delay); - Delay operator+(const Delay &delay) const; - Delay operator+(float delay) const; - Delay operator-(const Delay &delay) const; - Delay operator-(float delay) const; - Delay operator-() const; - void operator-=(float delay); - void operator-=(const Delay &delay); - bool operator==(const Delay &delay) const; - -protected: - static const int early_index = 0; - static const int late_index = 1; - -private: - float mean_; - // Sigma^2 - float sigma2_[EarlyLate::index_count]; - - friend class DelayDbl; -}; - -// Dwlay with doubles for accumulating delays. -class DelayDbl -{ -public: - DelayDbl(); - float mean() const { return mean_; } - float sigma() const; - // sigma^2 - float sigma2() const; - void operator=(float delay); - void operator+=(const Delay &delay); - void operator-=(const Delay &delay); - -protected: - static const int early_index = 0; - static const int late_index = 1; - -private: - double mean_; - // Sigma^2 - double sigma2_[EarlyLate::index_count]; - - friend class Delay; -}; - -const Delay delay_zero(0.0); - -void -initDelayConstants(); - -const char * -delayAsString(const Delay &delay, - const StaState *sta); -const char * -delayAsString(const Delay &delay, - const StaState *sta, - int digits); -const char * -delayAsString(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta, - int digits); - -Delay -makeDelay(float delay, - float sigma_early, - float sigma_late); - -Delay -makeDelay2(float delay, - // sigma^2 - float sigma_early, - float sigma_late); - -inline float -delayAsFloat(const Delay &delay) -{ - return delay.mean(); -} - -// mean late+/early- sigma -float -delayAsFloat(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta); -float -delaySigma2(const Delay &delay, - const EarlyLate *early_late); -const Delay & -delayInitValue(const MinMax *min_max); -bool -delayIsInitValue(const Delay &delay, - const MinMax *min_max); -bool -delayZero(const Delay &delay); -bool -delayInf(const Delay &delay); -bool -delayEqual(const Delay &delay1, - const Delay &delay2); -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const StaState *sta); -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta); -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); -bool -delayGreater(const Delay &delay1, - const Delay &delay2, - const StaState *sta); -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta); -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); -bool -delayGreater(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); - -// delay1-delay2 subtracting sigma instead of addiing. -Delay delayRemove(const Delay &delay1, - const Delay &delay2); -float -delayRatio(const Delay &delay1, - const Delay &delay2); - -// Most non-operator functions on Delay are not defined as member -// functions so they can be defined on floats, where there is no class -// to define them. - -Delay operator+(float delay1, - const Delay &delay2); -// Used for parallel gate delay calc. -Delay operator/(float delay1, - const Delay &delay2); -// Used for parallel gate delay calc. -Delay operator*(const Delay &delay1, - float delay2); - -} // namespace diff --git a/include/sta/DelayScalar.hh b/include/sta/DelayScalar.hh new file mode 100644 index 000000000..d255be3c1 --- /dev/null +++ b/include/sta/DelayScalar.hh @@ -0,0 +1,90 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include "Delay.hh" + +namespace sta { + +class DelayOpsScalar : public DelayOps +{ +public: + float stdDev2(const Delay &delay, + const EarlyLate *early_late) const override; + float asFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) const override; + double asFloat(const DelayDbl &delay, + const EarlyLate *early_late, + const StaState *sta) const override; + + bool isZero(const Delay &delay) const override; + bool isInf(const Delay &delay) const override; + bool equal(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool less(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool less(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *sta) const override; + bool lessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool greater(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool greaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + Delay sum(const Delay &delay1, + const Delay &delay2) const override; + Delay sum(const Delay &delay1, + float delay2) const override; + Delay diff(const Delay &delay1, + const Delay &delay2) const override; + Delay diff(const Delay &delay1, + float delay2) const override; + Delay diff(float delay1, + const Delay &delay2) const override; + void incr(Delay &delay1, + const Delay &delay2) const override; + void incr(DelayDbl &delay1, + const Delay &delay2) const override; + void decr(Delay &delay1, + const Delay &delay2) const override; + void decr(DelayDbl &delay1, + const Delay &delay2) const override; + Delay product(const Delay &delay1, + float delay2) const override; + Delay div(float delay1, + const Delay &delay2) const override; + std::string asStringVariance(const Delay &delay, + int digits, + const StaState *sta) const override; +}; + +} // namespace sta diff --git a/include/sta/DelaySkewNormal.hh b/include/sta/DelaySkewNormal.hh new file mode 100644 index 000000000..dacce47cd --- /dev/null +++ b/include/sta/DelaySkewNormal.hh @@ -0,0 +1,98 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include "Delay.hh" + +namespace sta { + +class DelayOpsSkewNormal : public DelayOps +{ +public: + float stdDev2(const Delay &delay, + const EarlyLate *early_late) const override; + float asFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) const override; + double asFloat(const DelayDbl &delay, + const EarlyLate *early_late, + const StaState *sta) const override; + + bool isZero(const Delay &delay) const override; + bool isInf(const Delay &delay) const override; + bool equal(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool less(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool less(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *sta) const override; + bool lessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *) const override; + bool greater(const Delay &delay1, + const Delay &delay2, + const StaState *) const override; + bool greaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *) const override; + Delay sum(const Delay &delay1, + const Delay &delay2) const override; + Delay sum(const Delay &delay1, + float delay2) const override; + Delay diff(const Delay &delay1, + const Delay &delay2) const override; + Delay diff(const Delay &delay1, + float delay2) const override; + Delay diff(float delay1, + const Delay &delay2) const override; + void incr(Delay &delay1, + const Delay &delay2) const override; + void incr(DelayDbl &delay1, + const Delay &delay2) const override; + void decr(Delay &delay1, + const Delay &delay2) const override; + void decr(DelayDbl &delay1, + const Delay &delay2) const override; + Delay product(const Delay &delay1, + float delay2) const override; + Delay div(float delay1, + const Delay &delay2) const override; + std::string asStringVariance(const Delay &delay, + int digits, + const StaState *sta) const override; + +private: + float skewnessSum(const Delay &delay1, + const Delay &delay2) const; + double skewnessSum(double std_dev1, + double skewness1, + double std_dev2, + double skewness2) const; +}; + +} // namespace sta diff --git a/include/sta/DeratingFactors.hh b/include/sta/DeratingFactors.hh index aa4808a0a..005318302 100644 --- a/include/sta/DeratingFactors.hh +++ b/include/sta/DeratingFactors.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,10 +24,10 @@ #pragma once -#include "MinMax.hh" #include "LibertyClass.hh" -#include "SdcClass.hh" +#include "MinMax.hh" #include "RiseFallMinMax.hh" +#include "SdcClass.hh" namespace sta { @@ -36,22 +36,22 @@ class DeratingFactors public: DeratingFactors(); void setFactor(PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float factor); + const RiseFallBoth *rf, + const EarlyLate *early_late, + float factor); void factor(PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late, - float &factor, - bool &exists) const; + const RiseFall *rf, + const EarlyLate *early_late, + float &factor, + bool &exists) const; void clear(); void isOneValue(const EarlyLate *early_late, - bool &is_one_value, - float &value) const; + bool &is_one_value, + float &value) const; void isOneValue(PathClkOrData clk_data, - const EarlyLate *early_late, - bool &is_one_value, - float &value) const; + const EarlyLate *early_late, + bool &is_one_value, + float &value) const; bool hasValue() const; private: @@ -63,22 +63,22 @@ class DeratingFactorsGlobal public: DeratingFactorsGlobal(); void setFactor(TimingDerateType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float factor); + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float factor); void factor(TimingDerateType type, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late, - float &factor, - bool &exists) const; + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late, + float &factor, + bool &exists) const; void factor(TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late, - float &factor, - bool &exists) const; + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late, + float &factor, + bool &exists) const; DeratingFactors *factors(TimingDerateType type); void clear(); @@ -91,30 +91,24 @@ class DeratingFactorsCell public: DeratingFactorsCell(); void setFactor(TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float factor); + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float factor); void factor(TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late, - float &factor, - bool &exists) const; + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late, + float &factor, + bool &exists) const; DeratingFactors *factors(TimingDerateCellType type); void clear(); void isOneValue(const EarlyLate *early_late, - bool &is_one_value, - float &value) const; + bool &is_one_value, + float &value) const; private: DeratingFactors factors_[timing_derate_cell_type_count]; }; -class DeratingFactorsNet : public DeratingFactors -{ -public: - DeratingFactorsNet(); -}; - -} // namespace +} // namespace sta diff --git a/include/sta/DisabledPorts.hh b/include/sta/DisabledPorts.hh index 6168247c5..2352b8a3e 100644 --- a/include/sta/DisabledPorts.hh +++ b/include/sta/DisabledPorts.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,9 +24,11 @@ #pragma once -#include "Map.hh" -#include "NetworkClass.hh" +#include +#include + #include "LibertyClass.hh" +#include "NetworkClass.hh" #include "SdcClass.hh" namespace sta { @@ -35,16 +37,15 @@ class TimingRole; class DisabledCellPorts; class DisabledInstancePorts; -typedef Vector DisabledInstancePortsSeq; -typedef Vector DisabledCellPortsSeq; -typedef Vector LibertyPortPairSeq; -typedef Set TimingArcSetSet; +using DisabledInstancePortsSeq = std::vector; +using DisabledCellPortsSeq = std::vector; +using LibertyPortPairSeq = std::vector; +using TimingArcSetSet = std::set; // Base class for disabled cell and instance ports. class DisabledPorts { public: - DisabledPorts(); ~DisabledPorts(); void setDisabledAll(); void removeDisabledAll(); @@ -56,19 +57,19 @@ public: LibertyPort *to); void removeDisabledFromTo(LibertyPort *from, LibertyPort *to); - bool isDisabled(LibertyPort *from, - LibertyPort *to, - const TimingRole *role); + [[nodiscard]] bool isDisabled(LibertyPort *from, + LibertyPort *to, + const TimingRole *role); LibertyPortPairSet *fromTo() const { return from_to_; } LibertyPortSet *from() const { return from_; } LibertyPortSet *to() const { return to_; } - bool all() const { return all_; } + [[nodiscard]] bool all() const { return all_; } private: - bool all_; - LibertyPortSet *from_; - LibertyPortSet *to_; - LibertyPortPairSet *from_to_; + bool all_{false}; + LibertyPortSet *from_{nullptr}; + LibertyPortSet *to_{nullptr}; + LibertyPortPairSet *from_to_{nullptr}; }; // set_disable_timing cell [-from] [-to] @@ -80,14 +81,14 @@ public: LibertyCell *cell() const { return cell_; } void setDisabled(TimingArcSet *arc_set); void removeDisabled(TimingArcSet *arc_set); - bool isDisabled(TimingArcSet *arc_set) const; + [[nodiscard]] bool isDisabled(TimingArcSet *arc_set) const; TimingArcSetSet *timingArcSets() const { return arc_sets_; } using DisabledPorts::isDisabled; private: LibertyCell *cell_; - TimingArcSetSet *arc_sets_; + TimingArcSetSet *arc_sets_{nullptr}; }; // set_disable_timing instance [-from] [-to] @@ -102,11 +103,11 @@ private: }; DisabledCellPortsSeq -sortByName(DisabledCellPortsMap *cell_map); +sortByName(const DisabledCellPortsMap *cell_map); DisabledInstancePortsSeq sortByPathName(const DisabledInstancePortsMap *inst_map, const Network *network); LibertyPortPairSeq sortByName(const LibertyPortPairSet *set); -} // namespace +} // namespace sta diff --git a/include/sta/DispatchQueue.hh b/include/sta/DispatchQueue.hh index f28d94c5e..ceda2e78f 100644 --- a/include/sta/DispatchQueue.hh +++ b/include/sta/DispatchQueue.hh @@ -5,23 +5,23 @@ #pragma once -#include -#include -#include +#include +#include #include -#include +#include #include -#include -#include +#include +#include +#include namespace sta { class DispatchQueue { - typedef std::function fp_t; + using fp_t = std::function; public: - DispatchQueue(size_t thread_cnt); + DispatchQueue(size_t thread_count); ~DispatchQueue(); void setThreadCount(size_t thread_count); size_t getThreadCount() const; @@ -49,4 +49,4 @@ private: bool quit_ = false; }; -} // namespace +} // namespace sta diff --git a/include/sta/EnumNameMap.hh b/include/sta/EnumNameMap.hh index d7c60c382..6af151c35 100644 --- a/include/sta/EnumNameMap.hh +++ b/include/sta/EnumNameMap.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -34,22 +34,23 @@ template class EnumNameMap { public: - EnumNameMap(std::initializer_list> enum_names); - const char *find(ENUM key) const; - ENUM find(std::string name, - ENUM unknown_key) const; - void find(std::string name, - // Return values. - ENUM &key, - bool &exists) const; + EnumNameMap(std::initializer_list> enum_names) noexcept; + const std::string &find(ENUM key) const; + ENUM find(std::string_view name, + ENUM unknown_key) const; + void find(std::string_view name, + // Return values. + ENUM &key, + bool &exists) const; private: std::map enum_map_; - std::map name_map_; + std::map> name_map_; }; template -EnumNameMap::EnumNameMap(std::initializer_list> enum_names) : +EnumNameMap::EnumNameMap(std::initializer_list> enum_names) noexcept : enum_map_(enum_names) { for (const auto& [key, name] : enum_map_) @@ -57,22 +58,24 @@ EnumNameMap::EnumNameMap(std::initializer_list -const char * +const std::string& EnumNameMap::find(ENUM key) const { auto find_iter = enum_map_.find(key); if (find_iter != enum_map_.end()) - return find_iter->second.c_str(); - else - return nullptr; + return find_iter->second; + else { + static std::string null_ref; + return null_ref; + } } template void -EnumNameMap::find(std::string name, - // Return values. - ENUM &key, - bool &exists) const +EnumNameMap::find(std::string_view name, + // Return values. + ENUM &key, + bool &exists) const { auto find_iter = name_map_.find(name); if (find_iter != name_map_.end()) { @@ -85,8 +88,8 @@ EnumNameMap::find(std::string name, template ENUM -EnumNameMap::find(std::string name, - ENUM unknown_key) const +EnumNameMap::find(std::string_view name, + ENUM unknown_key) const { auto find_iter = name_map_.find(name); if (find_iter != name_map_.end()) @@ -95,4 +98,4 @@ EnumNameMap::find(std::string name, return unknown_key; } -} // namespace +} // namespace sta diff --git a/include/sta/EquivCells.hh b/include/sta/EquivCells.hh index 4a337b0c5..510ee50f7 100644 --- a/include/sta/EquivCells.hh +++ b/include/sta/EquivCells.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,15 +24,15 @@ #pragma once -#include "Vector.hh" -#include "Map.hh" -#include "UnorderedMap.hh" +#include +#include + #include "LibertyClass.hh" namespace sta { -typedef Map EquivCellMap; -typedef UnorderedMap LibertyCellHashMap; +using EquivCellMap = std::map; +using LibertyCellHashMap = std::unordered_map; class EquivCells { @@ -40,16 +40,16 @@ public: // Find equivalent cells in equiv_libs. // Optionally add mappings for cells in map_libs. EquivCells(LibertyLibrarySeq *equiv_libs, - LibertyLibrarySeq *map_libs); + LibertyLibrarySeq *map_libs); ~EquivCells(); // Find equivalents for cell (member of from_libs) in to_libs. LibertyCellSeq *equivs(LibertyCell *cell); protected: void findEquivCells(const LibertyLibrary *library, - LibertyCellHashMap &hash_matches); + LibertyCellHashMap &hash_matches); void mapEquivCells(const LibertyLibrary *library, - LibertyCellHashMap &hash_matches); + LibertyCellHashMap &hash_matches); EquivCellMap equiv_cells_; // Unique cell for each equiv cell group. @@ -60,7 +60,7 @@ protected: // functions or timing arcs match. bool equivCells(const LibertyCell *cell1, - const LibertyCell *cell2); + const LibertyCell *cell2); // Predicate that is true when the ports, functions, sequentials and // timing arcs match. @@ -71,7 +71,7 @@ equivCellsArcs(const LibertyCell *cell1, // Predicate that is true when the ports match. bool equivCellPorts(const LibertyCell *cell1, - const LibertyCell *cell2); + const LibertyCell *cell2); // Predicate that is true cell functions match. bool @@ -81,10 +81,10 @@ equivCellFuncs(const LibertyCell *cell1, // Predicate that is true when the timing arc sets match. bool equivCellTimingArcSets(const LibertyCell *cell1, - const LibertyCell *cell2); + const LibertyCell *cell2); bool equivCellSequentials(const LibertyCell *cell1, - const LibertyCell *cell2); + const LibertyCell *cell2); -} // namespace +} // namespace sta diff --git a/include/sta/Error.hh b/include/sta/Error.hh index e5dc3b8f6..5754a99b0 100644 --- a/include/sta/Error.hh +++ b/include/sta/Error.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,6 +25,7 @@ #pragma once #include +#include #include "Report.hh" @@ -34,18 +35,16 @@ namespace sta { class Exception : public std::exception { public: - Exception(); - virtual ~Exception() {} - virtual const char *what() const noexcept = 0; + const char *what() const noexcept override = 0; }; class ExceptionMsg : public Exception { public: - ExceptionMsg(const char *msg, - const bool suppressed); - virtual const char *what() const noexcept; - virtual bool suppressed() const { return suppressed_; } + ExceptionMsg(const std::string &msg, + bool suppressed); + const char *what() const noexcept override; + bool suppressed() const { return suppressed_; } private: std::string msg_; @@ -55,11 +54,11 @@ private: class ExceptionLine : public Exception { public: - ExceptionLine(const char *filename, - int line); + ExceptionLine(const std::string &filename, + int line); protected: - const char *filename_; + std::string filename_; int line_; }; @@ -67,29 +66,29 @@ protected: class FileNotReadable : public Exception { public: - explicit FileNotReadable(const char *filename); - virtual const char *what() const noexcept; + FileNotReadable(std::string_view filename); + const char *what() const noexcept override; protected: - const char *filename_; + std::string msg_; }; // Failure opening filename for writing. class FileNotWritable : public Exception { public: - explicit FileNotWritable(const char *filename); - virtual const char *what() const noexcept; + FileNotWritable(std::string_view filename); + const char *what() const noexcept override; protected: - const char *filename_; + std::string msg_; }; // Report an error condition that should not be possible. // The default handler prints msg to stderr and exits. // The msg should NOT include a period or return. -// Only for use in those cases where a Report object is not available. -#define criticalError(id,msg) \ +// Only for use in those cases where a Report object is not available. +#define criticalError(id, msg) \ Report::defaultReport()->fileCritical(id, __FILE__, __LINE__, msg) -} // namespace +} // namespace sta diff --git a/include/sta/ExceptionPath.hh b/include/sta/ExceptionPath.hh index c1c099781..c67270ae3 100644 --- a/include/sta/ExceptionPath.hh +++ b/include/sta/ExceptionPath.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,10 +24,13 @@ #pragma once +#include +#include +#include + #include "Error.hh" -#include "Set.hh" -#include "SdcCmdComment.hh" #include "SdcClass.hh" +#include "SdcCmdComment.hh" namespace sta { @@ -44,18 +47,18 @@ class ExceptionThru; class ExceptionTo; class ExceptionState; -typedef Vector ExceptionPathSeq; +using ExceptionPathSeq = std::vector; class ExceptionPath : public SdcCmdComment { public: ExceptionPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool own_pts, - int priority, - const char *comment); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool own_pts, + int priority, + std::string_view comment); virtual ~ExceptionPath(); size_t id() const { return id_; } void setId(size_t id); @@ -66,7 +69,8 @@ public: virtual bool isGroupPath() const { return false; } virtual bool isFilter() const { return false; } virtual ExceptionPathType type() const = 0; - virtual const char *asString(const Network *network) const; + virtual std::string to_string(const Network *network) const; + virtual std::string_view typeString() const = 0; ExceptionFrom *from() const { return from_; } ExceptionThruSeq *thrus() const { return thrus_; } ExceptionTo *to() const { return to_; } @@ -75,14 +79,14 @@ public: const Network *network) const; const MinMaxAll *minMax() const { return min_max_; } virtual bool matches(const MinMax *min_max, - bool exact) const; + bool exact) const; bool matchesFirstPt(const RiseFall *to_rf, - const MinMax *min_max); + const MinMax *min_max); ExceptionState *firstState(); virtual bool resetMatch(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, const Network *network); // The priority remains the same even though pin/clock/net/inst objects // are added to the exceptions points during exception merging because @@ -103,22 +107,22 @@ public: // they cannot be coded into the priority. virtual bool tighterThan(ExceptionPath *exception) const = 0; static int fromThruToPriority(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to); + ExceptionThruSeq *thrus, + ExceptionTo *to); size_t hash() const; size_t hash(ExceptionPt *missing_pt) const; // Mergeable properties (independent of exception points). virtual bool mergeable(ExceptionPath *exception) const; bool mergeablePts(ExceptionPath *exception) const; bool mergeablePts(ExceptionPath *exception2, - ExceptionPt *missing_pt2, - ExceptionPt *&missing_pt) const; + ExceptionPt *missing_pt2, + ExceptionPt *&missing_pt) const; // Overrides properties (independent of exception points). virtual bool overrides(ExceptionPath *exception) const = 0; virtual ExceptionPath *clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts) = 0; + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) = 0; void deleteInstance(const Instance *inst, const Network *network); @@ -126,14 +130,13 @@ public: virtual bool useEndClk() const { return false; } virtual int pathMultiplier() const { return 0; } virtual float delay() const { return 0.0; } - virtual const char *name() const { return nullptr; } + virtual std::string_view name() const { return {}; } virtual bool isDefault() const { return false; } virtual bool ignoreClkLatency() const { return false; } virtual bool breakPath() const { return false; } protected: - virtual const char *typeString() const = 0; - const char *fromThruToString(const Network *network) const; + std::string fromThruToString(const Network *network) const; void makeStates(); ExceptionFrom *from_; @@ -142,7 +145,7 @@ protected: const MinMaxAll *min_max_; bool own_pts_; int priority_; - size_t id_; // Unique ID assigned by Sdc. + size_t id_{0}; // Unique ID assigned by Sdc. ExceptionState *states_; }; @@ -151,29 +154,29 @@ class FalsePath : public ExceptionPath { public: FalsePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool own_pts, - const char *comment); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool own_pts, + std::string_view comment); FalsePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool own_pts, - int priority, - const char *comment); - virtual ExceptionPath *clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts); - virtual bool isFalse() const { return true; } - virtual ExceptionPathType type() const { return ExceptionPathType::false_path; } - virtual const char *typeString() const; - virtual bool mergeable(ExceptionPath *exception) const; - virtual bool overrides(ExceptionPath *exception) const; - virtual int typePriority() const; - virtual bool tighterThan(ExceptionPath *exception) const; + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool own_pts, + int priority, + std::string_view comment); + ExceptionPath *clone(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) override; + bool isFalse() const override { return true; } + ExceptionPathType type() const override { return ExceptionPathType::false_path; } + std::string_view typeString() const override; + bool mergeable(ExceptionPath *exception) const override; + bool overrides(ExceptionPath *exception) const override; + int typePriority() const override; + bool tighterThan(ExceptionPath *exception) const override; }; // Loop paths are false paths used to disable paths around @@ -182,11 +185,11 @@ class LoopPath : public FalsePath { public: LoopPath(ExceptionThruSeq *thrus, - bool own_pts); - virtual bool isLoop() const { return true; } - virtual ExceptionPathType type() const { return ExceptionPathType::loop; } - virtual const char *typeString() const; - virtual bool mergeable(ExceptionPath *exception) const; + bool own_pts); + bool isLoop() const override { return true; } + ExceptionPathType type() const override { return ExceptionPathType::loop; } + std::string_view typeString() const override; + bool mergeable(ExceptionPath *exception) const override; }; // set_max_delay/set_min_delay @@ -194,29 +197,29 @@ class PathDelay : public ExceptionPath { public: PathDelay(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMax *min_max, - bool ignore_clk_latency, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMax *min_max, + bool ignore_clk_latency, bool break_path, - float delay, - bool own_pts, - const char *comment); - virtual ExceptionPath *clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts); - virtual bool isPathDelay() const { return true; } - virtual ExceptionPathType type() const { return ExceptionPathType::path_delay; } - virtual const char *asString(const Network *network) const; - virtual const char *typeString() const; - virtual bool mergeable(ExceptionPath *exception) const; - virtual bool overrides(ExceptionPath *exception) const; - virtual float delay() const { return delay_; } - virtual int typePriority() const; - virtual bool tighterThan(ExceptionPath *exception) const; - virtual bool ignoreClkLatency() const { return ignore_clk_latency_; } - virtual bool breakPath() const { return break_path_; } + float delay, + bool own_pts, + std::string_view comment); + ExceptionPath *clone(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) override; + bool isPathDelay() const override { return true; } + ExceptionPathType type() const override { return ExceptionPathType::path_delay; } + std::string to_string(const Network *network) const override; + std::string_view typeString() const override; + bool mergeable(ExceptionPath *exception) const override; + bool overrides(ExceptionPath *exception) const override; + float delay() const override { return delay_; } + int typePriority() const override; + bool tighterThan(ExceptionPath *exception) const override; + bool ignoreClkLatency() const override { return ignore_clk_latency_; } + bool breakPath() const override { return break_path_; } protected: bool ignore_clk_latency_; @@ -229,31 +232,31 @@ class MultiCyclePath : public ExceptionPath { public: MultiCyclePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool use_end_clk, - int path_multiplier, - bool own_pts, - const char *comment); - virtual ExceptionPath *clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts); - virtual bool isMultiCycle() const { return true; } - virtual ExceptionPathType type() const { return ExceptionPathType::multi_cycle; } - virtual bool matches(const MinMax *min_max, - bool exactly) const; - virtual const char *asString(const Network *network) const; - virtual const char *typeString() const; - virtual bool mergeable(ExceptionPath *exception) const; - virtual bool overrides(ExceptionPath *exception) const; - virtual bool useEndClk() const { return use_end_clk_; } - virtual int pathMultiplier(const MinMax *min_max) const; - virtual int pathMultiplier() const { return path_multiplier_; } - virtual int priority(const MinMax *min_max) const; - virtual int typePriority() const; - virtual bool tighterThan(ExceptionPath *exception) const; + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, + bool own_pts, + std::string_view comment); + ExceptionPath *clone(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) override; + bool isMultiCycle() const override { return true; } + ExceptionPathType type() const override { return ExceptionPathType::multi_cycle; } + bool matches(const MinMax *min_max, + bool exactly) const override; + std::string to_string(const Network *network) const override; + std::string_view typeString() const override; + bool mergeable(ExceptionPath *exception) const override; + bool overrides(ExceptionPath *exception) const override; + bool useEndClk() const override { return use_end_clk_; } + int pathMultiplier(const MinMax *min_max) const; // overload, not override + int pathMultiplier() const override { return path_multiplier_; } + int priority(const MinMax *min_max) const override; + int typePriority() const override; + bool tighterThan(ExceptionPath *exception) const override; using ExceptionPath::priority; @@ -267,54 +270,53 @@ class FilterPath : public ExceptionPath { public: FilterPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts); - virtual ExceptionPath *clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts); - virtual bool isFilter() const { return true; } - virtual ExceptionPathType type() const { return ExceptionPathType::filter; } - virtual const char *typeString() const; - virtual bool mergeable(ExceptionPath *exception) const; - virtual bool overrides(ExceptionPath *exception) const; - virtual bool resetMatch(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - const Network *network); - virtual int typePriority() const; - virtual bool tighterThan(ExceptionPath *exception) const; + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts); + ExceptionPath *clone(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) override; + bool isFilter() const override { return true; } + ExceptionPathType type() const override { return ExceptionPathType::filter; } + std::string_view typeString() const override; + bool mergeable(ExceptionPath *exception) const override; + bool overrides(ExceptionPath *exception) const override; + bool resetMatch(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + const Network *network) override; + int typePriority() const override; + bool tighterThan(ExceptionPath *exception) const override; }; class GroupPath : public ExceptionPath { public: - GroupPath(const char *name, - bool is_default, - ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts, - const char *comment); - virtual ~GroupPath(); - virtual ExceptionPath *clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts); - virtual bool isGroupPath() const { return true; } - virtual ExceptionPathType type() const { return ExceptionPathType::group_path; } - virtual const char *typeString() const; - virtual bool mergeable(ExceptionPath *exception) const; - virtual bool overrides(ExceptionPath *exception) const; - virtual int typePriority() const; - virtual bool tighterThan(ExceptionPath *exception) const; - virtual const char *name() const { return name_; } - virtual bool isDefault() const { return is_default_; } + GroupPath(std::string_view name, + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts, + std::string_view comment); + ExceptionPath *clone(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) override; + bool isGroupPath() const override { return true; } + ExceptionPathType type() const override { return ExceptionPathType::group_path; } + std::string_view typeString() const override; + bool mergeable(ExceptionPath *exception) const override; + bool overrides(ExceptionPath *exception) const override; + int typePriority() const override; + bool tighterThan(ExceptionPath *exception) const override; + std::string_view name() const override { return name_; } + bool isDefault() const override { return is_default_; } protected: - const char *name_; + std::string name_; bool is_default_; }; @@ -323,8 +325,8 @@ class ExceptionPt { public: ExceptionPt(const RiseFallBoth *rf, - bool own_pts); - virtual ~ExceptionPt() {}; + bool own_pts); + virtual ~ExceptionPt() = default; virtual bool isFrom() const { return false; } virtual bool isThru() const { return false; } virtual bool isTo() const { return false; } @@ -342,7 +344,7 @@ public: // All pins and instance/net pins. virtual PinSet allPins(const Network *network) = 0; virtual int typePriority() const = 0; - virtual const char *asString(const Network *network) const = 0; + virtual std::string to_string(const Network *network) const = 0; virtual size_t objectCount() const = 0; virtual void addPin(const Pin *pin, const Network *network) = 0; @@ -364,10 +366,10 @@ protected: bool own_pts_; // Hash is cached because there may be many objects to speed up // exception merging. - size_t hash_; + size_t hash_{0}; - // Maximum number of objects for asString() to show. - static const int as_string_max_objects_; + // Maximum number of objects for to_string() to show. + static const int to_string_max_objects_; static const size_t hash_clk = 3; static const size_t hash_pin = 5; static const size_t hash_net = 7; @@ -379,44 +381,44 @@ class ExceptionFromTo : public ExceptionPt public: ExceptionFromTo(PinSet *pins, ClockSet *clks, - InstanceSet *insts, - const RiseFallBoth *rf, - bool own_pts, + InstanceSet *insts, + const RiseFallBoth *rf, + bool own_pts, const Network *network); - ~ExceptionFromTo(); - virtual PinSet *pins() { return pins_; } + ~ExceptionFromTo() override; + PinSet *pins() override { return pins_; } bool hasPins() const; - ClockSet *clks() { return clks_; } + ClockSet *clks() override { return clks_; } bool hasClocks() const; - InstanceSet *instances() { return insts_; } + InstanceSet *instances() override { return insts_; } bool hasInstances() const; - virtual NetSet *nets() { return nullptr; } - virtual EdgePinsSet *edges() { return nullptr; } + NetSet *nets() override { return nullptr; } + EdgePinsSet *edges() override { return nullptr; } bool hasObjects() const; void deleteObjects(ExceptionFromTo *pt, const Network *network); - virtual PinSet allPins(const Network *network); + PinSet allPins(const Network *network) override; bool equal(ExceptionFromTo *from_to) const; - virtual int compare(ExceptionPt *pt, - const Network *network) const; - virtual void mergeInto(ExceptionPt *pt, - const Network *network); - virtual const char *asString(const Network *network) const; - virtual size_t objectCount() const; + int compare(ExceptionPt *pt, + const Network *network) const override; + void mergeInto(ExceptionPt *pt, + const Network *network) override; + std::string to_string(const Network *network) const override; + size_t objectCount() const override; void deleteClock(Clock *clk); - virtual void addPin(const Pin *pin, - const Network *network); - virtual void addClock(Clock *clk); - virtual void addInstance(const Instance *inst, - const Network *network); - virtual void addNet(const Net *, - const Network *) {} - virtual void addEdge(const EdgePins &, - const Network *) {} - virtual void connectPinAfter(PinSet *, - Network *) {} - virtual void deletePinBefore(const Pin *, - Network *); + void addPin(const Pin *pin, + const Network *network) override; + void addClock(Clock *clk) override; + void addInstance(const Instance *inst, + const Network *network) override; + void addNet(const Net *, + const Network *) override {} + void addEdge(const EdgePins &, + const Network *) override {} + void connectPinAfter(PinSet *, + Network *) override {} + void deletePinBefore(const Pin *, + Network *) override; void deleteInstance(const Instance *inst, const Network *network); @@ -436,65 +438,65 @@ class ExceptionFrom : public ExceptionFromTo { public: ExceptionFrom(PinSet *pins, - ClockSet *clks, - InstanceSet *insts, - const RiseFallBoth *rf, - bool own_pts, + ClockSet *clks, + InstanceSet *insts, + const RiseFallBoth *rf, + bool own_pts, const Network *network); ExceptionFrom *clone(const Network *network); - virtual bool isFrom() const { return true; } + bool isFrom() const override { return true; } bool intersectsPts(ExceptionFrom *from, const Network *network) const; - virtual int typePriority() const { return 0; } + int typePriority() const override { return 0; } protected: - virtual const char *cmdKeyword() const; - virtual void findHash(const Network *network); + const char *cmdKeyword() const override; + void findHash(const Network *network) override; }; class ExceptionTo : public ExceptionFromTo { public: ExceptionTo(PinSet *pins, - ClockSet *clks, - InstanceSet *insts, - // -to|-rise_to|-fall_to - const RiseFallBoth *rf, - // -rise|-fall endpoint transition. - const RiseFallBoth *end_rf, - bool own_pts, + ClockSet *clks, + InstanceSet *insts, + // -to|-rise_to|-fall_to + const RiseFallBoth *rf, + // -rise|-fall endpoint transition. + const RiseFallBoth *end_rf, + bool own_pts, const Network *network); ExceptionTo *clone(const Network *network); - virtual bool isTo() const { return true; } - const char *asString(const Network *network) const; + bool isTo() const override { return true; } + std::string to_string(const Network *network) const override; const RiseFallBoth *endTransition() { return end_rf_; } bool intersectsPts(ExceptionTo *to, const Network *network) const; - virtual int typePriority() const { return 1; } + int typePriority() const override { return 1; } bool matches(const Pin *pin, - const ClockEdge *clk_edge, - const RiseFall *end_rf, - const Network *network) const; + const ClockEdge *clk_edge, + const RiseFall *end_rf, + const Network *network) const; bool matches(const Pin *pin, - const RiseFall *end_rf) const; + const RiseFall *end_rf) const; bool matches(const Clock *clk) const; bool matches(const Pin *pin, const RiseFall *end_rf, const Network *network) const; bool matchesFilter(const Pin *pin, - const ClockEdge *clk_edge, - const RiseFall *end_rf, - const Network *network) const; - virtual int compare(ExceptionPt *pt, - const Network *network) const; + const ClockEdge *clk_edge, + const RiseFall *end_rf, + const Network *network) const; + int compare(ExceptionPt *pt, + const Network *network) const override; protected: bool matches(const Pin *pin, - const ClockEdge *clk_edge, - const RiseFall *end_rf, - bool inst_matches_reg_clk_pin, - const Network *network) const; - virtual const char *cmdKeyword() const; + const ClockEdge *clk_edge, + const RiseFall *end_rf, + bool inst_matches_reg_clk_pin, + const Network *network) const; + const char *cmdKeyword() const override; // -rise|-fall endpoint transition. const RiseFallBoth *end_rf_; @@ -504,55 +506,55 @@ class ExceptionThru : public ExceptionPt { public: ExceptionThru(PinSet *pins, - NetSet *nets, - InstanceSet *insts, - const RiseFallBoth *rf, - bool own_pts, - const Network *network); - ~ExceptionThru(); + NetSet *nets, + InstanceSet *insts, + const RiseFallBoth *rf, + bool own_pts, + const Network *network); + ~ExceptionThru() override; ExceptionThru *clone(const Network *network); - virtual const char *asString(const Network *network) const; - virtual bool isThru() const { return true; } - PinSet *pins() { return pins_; } - EdgePinsSet *edges() { return edges_; } - NetSet *nets() { return nets_; } - InstanceSet *instances() { return insts_; } - virtual ClockSet *clks() { return nullptr; } + std::string to_string(const Network *network) const override; + bool isThru() const override { return true; } + PinSet *pins() override { return pins_; } + EdgePinsSet *edges() override { return edges_; } + NetSet *nets() override { return nets_; } + InstanceSet *instances() override { return insts_; } + ClockSet *clks() override { return nullptr; } bool hasObjects() const; void deleteObjects(ExceptionThru *pt, const Network *network); - virtual PinSet allPins(const Network *network); + PinSet allPins(const Network *network) override; bool matches(const Pin *from_pin, - const Pin *to_pin, - const RiseFall *to_rf, - const Network *network); + const Pin *to_pin, + const RiseFall *to_rf, + const Network *network); bool equal(ExceptionThru *thru) const; - virtual int compare(ExceptionPt *pt, - const Network *network) const; - virtual void mergeInto(ExceptionPt *pt, - const Network *network); + int compare(ExceptionPt *pt, + const Network *network) const override; + void mergeInto(ExceptionPt *pt, + const Network *network) override; bool intersectsPts(ExceptionThru *thru, const Network *network) const; - virtual int typePriority() const { return 2; } - virtual size_t objectCount() const; - virtual void connectPinAfter(PinSet *drvrs, - Network *network); - virtual void deletePinBefore(const Pin *pin, - Network *network); + int typePriority() const override { return 2; } + size_t objectCount() const override; + void addPin(const Pin *pin, + const Network *network) override; + void addEdge(const EdgePins &edge, + const Network *network) override; + void addNet(const Net *net, + const Network *network) override; + void addInstance(const Instance *inst, + const Network *network) override; + void addClock(Clock *) override {} + void connectPinAfter(PinSet *drvrs, + Network *network) override; + void deletePinBefore(const Pin *pin, + Network *network) override; void deleteInstance(const Instance *inst, const Network *network); protected: void findHash(const Network *network); - virtual void addPin(const Pin *pin, - const Network *network); - virtual void addEdge(const EdgePins &edge, - const Network *network); - virtual void addNet(const Net *net, - const Network *network); - virtual void addInstance(const Instance *inst, - const Network *network); - virtual void addClock(Clock *) {} void deletePin(const Pin *pin, const Network *network); void deleteEdge(const EdgePins &edge); @@ -563,45 +565,45 @@ protected: void makeNetEdges(const Network *network); void makeInstEdges(const Network *network); void makeHpinEdges(const Pin *pin, - const Network *network); + const Network *network); void makePinEdges(const Pin *pin, - const Network *network); + const Network *network); void makeNetEdges(const Net *net, - const Network *network); + const Network *network); void makeInstEdges(Instance *inst, - Network *network); + Network *network); void deletePinEdges(const Pin *pin, - Network *network); + Network *network); void deleteNetEdges(Net *net, - const Network *network); + const Network *network); void deleteInstEdges(Instance *inst, - Network *network); + Network *network); // Leaf/port pins. PinSet *pins_; // Graph edges that traverse thru hierarchical pins. - EdgePinsSet *edges_; + EdgePinsSet *edges_{nullptr}; NetSet *nets_; InstanceSet *insts_; }; ExceptionThruSeq * exceptionThrusClone(ExceptionThruSeq *thrus, - const Network *network); + const Network *network); // Iterate uniformly across exception from/thru/to's. class ExceptionPtIterator { public: - explicit ExceptionPtIterator(const ExceptionPath *exception); + ExceptionPtIterator(const ExceptionPath *exception); bool hasNext(); ExceptionPt *next(); private: const ExceptionPath *exception_; - bool from_done_; - ExceptionThruSeq::Iterator thru_iter_; - bool to_done_; + bool from_done_{false}; + ExceptionThruSeq::iterator thru_iter_; + bool to_done_{false}; }; // Visitor for exception point sets expanded into single object paths. @@ -616,13 +618,12 @@ class ExpandedExceptionVisitor { public: ExpandedExceptionVisitor(ExceptionPath *exception, - const Network *network); - virtual ~ExpandedExceptionVisitor() {} + const Network *network); void visitExpansions(); // From/thrus/to have a single exception point (pin/instance/net/clock). virtual void visit(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to) = 0; + ExceptionThruSeq *thrus, + ExceptionTo *to) = 0; protected: ExceptionPath *exception_; @@ -632,10 +633,10 @@ private: void expandFrom(); void expandThrus(ExceptionFrom *expanded_from); void expandThru(ExceptionFrom *expanded_from, - size_t next_thru_idx, - ExceptionThruSeq *expanded_thrus); + size_t next_thru_idx, + ExceptionThruSeq *expanded_thrus); void expandTo(ExceptionFrom *expanded_from, - ExceptionThruSeq *expanded_thrus); + ExceptionThruSeq *expanded_thrus); }; // States used by tags to know what exception points have been seen @@ -644,15 +645,15 @@ class ExceptionState { public: ExceptionState(ExceptionPath *exception, - ExceptionThru *next_thru, - int index); + ExceptionThru *next_thru, + int index); ExceptionPath *exception() { return exception_; } const ExceptionPath *exception() const { return exception_; } bool matchesNextThru(const Pin *from_pin, - const Pin *to_pin, - const RiseFall *to_rf, - const MinMax *min_max, - const Network *network) const; + const Pin *to_pin, + const RiseFall *to_rf, + const MinMax *min_max, + const Network *network) const; bool isComplete() const; ExceptionThru *nextThru() const { return next_thru_; } ExceptionState *nextState() const { return next_state_; } @@ -663,19 +664,19 @@ public: private: ExceptionPath *exception_; ExceptionThru *next_thru_; - ExceptionState *next_state_; + ExceptionState *next_state_{nullptr}; int index_; }; -bool -exceptionStateLess(const ExceptionState *state1, - const ExceptionState *state2); +int +exceptionStateCmp(const ExceptionState *state1, + const ExceptionState *state2); // Exception thrown by check. class EmptyExpceptionPt : public Exception { public: - virtual const char *what() const noexcept; + const char *what() const noexcept override; }; class ExceptionPathLess @@ -683,7 +684,7 @@ class ExceptionPathLess public: ExceptionPathLess(const Network *network); bool operator()(const ExceptionPath *except1, - const ExceptionPath *except2) const; + const ExceptionPath *except2) const; private: const Network *network_; @@ -692,7 +693,7 @@ private: // Throws EmptyExpceptionPt it finds an empty exception point. void checkFromThrusTo(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to); + ExceptionThruSeq *thrus, + ExceptionTo *to); -} // namespace +} // namespace sta diff --git a/include/sta/FilterExpr.hh b/include/sta/FilterExpr.hh deleted file mode 100644 index ab279eb0e..000000000 --- a/include/sta/FilterExpr.hh +++ /dev/null @@ -1,252 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include -#include -#include -#include -#include "StringSeq.hh" -#include "Error.hh" -#include "Sta.hh" -#include "Property.hh" -#include "PatternMatch.hh" - -namespace sta { - -using std::string; - -class FilterError : public Exception -{ -public: - explicit FilterError(std::string_view error); - virtual ~FilterError() noexcept {} - virtual const char *what() const noexcept; - -private: - std::string error_; -}; - -class FilterExpr { -public: - struct Token { - enum class Kind { - skip = 0, - predicate, - op_lparen, - op_rparen, - op_or, - op_and, - op_inv, - defined, - undefined - }; - - Token(std::string text, Kind kind); - - std::string text; - Kind kind; - }; - - struct PredicateToken : public Token { - PredicateToken(std::string property, std::string op, std::string arg); - - std::string property; - std::string op; - std::string arg; - }; - - FilterExpr(std::string expression); - - std::vector> postfix(bool sta_boolean_props_as_int); -private: - std::vector> lex(bool sta_boolean_props_as_int); - std::vector> shuntingYard(std::vector>& infix); - - std::string raw_; -}; - -template std::set -process_predicate(const char *property, - const char *op, - const char *pattern, - std::set &all) -{ - auto filtered_objects = std::set(); - bool exact_match = stringEq(op, "=="); - bool pattern_match = stringEq(op, "=~"); - bool not_match = stringEq(op, "!="); - bool not_pattern_match = stringEq(op, "!~"); - for (T *object : all) { - PropertyValue value = Sta::sta()->properties().getProperty(object, property); - std::string prop_str = value.to_string(Sta::sta()->network()); - const char *prop = prop_str.c_str(); - if (prop && - ((exact_match && stringEq(prop, pattern)) - || (not_match && !stringEq(prop, pattern)) - || (pattern_match && patternMatch(pattern, prop)) - || (not_pattern_match && !patternMatch(pattern, prop)))) - filtered_objects.insert(object); - } - return filtered_objects; -} - -template Vector -filter_objects(const char *filter_expression, - Vector *objects, - bool sta_boolean_props_as_int - ) { - Vector result; - if (objects) { - auto all = std::set(); - for (auto object: *objects) { - all.insert(object); - } - auto postfix = sta::FilterExpr(filter_expression).postfix(sta_boolean_props_as_int); - std::stack> eval_stack; - for (auto &pToken: postfix) { - if (pToken->kind == sta::FilterExpr::Token::Kind::op_or) { - if (eval_stack.size() < 2) { - throw sta::FilterError("attempted to run a logical or on less than two predicates"); - } - auto arg0 = eval_stack.top(); - eval_stack.pop(); - auto arg1 = eval_stack.top(); - eval_stack.pop(); - auto union_result = std::set(); - std::set_union( - arg0.cbegin(), arg0.cend(), - arg1.cbegin(), arg1.cend(), - std::inserter(union_result, union_result.begin()) - ); - eval_stack.push(union_result); - } else if (pToken->kind == sta::FilterExpr::Token::Kind::op_and) { - if (eval_stack.size() < 2) { - throw sta::FilterError("attempted to run a logical and on less than two predicates"); - } - auto arg0 = eval_stack.top(); - eval_stack.pop(); - auto arg1 = eval_stack.top(); - eval_stack.pop(); - auto intersection_result = std::set(); - std::set_intersection( - arg0.cbegin(), arg0.cend(), - arg1.cbegin(), arg1.cend(), - std::inserter(intersection_result, intersection_result.begin()) - ); - eval_stack.push(intersection_result); - } else if (pToken->kind == sta::FilterExpr::Token::Kind::op_inv) { - if (eval_stack.size() < 1) { - throw sta::FilterError("attempted to run an inversion on no predicates"); - } - auto arg0 = eval_stack.top(); - eval_stack.pop(); - - auto difference_result = std::set(); - std::set_difference( - all.cbegin(), all.cend(), - arg0.cbegin(), arg0.cend(), - std::inserter(difference_result, difference_result.begin()) - ); - eval_stack.push(difference_result); - } else if (pToken->kind == sta::FilterExpr::Token::Kind::defined || - pToken->kind == sta::FilterExpr::Token::Kind::undefined) { - bool should_be_defined = (pToken->kind == sta::FilterExpr::Token::Kind::defined); - auto result = std::set(); - for (auto object : all) { - PropertyValue value = Sta::sta()->properties().getProperty(object, pToken->text); - bool is_defined = false; - switch (value.type()) { - case PropertyValue::Type::type_float: - is_defined = value.floatValue() != 0; - break; - case PropertyValue::Type::type_bool: - is_defined = value.boolValue(); - break; - case PropertyValue::Type::type_string: - case PropertyValue::Type::type_liberty_library: - case PropertyValue::Type::type_liberty_cell: - case PropertyValue::Type::type_liberty_port: - case PropertyValue::Type::type_library: - case PropertyValue::Type::type_cell: - case PropertyValue::Type::type_port: - case PropertyValue::Type::type_instance: - case PropertyValue::Type::type_pin: - case PropertyValue::Type::type_net: - case PropertyValue::Type::type_clk: - is_defined = value.to_string(Sta::sta()->network()) != ""; - break; - case PropertyValue::Type::type_none: - is_defined = false; - break; - case PropertyValue::Type::type_pins: - is_defined = value.pins()->size() > 0; - break; - case PropertyValue::Type::type_clks: - is_defined = value.clocks()->size() > 0; - break; - case PropertyValue::Type::type_paths: - is_defined = value.paths()->size() > 0; - break; - case PropertyValue::Type::type_pwr_activity: - is_defined = value.pwrActivity().isSet(); - break; - } - if (is_defined == should_be_defined) { - result.insert(object); - } - } - eval_stack.push(result); - } else if (pToken->kind == sta::FilterExpr::Token::Kind::predicate) { - auto predicate_token = std::static_pointer_cast(pToken); - auto predicate_result = process_predicate(predicate_token->property.c_str(), predicate_token->op.c_str(), predicate_token->arg.c_str(), all); - eval_stack.push(predicate_result); - } - } - if (eval_stack.size() == 0) { - throw sta::FilterError("filter expression is empty"); - } - if (eval_stack.size() > 1) { - throw sta::FilterError("filter expression evaluated to multiple sets"); - } - auto result_set = eval_stack.top(); - result.resize(result_set.size()); - std::copy(result_set.begin(), result_set.end(), result.begin()); - - // Maintain pre-filter ordering - std::map objects_i; - for (int i = 0; i < objects->size(); ++i) - objects_i[objects->at(i)] = i; - - std::sort(result.begin(), result.end(), - [&](T* a, T* b) { - return objects_i[a] < objects_i[b]; - }); - } - return result; -} - - -} // namespace diff --git a/include/sta/FilterObjects.hh b/include/sta/FilterObjects.hh new file mode 100644 index 000000000..71bea6c0b --- /dev/null +++ b/include/sta/FilterObjects.hh @@ -0,0 +1,97 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include +#include +#include + +#include "GraphClass.hh" +#include "NetworkClass.hh" +#include "SdcClass.hh" +#include "SearchClass.hh" +#include "StringUtil.hh" + +namespace sta { + +class Sta; +class Report; + +PortSeq +filterPorts(std::string_view filter_expression, + PortSeq *ports, + Sta *sta); + +InstanceSeq +filterInstances(std::string_view filter_expression, + InstanceSeq *insts, + Sta *sta); + +PinSeq +filterPins(std::string_view filter_expression, + PinSeq *pins, + Sta *sta); + +NetSeq +filterNets(std::string_view filter_expression, + NetSeq *nets, + Sta *sta); + +ClockSeq +filterClocks(std::string_view filter_expression, + ClockSeq *clks, + Sta *sta); + +LibertyCellSeq +filterLibCells(std::string_view filter_expression, + LibertyCellSeq *cells, + Sta *sta); + +LibertyPortSeq +filterLibPins(std::string_view filter_expression, + LibertyPortSeq *ports, + Sta *sta); + +LibertyLibrarySeq +filterLibertyLibraries(std::string_view filter_expression, + LibertyLibrarySeq *libs, + Sta *sta); + +EdgeSeq +filterTimingArcs(std::string_view filter_expression, + EdgeSeq *edges, + Sta *sta); + +PathEndSeq +filterPathEnds(std::string_view filter_expression, + PathEndSeq *ends, + Sta *sta); + +// For FilterExpr unit tests. +StringSeq +filterExprToPostfix(std::string_view expr, + Report *report); + +} // namespace sta diff --git a/include/sta/FindObjects.hh b/include/sta/FindObjects.hh index 4c77eb6cb..4b6675073 100644 --- a/include/sta/FindObjects.hh +++ b/include/sta/FindObjects.hh @@ -25,14 +25,12 @@ #pragma once #include "PatternMatch.hh" -#include "FilterExpr.hh" #include - -template +template SEQ_TYPE * find_objects_complete(SEQ_TYPE *collection, - StringSeq *patterns, + const StringSeq &patterns, bool regexp, bool nocase, bool quiet, @@ -41,12 +39,12 @@ find_objects_complete(SEQ_TYPE *collection, { collection = collection ? new SEQ_TYPE(*collection) : new SEQ_TYPE(); auto sta = Sta::sta(); - for (const char *pattern: *patterns) { + for (const std::string &pattern: patterns) { PatternMatch matcher(pattern, regexp, nocase, sta->tclInterp()); auto result = get_pattern(&matcher); if (result.size() == 0) { if (!quiet) - sta->report()->warn(ERROR_CODE, "%s '%s' not found.", OBJECT_TYPE_NAME, pattern); + sta->report()->warn(ERROR_CODE, "{} '{}' not found.", OBJECT_TYPE_NAME, pattern); } else { auto entries = collection->size(); collection->resize(entries + result.size()); @@ -58,7 +56,7 @@ find_objects_complete(SEQ_TYPE *collection, } } if (filter_expression != nullptr) { - auto filtered = filter_objects(filter_expression, collection, sta->booleanPropsAsInt()); + auto filtered = FILTER_FN(filter_expression, collection, sta); delete collection; collection = new SEQ_TYPE(filtered); } diff --git a/include/sta/Format.hh b/include/sta/Format.hh new file mode 100644 index 000000000..88555a1e3 --- /dev/null +++ b/include/sta/Format.hh @@ -0,0 +1,150 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include +#include +#include +#include + +#include "StaConfig.hh" + +#ifdef ZLIB_FOUND +#include +#endif + +#if HAVE_CXX_STD_FORMAT +#include + +namespace sta { + +template +std::string format(std::format_string fmt, + Args &&...args) { + return std::format(fmt, std::forward(args)...); +} + +template +void print(std::ofstream &stream, + std::format_string fmt, + Args &&...args) { + stream << std::format(fmt, std::forward(args)...); +} + +#ifdef ZLIB_FOUND +template +void print(gzFile stream, + std::format_string fmt, + Args &&...args) { + std::string s = sta::format(fmt, std::forward(args)...); + gzwrite(stream, s.c_str(), s.size()); +} +#endif +template +void print(FILE *stream, + std::format_string fmt, + Args &&...args) { + std::string s = sta::format(fmt, std::forward(args)...); + std::fprintf(stream, "%s", s.c_str()); +} + +inline std::string vformat(std::string_view fmt, + std::format_args args) { + return std::vformat(fmt, args); +} + +template +auto make_format_args(Args &&...args) { + return std::make_format_args(std::forward(args)...); +} + +// Format with runtime format string - captures args to avoid make_format_args +// rvalue reference issues. +template +std::string formatRuntime(std::string_view fmt, + Args &&...args) { + auto args_tuple = std::make_tuple(std::forward(args)...); + return std::apply( + [fmt](auto &...a) { + return std::vformat(fmt, std::make_format_args(a...)); + }, + args_tuple); +} + +} // namespace sta + +#else +#include + +namespace sta { + +template +std::string format(fmt::format_string fmt, + Args &&...args) { + return fmt::format(fmt, std::forward(args)...); +} +template +void print(std::ofstream &stream, + fmt::format_string fmt, + Args &&...args) { + stream << fmt::format(fmt, std::forward(args)...); +} + +#ifdef ZLIB_FOUND +template +void print(gzFile stream, + fmt::format_string fmt, + Args &&...args) { + std::string s = sta::format(fmt, std::forward(args)...); + gzwrite(stream, s.c_str(), s.size()); +} +#endif +template +void print(FILE *stream, + fmt::format_string fmt, + Args &&...args) { + std::string s = sta::format(fmt, std::forward(args)...); + std::fprintf(stream, "%s", s.c_str()); +} + +inline +std::string vformat(std::string_view fmt, + fmt::format_args args) { + return fmt::vformat(fmt, args); +} + +template +auto make_format_args(Args &&...args) { + return fmt::make_format_args(std::forward(args)...); +} + +template +std::string formatRuntime(std::string_view fmt, + Args &&...args) { + return fmt::format(fmt::runtime(fmt), std::forward(args)...); +} + +} // namespace sta +#endif diff --git a/include/sta/FuncExpr.hh b/include/sta/FuncExpr.hh index ae7a0bc10..72f7bf96a 100644 --- a/include/sta/FuncExpr.hh +++ b/include/sta/FuncExpr.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,55 +26,57 @@ #include -#include "Set.hh" -#include "NetworkClass.hh" #include "LibertyClass.hh" +#include "NetworkClass.hh" namespace sta { class FuncExpr { public: - enum Operator {op_port, - op_not, - op_or, - op_and, - op_xor, - op_one, - op_zero}; + enum class Op {port, + not_, + or_, + and_, + xor_, + one, + zero}; // Constructors. - FuncExpr(Operator op, - FuncExpr *left, - FuncExpr *right, - LibertyPort *port); + FuncExpr(Op op, + FuncExpr *left, + FuncExpr *right, + LibertyPort *port); + ~FuncExpr(); + void shallowDelete(); static FuncExpr *makePort(LibertyPort *port); static FuncExpr *makeNot(FuncExpr *expr); static FuncExpr *makeAnd(FuncExpr *left, - FuncExpr *right); + FuncExpr *right); static FuncExpr *makeOr(FuncExpr *left, - FuncExpr *right); + FuncExpr *right); static FuncExpr *makeXor(FuncExpr *left, - FuncExpr *right); + FuncExpr *right); static FuncExpr *makeZero(); static FuncExpr *makeOne(); static bool equiv(const FuncExpr *expr1, - const FuncExpr *expr2); + const FuncExpr *expr2); static bool less(const FuncExpr *expr1, - const FuncExpr *expr2); + const FuncExpr *expr2); + // Invert expr by deleting leading NOT if found. + FuncExpr *invert(); // Deep copy. FuncExpr *copy(); - // Delete expression and all of its subexpressions. - void deleteSubexprs(); - // op == op_port + // op == port LibertyPort *port() const; - Operator op() const { return op_; } + Op op() const { return op_; } // When operator is NOT left is the only operand. FuncExpr *left() const { return left_; } - // nullptr when op == op_not + // nullptr when op == not_ FuncExpr *right() const { return right_; } TimingSense portTimingSense(const LibertyPort *port) const; + LibertyPortSet ports() const; // Return true if expression has port as an input. bool hasPort(const LibertyPort *port) const; std::string to_string() const; @@ -86,11 +88,14 @@ public: bool checkSize(LibertyPort *port); private: + void findPorts(const FuncExpr *expr, + LibertyPortSet &ports) const; + std::string to_string(bool with_parens) const; std::string to_string(bool with_parens, char op) const; - Operator op_; + Op op_; FuncExpr *left_; FuncExpr *right_; LibertyPort *port_; @@ -100,18 +105,4 @@ private: FuncExpr * funcExprNot(FuncExpr *expr); -class FuncExprPortIterator : public Iterator -{ -public: - explicit FuncExprPortIterator(const FuncExpr *expr); - virtual bool hasNext() { return iter_.hasNext(); } - virtual LibertyPort *next() { return iter_.next(); } - -private: - void findPorts(const FuncExpr *expr); - - LibertyPortSet ports_; - LibertyPortSet::ConstIterator iter_; -}; - -} // namespace +} // namespace sta diff --git a/include/sta/Fuzzy.hh b/include/sta/Fuzzy.hh index ac4280f02..29963b230 100644 --- a/include/sta/Fuzzy.hh +++ b/include/sta/Fuzzy.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -30,22 +30,22 @@ namespace sta { bool fuzzyEqual(float v1, - float v2); + float v2); bool fuzzyZero(float v); bool fuzzyLess(float v1, - float v2); + float v2); bool fuzzyLessEqual(float v1, - float v2); + float v2); bool fuzzyGreater(float v1, - float v2); + float v2); bool fuzzyGreaterEqual(float v1, - float v2); + float v2); bool fuzzyInf(float value); -} // namespace +} // namespace sta diff --git a/include/sta/GeneratedClock.hh b/include/sta/GeneratedClock.hh index e9fb03998..7dc1e1fde 100644 --- a/include/sta/GeneratedClock.hh +++ b/include/sta/GeneratedClock.hh @@ -2,6 +2,7 @@ #include "LibertyClass.hh" #include "SdcClass.hh" +#include namespace sta { @@ -9,9 +10,9 @@ class GeneratedClock { public: ~GeneratedClock(); - const char *name() const { return name_; } - const char *clockPin() const { return clock_pin_; } - const char *masterPin() const { return master_pin_; } + std::string_view name() const { return name_; } + std::string_view clockPin() const { return clock_pin_; } + std::string_view masterPin() const { return master_pin_; } int dividedBy() const { return divided_by_; } int multipliedBy() const { return multiplied_by_; } float dutyCycle() const { return duty_cycle_; } @@ -30,9 +31,9 @@ protected: IntSeq *edges, FloatSeq *edge_shifts); - const char *name_; - const char *clock_pin_; - const char *master_pin_; + std::string name_; + std::string clock_pin_; + std::string master_pin_; int divided_by_; int multiplied_by_; float duty_cycle_; diff --git a/include/sta/Graph.hh b/include/sta/Graph.hh index 6bf86cfe6..233d363cf 100644 --- a/include/sta/Graph.hh +++ b/include/sta/Graph.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,32 +24,31 @@ #pragma once -#include #include +#include +#include +#include "Delay.hh" +#include "GraphClass.hh" #include "Iterator.hh" -#include "Map.hh" -#include "Vector.hh" -#include "ObjectTable.hh" #include "LibertyClass.hh" #include "NetworkClass.hh" -#include "Delay.hh" -#include "GraphClass.hh" -#include "VertexId.hh" +#include "ObjectTable.hh" #include "Path.hh" #include "StaState.hh" +#include "VertexId.hh" namespace sta { class MinMax; class Sdc; -typedef ObjectTable VertexTable; -typedef ObjectTable EdgeTable; -typedef Map PinVertexMap; -typedef Iterator VertexEdgeIterator; -typedef Map PeriodCheckAnnotations; -typedef ObjectId EdgeId; +using VertexTable = ObjectTable; +using EdgeTable = ObjectTable; +using PinVertexMap = std::map; +using VertexEdgeIterator = Iterator; +using PeriodCheckAnnotations = std::map; +using EdgeId = ObjectId; static constexpr EdgeId edge_id_null = object_id_null; static constexpr ObjectIdx edge_idx_null = object_id_null; @@ -59,16 +58,10 @@ static constexpr ObjectIdx vertex_idx_null = object_idx_null; class Graph : public StaState { public: - // slew_rf_count is - // 0 no slews - // 1 one slew for rise/fall - // 2 rise/fall slews - // ap_count is the dcalc analysis point count. Graph(StaState *sta, - int slew_rf_count, - DcalcAPIndex ap_count); + DcalcAPIndex ap_count); void makeGraph(); - ~Graph(); + ~Graph() override; // Number of arc delays and slews from sdf or delay calculation. void setDelayCount(DcalcAPIndex ap_count); @@ -80,13 +73,13 @@ public: VertexId id(const Vertex *vertex) const; void makePinVertices(Pin *pin); void makePinVertices(Pin *pin, - Vertex *&vertex, - Vertex *&bidir_drvr_vertex); + Vertex *&vertex, + Vertex *&bidir_drvr_vertex); // Both vertices for bidirects. void pinVertices(const Pin *pin, - // Return values. - Vertex *&vertex, - Vertex *&bidirect_drvr_vertex) const; + // Return values. + Vertex *&vertex, + Vertex *&bidirect_drvr_vertex) const; // Driver vertex for bidirects. Vertex *pinDrvrVertex(const Pin *pin) const; // Load vertex for bidirects. @@ -94,24 +87,22 @@ public: void deleteVertex(Vertex *vertex); bool hasFaninOne(Vertex *vertex) const; VertexId vertexCount() { return vertices_->size(); } - Path *makePaths(Vertex *vertex, - uint32_t count); - Path *paths(const Vertex *vertex) const; - void deletePaths(Vertex *vertex); // Reported slew are the same as those in the liberty tables. // reported_slews = measured_slews / slew_derate_from_library // Measured slews are between slew_lower_threshold and slew_upper_threshold. - const Slew &slew(const Vertex *vertex, - const RiseFall *rf, - DcalcAPIndex ap_index); + Slew slew(const Vertex *vertex, + const RiseFall *rf, + DcalcAPIndex ap_index); + Slew slew(const Vertex *vertex, + size_t index); void setSlew(Vertex *vertex, const RiseFall *rf, DcalcAPIndex ap_index, const Slew &slew); // Edge functions. - Edge *edge(EdgeId edge_index) const; + Edge *edge(EdgeId edge_id) const; EdgeId id(const Edge *edge) const; Edge *makeEdge(Vertex *from, Vertex *to, @@ -139,32 +130,30 @@ public: void setArcDelay(Edge *edge, const TimingArc *arc, DcalcAPIndex ap_index, - ArcDelay delay); + const ArcDelay &delay); // Alias for arcDelays using library wire arcs. - const ArcDelay &wireArcDelay(const Edge *edge, - const RiseFall *rf, - DcalcAPIndex ap_index); + ArcDelay wireArcDelay(const Edge *edge, + const RiseFall *rf, + DcalcAPIndex ap_index); void setWireArcDelay(Edge *edge, const RiseFall *rf, DcalcAPIndex ap_index, const ArcDelay &delay); // Is timing arc delay annotated. bool arcDelayAnnotated(const Edge *edge, - const TimingArc *arc, - DcalcAPIndex ap_index) const; + const TimingArc *arc, + DcalcAPIndex ap_index) const; void setArcDelayAnnotated(Edge *edge, - const TimingArc *arc, - DcalcAPIndex ap_index, - bool annotated); + const TimingArc *arc, + DcalcAPIndex ap_index, + bool annotated); bool wireDelayAnnotated(const Edge *edge, - const RiseFall *rf, - DcalcAPIndex ap_index) const; + const RiseFall *rf, + DcalcAPIndex ap_index) const; void setWireDelayAnnotated(Edge *edge, - const RiseFall *rf, - DcalcAPIndex ap_index, - bool annotated); - // True if any edge arc is annotated. - bool delayAnnotated(Edge *edge); + const RiseFall *rf, + DcalcAPIndex ap_index, + bool annotated); void minPulseWidthArc(Vertex *vertex, const RiseFall *hi_low, @@ -172,23 +161,24 @@ public: Edge *&edge, TimingArc *&arc); void minPeriodArc(Vertex *vertex, - const RiseFall *rf, - // Return values. - Edge *&edge, - TimingArc *&arc); + const RiseFall *rf, + // Return values. + Edge *&edge, + TimingArc *&arc); // Sdf period check annotation. void periodCheckAnnotation(const Pin *pin, - DcalcAPIndex ap_index, - // Return values. - float &period, - bool &exists); + DcalcAPIndex ap_index, + // Return values. + float &period, + bool &exists); void setPeriodCheckAnnotation(const Pin *pin, - DcalcAPIndex ap_index, - float period); + DcalcAPIndex ap_index, + float period); // Remove all delay and slew annotations. void removeDelaySlewAnnotations(); - VertexSet *regClkVertices() { return reg_clk_vertices_; } + VertexSet ®ClkVertices() { return reg_clk_vertices_; } + void makeSceneAfter(); static constexpr int vertex_level_bits = 24; static constexpr int vertex_level_max = (1<(slews_); } + const Slew *slews() const { return reinterpret_cast(slews_); } + float *slewsFloat() { return slews_; } + const float *slewsFloat() const { return slews_; } + void setSlews(float *slews); Pin *pin_; - EdgeId in_edges_; // Edges to this vertex. - EdgeId out_edges_; // Edges from this vertex. + EdgeId in_edges_; // Edges to this vertex. + EdgeId out_edges_; // Edges from this vertex. // Delay calc - Slew *slews_; + float *slews_; // Search Path *paths_; @@ -336,23 +315,19 @@ protected: int level_:Graph::vertex_level_bits; // 24 unsigned int slew_annotated_:slew_annotated_bits; // 4 - // LogicValue gcc barfs if this is dcl'd. - unsigned sim_value_:3; // Bidirect pins have two vertices. // This flag distinguishes the driver and load vertices. bool is_bidirect_drvr_:1; bool is_reg_clk_:1; - bool is_disabled_constraint_:1; - bool is_gated_clk_enable_:1; // Constrained by timing check edge. bool has_checks_:1; // Is the clock for a timing check. bool is_check_clk_:1; - bool is_constrained_:1; bool has_downstream_clk_pin_:1; bool visited1_:1; bool visited2_:1; + bool has_sim_value_; private: friend class Graph; @@ -378,22 +353,13 @@ public: TimingSense sense() const; TimingArcSet *timingArcSet() const { return arc_set_; } void setTimingArcSet(TimingArcSet *set); - ArcDelay *arcDelays() const { return arc_delays_; } - void setArcDelays(ArcDelay *arc_delays); + float *arcDelays() { return arc_delays_; } + const float *arcDelays() const { return arc_delays_; } + void setArcDelays(float *delays); bool delay_Annotation_Is_Incremental() const {return delay_annotation_is_incremental_;}; void setDelayAnnotationIsIncremental(bool is_incr); - // Edge is disabled by set_disable_timing constraint. - bool isDisabledConstraint() const; - void setIsDisabledConstraint(bool disabled); - // Timing sense for the to_pin function after simplifying the - // function based constants on the instance pins. - TimingSense simTimingSense() const; - void setSimTimingSense(TimingSense sense); - // Edge is disabled by constants in condition (when) function. - bool isDisabledCond() const { return is_disabled_cond_; } - void setIsDisabledCond(bool disabled); // Edge is disabled to break combinational loops. - bool isDisabledLoop() const { return is_disabled_loop_; } + [[nodiscard]] bool isDisabledLoop() const { return is_disabled_loop_; } void setIsDisabledLoop(bool disabled); // Edge is disabled to prevent converging clocks from merging (Xilinx). bool isBidirectInstPath() const { return is_bidirect_inst_path_; } @@ -401,6 +367,10 @@ public: bool isBidirectNetPath() const { return is_bidirect_net_path_; } void setIsBidirectNetPath(bool is_bidir); void removeDelayAnnotated(); + [[nodiscard]] bool hasSimSense() const { return has_sim_sense_; } + void setHasSimSense(bool has_sense); + [[nodiscard]] bool hasDisabledCond() const { return has_disabled_cond_; } + void setHasDisabledCond(bool has_disabled); // ObjectTable interface. ObjectIdx objectIdx() const { return object_idx_; } @@ -408,8 +378,8 @@ public: protected: void init(VertexId from, - VertexId to, - TimingArcSet *arc_set); + VertexId to, + TimingArcSet *arc_set); void clear(); bool arcDelayAnnotated(const TimingArc *arc, DcalcAPIndex ap_index, @@ -423,10 +393,10 @@ protected: TimingArcSet *arc_set_; VertexId from_; VertexId to_; - EdgeId vertex_in_link_; // Vertex in edges list. - EdgeId vertex_out_next_; // Vertex out edges doubly linked list. + EdgeId vertex_in_link_; // Vertex in edges list. + EdgeId vertex_out_next_; // Vertex out edges doubly linked list. EdgeId vertex_out_prev_; - ArcDelay *arc_delays_; + float *arc_delays_; union { uintptr_t bits_; std::vector *seq_; @@ -435,11 +405,9 @@ protected: bool delay_annotation_is_incremental_:1; bool is_bidirect_inst_path_:1; bool is_bidirect_net_path_:1; - // Timing sense from function and constants on edge instance. - unsigned sim_timing_sense_:timing_sense_bit_count; - bool is_disabled_constraint_:1; - bool is_disabled_cond_:1; bool is_disabled_loop_:1; + bool has_sim_sense_:1; + bool has_disabled_cond_:1; unsigned object_idx_:VertexTable::idx_bits; private: @@ -456,9 +424,9 @@ private: class VertexIterator : public Iterator { public: - explicit VertexIterator(Graph *graph); - virtual bool hasNext() { return vertex_ || bidir_vertex_; } - virtual Vertex *next(); + VertexIterator(Graph *graph); + bool hasNext() override { return vertex_ || bidir_vertex_; } + Vertex *next() override; private: bool findNextPin(); @@ -468,20 +436,20 @@ private: Network *network_; Instance *top_inst_; LeafInstanceIterator *inst_iter_; - InstancePinIterator *pin_iter_; - Vertex *vertex_; - Vertex *bidir_vertex_; + InstancePinIterator *pin_iter_{nullptr}; + Vertex *vertex_{nullptr}; + Vertex *bidir_vertex_{nullptr}; }; class VertexInEdgeIterator : public VertexEdgeIterator { public: VertexInEdgeIterator(Vertex *vertex, - const Graph *graph); + const Graph *graph); VertexInEdgeIterator(VertexId vertex_id, - const Graph *graph); - bool hasNext() { return (next_ != nullptr); } - Edge *next(); + const Graph *graph); + bool hasNext() override { return (next_ != nullptr); } + Edge *next() override; private: Edge *next_; @@ -492,9 +460,9 @@ class VertexOutEdgeIterator : public VertexEdgeIterator { public: VertexOutEdgeIterator(Vertex *vertex, - const Graph *graph); - bool hasNext() { return (next_ != nullptr); } - Edge *next(); + const Graph *graph); + bool hasNext() override { return (next_ != nullptr); } + Edge *next() override; private: Edge *next_; @@ -506,31 +474,21 @@ class EdgesThruHierPinIterator : public Iterator { public: EdgesThruHierPinIterator(const Pin *hpin, - Network *network, - Graph *graph); - virtual bool hasNext() { return edge_iter_.hasNext(); } - virtual Edge *next() { return edge_iter_.next(); } + Network *network, + Graph *graph); + bool hasNext() override; + Edge *next() override; private: EdgeSet edges_; - EdgeSet::Iterator edge_iter_; + EdgeSet::iterator edge_iter_; }; -class VertexIdLess +// Helper function to create a VertexSet with the comparator initialized +inline VertexSet +makeVertexSet(StaState *sta) { -public: - VertexIdLess(Graph *&graph); - bool operator()(const Vertex *vertex1, - const Vertex *vertex2) const; - -private: - Graph *&graph_; -}; - -class VertexSet : public Set -{ -public: - VertexSet(Graph *&graph); -}; + return VertexSet(VertexIdLess(sta->graphRef())); +} -} // namespace +} // namespace sta diff --git a/include/sta/GraphClass.hh b/include/sta/GraphClass.hh index 381a9b8b5..cdf7539b8 100644 --- a/include/sta/GraphClass.hh +++ b/include/sta/GraphClass.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,13 +25,13 @@ #pragma once #include +#include +#include -#include "ObjectId.hh" -#include "Set.hh" -#include "Vector.hh" +#include "Delay.hh" #include "MinMax.hh" +#include "ObjectId.hh" #include "Transition.hh" -#include "Delay.hh" namespace sta { @@ -42,19 +42,29 @@ class Edge; class VertexIterator; class VertexInEdgeIterator; class VertexOutEdgeIterator; -class GraphLoop; -class VertexSet; -typedef ObjectId VertexId; -typedef ObjectId EdgeId; -typedef Vector VertexSeq; -typedef Vector EdgeSeq; -typedef Set EdgeSet; -typedef int Level; -typedef int DcalcAPIndex; -typedef int TagGroupIndex; -typedef Vector GraphLoopSeq; -typedef std::vector SlewSeq; +class VertexIdLess +{ +public: + VertexIdLess() = delete; + VertexIdLess(Graph *&graph); + bool operator()(const Vertex *vertex1, + const Vertex *vertex2) const; + +private: + Graph *&graph_; +}; + +using VertexId = ObjectId; +using EdgeId = ObjectId; +using VertexSeq = std::vector; +using VertexSet = std::set; +using EdgeSeq = std::vector; +using EdgeSet = std::set; +using Level = int; +using DcalcAPIndex = int; +using TagGroupIndex = int; +using SlewSeq = std::vector; static constexpr int level_max = std::numeric_limits::max(); @@ -66,4 +76,4 @@ static constexpr int slew_annotated_bits = MinMax::index_count * RiseFall::index // Bit shifts used to mark vertices in a Bfs queue. enum class BfsIndex { dcalc, arrival, required, other, bits }; -} // namespace +} // namespace sta diff --git a/include/sta/GraphCmp.hh b/include/sta/GraphCmp.hh index 1cafb0816..9c714d9c8 100644 --- a/include/sta/GraphCmp.hh +++ b/include/sta/GraphCmp.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,18 +24,18 @@ #pragma once +#include "GraphClass.hh" #include "NetworkClass.hh" #include "NetworkCmp.hh" -#include "GraphClass.hh" namespace sta { class VertexNameLess { public: - explicit VertexNameLess(Network *network); + VertexNameLess(Network *network); bool operator()(const Vertex *vertex1, - const Vertex *vertex2); + const Vertex *vertex2); private: Network *network_; @@ -45,9 +45,9 @@ class EdgeLess { public: EdgeLess(const Network *network, - Graph *&graph); + Graph *&graph); bool operator()(const Edge *edge1, - const Edge *edge2) const; + const Edge *edge2) const; private: const PinPathNameLess pin_less_; @@ -56,7 +56,7 @@ private: void sortEdges(EdgeSeq *edges, - Network *network, - Graph *graph); + Network *network, + Graph *graph); -} // namespace +} // namespace sta diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index 6a746fc4a..caf309b5d 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,17 +24,17 @@ #pragma once -#include -#include #include +#include +#include +#include -#include "Map.hh" -#include "NetworkClass.hh" +#include "ArcDelayCalc.hh" #include "GraphClass.hh" +#include "NetworkClass.hh" +#include "SdcClass.hh" #include "SearchClass.hh" -#include "DcalcAnalysisPt.hh" #include "StaState.hh" -#include "ArcDelayCalc.hh" namespace sta { @@ -42,9 +42,10 @@ class DelayCalcObserver; class MultiDrvrNet; class FindVertexDelays; class NetCaps; +class SearchPred; -typedef Map MultiDrvrNetMap; -typedef std::vector DrvrLoadSlews; +using MultiDrvrNetMap = std::map; +using DrvrLoadSlews = std::vector; // This class traverses the graph calling the arc delay calculator and // annotating delays on graph edges. @@ -52,8 +53,8 @@ class GraphDelayCalc : public StaState { public: GraphDelayCalc(StaState *sta); - virtual ~GraphDelayCalc(); - virtual void copyState(const StaState *sta); + ~GraphDelayCalc() override; + void copyState(const StaState *sta) override; // Set the observer for edge delay changes. virtual void setObserver(DelayCalcObserver *observer); // Invalidate all delays/slews. @@ -73,7 +74,7 @@ public: // Returned string is owned by the caller. virtual std::string reportDelayCalc(const Edge *edge, const TimingArc *arc, - const Corner *corner, + const Scene *scene, const MinMax *min_max, int digits); // Percentage (0.0:1.0) change in delay that causes downstream @@ -82,27 +83,32 @@ public: virtual void setIncrementalDelayTolerance(float tol); float loadCap(const Pin *drvr_pin, - const DcalcAnalysisPt *dcalc_ap) const; + const Scene *scene, + const MinMax *min_max) const; float loadCap(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) const; + const Scene *scene, + const MinMax *min_max) const; void loadCap(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, // Return values. float &pin_cap, float &wire_cap) const; void netCaps(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, // Return values. float &pin_cap, float &wire_cap, float &fanout, - bool &has_set_load) const; + bool &has_net_load) const; void parasiticLoad(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const MultiDrvrNet *multi_drvr, ArcDelayCalc *arc_delay_calc, // Return values. @@ -112,14 +118,15 @@ public: void findDriverArcDelays(Vertex *drvr_vertex, Edge *edge, const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc); // Precedence: // SDF annotation // Liberty port timing group timing_type minimum_period. // Liberty port min_period attribute. void minPeriod(const Pin *pin, - const Corner *corner, + const Scene *scene, // Return values. float &min_period, bool &exists); @@ -127,11 +134,13 @@ public: Slew edgeFromSlew(const Vertex *from_vertex, const RiseFall *from_rf, const Edge *edge, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); Slew edgeFromSlew(const Vertex *from_vertex, const RiseFall *from_rf, const TimingRole *role, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); bool bidirectDrvrSlewFromLoad(const Pin *pin) const; protected: @@ -145,13 +154,15 @@ protected: void seedNoDrvrSlew(Vertex *drvr_vertex, const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc); void seedNoDrvrCellSlew(Vertex *drvr_vertex, const Pin *drvr_pin, const RiseFall *rf, const InputDrive *drive, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc); void seedLoadSlew(Vertex *vertex); void setInputPortWireDelays(Vertex *vertex); @@ -160,9 +171,11 @@ protected: Vertex *drvr_vertex, const RiseFall *rf, const LibertyPort *from_port, - float *from_slews, + const DriveCellSlews *from_slews, const LibertyPort *to_port, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max, + ArcDelayCalc *arc_delay_calc); LibertyPort *driveCellDefaultFromPort(const LibertyCell *cell, const LibertyPort *to_port); int findPortIndex(const LibertyCell *cell, @@ -171,12 +184,14 @@ protected: Vertex *drvr_vertex, const TimingArc *arc, float from_slew, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max, + ArcDelayCalc *arc_delay_calc); void findDriverDelays(Vertex *drvr_vertex, ArcDelayCalc *arc_delay_calc, LoadPinIndexMap &load_pin_index_map); MultiDrvrNet *multiDrvrNet(const Vertex *drvr_vertex) const; - MultiDrvrNet *findMultiDrvrNet(Vertex *drvr_pin); + MultiDrvrNet *findMultiDrvrNet(Vertex *drvr_vertex); MultiDrvrNet *makeMultiDrvrNet(Vertex *drvr_vertex); bool hasMultiDrvrs(Vertex *drvr_vertex); Vertex *firstLoad(Vertex *drvr_vertex); @@ -196,14 +211,16 @@ protected: const MultiDrvrNet *multi_drvr, Edge *edge, const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc, LoadPinIndexMap &load_pin_index_map); ArcDcalcArgSeq makeArcDcalcArgs(Vertex *drvr_vertex, const MultiDrvrNet *multi_drvr, Edge *edge, const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc); void findParallelEdge(Vertex *vertex, const TimingArc *drvr_arc, @@ -218,41 +235,47 @@ protected: ArcDelayCalc *arc_delay_calc, bool propagate); DrvrLoadSlews loadSlews(LoadPinIndexMap &load_pin_index_map); - bool loadSlewsChanged(DrvrLoadSlews &prev_load_slews, + bool loadSlewsChanged(DrvrLoadSlews &load_slews_prev, LoadPinIndexMap &load_pin_index_map); void enqueueTimingChecksEdges(Vertex *vertex); bool annotateDelaysSlews(Edge *edge, const TimingArc *arc, ArcDcalcResult &dcalc_result, LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); bool annotateDelaySlew(Edge *edge, const TimingArc *arc, - ArcDelay &gate_delay, - Slew &gate_slew, - const DcalcAnalysisPt *dcalc_ap); + const ArcDelay &gate_delay, + const Slew &gate_slew, + const Scene *scene, + const MinMax *min_max); bool annotateLoadDelays(Vertex *drvr_vertex, const RiseFall *drvr_rf, ArcDcalcResult &dcalc_result, LoadPinIndexMap &load_pin_index_map, const ArcDelay &extra_delay, bool merge, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); void findLatchEdgeDelays(Edge *edge); void findCheckEdgeDelays(Edge *edge, ArcDelayCalc *arc_delay_calc); void deleteMultiDrvrNets(); Slew checkEdgeClkSlew(const Vertex *from_vertex, const RiseFall *from_rf, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); float loadCap(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc) const; void parasiticLoad(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const MultiDrvrNet *multi_drvr, ArcDelayCalc *arc_delay_calc, // Return values. @@ -261,7 +284,8 @@ protected: const Parasitic *¶sitic) const; void netCaps(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const MultiDrvrNet *multi_drvr, // Return values. float &pin_cap, @@ -270,12 +294,12 @@ protected: bool &has_net_load) const; // Observer for edge delay changes. - DelayCalcObserver *observer_; - bool delays_seeded_; - bool incremental_; - bool delays_exist_; + DelayCalcObserver *observer_{nullptr}; + bool delays_seeded_{false}; + bool incremental_{false}; + bool delays_exist_{false}; // Vertices with invalid -to delays. - VertexSet *invalid_delays_; + VertexSet invalid_delays_; // Timing check edges with invalid delays. EdgeSet invalid_check_edges_; // Latch D->Q edges with invalid delays. @@ -284,13 +308,12 @@ protected: std::mutex invalid_edge_lock_; SearchPred *search_pred_; SearchPred *search_non_latch_pred_; - SearchPred *clk_pred_; BfsFwdIterator *iter_; MultiDrvrNetMap multi_drvr_net_map_; std::mutex multi_drvr_lock_; // Percentage (0.0:1.0) change in delay that causes downstream // delays to be recomputed during incremental delay calculation. - float incremental_delay_tolerance_; + float incremental_delay_tolerance_{0.0}; friend class FindVertexDelays; friend class MultiDrvrNet; @@ -300,8 +323,7 @@ protected: class DelayCalcObserver { public: - DelayCalcObserver() {} - virtual ~DelayCalcObserver() {} + virtual ~DelayCalcObserver() = default; virtual void delayChangedFrom(Vertex *vertex) = 0; virtual void delayChangedTo(Vertex *vertex) = 0; virtual void checkDelayChangedTo(Vertex *vertex) = 0; @@ -312,27 +334,27 @@ public: class MultiDrvrNet { public: - MultiDrvrNet(); VertexSeq &drvrs() { return drvrs_; } const VertexSeq &drvrs() const { return drvrs_; } bool parallelGates(const Network *network) const; Vertex *dcalcDrvr() const { return dcalc_drvr_; } void setDcalcDrvr(Vertex *drvr); void netCaps(const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, // Return values. float &pin_cap, float &wire_cap, float &fanout, bool &has_net_load) const; - void findCaps(const Sdc *sdc); + void findCaps(const StaState *sta); private: // Driver that triggers delay calculation for all the drivers on the net. - Vertex *dcalc_drvr_; + Vertex *dcalc_drvr_{nullptr}; VertexSeq drvrs_; // [drvr_rf->index][dcalc_ap->index] std::vector net_caps_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Hash.hh b/include/sta/Hash.hh index 4389d7ce3..5cfd423df 100644 --- a/include/sta/Hash.hh +++ b/include/sta/Hash.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,8 +24,9 @@ #pragma once -#include #include +#include +#include namespace sta { @@ -34,7 +35,7 @@ const size_t hash_init_value = 5381; // Dan Bernstein, comp.lang.c. inline size_t hashSum(size_t hash, - size_t add) + size_t add) { // hash * 31 ^ add. return ((hash << 5) + hash) ^ add; @@ -42,7 +43,7 @@ hashSum(size_t hash, inline void hashIncr(size_t &hash, - size_t add) + size_t add) { // hash * 31 ^ add. hash = ((hash << 5) + hash) ^ add; @@ -56,7 +57,7 @@ nextMersenne(size_t n) // Sadly necessary until c++ std::hash works for char *. size_t -hashString(const char *str); +hashString(std::string_view str); // Pointer hashing is strongly discouraged because it causes results to change // from run to run. Use Network::id functions instead. @@ -66,4 +67,4 @@ hashString(const char *str); #define hashPtr(ptr) (reinterpret_cast(ptr) >> 2) #endif -} // namespace +} // namespace sta diff --git a/include/sta/HpinDrvrLoad.hh b/include/sta/HpinDrvrLoad.hh index dba934ccb..4b68cae82 100644 --- a/include/sta/HpinDrvrLoad.hh +++ b/include/sta/HpinDrvrLoad.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,7 +24,6 @@ #pragma once -#include "Set.hh" #include "NetworkClass.hh" namespace sta { @@ -34,22 +33,21 @@ class HpinDrvrLoadVisitor; void visitHpinDrvrLoads(const Pin *pin, - const Network *network, - HpinDrvrLoadVisitor *visitor); + const Network *network, + HpinDrvrLoadVisitor *visitor); class HpinDrvrLoadLess { public: bool operator()(const HpinDrvrLoad *drvr_load1, - const HpinDrvrLoad *drvr_load2) const; + const HpinDrvrLoad *drvr_load2) const; }; // Abstract base class for visitDrvrLoadsThruHierPin visitor. class HpinDrvrLoadVisitor { public: - HpinDrvrLoadVisitor() {} - virtual ~HpinDrvrLoadVisitor() {} + virtual ~HpinDrvrLoadVisitor() = default; virtual void visit(HpinDrvrLoad *drvr_load) = 0; }; @@ -57,13 +55,13 @@ class HpinDrvrLoad { public: HpinDrvrLoad(const Pin *drvr, - const Pin *load, - PinSet *hpins_from_drvr, - PinSet *hpins_to_load); + const Pin *load, + PinSet *hpins_from_drvr, + PinSet *hpins_to_load); ~HpinDrvrLoad(); void report(const Network *network); HpinDrvrLoad(const Pin *drvr, - const Pin *load); + const Pin *load); const Pin *drvr() const { return drvr_; } const Pin *load() const { return load_; } PinSet *hpinsFromDrvr() { return hpins_from_drvr_; } @@ -77,4 +75,4 @@ private: PinSet *hpins_to_load_; }; -} // namespace +} // namespace sta diff --git a/include/sta/InputDrive.hh b/include/sta/InputDrive.hh index 1da228171..30bdd5f30 100644 --- a/include/sta/InputDrive.hh +++ b/include/sta/InputDrive.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,10 +24,11 @@ #pragma once -#include "MinMax.hh" #include "LibertyClass.hh" +#include "MinMax.hh" #include "NetworkClass.hh" #include "RiseFallMinMax.hh" +#include "SdcClass.hh" namespace sta { @@ -40,45 +41,45 @@ class InputDriveCell; class InputDrive { public: - explicit InputDrive(); + InputDrive(); ~InputDrive(); void setSlew(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew); + const MinMaxAll *min_max, + float slew); void setDriveResistance(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float res); + const MinMaxAll *min_max, + float res); void driveResistance(const RiseFall *rf, - const MinMax *min_max, - float &res, - bool &exists) const; + const MinMax *min_max, + float &res, + bool &exists) const; bool hasDriveResistance(const RiseFall *rf, - const MinMax *min_max) const; + const MinMax *min_max) const; bool driveResistanceMinMaxEqual(const RiseFall *rf) const; void setDriveCell(const LibertyLibrary *library, - const LibertyCell *cell, - const LibertyPort *from_port, - float *from_slews, - const LibertyPort *to_port, - const RiseFallBoth *rf, - const MinMaxAll *min_max); + const LibertyCell *cell, + const LibertyPort *from_port, + const DriveCellSlews &from_slews, + const LibertyPort *to_port, + const RiseFallBoth *rf, + const MinMaxAll *min_max); void driveCell(const RiseFall *rf, - const MinMax *min_max, + const MinMax *min_max, // Return values. - const LibertyCell *&cell, - const LibertyPort *&from_port, - float *&from_slews, - const LibertyPort *&to_port) const; + const LibertyCell *&cell, + const LibertyPort *&from_port, + const DriveCellSlews *&from_slews, + const LibertyPort *&to_port) const; InputDriveCell *driveCell(const RiseFall *rf, - const MinMax *min_max) const; + const MinMax *min_max) const; bool hasDriveCell(const RiseFall *rf, - const MinMax *min_max) const; + const MinMax *min_max) const; // True if rise/fall/min/max drive cells are equal. bool driveCellsEqual() const; void slew(const RiseFall *rf, - const MinMax *min_max, - float &slew, - bool &exists) const; + const MinMax *min_max, + float &slew, + bool &exists) const; const RiseFallMinMax *slews() const { return &slews_; } private: @@ -92,18 +93,18 @@ class InputDriveCell { public: InputDriveCell(const LibertyLibrary *library, - const LibertyCell *cell, - const LibertyPort *from_port, - float *from_slews, - const LibertyPort *to_port); + const LibertyCell *cell, + const LibertyPort *from_port, + const DriveCellSlews &from_slews, + const LibertyPort *to_port); const LibertyLibrary *library() const { return library_; } void setLibrary(const LibertyLibrary *library); const LibertyCell *cell() const { return cell_; } void setCell(const LibertyCell *cell); const LibertyPort *fromPort() const { return from_port_; } void setFromPort(const LibertyPort *from_port); - float *fromSlews() { return from_slews_; } - void setFromSlews(float *from_slews); + const DriveCellSlews &fromSlews() const { return from_slews_; } + void setFromSlews(const DriveCellSlews &from_slews); const LibertyPort *toPort() const { return to_port_; } void setToPort(const LibertyPort *to_port); bool equal(const InputDriveCell *drive) const; @@ -112,8 +113,8 @@ private: const LibertyLibrary *library_; const LibertyCell *cell_; const LibertyPort *from_port_; - float from_slews_[RiseFall::index_count]; + DriveCellSlews from_slews_; const LibertyPort *to_port_; }; -} // namespace +} // namespace sta diff --git a/include/sta/InternalPower.hh b/include/sta/InternalPower.hh index 0c30a38d3..fb1c2d013 100644 --- a/include/sta/InternalPower.hh +++ b/include/sta/InternalPower.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,89 +24,74 @@ #pragma once +#include +#include +#include + #include "LibertyClass.hh" #include "Transition.hh" namespace sta { -class InternalPowerAttrs; -class InternalPowerModel; - -class InternalPowerAttrs +class InternalPowerModel { public: - InternalPowerAttrs(); - virtual ~InternalPowerAttrs(); - void deleteContents(); - FuncExpr *when() const { return when_; } - void setWhen(FuncExpr *when); - void setModel(const RiseFall *rf, - InternalPowerModel *model); - InternalPowerModel *model(const RiseFall *rf) const; - const char *relatedPgPin() const { return related_pg_pin_; } - void setRelatedPgPin(const char *related_pg_pin); + InternalPowerModel(); + InternalPowerModel(std::shared_ptr model); + float power(const LibertyCell *cell, + const Pvt *pvt, + float in_slew, + float load_cap) const; + std::string reportPower(const LibertyCell *cell, + const Pvt *pvt, + float in_slew, + float load_cap, + int digits) const; + const TableModel *model() const { return model_.get(); } protected: - FuncExpr *when_; - InternalPowerModel *models_[RiseFall::index_count]; - const char *related_pg_pin_; + void findAxisValues(float in_slew, + float load_cap, + // Return values. + float &axis_value1, + float &axis_value2, + float &axis_value3) const; + float axisValue(const TableAxis *axis, + float in_slew, + float load_cap) const; + bool checkAxes(const TableModel *model); + bool checkAxis(const TableAxis *axis); + + std::shared_ptr model_; }; +using InternalPowerModels = std::array; + class InternalPower { public: - InternalPower(LibertyCell *cell, - LibertyPort *port, - LibertyPort *related_port, - InternalPowerAttrs *attrs); - ~InternalPower(); + InternalPower(LibertyPort *port, + LibertyPort *related_port, + LibertyPort *related_pg_pin, + const std::shared_ptr &when, + const InternalPowerModels &models); LibertyCell *libertyCell() const; LibertyPort *port() const { return port_; } LibertyPort *relatedPort() const { return related_port_; } - FuncExpr *when() const { return when_; } - const char *relatedPgPin() const { return related_pg_pin_; } + FuncExpr *when() const { return when_.get(); } + LibertyPort *relatedPgPin() const { return related_pg_pin_; } float power(const RiseFall *rf, - const Pvt *pvt, - float in_slew, - float load_cap); + const Pvt *pvt, + float in_slew, + float load_cap) const; + const InternalPowerModel &model(const RiseFall *rf) const; protected: LibertyPort *port_; LibertyPort *related_port_; - FuncExpr *when_; - const char *related_pg_pin_; - InternalPowerModel *models_[RiseFall::index_count]; -}; - -class InternalPowerModel -{ -public: - explicit InternalPowerModel(TableModel *model); - ~InternalPowerModel(); - float power(const LibertyCell *cell, - const Pvt *pvt, - float in_slew, - float load_cap) const; - std::string reportPower(const LibertyCell *cell, - const Pvt *pvt, - float in_slew, - float load_cap, - int digits) const; - -protected: - void findAxisValues(float in_slew, - float load_cap, - // Return values. - float &axis_value1, - float &axis_value2, - float &axis_value3) const; - float axisValue(const TableAxis *axis, - float in_slew, - float load_cap) const; - bool checkAxes(const TableModel *model); - bool checkAxis(const TableAxis *axis); - - TableModel *model_; + LibertyPort *related_pg_pin_; + std::shared_ptr when_; + InternalPowerModels models_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Iterator.hh b/include/sta/Iterator.hh index 6c1ea6210..0c7e9ba30 100644 --- a/include/sta/Iterator.hh +++ b/include/sta/Iterator.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -35,9 +35,85 @@ template class Iterator { public: - virtual ~Iterator() {} + virtual ~Iterator() = default; virtual bool hasNext() = 0; virtual OBJ next() = 0; }; -} // namespace +template +class VectorIterator : public Iterator +{ +public: + VectorIterator(const VECTOR_TYPE *seq) : + seq_(seq) + { + if (seq_) + itr_ = seq_->begin(); + } + VectorIterator(const VECTOR_TYPE &seq) : + seq_(&seq), + itr_(seq.begin()) + { + } + + bool hasNext() override { return seq_ && itr_ != seq_->end(); } + OBJ_TYPE next() override { return *itr_++; } + +protected: + const VECTOR_TYPE *seq_; + VECTOR_TYPE::const_iterator itr_; +}; + +template +class MapIterator : public Iterator +{ +public: + MapIterator(const MAP_TYPE *map) : + map_(map) + { + if (map) + itr_ = map->begin(); + } + MapIterator(const MAP_TYPE &map) : + map_(&map), + itr_(map.begin()) + { + } + + bool hasNext() override { return map_ && itr_ != map_->end(); } + OBJ_TYPE next() override { + OBJ_TYPE next = itr_->second; + itr_++; + return next; + } + +protected: + const MAP_TYPE *map_; + MAP_TYPE::const_iterator itr_; +}; + +template +class SetIterator : public Iterator +{ +public: + SetIterator(const SET_TYPE *set) : + set_(set) + { + if (set) + itr_ = set->begin(); + } + SetIterator(const SET_TYPE &set) : + set_(&set), + itr_(set.begin()) + { + } + + bool hasNext() override { return set_ && itr_ != set_->end(); } + OBJ_TYPE next() override { return *itr_++; } + +protected: + const SET_TYPE *set_; + SET_TYPE::const_iterator itr_; +}; + +} // namespace sta diff --git a/include/sta/LeakagePower.hh b/include/sta/LeakagePower.hh index 18f886c6b..265e83f23 100644 --- a/include/sta/LeakagePower.hh +++ b/include/sta/LeakagePower.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -36,6 +36,7 @@ public: LibertyPort *related_pg_port, FuncExpr *when, float power); + LeakagePower(LeakagePower &&other) noexcept; ~LeakagePower(); LibertyCell *libertyCell() const { return cell_; } LibertyPort *relatedPgPort() const { return related_pg_port_; } @@ -49,4 +50,4 @@ protected: float power_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index e90f121d4..3944aeb1e 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,10 +24,17 @@ #pragma once +#include #include #include #include +#include +#include +#include +#include +#include +#include "ContainerHelpers.hh" #include "MinMax.hh" #include "RiseFallMinMax.hh" #include "ConcreteLibrary.hh" @@ -35,6 +42,8 @@ #include "MinMaxValues.hh" #include "Transition.hh" #include "Delay.hh" +#include "InternalPower.hh" +#include "LeakagePower.hh" #include "LibertyClass.hh" #include "SdcClass.hh" @@ -45,52 +54,98 @@ class LibertyCellIterator; class LibertyCellPortIterator; class LibertyCellPortBitIterator; class LibertyPortMemberIterator; -class ModeValueDef; class TestCell; class PatternMatch; -class LatchEnable; class Report; class Debug; class LibertyBuilder; class LibertyReader; class OcvDerate; class TimingArcAttrs; -class InternalPowerAttrs; class StaState; -class Corner; -class Corners; -class DcalcAnalysisPt; +class Scene; class DriverWaveform; -typedef Map TableTemplateMap; -typedef Vector TableTemplateSeq; -typedef Map BusDclMap; -typedef Vector BusDclSeq; -typedef Map ScaleFactorsMap; -typedef Map WireloadMap; -typedef Map WireloadSelectionMap; -typedef Map OperatingConditionsMap; -typedef Map PortToSequentialMap; -typedef Vector TimingArcSetSeq; -typedef Set TimingArcSetMap; -typedef Map LibertyPortPairTimingArcMap; -typedef Vector InternalPowerSeq; -typedef Map PortInternalPowerSeq; -typedef Vector LeakagePowerSeq; -typedef Map LibertyPortTimingArcMap; -typedef Map ScaledCellMap; -typedef Map ScaledPortMap; -typedef Map ModeDefMap; -typedef Map ModeValueMap; -typedef Map LatchEnableMap; -typedef Vector LatchEnableSeq; -typedef Map OcvDerateMap; -typedef Vector InternalPowerAttrsSeq; -typedef Map SupplyVoltageMap; -typedef Map DriverWaveformMap; -typedef Vector DcalcAnalysisPtSeq; +// Mode definition mode_value group (defined before ModeValueMap / ModeDef). +class ModeValueDef +{ +public: + ModeValueDef(std::string_view value); + ModeValueDef(ModeValueDef &&other) noexcept; + ~ModeValueDef(); + const std::string &value() const { return value_; } + FuncExpr *cond() const { return cond_; } + void setCond(FuncExpr *cond); + const std::string &sdfCond() const { return sdf_cond_; } + void setSdfCond(std::string sdf_cond); + +private: + std::string value_; + FuncExpr *cond_{nullptr}; + std::string sdf_cond_; +}; + +// Latch enable port/function for a latch D->Q timing arc set. +class LatchEnable +{ +public: + LatchEnable(LibertyPort *data, + LibertyPort *enable, + const RiseFall *enable_edge, + FuncExpr *enable_func, + LibertyPort *output, + TimingArcSet *d_to_q, + TimingArcSet *en_to_q, + TimingArcSet *setup_check); + LibertyPort *data() const { return data_; } + LibertyPort *output() const { return output_; } + LibertyPort *enable() const { return enable_; } + FuncExpr *enableFunc() const { return enable_func_; } + const RiseFall *enableEdge() const { return enable_edge_; } + TimingArcSet *dToQ() const { return d_to_q_; } + TimingArcSet *enToQ() const { return en_to_q_; } + TimingArcSet *setupCheck() const { return setup_check_; } + +private: + LibertyPort *data_; + LibertyPort *enable_; + const RiseFall *enable_edge_; + FuncExpr *enable_func_; + LibertyPort *output_; + TimingArcSet *d_to_q_; + TimingArcSet *en_to_q_; + TimingArcSet *setup_check_; +}; + +using TableTemplateMap = std::map>; +using TableTemplateSeq = std::vector; +using BusDclMap = std::map>; +using BusDclSeq = std::vector; +using ScaleFactorsMap = std::map>; +using WireloadMap = std::map>; +using WireloadSelectionMap = std::map>; +using OperatingConditionsMap = std::map>; +using PortToSequentialMap = std::map; +using TimingArcSetSeq = std::vector; +using TimingArcSetSet = std::set; +using LibertyPortPairTimingArcMap = std::map; +using InternalPowerSeq = std::vector; +using InternalPowerPtrSeq = std::vector; +using InternalPowerIndexSeq = std::vector; +using PortInternalPowerMap = std::map; +using LeakagePowerSeq = std::vector; +using ScaledCellMap = std::map; +using ScaledPortMap = std::map; +using ModeDefMap = std::map>; +using ModeValueMap = std::map>; +using LatchEnableIndexMap = std::map; +using LatchEnableSeq = std::vector; +using OcvDerateMap = std::map>; +using SupplyVoltageMap = std::map>; +using DriverWaveformMap = std::map>; +using SceneSeq = std::vector; +using GeneratedClockSeq = std::vector; enum class ClockGateType { none, latch_posedge, latch_negedge, other }; @@ -99,17 +154,17 @@ enum class DelayModelType { cmos_linear, cmos_pwl, cmos2, table, polynomial, dcm enum class ScanSignalType { enable, enable_inverted, clock, clock_a, clock_b, input, input_inverted, output, output_inverted, none }; enum class PwrGndType { none, - primary_power, primary_ground, - backup_power, backup_ground, - internal_power, internal_ground, - nwell, pwell, - deepnwell, deeppwell}; + primary_power, primary_ground, + backup_power, backup_ground, + internal_power, internal_ground, + nwell, pwell, + deepnwell, deeppwell}; enum class ScaleFactorPvt { process, volt, temp, unknown }; -constexpr int scale_factor_pvt_count = int(ScaleFactorPvt::unknown) + 1; +constexpr int scale_factor_pvt_count = static_cast(ScaleFactorPvt::unknown) + 1; enum class TableTemplateType { delay, power, output_current, capacitance, ocv }; -constexpr int table_template_type_count = int(TableTemplateType::ocv) + 1; +constexpr int table_template_type_count = static_cast(TableTemplateType::ocv) + 1; enum class LevelShifterType { HL, LH, HL_LH }; @@ -123,13 +178,13 @@ void deleteLiberty(); ScaleFactorPvt -findScaleFactorPvt(const char *name); -const char * +findScaleFactorPvt(std::string_view name); +const std::string& scaleFactorPvtName(ScaleFactorPvt pvt); ScaleFactorType -findScaleFactorType(const char *name); -const char * +findScaleFactorType(std::string_view name); +const std::string& scaleFactorTypeName(ScaleFactorType type); bool scaleFactorTypeRiseFallSuffix(ScaleFactorType type); @@ -139,7 +194,7 @@ bool scaleFactorTypeLowHighSuffix(ScaleFactorType type); // Timing sense as a string. -const char * +const std::string& to_string(TimingSense sense); // Opposite timing sense. @@ -151,10 +206,10 @@ timingSenseOpposite(TimingSense sense); class LibertyLibrary : public ConcreteLibrary { public: - LibertyLibrary(const char *name, - const char *filename); - virtual ~LibertyLibrary(); - LibertyCell *findLibertyCell(const char *name) const; + LibertyLibrary(std::string_view name, + std::string_view filename); + ~LibertyLibrary() override; + LibertyCell *findLibertyCell(std::string_view name) const; LibertyCellSeq findLibertyCellsMatching(PatternMatch *pattern); // Liberty cells that are buffers. LibertyCellSeq *buffers(); @@ -162,14 +217,17 @@ public: DelayModelType delayModelType() const { return delay_model_type_; } void setDelayModelType(DelayModelType type); - void addBusDcl(BusDcl *bus_dcl); - BusDcl *findBusDcl(const char *name) const; + BusDcl *makeBusDcl(std::string_view name, + int from, + int to); + BusDcl *findBusDcl(std::string_view name); BusDclSeq busDcls() const; - void addTableTemplate(TableTemplate *tbl_template, - TableTemplateType type); - TableTemplate *findTableTemplate(const char *name, - TableTemplateType type); + TableTemplate *makeTableTemplate(std::string_view name, + TableTemplateType type); + TableTemplate *findTableTemplate(std::string_view name, + TableTemplateType type); TableTemplateSeq tableTemplates() const; + TableTemplateSeq tableTemplates(TableTemplateType type) const; float nominalProcess() const { return nominal_process_; } void setNominalProcess(float process); float nominalVoltage() const { return nominal_voltage_; } @@ -178,29 +236,29 @@ public: void setNominalTemperature(float temperature); void setScaleFactors(ScaleFactors *scales); - // Add named scale factor group. - void addScaleFactors(ScaleFactors *scales); - ScaleFactors *findScaleFactors(const char *name); + // Make named scale factor group. Returns pointer to the inserted element. + ScaleFactors *makeScaleFactors(std::string_view name); + ScaleFactors *findScaleFactors(std::string_view name); ScaleFactors *scaleFactors() const { return scale_factors_; } float scaleFactor(ScaleFactorType type, - const Pvt *pvt) const; + const Pvt *pvt) const; float scaleFactor(ScaleFactorType type, - const LibertyCell *cell, - const Pvt *pvt) const; + const LibertyCell *cell, + const Pvt *pvt) const; float scaleFactor(ScaleFactorType type, - int rf_index, - const LibertyCell *cell, - const Pvt *pvt) const; + int rf_index, + const LibertyCell *cell, + const Pvt *pvt) const; void setWireSlewDegradationTable(TableModel *model, - const RiseFall *rf); + const RiseFall *rf); TableModel *wireSlewDegradationTable(const RiseFall *rf) const; float degradeWireSlew(const RiseFall *rf, - float in_slew, - float wire_delay) const; + float in_slew, + float wire_delay) const; // Check for supported axis variables. // Return true if axes are supported. - static bool checkSlewDegradationAxes(const TablePtr &table); + static bool checkSlewDegradationAxes(const TableModel *table_model); float defaultInputPinCap() const { return default_input_pin_cap_; } void setDefaultInputPinCap(float cap); @@ -213,61 +271,61 @@ public: void defaultIntrinsic(const RiseFall *rf, // Return values. - float &intrisic, + float &intrinsic, bool &exists) const; void setDefaultIntrinsic(const RiseFall *rf, - float value); + float value); // Uses defaultOutputPinRes or defaultBidirectPinRes based on dir. void defaultPinResistance(const RiseFall *rf, - const PortDirection *dir, - // Return values. - float &res, - bool &exists) const; + const PortDirection *dir, + // Return values. + float &res, + bool &exists) const; void defaultBidirectPinRes(const RiseFall *rf, - // Return values. - float &res, - bool &exists) const; + // Return values. + float &res, + bool &exists) const; void setDefaultBidirectPinRes(const RiseFall *rf, - float value); + float value); void defaultOutputPinRes(const RiseFall *rf, - // Return values. - float &res, - bool &exists) const; + // Return values. + float &res, + bool &exists) const; void setDefaultOutputPinRes(const RiseFall *rf, - float value); + float value); void defaultMaxSlew(float &slew, - bool &exists) const; + bool &exists) const; void setDefaultMaxSlew(float slew); void defaultMaxCapacitance(float &cap, - bool &exists) const; + bool &exists) const; void setDefaultMaxCapacitance(float cap); void defaultMaxFanout(float &fanout, - bool &exists) const; + bool &exists) const; void setDefaultMaxFanout(float fanout); void defaultFanoutLoad(// Return values. - float &fanout, - bool &exists) const; + float &fanout, + bool &exists) const; void setDefaultFanoutLoad(float load); // Logic thresholds. float inputThreshold(const RiseFall *rf) const; void setInputThreshold(const RiseFall *rf, - float th); + float th); float outputThreshold(const RiseFall *rf) const; void setOutputThreshold(const RiseFall *rf, - float th); + float th); // Slew thresholds (measured). float slewLowerThreshold(const RiseFall *rf) const; void setSlewLowerThreshold(const RiseFall *rf, - float th); + float th); float slewUpperThreshold(const RiseFall *rf) const; void setSlewUpperThreshold(const RiseFall *rf, - float th); + float th); // The library and delay calculator use the liberty slew upper/lower // (measured) thresholds for the table axes and value. These slews // are scaled by slew_derate_from_library to get slews reported to @@ -280,20 +338,20 @@ public: Units *units() { return units_; } const Units *units() const { return units_; } - Wireload *findWireload(const char *name) const; - void setDefaultWireload(Wireload *wireload); - Wireload *defaultWireload() const; - WireloadSelection *findWireloadSelection(const char *name) const; - WireloadSelection *defaultWireloadSelection() const; - void addWireload(Wireload *wireload); + Wireload *makeWireload(std::string_view name); + const Wireload *findWireload(std::string_view name); + void setDefaultWireload(const Wireload *wireload); + const Wireload *defaultWireload() const; + WireloadSelection *makeWireloadSelection(std::string_view name); + const WireloadSelection *findWireloadSelection(std::string_view name) const; + const WireloadSelection *defaultWireloadSelection() const; WireloadMode defaultWireloadMode() const; void setDefaultWireloadMode(WireloadMode mode); - void addWireloadSelection(WireloadSelection *selection); - void setDefaultWireloadSelection(WireloadSelection *selection); + void setDefaultWireloadSelection(const WireloadSelection *selection); - OperatingConditions *findOperatingConditions(const char *name); + OperatingConditions *makeOperatingConditions(std::string_view name); + OperatingConditions *findOperatingConditions(std::string_view name); OperatingConditions *defaultOperatingConditions() const; - void addOperatingConditions(OperatingConditions *op_cond); void setDefaultOperatingConditions(OperatingConditions *op_cond); // AOCV @@ -302,100 +360,99 @@ public: void setOcvArcDepth(float depth); OcvDerate *defaultOcvDerate() const; void setDefaultOcvDerate(OcvDerate *derate); - OcvDerate *findOcvDerate(const char *derate_name); - void addOcvDerate(OcvDerate *derate); - void addSupplyVoltage(const char *suppy_name, - float voltage); - bool supplyExists(const char *suppy_name) const; - void supplyVoltage(const char *supply_name, - // Return value. - float &voltage, - bool &exists) const; + OcvDerate *makeOcvDerate(std::string_view name); + OcvDerate *findOcvDerate(std::string_view derate_name); + void addSupplyVoltage(std::string_view supply_name, + float voltage); + bool supplyExists(std::string_view supply_name) const; + void supplyVoltage(std::string_view supply_name, + // Return value. + float &voltage, + bool &exists) const; // Make scaled cell. Call LibertyCell::addScaledCell after it is complete. - LibertyCell *makeScaledCell(const char *name, - const char *filename); + LibertyCell *makeScaledCell(std::string_view name, + std::string_view filename); static void - makeCornerMap(LibertyLibrary *lib, - int ap_index, - Network *network, - Report *report); - static void - makeCornerMap(LibertyCell *link_cell, - LibertyCell *map_cell, - int ap_index, - Report *report); - static void - makeCornerMap(LibertyCell *cell1, - LibertyCell *cell2, - bool link, - int ap_index, - Report *report); + makeSceneMap(LibertyLibrary *lib, + Scene *scene, + const MinMaxAll *min_max, + Network *network, + Report *report); static void - checkCorners(LibertyCell *cell, - Corners *corners, + makeSceneMap(LibertyCell *link_cell, + LibertyCell *scene_cell, + Scene *scene, + const MinMaxAll *min_max, Report *report); + static void + checkScenes(LibertyCell *cell, + const SceneSeq &scenes, + Report *report); - DriverWaveform *findDriverWaveform(const char *name); - DriverWaveform *driverWaveformDefault() { return driver_waveform_default_; } - void addDriverWaveform(DriverWaveform *driver_waveform); + DriverWaveform *findDriverWaveform(std::string_view name); + DriverWaveform *driverWaveformDefault() { return findDriverWaveform(""); } + DriverWaveform *makeDriverWaveform(std::string_view name, + const TablePtr &waveforms); protected: float degradeWireSlew(const TableModel *model, - float in_slew, - float wire_delay) const; + float in_slew, + float wire_delay) const; + + static constexpr float input_threshold_default_ = .5; + static constexpr float output_threshold_default_ = .5; + static constexpr float slew_lower_threshold_default_ = .2; + static constexpr float slew_upper_threshold_default_ = .8; - Units *units_; - DelayModelType delay_model_type_; + Units *units_{nullptr}; + DelayModelType delay_model_type_{DelayModelType::table}; BusDclMap bus_dcls_; TableTemplateMap template_maps_[table_template_type_count]; - float nominal_process_; - float nominal_voltage_; - float nominal_temperature_; - ScaleFactors *scale_factors_; + float nominal_process_{0.0F}; + float nominal_voltage_{0.0F}; + float nominal_temperature_{0.0F}; + ScaleFactors *scale_factors_{nullptr}; ScaleFactorsMap scale_factors_map_; - TableModel *wire_slew_degradation_tbls_[RiseFall::index_count]; - float default_input_pin_cap_; - float default_output_pin_cap_; - float default_bidirect_pin_cap_; + TableModel *wire_slew_degradation_tbls_[RiseFall::index_count]{nullptr, nullptr}; + float default_input_pin_cap_{0.0F}; + float default_output_pin_cap_{0.0F}; + float default_bidirect_pin_cap_{0.0F}; RiseFallValues default_intrinsic_; RiseFallValues default_inout_pin_res_; RiseFallValues default_output_pin_res_; - float default_fanout_load_; - bool default_fanout_load_exists_; - float default_max_cap_; - bool default_max_cap_exists_; - float default_max_fanout_; - bool default_max_fanout_exists_; - float default_max_slew_; - bool default_max_slew_exists_; - float input_threshold_[RiseFall::index_count]; - float output_threshold_[RiseFall::index_count]; - float slew_lower_threshold_[RiseFall::index_count]; - float slew_upper_threshold_[RiseFall::index_count]; - float slew_derate_from_library_; + float default_fanout_load_{0.0F}; + bool default_fanout_load_exists_{false}; + float default_max_cap_{0.0F}; + bool default_max_cap_exists_{false}; + float default_max_fanout_{0.0F}; + bool default_max_fanout_exists_{false}; + float default_max_slew_{0.0F}; + bool default_max_slew_exists_{false}; + float input_threshold_[RiseFall::index_count]{input_threshold_default_, + input_threshold_default_}; + float output_threshold_[RiseFall::index_count]{output_threshold_default_, + output_threshold_default_}; + float slew_lower_threshold_[RiseFall::index_count]{slew_lower_threshold_default_, + slew_lower_threshold_default_}; + float slew_upper_threshold_[RiseFall::index_count]{slew_upper_threshold_default_, + slew_upper_threshold_default_}; + float slew_derate_from_library_{1.0F}; WireloadMap wireloads_; - Wireload *default_wire_load_; - WireloadMode default_wire_load_mode_; - WireloadSelection *default_wire_load_selection_; + const Wireload *default_wire_load_{nullptr}; + WireloadMode default_wire_load_mode_{WireloadMode::unknown}; + const WireloadSelection *default_wire_load_selection_{nullptr}; WireloadSelectionMap wire_load_selections_; OperatingConditionsMap operating_conditions_; - OperatingConditions *default_operating_conditions_; - float ocv_arc_depth_; - OcvDerate *default_ocv_derate_; + OperatingConditions *default_operating_conditions_{nullptr}; + float ocv_arc_depth_{0.0F}; + OcvDerate *default_ocv_derate_{nullptr}; OcvDerateMap ocv_derate_map_; SupplyVoltageMap supply_voltage_map_; - LibertyCellSeq *buffers_; - LibertyCellSeq *inverters_; + LibertyCellSeq *buffers_{nullptr}; + LibertyCellSeq *inverters_{nullptr}; DriverWaveformMap driver_waveform_map_; - // Unnamed driver waveform. - DriverWaveform *driver_waveform_default_; - - static constexpr float input_threshold_default_ = .5; - static constexpr float output_threshold_default_ = .5; - static constexpr float slew_lower_threshold_default_ = .2; - static constexpr float slew_upper_threshold_default_ = .8; private: friend class LibertyCell; @@ -405,12 +462,12 @@ private: class LibertyCellIterator : public Iterator { public: - explicit LibertyCellIterator(const LibertyLibrary *library); - bool hasNext(); - LibertyCell *next(); + LibertyCellIterator(const LibertyLibrary *library); + bool hasNext() override; + LibertyCell *next() override; private: - ConcreteCellMap::ConstIterator iter_; + ConcreteLibraryCellIterator iter_; }; //////////////////////////////////////////////////////////////// @@ -419,18 +476,17 @@ class LibertyCell : public ConcreteCell { public: LibertyCell(LibertyLibrary *library, - const char *name, - const char *filename); - virtual ~LibertyCell(); + std::string_view name, + std::string_view filename); + ~LibertyCell() override; LibertyLibrary *libertyLibrary() const { return liberty_library_; } LibertyLibrary *libertyLibrary() { return liberty_library_; } - LibertyPort *findLibertyPort(const char *name) const; + LibertyPort *findLibertyPort(std::string_view name) const; LibertyPortSeq findLibertyPortsMatching(PatternMatch *pattern) const; - bool hasInternalPorts() const { return has_internal_ports_; } ScaleFactors *scaleFactors() const { return scale_factors_; } void setScaleFactors(ScaleFactors *scale_factors); - ModeDef *makeModeDef(const char *name); - ModeDef *findModeDef(const char *name); + ModeDef *makeModeDef(std::string_view name); + const ModeDef *findModeDef(std::string_view name) const; float area() const { return area_; } void setArea(float area); @@ -466,22 +522,24 @@ public: void setHasClkGateEnablePin() { has_clk_gate_enable_pin_ = true; } const char *getDesignType() const; const TimingArcSetSeq &timingArcSets() const { return timing_arc_sets_; } + const TimingArcSetSeq &timingArcSetsFrom(const LibertyPort *from) const; + const TimingArcSetSeq &timingArcSetsTo(const LibertyPort *to) const; // from or to may be nullptr to wildcard. const TimingArcSetSeq &timingArcSets(const LibertyPort *from, const LibertyPort *to) const; size_t timingArcSetCount() const; // Find a timing arc set equivalent to key. - TimingArcSet *findTimingArcSet(TimingArcSet *key) const; - TimingArcSet *findTimingArcSet(unsigned arc_set_index) const; + TimingArcSet *findTimingArcSet(TimingArcSet *arc_set) const; + TimingArcSet *findTimingArcSet(size_t index) const; bool hasTimingArcs() const; bool hasTimingArcs(LibertyPort *port) const; const InternalPowerSeq &internalPowers() const { return internal_powers_; } - const InternalPowerSeq &internalPowers(const LibertyPort *port); - LeakagePowerSeq *leakagePowers() { return &leakage_powers_; } + InternalPowerPtrSeq internalPowers(const LibertyPort *port) const; + const LeakagePowerSeq &leakagePowers() const { return leakage_powers_; } void leakagePower(// Return values. - float &leakage, - bool &exists) const; + float &leakage, + bool &exists) const; bool leakagePowerExists() const { return leakage_power_exists_; } // Register, Latch or Statetable. @@ -494,27 +552,29 @@ public: const Statetable *statetable() const { return statetable_; } // Find bus declaration local to this cell. - BusDcl *findBusDcl(const char *name) const; + BusDcl *makeBusDcl(std::string_view name, + int from, + int to); + BusDcl *findBusDcl(std::string_view name); // True when TimingArcSetBuilder::makeRegLatchArcs infers register // timing arcs. bool hasInferedRegTimingArcs() const { return has_infered_reg_timing_arcs_; } TestCell *testCell() const { return test_cell_; } - void latchEnable(const TimingArcSet *arc_set, - // Return values. - const LibertyPort *&enable_port, - const FuncExpr *&enable_func, - const RiseFall *&enable_rf) const; + void latchEnable(const TimingArcSet *d_to_q_set, + // Return values. + const LibertyPort *&enable_port, + const FuncExpr *&enable_func, + const RiseFall *&enable_rf) const; const RiseFall *latchCheckEnableEdge(TimingArcSet *check_set); - bool isDisabledConstraint() const { return is_disabled_constraint_; } - LibertyCell *cornerCell(const Corner *corner, - const MinMax *min_max); - LibertyCell *cornerCell(const DcalcAnalysisPt *dcalc_ap); - LibertyCell *cornerCell(int ap_index); + LibertyCell *sceneCell(const Scene *scene, + const MinMax *min_max); + LibertyCell *sceneCell(size_t lib_ap_index); // AOCV float ocvArcDepth() const; OcvDerate *ocvDerate() const; - OcvDerate *findOcvDerate(const char *derate_name); + OcvDerate *makeOcvDerate(std::string_view name); + OcvDerate *findOcvDerate(std::string_view derate_name); // Build helpers. void makeGeneratedClock(const char *name, @@ -527,71 +587,77 @@ public: IntSeq *edges, FloatSeq *edge_shifts); void makeSequential(int size, - bool is_register, - FuncExpr *clk, - FuncExpr *data, - FuncExpr *clear, - FuncExpr *preset, - LogicValue clr_preset_out, - LogicValue clr_preset_out_inv, - LibertyPort *output, - LibertyPort *output_inv); + bool is_register, + FuncExpr *clk, + FuncExpr *data, + FuncExpr *clear, + FuncExpr *preset, + LogicValue clr_preset_out, + LogicValue clr_preset_out_inv, + LibertyPort *output, + LibertyPort *output_inv); void makeStatetable(LibertyPortSeq &input_ports, LibertyPortSeq &internal_ports, StatetableRows &table); - void addBusDcl(BusDcl *bus_dcl); // Add scaled cell after it is complete. void addScaledCell(OperatingConditions *op_cond, - LibertyCell *scaled_cell); - unsigned addTimingArcSet(TimingArcSet *set); - void addInternalPower(InternalPower *power); - void addInternalPowerAttrs(InternalPowerAttrs *attrs); - void addLeakagePower(LeakagePower *power); + LibertyCell *scaled_cell); + TimingArcSet *makeTimingArcSet(LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + const TimingRole *role, + TimingArcAttrsPtr attrs); + void makeInternalPower(LibertyPort *port, + LibertyPort *related_port, + LibertyPort *related_pg_pin, + const std::shared_ptr &when, + const InternalPowerModels &models); + void makeLeakagePower(LibertyPort *related_pg_port, + FuncExpr *when, + float power); void setLeakagePower(float leakage); void setOcvArcDepth(float depth); void setOcvDerate(OcvDerate *derate); - void addOcvDerate(OcvDerate *derate); void setTestCell(TestCell *test); void setHasInferedRegTimingArcs(bool infered); - void setIsDisabledConstraint(bool is_disabled); - void setCornerCell(LibertyCell *corner_cell, - int ap_index); + void setSceneCell(LibertyCell *scene_cell, + size_t lib_ap_index); // Call after cell is finished being constructed. void finish(bool infer_latches, - Report *report, - Debug *debug); + Report *report, + Debug *debug); bool isBuffer() const; bool isInverter() const; // Only valid when isBuffer() returns true. void bufferPorts(// Return values. - LibertyPort *&input, - LibertyPort *&output) const; + LibertyPort *&input, + LibertyPort *&output) const; // Check all liberty cells to make sure they exist - // for all the defined corners. - static void checkLibertyCorners(); - void ensureVoltageWaveforms(const DcalcAnalysisPtSeq &dcalc_aps); - const char *footprint() const; - void setFootprint(const char *footprint); - const char *userFunctionClass() const; - void setUserFunctionClass(const char *user_function_class); + // for all the defined scenes. + static void checkLibertyScenes(); + void ensureVoltageWaveforms(const SceneSeq &scenes); + const std::string &footprint() const { return footprint_; } + void setFootprint(std::string_view footprint); + const std::string &userFunctionClass() const { return user_function_class_; } + void setUserFunctionClass(std::string_view user_function_class); + void addPort(ConcretePort *port) override; protected: - void addPort(ConcretePort *port); void setHasInternalPorts(bool has_internal); void setLibertyLibrary(LibertyLibrary *library); void makeLatchEnables(Report *report, - Debug *debug); + Debug *debug); FuncExpr *findLatchEnableFunc(const LibertyPort *d, const LibertyPort *en, const RiseFall *en_rf) const; LatchEnable *makeLatchEnable(LibertyPort *d, - LibertyPort *en, + LibertyPort *en, const RiseFall *en_rf, - LibertyPort *q, - TimingArcSet *d_to_q, - TimingArcSet *en_to_q, - TimingArcSet *setup_check, - Debug *debug); + LibertyPort *q, + TimingArcSet *d_to_q, + TimingArcSet *en_to_q, + TimingArcSet *setup_check, + Debug *debug); TimingArcSet *findLatchSetup(const LibertyPort *d, const LibertyPort *en, const RiseFall *en_rf, @@ -604,67 +670,61 @@ protected: void translatePresetClrCheckRoles(); void inferLatchRoles(Report *report, Debug *debug); - void deleteInternalPowerAttrs(); - void makeTimingArcMap(Report *report); void makeTimingArcPortMaps(); bool hasBufferFunc(const LibertyPort *input, - const LibertyPort *output) const; + const LibertyPort *output) const; bool hasInverterFunc(const LibertyPort *input, - const LibertyPort *output) const; - bool checkCornerCell(const Corner *corner, + const LibertyPort *output) const; + bool checkSceneCell(const Scene *scene, const MinMax *min_max) const; - LibertyLibrary *liberty_library_; - float area_; - bool dont_use_; - bool is_macro_; - bool is_memory_; - bool is_pad_; - bool is_clock_cell_; - bool is_level_shifter_; - LevelShifterType level_shifter_type_; - bool is_isolation_cell_; - bool always_on_; - SwitchCellType switch_cell_type_; - bool interface_timing_; - ClockGateType clock_gate_type_; - bool has_clk_gate_clk_pin_; - bool has_clk_gate_enable_pin_; + LibertyLibrary *liberty_library_{nullptr}; + float area_{0.0F}; + bool dont_use_{false}; + bool is_macro_{false}; + bool is_memory_{false}; + bool is_pad_{false}; + bool is_clock_cell_{false}; + bool is_level_shifter_{false}; + LevelShifterType level_shifter_type_{LevelShifterType::HL_LH}; + bool is_isolation_cell_{false}; + bool always_on_{false}; + SwitchCellType switch_cell_type_{SwitchCellType::fine_grain}; + bool interface_timing_{false}; + ClockGateType clock_gate_type_{ClockGateType::none}; + bool has_clk_gate_clk_pin_{false}; + bool has_clk_gate_enable_pin_{false}; TimingArcSetSeq timing_arc_sets_; - TimingArcSetMap timing_arc_set_map_; + TimingArcSetSet timing_arc_set_set_; LibertyPortPairTimingArcMap port_timing_arc_set_map_; - LibertyPortTimingArcMap timing_arc_set_from_map_; - LibertyPortTimingArcMap timing_arc_set_to_map_; - bool has_infered_reg_timing_arcs_; + bool has_infered_reg_timing_arcs_{false}; InternalPowerSeq internal_powers_; - PortInternalPowerSeq port_internal_powers_; - InternalPowerAttrsSeq internal_power_attrs_; + PortInternalPowerMap port_internal_powers_; LeakagePowerSeq leakage_powers_; GeneratedClockSeq generated_clocks_; SequentialSeq sequentials_; PortToSequentialMap port_to_seq_map_; - Statetable *statetable_; + Statetable *statetable_{nullptr}; BusDclMap bus_dcls_; ModeDefMap mode_defs_; - ScaleFactors *scale_factors_; + ScaleFactors *scale_factors_{nullptr}; ScaledCellMap scaled_cells_; - TestCell *test_cell_; - // Latch D->Q to LatchEnable. - LatchEnableMap latch_d_to_q_map_; - // Latch EN->D setup to LatchEnable. - LatchEnableMap latch_check_map_; + TestCell *test_cell_{nullptr}; + // Latch D->Q to LatchEnable index. + LatchEnableIndexMap latch_d_to_q_map_; + // Latch EN->D setup to LatchEnable index. + LatchEnableIndexMap latch_check_map_; LatchEnableSeq latch_enables_; // Ports that have latch D->Q timing arc sets from them. LibertyPortSet latch_data_ports_; - float ocv_arc_depth_; - OcvDerate *ocv_derate_; + float ocv_arc_depth_{0.0F}; + OcvDerate *ocv_derate_{nullptr}; OcvDerateMap ocv_derate_map_; - bool is_disabled_constraint_; - Vector corner_cells_; - float leakage_power_; - bool leakage_power_exists_; - bool has_internal_ports_; - std::atomic have_voltage_waveforms_; + std::vector scene_cells_; + float leakage_power_{0.0F}; + bool leakage_power_exists_{false}; + bool has_internal_ports_{false}; + std::atomic have_voltage_waveforms_{false}; std::mutex waveform_lock_; std::string footprint_; std::string user_function_class_; @@ -679,21 +739,21 @@ private: class LibertyCellPortIterator : public Iterator { public: - explicit LibertyCellPortIterator(const LibertyCell *cell); - bool hasNext(); - LibertyPort *next(); + LibertyCellPortIterator(const LibertyCell *cell); + bool hasNext() override; + LibertyPort *next() override; private: - ConcretePortSeq::ConstIterator iter_; + ConcreteCellPortIterator iter_; }; class LibertyCellPortBitIterator : public Iterator { public: - explicit LibertyCellPortBitIterator(const LibertyCell *cell); - virtual ~LibertyCellPortBitIterator(); - bool hasNext(); - LibertyPort *next(); + LibertyCellPortBitIterator(const LibertyCell *cell); + ~LibertyCellPortBitIterator() override; + bool hasNext() override; + LibertyPort *next() override; private: ConcreteCellPortBitIterator *iter_; @@ -708,49 +768,47 @@ public: LibertyLibrary *libertyLibrary() const { return liberty_cell_->libertyLibrary(); } LibertyPort *findLibertyMember(int index) const; LibertyPort *findLibertyBusBit(int index) const; - LibertyPort *bundlePort() const; BusDcl *busDcl() const { return bus_dcl_; } - void setDirection(PortDirection *dir); //////////////////////////////////////////////////////////////// // pg_pin functions bool isPwrGnd() const; PwrGndType pwrGndType() const { return pwr_gnd_type_; } void setPwrGndType(PwrGndType type); - const char *voltageName() const { return voltage_name_.c_str(); } - void setVoltageName(const char *voltage_name); + const std::string &voltageName() const { return voltage_name_; } + void setVoltageName(std::string_view voltage_name); //////////////////////////////////////////////////////////////// ScanSignalType scanSignalType() const { return scan_signal_type_; } void setScanSignalType(ScanSignalType type); void fanoutLoad(// Return values. - float &fanout_load, - bool &exists) const; + float &fanout_load, + bool &exists) const; void setFanoutLoad(float fanout_load); float capacitance() const; float capacitance(const MinMax *min_max) const; float capacitance(const RiseFall *rf, - const MinMax *min_max) const; + const MinMax *min_max) const; void capacitance(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &cap, - bool &exists) const; + const MinMax *min_max, + // Return values. + float &cap, + bool &exists) const; // Capacitance at op_cond derated by library/cell scale factors // using pvt. float capacitance(const RiseFall *rf, - const MinMax *min_max, - const OperatingConditions *op_cond, - const Pvt *pvt) const; + const MinMax *min_max, + const OperatingConditions *op_cond, + const Pvt *pvt) const; bool capacitanceIsOneValue() const; void setCapacitance(float cap); void setCapacitance(const RiseFall *rf, - const MinMax *min_max, - float cap); + const MinMax *min_max, + float cap); // Max of rise/fall. float driveResistance() const; float driveResistance(const RiseFall *rf, - const MinMax *min_max) const; + const MinMax *min_max) const; // Zero load delay. ArcDelay intrinsicDelay(const StaState *sta) const; ArcDelay intrinsicDelay(const RiseFall *rf, @@ -762,38 +820,38 @@ public: FuncExpr *tristateEnable() const { return tristate_enable_; } void setTristateEnable(FuncExpr *enable); void slewLimit(const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const; + // Return values. + float &limit, + bool &exists) const; void setSlewLimit(float slew, - const MinMax *min_max); + const MinMax *min_max); void capacitanceLimit(const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const; + // Return values. + float &limit, + bool &exists) const; void setCapacitanceLimit(float cap, - const MinMax *min_max); + const MinMax *min_max); void fanoutLimit(const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const; + // Return values. + float &limit, + bool &exists) const; void setFanoutLimit(float fanout, - const MinMax *min_max); + const MinMax *min_max); void minPeriod(const OperatingConditions *op_cond, - const Pvt *pvt, - float &min_period, - bool &exists) const; + const Pvt *pvt, + float &min_period, + bool &exists) const; // Unscaled value. void minPeriod(float &min_period, - bool &exists) const; + bool &exists) const; void setMinPeriod(float min_period); // This corresponds to the min_pulse_width_high/low port attribute. // high = rise, low = fall void minPulseWidth(const RiseFall *hi_low, - float &min_width, - bool &exists) const; + float &min_width, + bool &exists) const; void setMinPulseWidth(const RiseFall *hi_low, - float min_width); + float min_width); bool isClock() const; void setIsClock(bool is_clk); bool isClockGateClock() const { return is_clk_gate_clk_; } @@ -832,24 +890,19 @@ public: const RiseFall *pulseClkTrigger() const { return pulse_clk_trigger_; } // Rise for high, fall for low. const RiseFall *pulseClkSense() const { return pulse_clk_sense_; } - void setPulseClk(const RiseFall *rfigger, - const RiseFall *sense); - bool isDisabledConstraint() const { return is_disabled_constraint_; } - void setIsDisabledConstraint(bool is_disabled); - LibertyPort *cornerPort(const Corner *corner, - const MinMax *min_max); - const LibertyPort *cornerPort(const Corner *corner, - const MinMax *min_max) const; - LibertyPort *cornerPort(const DcalcAnalysisPt *dcalc_ap); - const LibertyPort *cornerPort(const DcalcAnalysisPt *dcalc_ap) const; - LibertyPort *cornerPort(int ap_index); - const LibertyPort *cornerPort(int ap_index) const; - void setCornerPort(LibertyPort *corner_port, - int ap_index); - const char *relatedGroundPin() const; - void setRelatedGroundPin(const char *related_ground_pin); - const char *relatedPowerPin() const; - void setRelatedPowerPin(const char *related_power_pin); + void setPulseClk(const RiseFall *trigger, + const RiseFall *sense); + LibertyPort *scenePort(const Scene *scene, + const MinMax *min_max); + const LibertyPort *scenePort(const Scene *scene, + const MinMax *min_max) const; + const LibertyPort *scenePort(size_t lib_ap_index) const; + void setScenePort(LibertyPort *scene_port, + size_t lib_ap_index); + LibertyPort *relatedGroundPort() const { return related_ground_port_; } + void setRelatedGroundPort(LibertyPort *related_ground_port); + LibertyPort *relatedPowerPort() const { return related_power_port_; } + void setRelatedPowerPort(LibertyPort *related_power_port); const ReceiverModel *receiverModel() const { return receiver_model_.get(); } void setReceiverModel(ReceiverModelPtr receiver_model); DriverWaveform *driverWaveform(const RiseFall *rf) const; @@ -862,89 +915,78 @@ public: float clkTreeDelay(float in_slew, const RiseFall *from_rf, const MinMax *min_max) const; - void setClkTreeDelay(const TableModel *model, - const RiseFall *from_rf, - const RiseFall *to_rf, - const MinMax *min_max); - // deprecated 2024-06-22 - RiseFallMinMax clkTreeDelays() const __attribute__ ((deprecated)); - // deprecated 2024-02-27 - RiseFallMinMax clockTreePathDelays() const __attribute__ ((deprecated)); static bool equiv(const LibertyPort *port1, - const LibertyPort *port2); + const LibertyPort *port2); static bool less(const LibertyPort *port1, - const LibertyPort *port2); + const LibertyPort *port2); protected: // Constructor is internal to LibertyBuilder. LibertyPort(LibertyCell *cell, - const char *name, - bool is_bus, - BusDcl *bus_dcl, + std::string_view name, + bool is_bus, + BusDcl *bus_dcl, int from_index, - int to_index, - bool is_bundle, - ConcretePortSeq *members); - virtual ~LibertyPort(); + int to_index, + bool is_bundle, + ConcretePortSeq *members); + ~LibertyPort() override; void setMinPort(LibertyPort *min); void addScaledPort(OperatingConditions *op_cond, - LibertyPort *scaled_port); - RiseFallMinMax clkTreeDelays1() const; + LibertyPort *scaled_port); void setMemberFlag(bool value, - const std::function &setter); + const std::function &setter); void setMemberFloat(float value, - const std::function &setter); + const std::function &setter); void setMemberMinMaxFloat(float value, - const MinMax *min_max, - const std::function &setter); + const MinMax *min_max, + const std::function &setter); + LibertyPort *scenePort(size_t lib_ap_index); - LibertyCell *liberty_cell_; - BusDcl *bus_dcl_; - PwrGndType pwr_gnd_type_; + LibertyCell *liberty_cell_{nullptr}; + BusDcl *bus_dcl_{nullptr}; + PwrGndType pwr_gnd_type_{PwrGndType::none}; std::string voltage_name_; - ScanSignalType scan_signal_type_; - FuncExpr *function_; - FuncExpr *tristate_enable_; - ScaledPortMap *scaled_ports_; + ScanSignalType scan_signal_type_{ScanSignalType::none}; + FuncExpr *function_{nullptr}; + FuncExpr *tristate_enable_{nullptr}; + ScaledPortMap *scaled_ports_{nullptr}; RiseFallMinMax capacitance_; MinMaxFloatValues slew_limit_; // inputs and outputs MinMaxFloatValues cap_limit_; // outputs - float fanout_load_; // inputs - bool fanout_load_exists_; + float fanout_load_{0.0F}; // inputs + bool fanout_load_exists_{false}; MinMaxFloatValues fanout_limit_; // outputs - float min_period_; - float min_pulse_width_[RiseFall::index_count]; - const RiseFall *pulse_clk_trigger_; - const RiseFall *pulse_clk_sense_; - std::string related_ground_pin_; - std::string related_power_pin_; - Vector corner_ports_; - ReceiverModelPtr receiver_model_; - DriverWaveform *driver_waveform_[RiseFall::index_count]; - // Redundant with clock_tree_path_delay timing arcs but faster to access. - const TableModel *clk_tree_delay_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count]; - - unsigned int min_pulse_width_exists_:RiseFall::index_count; - bool min_period_exists_:1; - bool is_clk_:1; - bool is_reg_clk_:1; - bool is_reg_output_:1; - bool is_latch_data_: 1; - bool is_check_clk_:1; - bool is_clk_gate_clk_:1; - bool is_clk_gate_enable_:1; - bool is_clk_gate_out_:1; - bool is_pll_feedback_:1; - bool isolation_cell_data_:1; - bool isolation_cell_enable_:1; - bool level_shifter_data_:1; - bool is_switch_:1; - bool is_disabled_constraint_:1; - bool is_pad_:1; + float min_period_{0.0F}; + float min_pulse_width_[RiseFall::index_count]{0.0F, 0.0F}; + const RiseFall *pulse_clk_trigger_{nullptr}; + const RiseFall *pulse_clk_sense_{nullptr}; + LibertyPort *related_ground_port_{nullptr}; + LibertyPort *related_power_port_{nullptr}; + std::vector scene_ports_; + ReceiverModelPtr receiver_model_{nullptr}; + DriverWaveform *driver_waveform_[RiseFall::index_count]{nullptr, nullptr}; + + bool min_pulse_width_exists_:RiseFall::index_count {false}; + bool min_period_exists_:1 {false}; + bool is_clk_:1 {false}; + bool is_reg_clk_:1 {false}; + bool is_reg_output_:1 {false}; + bool is_latch_data_: 1 {false}; + bool is_check_clk_:1 {false}; + bool is_clk_gate_clk_:1 {false}; + bool is_clk_gate_enable_:1 {false}; + bool is_clk_gate_out_:1 {false}; + bool is_pll_feedback_:1 {false}; + bool isolation_cell_data_:1 {false}; + bool isolation_cell_enable_:1 {false}; + bool level_shifter_data_:1 {false}; + bool is_switch_:1 {false}; + bool is_pad_:1 {false}; private: friend class LibertyLibrary; @@ -959,10 +1001,10 @@ sortByName(const LibertyPortSet *set); class LibertyPortMemberIterator : public Iterator { public: - explicit LibertyPortMemberIterator(const LibertyPort *port); - virtual ~LibertyPortMemberIterator(); - virtual bool hasNext(); - virtual LibertyPort *next(); + LibertyPortMemberIterator(const LibertyPort *port); + ~LibertyPortMemberIterator() override; + bool hasNext() override; + LibertyPort *next() override; private: ConcretePortMemberIterator *iter_; @@ -975,7 +1017,7 @@ public: Pvt(float process, float voltage, float temperature); - virtual ~Pvt() {} + virtual ~Pvt() = default; float process() const { return process_; } void setProcess(float process); float voltage() const { return voltage_; } @@ -992,42 +1034,37 @@ protected: class OperatingConditions : public Pvt { public: - OperatingConditions(const char *name); - OperatingConditions(const char *name, - float process, - float voltage, - float temperature, - WireloadTree wire_load_tree); - const char *name() const { return name_.c_str(); } + OperatingConditions(std::string_view name); + const std::string &name() const { return name_; } WireloadTree wireloadTree() const { return wire_load_tree_; } void setWireloadTree(WireloadTree tree); protected: std::string name_; - WireloadTree wire_load_tree_; + WireloadTree wire_load_tree_{WireloadTree::unknown}; }; class ScaleFactors { public: - explicit ScaleFactors(const char *name); - const char *name() const { return name_.c_str(); } + ScaleFactors(std::string_view name); + const std::string &name() const { return name_; } float scale(ScaleFactorType type, - ScaleFactorPvt pvt, - const RiseFall *rf); + ScaleFactorPvt pvt, + const RiseFall *rf); float scale(ScaleFactorType type, - ScaleFactorPvt pvt, - int rf_index); + ScaleFactorPvt pvt, + int rf_index); float scale(ScaleFactorType type, - ScaleFactorPvt pvt); + ScaleFactorPvt pvt); void setScale(ScaleFactorType type, - ScaleFactorPvt pvt, - const RiseFall *rf, - float scale); + ScaleFactorPvt pvt, + const RiseFall *rf, + float scale); void setScale(ScaleFactorType type, - ScaleFactorPvt pvt, - float scale); - void print(); + ScaleFactorPvt pvt, + float scale); + void report(Report *report); protected: std::string name_; @@ -1037,10 +1074,10 @@ protected: class BusDcl { public: - BusDcl(const char *name, - int from, - int to); - const char *name() const { return name_.c_str(); } + BusDcl(std::string_view name, + int from, + int to); + const std::string &name() const { return name_; } int from() const { return from_; } int to() const { return to_; } @@ -1054,18 +1091,13 @@ protected: class ModeDef { public: - ~ModeDef(); - const char *name() const { return name_.c_str(); } - ModeValueDef *defineValue(const char *value, - FuncExpr *cond, - const char *sdf_cond); - ModeValueDef *findValueDef(const char *value); - ModeValueMap *values() { return &values_; } + ModeDef(std::string_view name); + const std::string &name() const { return name_; } + ModeValueDef *defineValue(std::string_view value); + const ModeValueDef *findValueDef(std::string_view value) const; + const ModeValueMap &values() const { return values_; } protected: - // Private to LibertyCell::makeModeDef. - ModeDef(const char *name); - std::string name_; ModeValueMap values_; @@ -1073,41 +1105,19 @@ private: friend class LibertyCell; }; -// Mode definition mode_value group. -class ModeValueDef -{ -public: - ~ModeValueDef(); - const char *value() const { return value_.c_str(); } - FuncExpr *cond() const { return cond_; } - void setCond(FuncExpr *cond); - const char *sdfCond() const { return sdf_cond_.c_str(); } - void setSdfCond(const char *sdf_cond); - -protected: - // Private to ModeDef::defineValue. - ModeValueDef(const char *value, - FuncExpr *cond, - const char *sdf_cond); - - std::string value_; - FuncExpr *cond_; - std::string sdf_cond_; - -private: - friend class ModeDef; -}; - class TableTemplate { public: - TableTemplate(const char *name); - TableTemplate(const char *name, - TableAxisPtr axis1, - TableAxisPtr axis2, - TableAxisPtr axis3); - const char *name() const { return name_.c_str(); } - void setName(const char *name); + TableTemplate(std::string_view name); + TableTemplate(std::string_view name, + TableTemplateType type); + TableTemplate(std::string_view name, + TableAxisPtr axis1, + TableAxisPtr axis2, + TableAxisPtr axis3); + const std::string &name() const { return name_; } + void setName(std::string_view name); + TableTemplateType type() const { return type_; } const TableAxis *axis1() const { return axis1_.get(); } TableAxisPtr axis1ptr() const { return axis1_; } void setAxis1(TableAxisPtr axis); @@ -1120,6 +1130,7 @@ public: protected: std::string name_; + TableTemplateType type_{TableTemplateType::delay}; TableAxisPtr axis1_; TableAxisPtr axis2_; TableAxisPtr axis3_; @@ -1129,41 +1140,36 @@ class TestCell : public LibertyCell { public: TestCell(LibertyLibrary *library, - const char *name, - const char *filename); - -protected: + std::string_view name, + std::string_view filename); }; class OcvDerate { public: - OcvDerate(const char *name); - ~OcvDerate(); - const char *name() const { return name_; } + OcvDerate(std::string_view name); + const std::string &name() const { return name_; } const Table *derateTable(const RiseFall *rf, const EarlyLate *early_late, PathType path_type); void setDerateTable(const RiseFall *rf, - const EarlyLate *early_late, - PathType path_type, - TablePtr derate); + const EarlyLate *early_late, + PathType path_type, + TablePtr derate); private: - const char *name_; + std::string name_; // [rf_type][derate_type][path_type] TablePtr derate_[RiseFall::index_count][EarlyLate::index_count][path_type_count]; }; std::string -portLibertyToSta(const char *port_name); -std::string -portStaToLiberty(const char *sta_name); -const char * +portLibertyToSta(std::string_view port_name); +const std::string & scanSignalTypeName(ScanSignalType scan_type); -const char * -pwrGndTypeName(PwrGndType pwr_gnd_type); +const std::string & +pwrGndTypeName(PwrGndType pg_type); PwrGndType -findPwrGndType(const char *pg_name); +findPwrGndType(std::string_view pg_name); -} // namespace +} // namespace sta diff --git a/include/sta/LibertyClass.hh b/include/sta/LibertyClass.hh index 4236183dd..2efe7f58d 100644 --- a/include/sta/LibertyClass.hh +++ b/include/sta/LibertyClass.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,11 +24,12 @@ #pragma once +#include #include +#include +#include -#include "Vector.hh" -#include "Map.hh" -#include "Set.hh" +#include "Iterator.hh" namespace sta { @@ -68,30 +69,29 @@ class ReceiverModel; class Statetable; class StatetableRow; -typedef Vector LibertyLibrarySeq; -typedef LibertyLibrarySeq::ConstIterator LibertyLibrarySeqIterator; -typedef Vector LibertyCellSeq; -typedef LibertyCellSeq::ConstIterator LibertyCellSeqIterator; -typedef Vector SequentialSeq; -typedef Vector GeneratedClockSeq; -typedef Map LibertyCellEquivMap; -typedef Vector LibertyPortSeq; -typedef LibertyPortSeq::ConstIterator LibertyPortSeqIterator; -typedef Set LibertyPortSet; -typedef std::pair LibertyPortPair; -typedef Set LibertyCellSet; -typedef std::shared_ptr TablePtr; -typedef std::shared_ptr TimingArcAttrsPtr; -typedef std::shared_ptr TableAxisPtr; -typedef std::shared_ptr ReceiverModelPtr; -typedef std::vector StatetableRows; +using LibertyLibrarySeq = std::vector; +using LibertyLibrarySeqIterator = VectorIterator; +using LibertyCellSeq = std::vector; +using LibertyCellSeqIterator = VectorIterator; +using SequentialSeq = std::vector; +using LibertyCellEquivMap = std::map; +using LibertyPortSeq = std::vector; +using LibertyPortSeqIterator = VectorIterator; +using LibertyPortSet = std::set; +using LibertyPortPair = std::pair; +using LibertyCellSet = std::set; +using TablePtr = std::shared_ptr
; +using TimingArcAttrsPtr = std::shared_ptr; +using TableAxisPtr = std::shared_ptr; +using ReceiverModelPtr = std::shared_ptr; +using StatetableRows = std::vector; enum class ScaleFactorType : unsigned { pin_cap, wire_cap, wire_res, min_period, - // Liberty attributes have rise/fall suffix. + // Liberty attributes with rise/fall suffix. cell, hold, setup, @@ -101,13 +101,13 @@ enum class ScaleFactorType : unsigned { skew, leakage_power, internal_power, - // Liberty attributes have rise/fall prefix. + // Liberty attributes with rise/fall prefix. transition, - // Liberty attributes have low/high suffix (indexed as rise/fall). + // Liberty attributes with low/high suffix (indexed as rise/fall). min_pulse_width, unknown, }; -const int scale_factor_type_count = int(ScaleFactorType::unknown) + 1; +const int scale_factor_type_count = static_cast(ScaleFactorType::unknown) + 1; // Enough bits to hold a ScaleFactorType enum. const int scale_factor_bits = 4; @@ -122,7 +122,7 @@ enum class TimingSense { none, unknown }; -const int timing_sense_count = int(TimingSense::unknown) + 1; +const int timing_sense_count = static_cast(TimingSense::unknown) + 1; const int timing_sense_bit_count = 3; enum class TableAxisVariable { @@ -166,22 +166,22 @@ class LibertyPortPairLess { public: bool operator()(const LibertyPortPair &pair1, - const LibertyPortPair &pair2) const; + const LibertyPortPair &pair2) const; }; bool timingArcSetLess(const TimingArcSet *set1, - const TimingArcSet *set2); + const TimingArcSet *set2); class TimingArcSetLess { public: bool operator()(const TimingArcSet *set1, - const TimingArcSet *set2) const + const TimingArcSet *set2) const { return timingArcSetLess(set1, set2); } }; -} // namespace +} // namespace sta diff --git a/include/sta/LibertyWriter.hh b/include/sta/LibertyWriter.hh index 1b142af52..0178ad8e9 100644 --- a/include/sta/LibertyWriter.hh +++ b/include/sta/LibertyWriter.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -35,4 +35,4 @@ writeLiberty(LibertyLibrary *lib, const char *filename, StaState *sta); -} // namespace +} // namespace sta diff --git a/include/sta/LinearModel.hh b/include/sta/LinearModel.hh index f70cd7375..d7e0218dd 100644 --- a/include/sta/LinearModel.hh +++ b/include/sta/LinearModel.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -37,20 +37,27 @@ public: void gateDelay(const Pvt *pvt, float in_slew, float load_cap, - bool pocv_enabled, // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew) const override; + float &gate_delay, + float &drvr_slew) const override; + void gateDelayPocv(const Pvt *pvt, + float in_slew, + float load_cap, + const MinMax *min_max, + PocvMode pocv_mode, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) const override; std::string reportGateDelay(const Pvt *pvt, float in_slew, float load_cap, - bool pocv_enabled, + const MinMax *min_max, + PocvMode pocv_mode, int digits) const override; float driveResistance(const Pvt *pvt) const override; - -protected: void setIsScaled(bool is_scaled) override; +protected: float intrinsic_; float resistance_; }; @@ -58,25 +65,26 @@ protected: class CheckLinearModel : public CheckTimingModel { public: - explicit CheckLinearModel(LibertyCell *cell, - float intrinsic); + CheckLinearModel(LibertyCell *cell, + float intrinsic); ArcDelay checkDelay(const Pvt *pvt, float from_slew, float to_slew, float related_out_cap, - bool pocv_enabled) const override; + const MinMax *min_max, + PocvMode pocv_mode) const override; std::string reportCheckDelay(const Pvt *pvt, float from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, float to_slew, float related_out_cap, - bool pocv_enabled, + const MinMax *min_max, + PocvMode pocv_mode, int digits) const override; - -protected: void setIsScaled(bool is_scaled) override; +protected: float intrinsic_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Machine.hh b/include/sta/Machine.hh index c66b3ab9b..1df4d8281 100644 --- a/include/sta/Machine.hh +++ b/include/sta/Machine.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,11 +26,6 @@ // This header contains global os/port specific definitions. -// Pragma placeholder for non-gcc compilers. -#ifndef __GNUC__ - #define __attribute__(x) -#endif // __GNUC__ - #ifdef _MSC_VER // Microcruft Visual C++ // Obtuse warning codes enabled by pragma. @@ -48,6 +43,7 @@ // 4611 = setjmp used in C++ function // 4701 = variable used but not initialized #pragma warning( 3 : 4018 4032 4132 4189 4201 4222 4234 4505 4611 4701 ) + // Disable security warnings for posix functions. // _CRT_SECURE_NO_WARNINGS does not seem to work #pragma warning( disable : 4996 ) @@ -65,14 +61,14 @@ // Flex doesn't check for unistd.h. #define YY_NO_UNISTD_H namespace sta { - int vsnprint(char *str, size_t size, const char *fmt, va_list args); + int vsnprint(char *str, size_t size, const char *fmt, const va_list args); int vasprintf(char **str, const char *fmt, va_list args); } #else #define vsnprint vsnprintf #endif -#include // size_t +#include // size_t namespace sta { diff --git a/include/sta/MakeConcreteNetwork.hh b/include/sta/MakeConcreteNetwork.hh index 3247a6f23..47dd56b42 100644 --- a/include/sta/MakeConcreteNetwork.hh +++ b/include/sta/MakeConcreteNetwork.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -31,4 +31,4 @@ class NetworkReader; NetworkReader * makeConcreteNetwork(); -} // namespace +} // namespace sta diff --git a/include/sta/MakeConcreteParasitics.hh b/include/sta/MakeConcreteParasitics.hh deleted file mode 100644 index 9bba835f4..000000000 --- a/include/sta/MakeConcreteParasitics.hh +++ /dev/null @@ -1,32 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -namespace sta { - -Parasitics * -makeConcreteParasitics(StaState *sta); - -} // namespace diff --git a/include/sta/Map.hh b/include/sta/Map.hh deleted file mode 100644 index bfcb64550..000000000 --- a/include/sta/Map.hh +++ /dev/null @@ -1,191 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include -#include - -namespace sta { - -// Add convenience functions around STL container. -template > -class Map : public std::map -{ -public: - Map() : - std::map() - { - } - explicit Map(const CMP &cmp) : - std::map(cmp) - { - } - - // Find out if key is in the set. - bool - hasKey(const KEY key) const - { - return this->find(key) != this->end(); - } - - // Find the value corresponding to key. - VALUE - findKey(const KEY key) const - { - auto find_iter = this->find(key); - if (find_iter != this->end()) - return find_iter->second; - else - return nullptr; - } - void - findKey(const KEY key, - // Return Values. - VALUE &value, - bool &exists) const - { - auto find_iter = this->find(key); - if (find_iter != this->end()) { - value = find_iter->second; - exists = true; - } - else - exists = false; - } - void - findKey(const KEY &key, - // Return Values. - KEY &map_key, - VALUE &value, - bool &exists) const - { - auto find_iter = this->find(key); - if (find_iter != this->end()) { - map_key = find_iter->first; - value = find_iter->second; - exists = true; - } - else - exists = false; - } - - void - insert(const KEY &key, - VALUE value) - { - this->operator[](key) = value; - } - - void - deleteContents() - { - Iterator iter(this); - while (iter.hasNext()) - delete iter.next(); - } - - void - deleteKeysContents() - { - for (const auto [key, value] : this) { - delete key; - delete value; - } - } - - void - deleteArrayContents() - { - Iterator iter(this); - while (iter.hasNext()) - delete [] iter.next(); - } - - void - deleteContentsClear() - { - deleteContents(); - std::map::clear(); - } - - // Java style container itererator - // Map::Iterator iter(map); - // while (iter.hasNext()) { - // Value *v = iter.next(); - // } - class Iterator - { - public: - Iterator() : container_(nullptr) {} - explicit Iterator(std::map *container) : - container_(container) - { if (container_ != nullptr) iter_ = container_->begin(); } - explicit Iterator(std::map &container) : - container_(&container) - { if (container_ != nullptr) iter_ = container_->begin(); } - void init(std::map *container) - { container_ = container; if (container_ != nullptr) iter_=container_->begin();} - void init(std::map &container) - { container_ = &container; if (container_ != nullptr) iter_=container_->begin();} - bool hasNext() { return container_ != nullptr && iter_ != container_->end(); } - VALUE next() { return iter_++->second; } - void next(KEY &key, - VALUE &value) - { key = iter_->first; value = iter_->second; iter_++; } - std::map *container() { return container_; } - - private: - std::map *container_; - typename std::map::iterator iter_; - }; - - class ConstIterator - { - public: - ConstIterator() : container_(nullptr) {} - explicit ConstIterator(const std::map *container) : - container_(container) - { if (container_ != nullptr) iter_ = container_->begin(); } - explicit ConstIterator(const std::map &container) : - container_(&container) - { if (container_ != nullptr) iter_ = container_->begin(); } - void init(const std::map *container) - { container_ = container; if (container_ != nullptr) iter_=container_->begin();} - void init(const std::map &container) - { container_ = &container; if (container_ != nullptr) iter_=container_->begin();} - bool hasNext() { return container_ != nullptr && iter_ != container_->end(); } - VALUE next() { return iter_++->second; } - void next(KEY &key, - VALUE &value) - { key = iter_->first; value = iter_->second; iter_++; } - const std::map *container() { return container_; } - - private: - const std::map *container_; - typename std::map::const_iterator iter_; - }; -}; - -} // namespace diff --git a/include/sta/MinMax.hh b/include/sta/MinMax.hh index 15de0e721..a23eda8e6 100644 --- a/include/sta/MinMax.hh +++ b/include/sta/MinMax.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,8 +25,8 @@ #pragma once #include -#include #include +#include #include "Iterator.hh" @@ -36,8 +36,8 @@ class MinMax; class MinMaxAll; // Use typedefs to make early/late functional equivalents to min/max. -typedef MinMax EarlyLate; -typedef MinMaxAll EarlyLateAll; +using EarlyLate = MinMax; +using EarlyLateAll = MinMaxAll; // Large value used for min/max initial values. extern const float INF; @@ -52,54 +52,54 @@ public: static const MinMax *max() { return &max_; } static const EarlyLate *early() { return &min_; } static const EarlyLate *late() { return &max_; } - static int minIndex() { return min_.index_; } - static int earlyIndex() { return min_.index_; } - static int maxIndex() { return max_.index_; } - static int lateIndex() { return max_.index_; } + static size_t minIndex() { return min_.index_; } + static size_t earlyIndex() { return min_.index_; } + static size_t maxIndex() { return max_.index_; } + static size_t lateIndex() { return max_.index_; } const std::string &to_string() const { return name_; } - int index() const { return index_; } + size_t index() const { return index_; } float initValue() const { return init_value_; } int initValueInt() const { return init_value_int_; } // Max value1 > value2, Min value1 < value2. bool compare(float value1, - float value2) const; + float value2) const; // min/max(value1, value2) float minMax(float value1, - float value2) const; + float value2) const; const MinMaxAll *asMinMaxAll() const; const MinMax *opposite() const; // for range support. // for (auto min_max : MinMax::range()) {} static const std::array &range() { return range_; } // for (auto mm_index : MinMax::rangeIndex()) {} - static const std::array &rangeIndex() { return range_index_; } + static const std::array &rangeIndex() { return range_index_; } // Find MinMax from name. - static const MinMax *find(const char *min_max); + static const MinMax *find(std::string_view min_max); // Find MinMax from index. - static const MinMax *find(int index); - static const int index_max = 1; - static const int index_count = 2; - static const int index_bit_count = 1; + static const MinMax *find(size_t index); + static const size_t index_max = 1; + static const size_t index_count = 2; + static const size_t index_bit_count = 1; private: - MinMax(const char *name, - int index, - float init_value, + MinMax(std::string_view name, + size_t index, + float init_value, int init_value_int, - bool (*compare)(float value1, - float value2)); + bool (*compare)(float value1, + float value2)); const std::string name_; int index_; float init_value_; int init_value_int_; bool (*compare_)(float value1, - float value2); + float value2); static const MinMax min_; static const MinMax max_; static const std::array range_; - static const std::array range_index_; + static const std::array range_index_; }; // Min/Max/All, where "All" means use both min and max. @@ -112,8 +112,9 @@ public: static const MinMaxAll *max() { return &max_; } static const MinMaxAll *late() { return &max_; } static const MinMaxAll *all() { return &all_; } + static const MinMaxAll *minMax() { return &all_; } const std::string &to_string() const { return name_; } - int index() const { return index_; } + size_t index() const { return index_; } const MinMax *asMinMax() const; bool matches(const MinMax *min_max) const; bool matches(const MinMaxAll *min_max) const; @@ -121,22 +122,22 @@ public: // for (const auto min_max : min_max->range()) {} const std::vector &range() const { return range_; } // for (const auto mm_index : min_max->rangeIndex()) {} - const std::vector &rangeIndex() const { return range_index_; } + const std::vector &rangeIndex() const { return range_index_; } private: - MinMaxAll(const char *name, - int index, - std::vector range, - std::vector range_index); + MinMaxAll(std::string_view name, + size_t index, + const std::vector &range, + const std::vector &range_index); const std::string name_; - int index_; + size_t index_; const std::vector range_; - const std::vector range_index_; + const std::vector range_index_; static const MinMaxAll min_; static const MinMaxAll max_; static const MinMaxAll all_; }; -} // namespace +} // namespace sta diff --git a/include/sta/MinMaxValues.hh b/include/sta/MinMaxValues.hh index aa3020710..c365d6130 100644 --- a/include/sta/MinMaxValues.hh +++ b/include/sta/MinMaxValues.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,8 +24,8 @@ #pragma once -#include "MinMax.hh" #include "Error.hh" +#include "MinMax.hh" namespace sta { @@ -82,7 +82,7 @@ public: void setValue(const MinMaxAll *min_max, - TYPE value) + TYPE value) { for (auto mm_index : min_max->rangeIndex()) { values_[mm_index] = value; @@ -92,7 +92,7 @@ public: void setValue(const MinMax *min_max, - TYPE value) + TYPE value) { int mm_index = min_max->index(); values_[mm_index] = value; @@ -101,11 +101,11 @@ public: void mergeValue(const MinMax *min_max, - TYPE value) + TYPE value) { int mm_index = min_max->index(); if (!exists_[mm_index] - || min_max->compare(value, values_[mm_index])) { + || min_max->compare(value, values_[mm_index])) { values_[mm_index] = value; exists_[mm_index] = true; } @@ -126,9 +126,9 @@ public: void value(const MinMax *min_max, - // Return values. - TYPE &value, - bool &exists) const + // Return values. + TYPE &value, + bool &exists) const { int mm_index = min_max->index(); exists = exists_[mm_index]; @@ -150,36 +150,41 @@ public: } static bool equal(const MinMaxValues *values1, - const MinMaxValues *values2) + const MinMaxValues *values2) { return ((!values1->exists_[MinMax::minIndex()] - && !values2->exists_[MinMax::minIndex()]) - || (values1->exists_[MinMax::minIndex()] - && values2->exists_[MinMax::minIndex()] - && values1->values_[MinMax::minIndex()] - == values2->values_[MinMax::minIndex()])) + && !values2->exists_[MinMax::minIndex()]) + || (values1->exists_[MinMax::minIndex()] + && values2->exists_[MinMax::minIndex()] + && values1->values_[MinMax::minIndex()] + == values2->values_[MinMax::minIndex()])) && ((!values1->exists_[MinMax::maxIndex()] - && !values2->exists_[MinMax::maxIndex()]) - || (values1->exists_[MinMax::maxIndex()] - && values2->exists_[MinMax::maxIndex()] - && values1->values_[MinMax::maxIndex()] - == values2->values_[MinMax::maxIndex()])); + && !values2->exists_[MinMax::maxIndex()]) + || (values1->exists_[MinMax::maxIndex()] + && values2->exists_[MinMax::maxIndex()] + && values1->values_[MinMax::maxIndex()] + == values2->values_[MinMax::maxIndex()])); } static int cmp(const MinMaxValues *values1, - const MinMaxValues *values2) + const MinMaxValues *values2) { if (!values1->exists_[MinMax::minIndex()] - && values2->exists_[MinMax::minIndex()]) + && !values2->exists_[MinMax::minIndex()] + && !values1->exists_[MinMax::maxIndex()] + && !values2->exists_[MinMax::maxIndex()]) + return 0; + if (!values1->exists_[MinMax::minIndex()] + && values2->exists_[MinMax::minIndex()]) return -1; if (values1->exists_[MinMax::minIndex()] - && !values2->exists_[MinMax::minIndex()]) + && !values2->exists_[MinMax::minIndex()]) return 1; if (!values1->exists_[MinMax::maxIndex()] - && values2->exists_[MinMax::maxIndex()]) + && values2->exists_[MinMax::maxIndex()]) return -1; if (values1->exists_[MinMax::maxIndex()] - && !values2->exists_[MinMax::maxIndex()]) + && !values2->exists_[MinMax::maxIndex()]) return 1; if (values1->values_[MinMax::minIndex()] < values2->values_[MinMax::minIndex()]) return -1; @@ -197,7 +202,7 @@ private: bool exists_[MinMax::index_count]; }; -typedef MinMaxValues MinMaxFloatValues; -typedef MinMaxValues MinMaxIntValues; +using MinMaxFloatValues = MinMaxValues; +using MinMaxIntValues = MinMaxValues; -} // namespace +} // namespace sta diff --git a/include/sta/Mode.hh b/include/sta/Mode.hh new file mode 100644 index 000000000..b3a2b9c04 --- /dev/null +++ b/include/sta/Mode.hh @@ -0,0 +1,97 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include + +#include "StaState.hh" + +namespace sta { + +class Sdc; +class Sim; +class ClkNetwork; +class Genclks; +class PathGroups; + +using PathGroupSeq = std::vector; + +// Sdc and dependent state. +class Mode +{ +public: + Mode(std::string_view name, + size_t mode_index, + StaState *sta); + ~Mode(); + void copyState(const StaState *sta); + void clear(); + const std::string &name() const { return name_; } + size_t modeIndex() const { return mode_index_; } + const SceneSeq &scenes() const { return scenes_; } + SceneSet sceneSet() const; + void addScene(Scene *scene); + void removeScene(Scene *scene); + StaState *sta() const { return sta_; } + Sdc *sdc() { return sdc_; } + Sdc *sdc() const { return sdc_; } + Sim *sim() { return sim_; } + Sim *sim() const { return sim_; } + ClkNetwork *clkNetwork() { return clk_network_; } + ClkNetwork *clkNetwork() const { return clk_network_; } + Genclks *genclks() { return genclks_; } + Genclks *genclks() const { return genclks_; } + PathGroups *pathGroups() { return path_groups_; } + PathGroups *pathGroups() const { return path_groups_; } + PathGroupSeq pathGroups(const PathEnd *path_end) const; + PathGroups *makePathGroups(int group_path_count, + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + StringSeq &group_names, + bool setup, + bool hold, + bool recovery, + bool removal, + bool clk_gating_setup, + bool clk_gating_hold, + bool unconstrained_paths); + void deletePathGroups(); + +private: + std::string name_; + size_t mode_index_; + SceneSeq scenes_; + Sdc *sdc_; + Sim *sim_; + ClkNetwork *clk_network_; + Genclks *genclks_; + PathGroups *path_groups_{nullptr}; + StaState *sta_; +}; + +} // namespace sta diff --git a/include/sta/Mutex.hh b/include/sta/Mutex.hh index 9a96a6ad8..565e02722 100644 --- a/include/sta/Mutex.hh +++ b/include/sta/Mutex.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -29,6 +29,6 @@ namespace sta { // Hide a bit of the std verbosity. -typedef std::lock_guard LockGuard; +using LockGuard = std::scoped_lock; -} // namespace +} // namespace sta diff --git a/include/sta/Network.hh b/include/sta/Network.hh index 75ca0d75d..ada7e803f 100644 --- a/include/sta/Network.hh +++ b/include/sta/Network.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,13 +25,15 @@ #pragma once #include +#include +#include +#include -#include "Map.hh" -#include "StringUtil.hh" #include "LibertyClass.hh" -#include "VertexId.hh" #include "NetworkClass.hh" #include "StaState.hh" +#include "StringUtil.hh" +#include "VertexId.hh" namespace sta { @@ -39,12 +41,12 @@ class Report; class PatternMatch; class PinVisitor; -typedef Map LibertyLibraryMap; +using LibertyLibraryMap = std::map>; // Link network function returns top level instance. // Return nullptr if link fails. -typedef std::function LinkNetworkFunc; -typedef Map NetDrvrPinsMap; +using LinkNetworkFunc = std::function; +using NetDrvrPinsMap = std::map; // The Network class defines the network API used by sta. // The interface to a network implementation is constructed by @@ -91,8 +93,8 @@ typedef Map NetDrvrPinsMap; class Network : public StaState { public: - Network(); - virtual ~Network(); + Network() = default; + ~Network() override; virtual void clear(); // Linking the hierarchy creates the instance/pin/net network hierarchy. @@ -100,31 +102,31 @@ public: // has been linked. When the network interfaces to an external database, // linking is not necessary because the network has already been expanded. // Return true if successful. - virtual bool linkNetwork(const char *top_cell_name, - bool make_black_boxes, - Report *report) = 0; + virtual bool linkNetwork(std::string_view top_cell_name, + bool make_black_boxes, + Report *report) = 0; virtual bool isLinked() const; virtual bool isEditable() const { return false; } //////////////////////////////////////////////////////////////// // Library functions. - virtual const char *name(const Library *library) const = 0; + virtual std::string name(const Library *library) const = 0; virtual ObjectId id(const Library *library) const = 0; virtual LibraryIterator *libraryIterator() const = 0; virtual LibertyLibraryIterator *libertyLibraryIterator() const = 0; - virtual Library *findLibrary(const char *name) = 0; - virtual LibertyLibrary *findLiberty(const char *name) = 0; + virtual Library *findLibrary(std::string_view name) = 0; + virtual LibertyLibrary *findLiberty(std::string_view name) = 0; // Find liberty library by filename. - virtual LibertyLibrary *findLibertyFilename(const char *filename); + virtual LibertyLibrary *findLibertyFilename(std::string_view filename); virtual Cell *findCell(const Library *library, - const char *name) const = 0; + std::string_view name) const = 0; // Search the design (non-liberty) libraries for cells matching pattern. virtual CellSeq findCellsMatching(const Library *library, const PatternMatch *pattern) const = 0; // Search liberty libraries for cell name. - virtual LibertyCell *findLibertyCell(const char *name) const; - virtual LibertyLibrary *makeLibertyLibrary(const char *name, - const char *filename) = 0; + virtual LibertyCell *findLibertyCell(std::string_view name) const; + virtual LibertyLibrary *makeLibertyLibrary(std::string_view name, + std::string_view filename) = 0; // Hook for network after reading liberty library. virtual void readLibertyAfter(LibertyLibrary *library); // First liberty library read is used to look up defaults. @@ -132,14 +134,14 @@ public: virtual LibertyLibrary *defaultLibertyLibrary() const; void setDefaultLibertyLibrary(LibertyLibrary *library); // Check liberty cells used by the network to make sure they exist - // for all the defined corners. - void checkNetworkLibertyCorners(); - // Check liberty cells to make sure they exist for all the defined corners. - void checkLibertyCorners(); + // for all the defined scenes. + void checkNetworkLibertyScenes(); + // Check liberty cells to make sure they exist for all the defined scenes. + void checkLibertyScenes(); //////////////////////////////////////////////////////////////// // Cell functions. - virtual const char *name(const Cell *cell) const = 0; + virtual std::string name(const Cell *cell) const = 0; virtual ObjectId id(const Cell *cell) const = 0; virtual Library *library(const Cell *cell) const = 0; virtual LibertyLibrary *libertyLibrary(const Cell *cell) const; @@ -149,14 +151,13 @@ public: virtual const Cell *cell(const LibertyCell *cell) const = 0; virtual Cell *cell(LibertyCell *cell) const = 0; // Filename may return null. - virtual const char *filename(const Cell *cell) = 0; - // Attributes can be null + virtual std::string_view filename(const Cell *cell) const = 0; virtual std::string getAttribute(const Cell *cell, - const std::string &key) const = 0; + std::string_view key) const = 0; virtual const AttributeMap &attributeMap(const Cell *cell) const = 0; // Name can be a simple, bundle, bus, or bus bit name. virtual Port *findPort(const Cell *cell, - const char *name) const = 0; + std::string_view name) const = 0; virtual PortSeq findPortsMatching(const Cell *cell, const PatternMatch *pattern) const; virtual bool isLeaf(const Cell *cell) const = 0; @@ -168,7 +169,7 @@ public: //////////////////////////////////////////////////////////////// // Port functions - virtual const char *name(const Port *port) const = 0; + virtual std::string name(const Port *port) const = 0; virtual ObjectId id(const Port *port) const = 0; virtual Cell *cell(const Port *port) const = 0; virtual LibertyPort *libertyPort(const Port *port) const = 0; @@ -179,20 +180,20 @@ public: // Size is the bus/bundle member count (1 for non-bus/bundle ports). virtual int size(const Port *port) const = 0; // Bus range bus[from:to]. - virtual const char *busName(const Port *port) const = 0; + virtual std::string busName(const Port *port) const = 0; // Bus member, bus[subscript]. virtual Port *findBusBit(const Port *port, - int index) const = 0; + int index) const = 0; virtual int fromIndex(const Port *port) const = 0; virtual int toIndex(const Port *port) const = 0; // Predicate to determine if index is within bus range. // (toIndex > fromIndex) && fromIndex <= index <= toIndex // || (fromIndex > toIndex) && fromIndex >= index >= toIndex bool busIndexInRange(const Port *port, - int index); + int index); // Find Bundle/bus member by index. virtual Port *findMember(const Port *port, - int index) const = 0; + int index) const = 0; // Iterate over the bits of a bus port or members of a bundle. // from_index -> to_index virtual PortMemberIterator *memberIterator(const Port *port) const = 0; @@ -202,49 +203,49 @@ public: //////////////////////////////////////////////////////////////// // Instance functions // Name local to containing cell/instance. - virtual const char *name(const Instance *instance) const = 0; + virtual std::string name(const Instance *instance) const = 0; virtual ObjectId id(const Instance *instance) const = 0; // Top level instance of the design (defined after link). virtual Instance *topInstance() const = 0; virtual bool isTopInstance(const Instance *inst) const; - virtual Instance *findInstance(const char *path_name) const; + virtual Instance *findInstance(std::string_view path_name) const; // Find instance relative to hierarchical instance. virtual Instance *findInstanceRelative(const Instance *inst, - const char *path_name) const; + std::string_view path_name) const; // Default implementation uses linear search. virtual InstanceSeq findInstancesMatching(const Instance *context, const PatternMatch *pattern) const; virtual InstanceSeq findInstancesHierMatching(const Instance *instance, const PatternMatch *pattern) const; virtual std::string getAttribute(const Instance *inst, - const std::string &key) const = 0; + std::string_view key) const = 0; virtual const AttributeMap &attributeMap(const Instance *inst) const = 0; // Hierarchical path name. - virtual const char *pathName(const Instance *instance) const; + virtual std::string pathName(const Instance *instance) const; bool pathNameLess(const Instance *inst1, - const Instance *inst2) const; + const Instance *inst2) const; int pathNameCmp(const Instance *inst1, - const Instance *inst2) const; + const Instance *inst2) const; // Path from instance up to top level (last in the sequence). void path(const Instance *inst, - // Return value. - InstanceSeq &path) const; + // Return value. + InstanceSeq &path) const; virtual Cell *cell(const Instance *instance) const = 0; - virtual const char *cellName(const Instance *instance) const; + virtual std::string cellName(const Instance *instance) const; virtual LibertyLibrary *libertyLibrary(const Instance *instance) const; virtual LibertyCell *libertyCell(const Instance *instance) const; virtual Instance *parent(const Instance *instance) const = 0; virtual bool isLeaf(const Instance *instance) const = 0; virtual bool isHierarchical(const Instance *instance) const; virtual Instance *findChild(const Instance *parent, - const char *name) const = 0; + std::string_view name) const = 0; virtual void findChildrenMatching(const Instance *parent, - const PatternMatch *pattern, + const PatternMatch *pattern, // Return value. InstanceSeq &matches) const; // Is inst inside of hier_inst? bool isInside(const Instance *inst, - const Instance *hier_inst) const; + const Instance *hier_inst) const; // Iterate over all of the leaf instances in the hierarchy // This iterator is not virtual because it can be written in terms of @@ -270,18 +271,18 @@ public: //////////////////////////////////////////////////////////////// // Pin functions // Name is instance_name/port_name (the same as path name). - virtual const char *name(const Pin *pin) const; + virtual std::string name(const Pin *pin) const; virtual ObjectId id(const Pin *pin) const = 0; - virtual Pin *findPin(const char *path_name) const; + virtual Pin *findPin(std::string_view path_name) const; virtual Pin *findPin(const Instance *instance, - const char *port_name) const = 0; + std::string_view port_name) const = 0; virtual Pin *findPin(const Instance *instance, - const Port *port) const; + const Port *port) const; virtual Pin *findPin(const Instance *instance, - const LibertyPort *port) const; + const LibertyPort *port) const; // Find pin relative to hierarchical instance. Pin *findPinRelative(const Instance *inst, - const char *path_name) const; + std::string_view path_name) const; // Default implementation uses linear search. virtual PinSeq findPinsMatching(const Instance *instance, const PatternMatch *pattern) const; @@ -289,13 +290,13 @@ public: // pattern of the form instance_name/port_name. virtual PinSeq findPinsHierMatching(const Instance *instance, const PatternMatch *pattern) const; - virtual const char *portName(const Pin *pin) const; + virtual std::string portName(const Pin *pin) const; // Path name is instance_name/port_name. - virtual const char *pathName(const Pin *pin) const; + virtual std::string pathName(const Pin *pin) const; bool pathNameLess(const Pin *pin1, - const Pin *pin2) const; + const Pin *pin2) const; int pathNameCmp(const Pin *pin1, - const Pin *pin2) const; + const Pin *pin2) const; virtual Port *port(const Pin *pin) const = 0; virtual LibertyPort *libertyPort(const Pin *pin) const; virtual Instance *instance(const Pin *pin) const = 0; @@ -303,47 +304,47 @@ public: virtual Term *term(const Pin *pin) const = 0; virtual PortDirection *direction(const Pin *pin) const = 0; virtual bool isLeaf(const Pin *pin) const; - bool isHierarchical(const Pin *pin) const; - bool isTopLevelPort(const Pin *pin) const; + [[nodiscard]] bool isHierarchical(const Pin *pin) const; + [[nodiscard]] bool isTopLevelPort(const Pin *pin) const; // Is pin inside the instance hier_pin is attached to? - bool isInside(const Pin *pin, - const Pin *hier_pin) const; + [[nodiscard]] bool isInside(const Pin *pin, + const Pin *hier_pin) const; // Is pin inside of hier_inst? - bool isInside(const Pin *pin, - const Instance *hier_inst) const; - bool isDriver(const Pin *pin) const; - bool isLoad(const Pin *pin) const; - bool isClock(const Pin *pin) const; - bool isRiseEdgeTriggered(const Pin *pin) const; - bool isFallEdgeTriggered(const Pin *pin) const; + [[nodiscard]] bool isInside(const Pin *pin, + const Instance *hier_inst) const; + [[nodiscard]] bool isDriver(const Pin *pin) const; + [[nodiscard]] bool isLoad(const Pin *pin) const; + [[nodiscard]] bool isClock(const Pin *pin) const; + [[nodiscard]] bool isRiseEdgeTriggered(const Pin *pin) const; + [[nodiscard]] bool isFallEdgeTriggered(const Pin *pin) const; // Has register/latch rise/fall edges from pin. - bool isRegClkPin(const Pin *pin) const; + [[nodiscard]] bool isRegClkPin(const Pin *pin) const; // Pin clocks a timing check. - bool isCheckClk(const Pin *pin) const; - bool isLatchData(const Pin *pin) const; + [[nodiscard]] bool isCheckClk(const Pin *pin) const; + [[nodiscard]] bool isLatchData(const Pin *pin) const; // Iterate over all of the pins connected to a pin and the parent // and child nets it is hierarchically connected to (port, leaf and // hierarchical pins). virtual PinConnectedPinIterator *connectedPinIterator(const Pin *pin) const; virtual void visitConnectedPins(const Pin *pin, - PinVisitor &visitor) const; + PinVisitor &visitor) const; // Find driver pins for the net connected to pin. // Return value is owned by the network. virtual PinSet *drivers(const Pin *pin); virtual bool pinLess(const Pin *pin1, - const Pin *pin2) const; + const Pin *pin2) const; // Return the id of the pin graph vertex. virtual VertexId vertexId(const Pin *pin) const = 0; virtual void setVertexId(Pin *pin, - VertexId id) = 0; + VertexId id) = 0; // Return the physical X/Y coordinates of the pin. virtual void location(const Pin *pin, - // Return values. - double &x, - double &y, - bool &exists) const; + // Return values. + double &x, + double &y, + bool &exists) const; int pinCount(); int pinCount(Instance *inst); @@ -352,27 +353,27 @@ public: //////////////////////////////////////////////////////////////// // Terminal functions // Name is instance_name/port_name (the same as path name). - virtual const char *name(const Term *term) const; + virtual std::string name(const Term *term) const; virtual ObjectId id(const Term *term) const = 0; - virtual const char *portName(const Term *term) const; + virtual std::string portName(const Term *term) const; // Path name is instance_name/port_name (pin name). - virtual const char *pathName(const Term *term) const; + virtual std::string pathName(const Term *term) const; virtual Net *net(const Term *term) const = 0; virtual Pin *pin(const Term *term) const = 0; //////////////////////////////////////////////////////////////// // Net functions - virtual const char *name(const Net *net) const = 0; // no hierarchy prefix + virtual std::string name(const Net *net) const = 0; // no hierarchy prefix virtual ObjectId id(const Net *net) const = 0; - virtual Net *findNet(const char *path_name) const; + virtual Net *findNet(std::string_view path_name) const; // Find net relative to hierarchical instance. virtual Net *findNetRelative(const Instance *inst, - const char *path_name) const; + std::string_view path_name) const; // Default implementation uses linear search. virtual NetSeq findNetsMatching(const Instance *context, const PatternMatch *pattern) const; virtual Net *findNet(const Instance *instance, - const char *net_name) const = 0; + std::string_view net_name) const = 0; // Traverse the hierarchy from instance down and find nets matching // pattern of the form instance_name/net_name. virtual NetSeq findNetsHierMatching(const Instance *instance, @@ -381,27 +382,27 @@ public: virtual void findInstNetsMatching(const Instance *instance, const PatternMatch *pattern, NetSeq &matches) const = 0; - virtual const char *pathName(const Net *net) const; + virtual std::string pathName(const Net *net) const; bool pathNameLess(const Net *net1, - const Net *net2) const; + const Net *net2) const; int pathNameCmp(const Net *net1, - const Net *net2) const; + const Net *net2) const; virtual Instance *instance(const Net *net) const = 0; // Is net inside of hier_inst? virtual bool isInside(const Net *net, - const Instance *hier_inst) const; + const Instance *hier_inst) const; // Is pin connected to net anywhere in the hierarchy? virtual bool isConnected(const Net *net, - const Pin *pin) const; + const Pin *pin) const; // Is net1 connected to net2 anywhere in the hierarchy? virtual bool isConnected(const Net *net1, - const Net *net2) const; + const Net *net2) const; virtual Net *highestNetAbove(Net *net) const; virtual const Net *highestConnectedNet(Net *net) const; virtual void connectedNets(Net *net, - NetSet *nets) const; + NetSet *nets) const; virtual void connectedNets(const Pin *pin, - NetSet *nets) const; + NetSet *nets) const; virtual bool isPower(const Net *net) const = 0; virtual bool isGround(const Net *net) const = 0; @@ -414,7 +415,7 @@ public: // hierarchical pins). virtual NetConnectedPinIterator *connectedPinIterator(const Net *net) const; virtual void visitConnectedPins(const Net *net, - PinVisitor &visitor) const; + PinVisitor &visitor) const; // Find driver pins for net. // Return value is owned by the network. virtual PinSet *drivers(const Net *net); @@ -427,17 +428,13 @@ public: //////////////////////////////////////////////////////////////// // Parse path into first/tail (first hierarchy divider separated token). - // first and tail are both null if there are no dividers in path. - // Caller must delete first and tail. - void pathNameFirst(const char *path_name, - char *&first, - char *&tail) const; + void pathNameFirst(std::string_view path_name, + std::string &first, + std::string &tail) const; // Parse path into head/last (last hierarchy divider separated token). - // head and last are both null if there are no dividers in path. - // Caller must delete head and last. - void pathNameLast(const char *path_name, - char *&head, - char *&last) const; + void pathNameLast(std::string_view path_name, + std::string &head, + std::string &last) const; // Divider between instance names in a hierarchical path name. virtual char pathDivider() const { return divider_; } @@ -448,17 +445,17 @@ public: // Generated clocks related functions void addGeneratedClockPinToCell(const char *pinName, LibertyCell *cell); - const Map &generatedClockPinsToCellMap() const { + const std::map &generatedClockPinsToCellMap() const { return generated_clock_pins_to_cells_; } protected: Pin *findPinLinear(const Instance *instance, - const char *port_name) const; + std::string_view port_name) const; void findInstancesMatching1(const Instance *context, - size_t context_name_length, - const PatternMatch *pattern, - InstanceSeq &insts) const; + size_t context_name_length, + const PatternMatch *pattern, + InstanceSeq &matches) const; void findInstancesHierMatching1(const Instance *instance, const PatternMatch *pattern, InstanceSeq &matches) const; @@ -473,27 +470,27 @@ protected: // Return value. PinSeq &matches) const; bool isConnected(const Net *net, - const Pin *pin, - NetSet &nets) const; + const Pin *pin, + NetSet &nets) const; bool isConnected(const Net *net1, - const Net *net2, - NetSet &nets) const; + const Net *net2, + NetSet &nets) const; int hierarchyLevel(const Net *net) const; virtual void visitConnectedPins(const Net *net, - PinVisitor &visitor, - NetSet &visited_nets) const; + PinVisitor &visitor, + NetSet &visited_nets) const; // Default implementation uses linear search. virtual void findInstPinsMatching(const Instance *instance, const PatternMatch *pattern, // Return value. PinSeq &matches) const; - void findInstPinsHierMatching(const Instance *parent, + void findInstPinsHierMatching(const Instance *instance, const PatternMatch *pattern, // Return value. PinSeq &matches) const; // findNet using linear search. Net *findNetLinear(const Instance *instance, - const char *net_name) const; + std::string_view net_name) const; // findNetsMatching using linear search. NetSeq findNetsMatchingLinear(const Instance *instance, const PatternMatch *pattern) const; @@ -502,49 +499,45 @@ protected: // nets may be connected across hierarchy levels. void clearNetDrvrPinMap(); - LibertyLibrary *default_liberty_; - char divider_; - char escape_; + LibertyLibrary *default_liberty_{nullptr}; + char divider_{'/'}; + char escape_{'\\'}; NetDrvrPinsMap net_drvr_pin_map_; // Map of generated clock pins to their corresponding liberty cell - Map generated_clock_pins_to_cells_; + std::map generated_clock_pins_to_cells_; }; // Network API to support network edits. class NetworkEdit : public Network { public: - NetworkEdit(); - virtual bool isEditable() const { return true; } + NetworkEdit() = default; + bool isEditable() const override { return true; } virtual Instance *makeInstance(LibertyCell *cell, - const char *name, - Instance *parent) = 0; + std::string_view name, + Instance *parent) = 0; virtual void makePins(Instance *inst) = 0; virtual void replaceCell(Instance *inst, - Cell *cell) = 0; + Cell *cell) = 0; // Deleting instance also deletes instance pins. virtual void deleteInstance(Instance *inst) = 0; // Connect the port on an instance to a net. virtual Pin *connect(Instance *inst, - Port *port, - Net *net) = 0; + Port *port, + Net *net) = 0; virtual Pin *connect(Instance *inst, - LibertyPort *port, - Net *net) = 0; - // makePin/connectPin replaced by connect. - // deprecated 2018-09-28 - virtual void connectPin(Pin *pin, - Net *net) __attribute__ ((deprecated)); + LibertyPort *port, + Net *net) = 0; // Disconnect pin from net. virtual void disconnectPin(Pin *pin) = 0; virtual void deletePin(Pin *pin) = 0; - virtual Net *makeNet(const char *name, - Instance *parent) = 0; + virtual Net *makeNet(std::string_view name, + Instance *parent) = 0; // Deleting net disconnects (but does not delete) net pins. virtual void deleteNet(Net *net) = 0; virtual void mergeInto(Net *net, - Net *into_net) = 0; + Net *into_net) = 0; virtual Net *mergedInto(Net *net) = 0; }; @@ -552,77 +545,75 @@ public: class NetworkReader : public NetworkEdit { public: - NetworkReader() {} // Called before reading a netlist to delete any previously linked network. virtual void readNetlistBefore() = 0; virtual void setLinkFunc(LinkNetworkFunc link) = 0; - virtual Library *makeLibrary(const char *name, - const char *filename) = 0; + virtual Library *makeLibrary(std::string_view name, + std::string_view filename) = 0; virtual void deleteLibrary(Library *library) = 0; // Search the libraries in read order for a cell by name. - virtual Cell *findAnyCell(const char *name) = 0; + virtual Cell *findAnyCell(std::string_view name) = 0; virtual Cell *makeCell(Library *library, - const char *name, - bool is_leaf, - const char *filename) = 0; + std::string_view name, + bool is_leaf, + std::string_view filename) = 0; virtual void deleteCell(Cell *cell) = 0; virtual void setName(Cell *cell, - const char *name) = 0; + std::string_view name) = 0; virtual void setIsLeaf(Cell *cell, - bool is_leaf) = 0; + bool is_leaf) = 0; virtual void setAttribute(Cell *cell, - const std::string &key, - const std::string &value) = 0; + std::string_view key, + std::string_view value) = 0; virtual void setAttribute(Instance *instance, - const std::string &key, - const std::string &value) = 0; + std::string_view key, + std::string_view value) = 0; virtual Port *makePort(Cell *cell, - const char *name) = 0; + std::string_view name) = 0; virtual Port *makeBusPort(Cell *cell, - const char *name, - int from_index, - int to_index) = 0; + std::string_view name, + int from_index, + int to_index) = 0; virtual void groupBusPorts(Cell *cell, - std::function port_msb_first) = 0; + std::function port_msb_first) = 0; virtual Port *makeBundlePort(Cell *cell, - const char *name, - PortSeq *members) = 0; + std::string_view name, + PortSeq *members) = 0; virtual Instance *makeInstance(Cell *cell, - const char *name, - Instance *parent) = 0; + std::string_view name, + Instance *parent) = 0; virtual Pin *makePin(Instance *inst, - Port *port, - Net *net) = 0; + Port *port, + Net *net) = 0; virtual Term *makeTerm(Pin *pin, - Net *net) = 0; + Net *net) = 0; virtual void setDirection(Port *port, - PortDirection *dir) = 0; + PortDirection *dir) = 0; // Instance is the network view for cell. virtual void setCellNetworkView(Cell *cell, - Instance *inst) = 0; + Instance *inst) = 0; virtual Instance *cellNetworkView(Cell *cell) = 0; virtual void deleteCellNetworkViews() = 0; virtual void addConstantNet(Net *net, - LogicValue const_value) = 0; + LogicValue const_value) = 0; using NetworkEdit::makeInstance; }; Instance * linkReaderNetwork(Cell *top_cell, - bool make_black_boxes, - Report *report, - NetworkReader *network); + bool make_black_boxes, + Report *report, + NetworkReader *network); // Abstract class for Network::constantPinIterator(). class ConstantPinIterator { public: - ConstantPinIterator() {} - virtual ~ConstantPinIterator() {} + virtual ~ConstantPinIterator() = default; virtual bool hasNext() = 0; virtual void next(const Pin *&pin, - LogicValue &value) = 0; + LogicValue &value) = 0; }; // Implementation class for Network::constantPinIterator(). @@ -630,36 +621,34 @@ class NetworkConstantPinIterator : public ConstantPinIterator { public: NetworkConstantPinIterator(const Network *network, - NetSet &zero_nets, - NetSet &one_nets); - ~NetworkConstantPinIterator(); - virtual bool hasNext(); - virtual void next(const Pin *&pin, LogicValue &value); + NetSet &zero_nets, + NetSet &one_nets); + bool hasNext() override; + void next(const Pin *&pin, LogicValue &value) override; private: void findConstantPins(NetSet &nets, - PinSet &pins); + PinSet &pins); const Network *network_; - PinSet constant_pins_[2]; + PinSet constant_pins_[2]{PinSet(network_), PinSet(network_)}; LogicValue value_; - PinSet::Iterator *pin_iter_; + PinSet::iterator pin_iter_; }; // Abstract base class for visitDrvrLoadsThruHierPin visitor. class HierPinThruVisitor { public: - HierPinThruVisitor() {} - virtual ~HierPinThruVisitor() {} + virtual ~HierPinThruVisitor() = default; virtual void visit(const Pin *drvr, - const Pin *load) = 0; + const Pin *load) = 0; }; class PinVisitor { public: - virtual ~PinVisitor() {} + virtual ~PinVisitor() = default; virtual void operator()(const Pin *pin) = 0; }; @@ -667,11 +656,11 @@ class FindNetDrvrLoads : public PinVisitor { public: FindNetDrvrLoads(const Pin *drvr_pin, - PinSet &visited_drvrs, - PinSeq &loads, - PinSeq &drvrs, - const Network *network); - virtual void operator()(const Pin *pin); + PinSet &visited_drvrs, + PinSeq &loads, + PinSeq &drvrs, + const Network *network); + void operator()(const Pin *pin) override; protected: const Pin *drvr_pin_; @@ -684,14 +673,14 @@ protected: // Visit driver/loads pins through a hierarcial pin. void visitDrvrLoadsThruHierPin(const Pin *hpin, - const Network *network, - HierPinThruVisitor *visitor); + const Network *network, + HierPinThruVisitor *visitor); void visitDrvrLoadsThruNet(const Net *net, - const Network *network, - HierPinThruVisitor *visitor); + const Network *network, + HierPinThruVisitor *visitor); char logicValueString(LogicValue value); -} // namespace +} // namespace sta diff --git a/include/sta/NetworkClass.hh b/include/sta/NetworkClass.hh index 7ec2de9ec..3b4b129a1 100644 --- a/include/sta/NetworkClass.hh +++ b/include/sta/NetworkClass.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,11 +25,12 @@ #pragma once #include -#include #include +#include +#include +#include +#include -#include "Set.hh" -#include "Vector.hh" #include "Iterator.hh" namespace sta { @@ -49,35 +50,36 @@ class ConstantPinIterator; class ViewType; class LibertyLibrary; -typedef Iterator LibraryIterator; -typedef Iterator LibertyLibraryIterator; -typedef Vector CellSeq; -typedef CellSeq::ConstIterator CellSeqIterator; -typedef Vector PortSeq; -typedef PortSeq::ConstIterator PortSeqIterator; -typedef Iterator CellPortIterator; -typedef Iterator CellPortBitIterator; -typedef Iterator PortMemberIterator; - -typedef Vector PinSeq; -typedef PinSeq::ConstIterator PinSeqIterator; -typedef Vector InstanceSeq; -typedef InstanceSeq::ConstIterator InstanceSeqIterator; -typedef Vector NetSeq; -typedef NetSeq::ConstIterator NetSeqIterator; -typedef std::vector ConstNetSeq; -typedef Iterator InstanceChildIterator; -typedef Iterator InstancePinIterator; -typedef Iterator InstanceNetIterator; -typedef Iterator LeafInstanceIterator; -typedef Iterator NetIterator; -typedef Iterator NetPinIterator; -typedef Iterator NetTermIterator; -typedef Iterator ConnectedPinIterator; -typedef ConnectedPinIterator NetConnectedPinIterator; -typedef ConnectedPinIterator PinConnectedPinIterator; -typedef uint32_t ObjectId; -typedef std::map AttributeMap; +using LibraryIterator = Iterator; +using LibertyLibraryIterator = Iterator; +using CellSeq = std::vector; +using CellSeqIterator = VectorIterator; +using PortSeq = std::vector; +using PortSeqIterator = VectorIterator; +using CellPortIterator = Iterator; +using CellPortBitIterator = Iterator; +using PortMemberIterator = Iterator; + +using PinSeq = std::vector; +using PinSeqIterator = VectorIterator; +using PinUnorderedSet = std::unordered_set; +using InstanceSeq = std::vector; +using InstanceSeqIterator = VectorIterator; +using NetSeq = std::vector; +using NetSeqIterator = VectorIterator; +using ConstNetSeq = std::vector; +using InstanceChildIterator = Iterator; +using InstancePinIterator = Iterator; +using InstanceNetIterator = Iterator; +using LeafInstanceIterator = Iterator; +using NetIterator = Iterator; +using NetPinIterator = Iterator; +using NetTermIterator = Iterator; +using ConnectedPinIterator = Iterator; +using NetConnectedPinIterator = ConnectedPinIterator; +using PinConnectedPinIterator = ConnectedPinIterator; +using ObjectId = uint32_t; +using AttributeMap = std::map>; enum class LogicValue : unsigned { zero, one, unknown, rise, fall }; @@ -143,55 +145,37 @@ private: //////////////////////////////////////////////////////////////// -class CellSet : public Set +class CellSet : public std::set { public: CellSet(const Network *network); }; -class PortSet : public Set +class PortSet : public std::set { public: PortSet(const Network *network); }; -class InstanceSet : public Set +class InstanceSet : public std::set { public: InstanceSet(); InstanceSet(const Network *network); - static int compare(const InstanceSet *set1, - const InstanceSet *set2, - const Network *network); - static bool intersects(const InstanceSet *set1, - const InstanceSet *set2, - const Network *network); }; -class PinSet : public Set +class PinSet : public std::set { public: PinSet(); PinSet(const Network *network); - static int compare(const PinSet *set1, - const PinSet *set2, - const Network *network); - static bool intersects(const PinSet *set1, - const PinSet *set2, - const Network *network); }; -class NetSet : public Set +class NetSet : public std::set { public: NetSet(); NetSet(const Network *network); - static int compare(const NetSet *set1, - const NetSet *set2, - const Network *network); - static bool intersects(const NetSet *set1, - const NetSet *set2, - const Network *network); }; -} // namespace +} // namespace sta diff --git a/include/sta/NetworkCmp.hh b/include/sta/NetworkCmp.hh index c343ccab8..019eb9b72 100644 --- a/include/sta/NetworkCmp.hh +++ b/include/sta/NetworkCmp.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -34,9 +34,9 @@ namespace sta { class PortNameLess { public: - explicit PortNameLess(const Network *network); + PortNameLess(const Network *network); bool operator()(const Port *port1, - const Port *port2) const; + const Port *port2) const; private: const Network *network_; @@ -45,9 +45,9 @@ private: class PinPathNameLess { public: - explicit PinPathNameLess(const Network *network); + PinPathNameLess(const Network *network); bool operator()(const Pin *pin1, - const Pin *pin2) const; + const Pin *pin2) const; private: const Network *network_; @@ -56,9 +56,9 @@ private: class InstancePathNameLess { public: - explicit InstancePathNameLess(const Network *network); + InstancePathNameLess(const Network *network); bool operator()(const Instance *inst1, - const Instance *inst2) const; + const Instance *inst2) const; private: const Network *network_; @@ -67,9 +67,9 @@ private: class NetPathNameLess { public: - explicit NetPathNameLess(const Network *network); + NetPathNameLess(const Network *network); bool operator()(const Net *net1, - const Net *net2) const; + const Net *net2) const; private: const Network *network_; @@ -78,6 +78,9 @@ private: PinSeq sortByPathName(const PinSet *set, const Network *network); +PinSeq +sortByPathName(const PinUnorderedSet *set, + const Network *network); PortSeq sortByName(const PortSet *set, const Network *network); @@ -88,4 +91,4 @@ NetSeq sortByPathName(NetSet *set, const Network *network); -} // namespace +} // namespace sta diff --git a/include/sta/ObjectId.hh b/include/sta/ObjectId.hh index 6f3b77036..905f7b1b1 100644 --- a/include/sta/ObjectId.hh +++ b/include/sta/ObjectId.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -29,15 +29,15 @@ namespace sta { // ObjectId is block index and object index within the block. -typedef uint32_t ObjectId; +using ObjectId = uint32_t; // Block index. -typedef uint32_t BlockIdx; +using BlockIdx = uint32_t; // Object index within a block. -typedef uint32_t ObjectIdx; +using ObjectIdx = uint32_t; static constexpr int object_id_bits = sizeof(ObjectId) * 8; static constexpr BlockIdx block_idx_null = 0; static constexpr ObjectId object_id_null = 0; static constexpr ObjectIdx object_idx_null = 0; -} // Namespace +} // namespace sta diff --git a/include/sta/ObjectTable.hh b/include/sta/ObjectTable.hh index 3c7521786..b1568b936 100644 --- a/include/sta/ObjectTable.hh +++ b/include/sta/ObjectTable.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,7 +24,9 @@ #pragma once -#include "Vector.hh" +#include + +#include "ContainerHelpers.hh" #include "Error.hh" #include "ObjectId.hh" @@ -48,7 +50,6 @@ template class ObjectTable { public: - ObjectTable(); ~ObjectTable(); TYPE *make(); void destroy(TYPE *object); @@ -66,26 +67,19 @@ public: private: void makeBlock(); void freePush(TYPE *object, - ObjectId id); + ObjectId id); - size_t size_; + size_t size_{0}; // Object ID of next free object. - ObjectId free_; - Vector*> blocks_; + ObjectId free_{object_id_null}; + std::vector*> blocks_; static constexpr ObjectId idx_mask_ = block_object_count - 1; }; -template -ObjectTable::ObjectTable() : - size_(0), - free_(object_id_null) -{ -} - template ObjectTable::~ObjectTable() { - blocks_.deleteContents(); + deleteContents(blocks_); } template @@ -106,7 +100,7 @@ ObjectTable::make() template void ObjectTable::freePush(TYPE *object, - ObjectId id) + ObjectId id) { // Link free objects into a list linked by Object ID. ObjectId *free_next = reinterpret_cast(object); @@ -181,7 +175,7 @@ template void ObjectTable::clear() { - blocks_.deleteContentsClear(); + deleteContents(blocks_);; size_ = 0; } @@ -192,7 +186,7 @@ class TableBlock { public: TableBlock(BlockIdx block_idx, - ObjectTable *table); + ObjectTable *table); BlockIdx index() const { return block_idx_; } TYPE &ref(ObjectIdx idx) { return objects_[idx]; } TYPE *pointer(ObjectIdx idx) { return &objects_[idx]; } @@ -205,10 +199,10 @@ private: template TableBlock::TableBlock(BlockIdx block_idx, - ObjectTable *table) : + ObjectTable *table) : block_idx_(block_idx), table_(table) { } -} // Namespace +} // namespace sta diff --git a/include/sta/Parasitics.hh b/include/sta/Parasitics.hh index 948843f2c..c6706f90f 100644 --- a/include/sta/Parasitics.hh +++ b/include/sta/Parasitics.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -28,55 +28,50 @@ #include #include -#include "StaState.hh" #include "LibertyClass.hh" #include "NetworkClass.hh" -#include "SdcClass.hh" #include "ParasiticsClass.hh" +#include "SdcClass.hh" +#include "StaState.hh" namespace sta { class Wireload; -class Corner; +class Scene; -typedef std::complex ComplexFloat; -typedef Vector ComplexFloatSeq; -typedef std::vector ParasiticNodeSeq; -typedef std::vector ParasiticResistorSeq; -typedef std::vector ParasiticCapacitorSeq; -typedef std::map ParasiticNodeResistorMap; -typedef std::map ParasiticNodeCapacitorMap; +using ComplexFloat = std::complex; +using ComplexFloatSeq = std::vector; +using ParasiticNodeSeq = std::vector; +using ParasiticResistorSeq = std::vector; +using ParasiticCapacitorSeq = std::vector; +using ParasiticNodeResistorMap = std::map; +using ParasiticNodeCapacitorMap = std::map; // Parasitics API. -// All parasitic parameters can have multiple values, each corresponding -// to an analysis point. -// Parasitic annotation for a pin or net may exist for one analysis point -// and not another. class Parasitics : public StaState { public: Parasitics(StaState *sta); - virtual ~Parasitics() {} + virtual const std::string &name() const = 0; + virtual const std::string &filename() const = 0; virtual bool haveParasitics() = 0; + // Clear all state. virtual void clear() = 0; // Delete all parasitics. virtual void deleteParasitics() = 0; // Delete all parasitics on net at analysis point. - virtual void deleteParasitics(const Net *net, - const ParasiticAnalysisPt *ap) = 0; + virtual void deleteParasitics(const Net *net) = 0; // Delete all parasitics on pin at analysis point. - virtual void deleteParasitics(const Pin *pin, - const ParasiticAnalysisPt *ap) = 0; - virtual void deleteReducedParasitics(const Net *net, - const ParasiticAnalysisPt *ap) = 0; + virtual void deleteParasitics(const Pin *pin) = 0; + virtual void deleteReducedParasitics(const Net *net) = 0; virtual void deleteDrvrReducedParasitics(const Pin *drvr_pin) = 0; virtual bool isReducedParasiticNetwork(const Parasitic *parasitic) const = 0; // Flag this parasitic as reduced from a parasitic network. virtual void setIsReducedParasiticNetwork(Parasitic *parasitic, - bool is_reduced) = 0; + bool is_reduced) = 0; // Capacitance value of parasitic object. virtual float capacitance(const Parasitic *parasitic) const = 0; @@ -87,115 +82,109 @@ public: // capacitor on the driver pin. virtual bool isPiElmore(const Parasitic *parasitic) const = 0; virtual Parasitic *findPiElmore(const Pin *drvr_pin, - const RiseFall *rf, - const ParasiticAnalysisPt *ap) const = 0; + const RiseFall *rf, + const MinMax *min_max) const = 0; virtual Parasitic *makePiElmore(const Pin *drvr_pin, - const RiseFall *rf, - const ParasiticAnalysisPt *ap, - float c2, - float rpi, - float c1) = 0; + const RiseFall *rf, + const MinMax *min_max, + float c2, + float rpi, + float c1) = 0; //////////////////////////////////////////////////////////////// // Pi models are common to PiElmore and PiPoleResidue. virtual bool isPiModel(const Parasitic *parasitic) const = 0; virtual void piModel(const Parasitic *parasitic, - float &c2, - float &rpi, - float &c1) const = 0; + float &c2, + float &rpi, + float &c1) const = 0; // Set PI model parameters. virtual void setPiModel(Parasitic *parasitic, - float c2, - float rpi, - float c1) = 0; + float c2, + float rpi, + float c1) = 0; //////////////////////////////////////////////////////////////// // Elmore driver to load delay. // Common to LumpedElmore and PiElmore parasitics. virtual void findElmore(const Parasitic *parasitic, - const Pin *load_pin, - float &elmore, - bool &exists) const = 0; + const Pin *load_pin, + float &elmore, + bool &exists) const = 0; // Set load elmore delay. virtual void setElmore(Parasitic *parasitic, - const Pin *load_pin, - float elmore) = 0; + const Pin *load_pin, + float elmore) = 0; //////////////////////////////////////////////////////////////// // Pi model driver load with pole/residue interconnect model to load pins. virtual bool isPiPoleResidue(const Parasitic* parasitic) const = 0; virtual Parasitic *findPiPoleResidue(const Pin *drvr_pin, - const RiseFall *rf, - const ParasiticAnalysisPt *ap) const=0; + const RiseFall *rf, + const MinMax *min_max) const = 0; virtual Parasitic *makePiPoleResidue(const Pin *drvr_pin, - const RiseFall *rf, - const ParasiticAnalysisPt *ap, - float c2, - float rpi, - float c1) = 0; + const RiseFall *rf, + const MinMax *min_max, + float c2, + float rpi, + float c1) = 0; virtual Parasitic *findPoleResidue(const Parasitic *parasitic, - const Pin *load_pin) const = 0; + const Pin *load_pin) const = 0; // Make pole/residue model for load_pin. virtual void setPoleResidue(Parasitic *parasitic, - const Pin *load_pin, - ComplexFloatSeq *poles, - ComplexFloatSeq *residues) = 0; + const Pin *load_pin, + ComplexFloatSeq *poles, + ComplexFloatSeq *residues) = 0; virtual bool isPoleResidue(const Parasitic* parasitic) const = 0; // Return the number of poles and residues in a pole/residue parasitic. virtual size_t poleResidueCount(const Parasitic *parasitic) const = 0; // Find the pole_index'th pole/residue in a pole/residue parasitic. virtual void poleResidue(const Parasitic *parasitic, - int pole_index, - ComplexFloat &pole, - ComplexFloat &residue) const = 0; + int pole_index, + ComplexFloat &pole, + ComplexFloat &residue) const = 0; //////////////////////////////////////////////////////////////// // Parasitic Network (detailed parasitics). // This api assumes that parasitic networks are not rise/fall // dependent because they do not include pin capacitances. virtual bool isParasiticNetwork(const Parasitic *parasitic) const = 0; - virtual Parasitic *findParasiticNetwork(const Net *net, - const ParasiticAnalysisPt *ap) const = 0; - virtual Parasitic *findParasiticNetwork(const Pin *pin, - const ParasiticAnalysisPt *ap) const = 0; + virtual Parasitic *findParasiticNetwork(const Net *net) = 0; + virtual Parasitic *findParasiticNetwork(const Pin *pin) = 0; virtual Parasitic *makeParasiticNetwork(const Net *net, - bool includes_pin_caps, - const ParasiticAnalysisPt *ap) = 0; + bool includes_pin_caps) = 0; virtual ParasiticNodeSeq nodes(const Parasitic *parasitic) const = 0; virtual void report(const Parasitic *parasitic) const; virtual const Net *net(const Parasitic *parasitic) const = 0; virtual ParasiticResistorSeq resistors(const Parasitic *parasitic) const = 0; virtual ParasiticCapacitorSeq capacitors(const Parasitic *parasitic) const = 0; - // Delete parasitic network if it exists. - virtual void deleteParasiticNetwork(const Net *net, - const ParasiticAnalysisPt *ap) = 0; - virtual void deleteParasiticNetworks(const Net *net) = 0; + virtual void deleteParasiticNetwork(const Net *net) = 0; // True if the parasitic network caps include pin capacitances. virtual bool includesPinCaps(const Parasitic *parasitic) const = 0; // Parasitic network component builders. virtual ParasiticNode *findParasiticNode(Parasitic *parasitic, const Net *net, - int id, + uint32_t id, const Network *network) const = 0; // Make a subnode of the parasitic network net. virtual ParasiticNode *ensureParasiticNode(Parasitic *parasitic, - const Net *net, - int id, + const Net *net, + uint32_t id, const Network *network) = 0; // Find the parasitic node connected to pin. virtual ParasiticNode *findParasiticNode(const Parasitic *parasitic, const Pin *pin) const = 0; // deprecated 2024-02-27 virtual ParasiticNode *findNode(const Parasitic *parasitic, - const Pin *pin) const __attribute__ ((deprecated)); + const Pin *pin) const __attribute__ ((deprecated)); // Make a subnode of the parasitic network net connected to pin. virtual ParasiticNode *ensureParasiticNode(Parasitic *parasitic, - const Pin *pin, + const Pin *pin, const Network *network) = 0; // Increment the grounded capacitance on node. virtual void incrCap(ParasiticNode *node, - float cap) = 0; - virtual const char *name(const ParasiticNode *node) const = 0; + float cap) = 0; + virtual std::string name(const ParasiticNode *node) const = 0; virtual const Pin *pin(const ParasiticNode *node) const = 0; virtual const Net *net(const ParasiticNode *node, const Network *network) const = 0; @@ -206,11 +195,11 @@ public: // Coupling capacitor between parasitic nodes on a net. virtual void makeCapacitor(Parasitic *parasitic, - size_t id, + uint32_t id, float cap, ParasiticNode *node1, ParasiticNode *node2) = 0; - virtual size_t id(const ParasiticCapacitor *capacitor) const = 0; + virtual uint32_t id(const ParasiticCapacitor *capacitor) const = 0; virtual float value(const ParasiticCapacitor *capacitor) const = 0; virtual ParasiticNode *node1(const ParasiticCapacitor *capacitor) const = 0; virtual ParasiticNode *node2(const ParasiticCapacitor *capacitor) const = 0; @@ -218,15 +207,15 @@ public: ParasiticNode *node) const; virtual void makeResistor(Parasitic *parasitic, - size_t id, - float res, + uint32_t id, + float res, ParasiticNode *node1, - ParasiticNode *node2) = 0; - virtual size_t id(const ParasiticResistor *resistor) const = 0; + ParasiticNode *node2) = 0; + virtual uint32_t id(const ParasiticResistor *resistor) const = 0; virtual float value(const ParasiticResistor *resistor) const = 0; virtual ParasiticNode *node1(const ParasiticResistor *resistor) const = 0; virtual ParasiticNode *node2(const ParasiticResistor *resistor) const = 0; - virtual ParasiticNode *otherNode(const ParasiticResistor *capacitor, + virtual ParasiticNode *otherNode(const ParasiticResistor *resistor, ParasiticNode *node) const; // Iteration over resistors connected to a nodes. @@ -248,17 +237,15 @@ public: Parasitic *reduceToPiElmore(const Parasitic *parasitic, const Pin *drvr_pin, const RiseFall *rf, - const Corner *corner, - const MinMax *cnst_min_max, - const ParasiticAnalysisPt *ap); + const Scene *scene, + const MinMax *min_max); // Reduce parasitic network to pi and 2nd order pole/residue models // for drvr_pin. Parasitic *reduceToPiPoleResidue2(const Parasitic *parasitic, const Pin *drvr_pin, const RiseFall *rf, - const Corner *corner, - const MinMax *cnst_min_max, - const ParasiticAnalysisPt *ap); + const Scene *scene, + const MinMax *min_max); // Estimate parasitic as pi elmore using wireload model. Parasitic *estimatePiElmore(const Pin *drvr_pin, @@ -266,67 +253,49 @@ public: const Wireload *wireload, float fanout, float net_pin_cap, - const Corner *corner, + const Scene *scene, const MinMax *min_max); Parasitic *makeWireloadNetwork(const Pin *drvr_pin, - const Wireload *wireload, - float fanout, - const MinMax *min_max, - const ParasiticAnalysisPt *ap); + const Wireload *wireload, + float fanout, + const Scene *scene, + const MinMax *min_max); // Network edit before/after methods. - virtual void disconnectPinBefore(const Pin *pin, - const Network *network) = 0; + virtual void disconnectPinBefore(const Pin *pin) = 0; virtual void deletePinBefore(const Pin *pin) = 0; virtual void loadPinCapacitanceChanged(const Pin *pin) = 0; + float couplingCapFactor() const { return coupling_cap_factor_; } + void setCouplingCapFactor(float factor); protected: void makeWireloadNetworkWorst(Parasitic *parasitic, - const Pin *drvr_pin, + const Pin *drvr_pin, const Net *net, - float wireload_cap, - float wireload_res, - float fanout); + float wireload_cap, + float wireload_res, + float fanout); void makeWireloadNetworkBest(Parasitic *parasitic, - const Pin *drvr_pin, - float wireload_cap, - float wireload_res, - float fanout); + const Pin *drvr_pin, + float wireload_cap, + float wireload_res, + float fanout); void makeWireloadNetworkBalanced(Parasitic *parasitic, - const Pin *drvr_pin, - float wireload_cap, - float wireload_res, - float fanout); + const Pin *drvr_pin, + float wireload_cap, + float wireload_res, + float fanout); const Net *findParasiticNet(const Pin *pin) const; -}; -// Managed by the Corner class. -class ParasiticAnalysisPt -{ -public: - ParasiticAnalysisPt(const char *name, - int index, - int index_max); - const char *name() const { return name_.c_str(); } - int index() const { return index_; } - int indexMax() const { return index_max_; } - // Coupling capacitor factor used by all reduction functions. - float couplingCapFactor() const { return coupling_cap_factor_; } - void setCouplingCapFactor(float factor); - -private: - std::string name_; - int index_; - int index_max_; - float coupling_cap_factor_; + float coupling_cap_factor_ {1.0}; }; class ParasiticNodeLess { public: + ParasiticNodeLess(); ParasiticNodeLess(const Parasitics *parasitics, const Network *network); - ParasiticNodeLess(const ParasiticNodeLess &less); bool operator()(const ParasiticNode *node1, const ParasiticNode *node2) const; private: @@ -334,4 +303,4 @@ private: const Network *network_; }; -} // namespace +} // namespace sta diff --git a/include/sta/ParasiticsClass.hh b/include/sta/ParasiticsClass.hh index 2d35781d5..590156b10 100644 --- a/include/sta/ParasiticsClass.hh +++ b/include/sta/ParasiticsClass.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -29,8 +29,7 @@ namespace sta { class Parasitics; class Parasitic; class ParasiticNode; -class ParasiticAnalysisPt; class ParasiticResistor; class ParasiticCapacitor; -} // namespace +} // namespace sta diff --git a/include/sta/ParseBus.hh b/include/sta/ParseBus.hh index a7058efd8..e6236fa8f 100644 --- a/include/sta/ParseBus.hh +++ b/include/sta/ParseBus.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,15 +25,16 @@ #pragma once #include +#include namespace sta { // Return true if name is a bus. bool -isBusName(const char *name, - const char brkt_left, - const char brkt_right, - char escape); +isBusName(std::string_view name, + char brkt_left, + char brkt_right, + char escape); // Parse name as a bus. // signal @@ -43,32 +44,32 @@ isBusName(const char *name, // index = bit // Caller must delete returned bus_name string. void -parseBusName(const char *name, - const char brkt_left, - const char brkt_right, - char escape, - // Return values. - bool &is_bus, +parseBusName(std::string_view name, + char brkt_left, + char brkt_right, + char escape, + // Return values. + bool &is_bus, std::string &bus_name, - int &index); + int &index); // Allow multiple different left/right bus brackets. void -parseBusName(const char *name, - const char *brkts_left, - const char *brkts_right, - char escape, - // Return values. - bool &is_bus, - std::string &bus_name, - int &index); +parseBusName(std::string_view name, + std::string_view brkts_left, + std::string_view brkts_right, + char escape, + // Return values. + bool &is_bus, + std::string &bus_name, + int &index); // Parse a bus range, such as BUS[4:0]. // bus_name is set to null if name is not a range. // Caller must delete returned bus_name string. void -parseBusName(const char *name, - const char brkt_left, - const char brkt_right, +parseBusName(std::string_view name, + char brkt_left, + char brkt_right, char escape, // Return values. bool &is_bus, @@ -81,10 +82,10 @@ parseBusName(const char *name, // brkt_lefts and brkt_rights are corresponding strings of legal // bus brackets such as "[(<" and "])>". void -parseBusName(const char *name, - const char *brkts_left, - const char *brkts_right, - const char escape, +parseBusName(std::string_view name, + std::string_view brkts_left, + std::string_view brkts_right, + char escape, // Return values. bool &is_bus, bool &is_range, @@ -95,9 +96,9 @@ parseBusName(const char *name, // Insert escapes before ch1 and ch2 in token. std::string -escapeChars(const char *token, - const char ch1, - const char ch2, - const char escape); +escapeChars(std::string_view token, + char ch1, + char ch2, + char escape); -} // namespace +} // namespace sta diff --git a/include/sta/Path.hh b/include/sta/Path.hh index 09763f730..d04f9827f 100644 --- a/include/sta/Path.hh +++ b/include/sta/Path.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,19 +24,17 @@ #pragma once +#include "Delay.hh" +#include "GraphClass.hh" #include "MinMax.hh" #include "NetworkClass.hh" #include "SdcClass.hh" -#include "Transition.hh" -#include "GraphClass.hh" -#include "Delay.hh" -#include "StaState.hh" #include "SearchClass.hh" +#include "StaState.hh" +#include "Transition.hh" namespace sta { -class DcalcAnalysisPt; - class Path { public: @@ -47,29 +45,28 @@ public: const StaState *sta); Path(Vertex *vertex, Tag *tag, - Arrival arrival, + const Arrival &arrival, Path *prev_path, Edge *prev_edge, TimingArc *prev_arc, const StaState *sta); Path(Vertex *vertex, Tag *tag, - Arrival arrival, + const Arrival &arrival, Path *prev_path, Edge *prev_edge, TimingArc *prev_arc, bool is_enum, const StaState *sta); - ~Path(); std::string to_string(const StaState *sta) const; bool isNull() const; // prev_path null void init(Vertex *vertex, - Arrival arrival, + const Arrival &arrival, const StaState *sta); void init(Vertex *vertex, Tag *tag, - Arrival arrival, + const Arrival &arrival, Path *prev_path, Edge *prev_edge, TimingArc *prev_arc, @@ -79,13 +76,16 @@ public: const StaState *sta); void init(Vertex *vertex, Tag *tag, - Arrival arrival, + const Arrival &arrival, const StaState *sta); Vertex *vertex(const StaState *sta) const; VertexId vertexId(const StaState *sta) const; Pin *pin(const StaState *sta) const; Tag *tag(const StaState *sta) const; + Scene *scene(const StaState *sta) const; + Mode *mode(const StaState *sta) const; + Sdc *sdc(const StaState *sta) const; TagIndex tagIndex(const StaState *sta) const; void setTag(Tag *tag); size_t pathIndex(const StaState *sta) const; @@ -96,13 +96,10 @@ public: const RiseFall *transition(const StaState *sta) const; int rfIndex(const StaState *sta) const; const MinMax *minMax(const StaState *sta) const; - PathAnalysisPt *pathAnalysisPt(const StaState *sta) const; PathAPIndex pathAnalysisPtIndex(const StaState *sta) const; - DcalcAnalysisPt *dcalcAnalysisPt(const StaState *sta) const; - Arrival &arrival() { return arrival_; } + DcalcAPIndex dcalcAnalysisPtIndex(const StaState *sta) const; const Arrival &arrival() const { return arrival_; } void setArrival(Arrival arrival); - Required &required() { return required_; } const Required &required() const {return required_; } void setRequired(const Required &required); Slack slack(const StaState *sta) const; @@ -121,6 +118,8 @@ public: void setIsEnum(bool is_enum); void checkPrevPath(const StaState *sta) const; + const MinMax *tgtClkMinMax(const StaState *sta) const; + static Path *vertexPath(const Path *path, const StaState *sta); static Path *vertexPath(const Path &path, @@ -130,34 +129,34 @@ public: const StaState *sta); static bool less(const Path *path1, - const Path *path2, - const StaState *sta); + const Path *path2, + const StaState *sta); static int cmp(const Path *path1, - const Path *path2, - const StaState *sta); + const Path *path2, + const StaState *sta); // Compare all path attributes (vertex, transition, tag, analysis point). static bool equal(const Path *path1, - const Path *path2, - const StaState *sta); + const Path *path2, + const StaState *sta); // Compare pin name and transition and source clock edge. static int cmpPinTrClk(const Path *path1, - const Path *path2, - const StaState *sta); + const Path *path2, + const StaState *sta); // Compare source clock edge. static int cmpClk(const Path *path1, - const Path *path2, - const StaState *sta); + const Path *path2, + const StaState *sta); // Compare vertex, transition, path ap and tag without crpr clk pin. static int cmpNoCrpr(const Path *path1, - const Path *path2, - const StaState *sta); + const Path *path2, + const StaState *sta); // Search back on each path until finding a difference. static int cmpAll(const Path *path1, - const Path *path2, - const StaState *sta); + const Path *path2, + const StaState *sta); static bool lessAll(const Path *path1, - const Path *path2, - const StaState *sta); + const Path *path2, + const StaState *sta); protected: Path *prev_path_; @@ -176,9 +175,9 @@ protected: class PathLess { public: - explicit PathLess(const StaState *sta); + PathLess(const StaState *sta); bool operator()(const Path *path1, - const Path *path2) const; + const Path *path2) const; protected: const StaState *sta_; @@ -190,40 +189,33 @@ class VertexPathIterator : public Iterator public: // Iterate over all vertex paths. VertexPathIterator(Vertex *vertex, - const StaState *sta); - // Iterate over vertex paths with the same transition and - // analysis pt but different tags. + const StaState *sta); VertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap, - const StaState *sta); + const Scene *scene, + const MinMax *min_max, + const RiseFall *rf, + const StaState *sta); // Iterate over vertex paths with the same transition and // analysis pt min/max but different tags. - VertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max, - const StaState *sta); VertexPathIterator(Vertex *vertex, const RiseFall *rf, - const PathAnalysisPt *path_ap, const MinMax *min_max, const StaState *sta); - virtual ~VertexPathIterator(); - virtual bool hasNext(); - virtual Path *next(); + bool hasNext() override; + Path *next() override; private: void findNext(); const Search *search_; - bool filtered_; - const RiseFall *rf_; - const PathAnalysisPt *path_ap_; + const Scene *scene_; const MinMax *min_max_; + const RiseFall *rf_; + bool filtered_; Path *paths_; size_t path_count_; size_t path_index_; Path *next_; }; -} // namespace +} // namespace sta diff --git a/include/sta/PathAnalysisPt.hh b/include/sta/PathAnalysisPt.hh deleted file mode 100644 index 6d6a7e943..000000000 --- a/include/sta/PathAnalysisPt.hh +++ /dev/null @@ -1,69 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include - -#include "Iterator.hh" -#include "MinMax.hh" -#include "SdcClass.hh" -#include "SearchClass.hh" - -namespace sta { - -class MinMax; -class DcalcAnalysisPt; -class Corner; - -class PathAnalysisPt -{ -public: - PathAnalysisPt(Corner *corner, - PathAPIndex index, - const MinMax *path_min_max, - DcalcAnalysisPt *dcalc_ap); - std::string to_string() const; - Corner *corner() const { return corner_; } - PathAPIndex index() const { return index_; } - const MinMax *pathMinMax() const { return path_min_max_; } - // Converging path arrival merging. - const MinMax *mergeMinMax() const { return path_min_max_; } - // Path analysis point for timing check target clock arrivals. - PathAnalysisPt *tgtClkAnalysisPt() const { return tgt_clk_ap_; } - void setTgtClkAnalysisPt(PathAnalysisPt *path_ap); - DcalcAnalysisPt *dcalcAnalysisPt() const { return dcalc_ap_; } - PathAnalysisPt *insertionAnalysisPt(const EarlyLate *early_late) const; - void setInsertionAnalysisPt(const EarlyLate *early_late, PathAnalysisPt *ap); - -private: - Corner *corner_; - PathAPIndex index_; - const MinMax *path_min_max_; - PathAnalysisPt *tgt_clk_ap_; - PathAnalysisPt *insertion_aps_[EarlyLate::index_count]; - DcalcAnalysisPt *dcalc_ap_; -}; - -} // namespace diff --git a/include/sta/PathEnd.hh b/include/sta/PathEnd.hh index c61eb16ae..be54fa735 100644 --- a/include/sta/PathEnd.hh +++ b/include/sta/PathEnd.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,11 +26,11 @@ #include -#include "LibertyClass.hh" #include "GraphClass.hh" +#include "LibertyClass.hh" +#include "Path.hh" #include "SdcClass.hh" #include "SearchClass.hh" -#include "Path.hh" #include "StaState.hh" namespace sta { @@ -59,17 +59,17 @@ class ReportPath; class PathEnd { public: - enum Type { unconstrained, - check, - data_check, - latch_check, - output_delay, - gated_clk, - path_delay + enum class Type { unconstrained, + check, + data_check, + latch_check, + output_delay, + gated_clk, + path_delay }; virtual PathEnd *copy() const = 0; - virtual ~PathEnd() {} + virtual ~PathEnd() = default; void deletePath(); Path *path() { return path_; } const Path *path() const { return path_; } @@ -80,8 +80,6 @@ public: const EarlyLate *pathEarlyLate(const StaState *sta) const; virtual const EarlyLate *clkEarlyLate(const StaState *sta) const; const RiseFall *transition(const StaState *sta) const; - PathAnalysisPt *pathAnalysisPt(const StaState *sta) const; - PathAPIndex pathIndex(const StaState *sta) const; virtual void reportShort(const ReportPath *report) const = 0; virtual void reportFull(const ReportPath *report) const = 0; PathGroup *pathGroup() const { return path_group_; } @@ -89,18 +87,18 @@ public: // Predicates for PathEnd type. // Default methods overridden by respective types. - virtual bool isUnconstrained() const { return false; } - virtual bool isCheck() const { return false; } - virtual bool isDataCheck() const { return false; } - virtual bool isLatchCheck() const { return false; } - virtual bool isOutputDelay() const { return false; } - virtual bool isGatedClock() const { return false; } - virtual bool isPathDelay() const { return false; } + [[nodiscard]] virtual bool isUnconstrained() const { return false; } + [[nodiscard]] virtual bool isCheck() const { return false; } + [[nodiscard]] virtual bool isDataCheck() const { return false; } + [[nodiscard]] virtual bool isLatchCheck() const { return false; } + [[nodiscard]] virtual bool isOutputDelay() const { return false; } + [[nodiscard]] virtual bool isGatedClock() const { return false; } + [[nodiscard]] virtual bool isPathDelay() const { return false; } virtual Type type() const = 0; virtual const char *typeName() const = 0; virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; - virtual Arrival dataArrivalTime(const StaState *sta) const; + const StaState *sta) const; + virtual const Arrival &dataArrivalTime(const StaState *sta) const; // Arrival time with source clock offset. Arrival dataArrivalTimeOffset(const StaState *sta) const; virtual Required requiredTime(const StaState *sta) const = 0; @@ -149,25 +147,28 @@ public: virtual TimingArc *checkArc() const { return nullptr; } // PathEndDataCheck data clock path. virtual const Path *dataClkPath() const { return nullptr; } - virtual int setupDefaultCycles() const { return 1; } virtual Delay clkSkew(const StaState *sta); - virtual bool ignoreClkLatency(const StaState * /* sta */) const { return false; } + [[nodiscard]] virtual bool ignoreClkLatency(const StaState *) const { return false; } static bool less(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta); + const PathEnd *path_end2, + // Compare slack (if constrained), or arrival when false. + bool cmp_slack, + const StaState *sta); static int cmp(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta); + const PathEnd *path_end2, + // Compare slack (if constrained), or arrival when false. + bool cmp_slack, + const StaState *sta); static int cmpSlack(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta); + const PathEnd *path_end2, + const StaState *sta); static int cmpArrival(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta); + const PathEnd *path_end2, + const StaState *sta); static int cmpNoCrpr(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta); + const PathEnd *path_end2, + const StaState *sta); // Helper common to multiple PathEnd classes and used // externally. @@ -177,139 +178,131 @@ public: const TimingRole *check_role, const StaState *sta); static void checkTgtClkDelay(const Path *tgt_clk_path, - const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta, - // Return values. - Delay &insertion, - Delay &latency); + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta, + // Return values. + Delay &insertion, + Delay &latency); static float checkClkUncertainty(const ClockEdge *src_clk_edge, - const ClockEdge *tgt_clk_edge, - const Path *tgt_clk_path, - const TimingRole *check_role, - const StaState *sta); + const ClockEdge *tgt_clk_edge, + const Path *tgt_clk_path, + const TimingRole *check_role, + const Sdc *sdc); // Non inter-clock uncertainty. static float checkTgtClkUncertainty(const Path *tgt_clk_path, const ClockEdge *tgt_clk_edge, const TimingRole *check_role, const StaState *sta); static float checkSetupMcpAdjustment(const ClockEdge *src_clk_edge, - const ClockEdge *tgt_clk_edge, - const MultiCyclePath *mcp, - int default_cycles, - Sdc *sdc); + const ClockEdge *tgt_clk_edge, + const MultiCyclePath *mcp, + int default_cycles, + Sdc *sdc); protected: PathEnd(Path *path); static void checkInterClkUncertainty(const ClockEdge *src_clk_edge, - const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta, - float &uncertainty, - bool &exists); + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const Sdc *sdc, + float &uncertainty, + bool &exists); static float outputDelayMargin(OutputDelay *output_delay, - const Path *path, - const StaState *sta); + const Path *path, + const StaState *sta); static float pathDelaySrcClkOffset(const Path *path, - PathDelay *path_delay, - Arrival src_clk_arrival, - const StaState *sta); + PathDelay *path_delay, + Arrival src_clk_arrival, + const StaState *sta); static bool ignoreClkLatency(const Path *path, PathDelay *path_delay, const StaState *sta); + virtual int setupDefaultCycles() const { return 1; } + Path *path_; - PathGroup *path_group_; + PathGroup *path_group_{nullptr}; }; class PathEndUnconstrained : public PathEnd { public: - explicit PathEndUnconstrained(Path *path); - virtual Type type() const; - virtual const char *typeName() const; - virtual PathEnd *copy() const; - virtual void reportShort(const ReportPath *report) const; - virtual void reportFull(const ReportPath *report) const; - virtual bool isUnconstrained() const; - virtual Required requiredTime(const StaState *sta) const; - virtual Required requiredTimeOffset(const StaState *sta) const; - virtual ArcDelay margin(const StaState *sta) const; - virtual Slack slack(const StaState *sta) const; - virtual Slack slackNoCrpr(const StaState *sta) const; - virtual float sourceClkOffset(const StaState *sta) const; + PathEndUnconstrained(Path *path); + Type type() const override; + const char *typeName() const override; + PathEnd *copy() const override; + void reportShort(const ReportPath *report) const override; + void reportFull(const ReportPath *report) const override; + bool isUnconstrained() const override; + Required requiredTime(const StaState *sta) const override; + Required requiredTimeOffset(const StaState *sta) const override; + ArcDelay margin(const StaState *sta) const override; + Slack slack(const StaState *sta) const override; + Slack slackNoCrpr(const StaState *sta) const override; + float sourceClkOffset(const StaState *sta) const override; }; class PathEndClkConstrained : public PathEnd { public: - virtual float sourceClkOffset(const StaState *sta) const; - virtual Delay sourceClkLatency(const StaState *sta) const; - virtual Delay sourceClkInsertionDelay(const StaState *sta) const; - virtual const Clock *targetClk(const StaState *sta) const; - virtual const ClockEdge *targetClkEdge(const StaState *sta) const; - virtual Path *targetClkPath(); - virtual const Path *targetClkPath() const; - virtual float targetClkTime(const StaState *sta) const; - virtual float targetClkOffset(const StaState *sta) const; - virtual Arrival targetClkArrival(const StaState *sta) const; - virtual Delay targetClkDelay(const StaState *sta) const; - virtual Delay targetClkInsertionDelay(const StaState *sta) const; - virtual float targetNonInterClkUncertainty(const StaState *sta) const; - virtual float interClkUncertainty(const StaState *sta) const; - virtual float targetClkUncertainty(const StaState *sta) const; - virtual Crpr crpr(const StaState *sta) const; - virtual Required requiredTime(const StaState *sta) const; - virtual Slack slack(const StaState *sta) const; - virtual Slack slackNoCrpr(const StaState *sta) const; - virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; - virtual void setPath(Path *path); + float sourceClkOffset(const StaState *sta) const override; + Delay sourceClkLatency(const StaState *sta) const override; + Delay sourceClkInsertionDelay(const StaState *sta) const override; + const Clock *targetClk(const StaState *sta) const override; + const ClockEdge *targetClkEdge(const StaState *sta) const override; + Path *targetClkPath() override; + const Path *targetClkPath() const override; + float targetClkTime(const StaState *sta) const override; + float targetClkOffset(const StaState *sta) const override; + Arrival targetClkArrival(const StaState *sta) const override; + Delay targetClkDelay(const StaState *sta) const override; + Delay targetClkInsertionDelay(const StaState *sta) const override; + float targetNonInterClkUncertainty(const StaState *sta) const override; + float interClkUncertainty(const StaState *sta) const override; + float targetClkUncertainty(const StaState *sta) const override; + Crpr crpr(const StaState *sta) const override; + Required requiredTime(const StaState *sta) const override; + Slack slack(const StaState *sta) const override; + Slack slackNoCrpr(const StaState *sta) const override; + int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const override; + void setPath(Path *path) override; protected: PathEndClkConstrained(Path *path, - Path *clk_path); - PathEndClkConstrained(Path *path, - Path *clk_path, - Crpr crpr, - bool crpr_valid); - + Path *clk_path); float sourceClkOffset(const ClockEdge *src_clk_edge, - const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta) const; + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta) const; // Internal to slackNoCrpr. virtual Arrival targetClkArrivalNoCrpr(const StaState *sta) const; virtual Required requiredTimeNoCrpr(const StaState *sta) const; Path *clk_path_; mutable Crpr crpr_; - mutable bool crpr_valid_; + mutable bool crpr_valid_{false}; }; class PathEndClkConstrainedMcp : public PathEndClkConstrained { public: - virtual MultiCyclePath *multiCyclePath() const { return mcp_; } - virtual float targetClkMcpAdjustment(const StaState *sta) const; - virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; + MultiCyclePath *multiCyclePath() const override { return mcp_; } + float targetClkMcpAdjustment(const StaState *sta) const override; + int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const override; protected: PathEndClkConstrainedMcp(Path *path, - Path *clk_path, - MultiCyclePath *mcp); - PathEndClkConstrainedMcp(Path *path, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid); + Path *clk_path, + MultiCyclePath *mcp); float checkMcpAdjustment(const Path *path, - const ClockEdge *tgt_clk_edge, - const StaState *sta) const; + const ClockEdge *tgt_clk_edge, + const StaState *sta) const; void findHoldMcps(const ClockEdge *tgt_clk_edge, - const MultiCyclePath *&setup_mcp, - const MultiCyclePath *&hold_mcp, - const StaState *sta) const; + const MultiCyclePath *&setup_mcp, + const MultiCyclePath *&hold_mcp, + const StaState *sta) const; MultiCyclePath *mcp_; }; @@ -319,35 +312,28 @@ class PathEndCheck : public PathEndClkConstrainedMcp { public: PathEndCheck(Path *path, - TimingArc *check_arc, - Edge *check_edge, - Path *clk_path, - MultiCyclePath *mcp, - const StaState *sta); - virtual PathEnd *copy() const; - virtual Type type() const; - virtual const char *typeName() const; - virtual void reportShort(const ReportPath *report) const; - virtual void reportFull(const ReportPath *report) const; - virtual bool isCheck() const { return true; } - virtual ArcDelay margin(const StaState *sta) const; - virtual float macroClkTreeDelay(const StaState *sta) const; - virtual const TimingRole *checkRole(const StaState *sta) const; - virtual TimingArc *checkArc() const { return check_arc_; } - virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; - virtual Delay clkSkew(const StaState *sta); + TimingArc *check_arc, + Edge *check_edge, + Path *clk_path, + MultiCyclePath *mcp, + const StaState *sta); + PathEnd *copy() const override; + Type type() const override; + const char *typeName() const override; + void reportShort(const ReportPath *report) const override; + void reportFull(const ReportPath *report) const override; + bool isCheck() const override { return true; } + ArcDelay margin(const StaState *sta) const override; + float macroClkTreeDelay(const StaState *sta) const override; + const TimingRole *checkRole(const StaState *sta) const override; + TimingArc *checkArc() const override { return check_arc_; } + int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const override; + Delay clkSkew(const StaState *sta) override; protected: - PathEndCheck(Path *path, - TimingArc *check_arc, - Edge *check_edge, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid); Delay sourceClkDelay(const StaState *sta) const; - virtual Required requiredTimeNoCrpr(const StaState *sta) const; + Required requiredTimeNoCrpr(const StaState *sta) const override; TimingArc *check_arc_; Edge *check_edge_; @@ -358,62 +344,50 @@ class PathEndLatchCheck : public PathEndCheck { public: PathEndLatchCheck(Path *path, - TimingArc *check_arc, - Edge *check_edge, - Path *disable_path, - MultiCyclePath *mcp, - PathDelay *path_delay, - const StaState *sta); - virtual Type type() const; - virtual const char *typeName() const; - virtual float sourceClkOffset(const StaState *sta) const; - virtual bool isCheck() const { return false; } - virtual bool isLatchCheck() const { return true; } - virtual PathDelay *pathDelay() const { return path_delay_; } - virtual PathEnd *copy() const; + TimingArc *check_arc, + Edge *check_edge, + Path *disable_path, + MultiCyclePath *mcp, + PathDelay *path_delay, + const StaState *sta); + Type type() const override; + const char *typeName() const override; + float sourceClkOffset(const StaState *sta) const override; + bool isCheck() const override { return false; } + bool isLatchCheck() const override { return true; } + PathDelay *pathDelay() const override { return path_delay_; } + PathEnd *copy() const override; Path *latchDisable(); const Path *latchDisable() const; - virtual void reportShort(const ReportPath *report) const; - virtual void reportFull(const ReportPath *report) const; - virtual const TimingRole *checkRole(const StaState *sta) const; - virtual Required requiredTime(const StaState *sta) const; - virtual Arrival borrow(const StaState *sta) const; - virtual float targetClkTime(const StaState *sta) const; - virtual float targetClkOffset(const StaState *sta) const; + void reportShort(const ReportPath *report) const override; + void reportFull(const ReportPath *report) const override; + const TimingRole *checkRole(const StaState *sta) const override; + Required requiredTime(const StaState *sta) const override; + Arrival borrow(const StaState *sta) const override; + float targetClkTime(const StaState *sta) const override; + float targetClkOffset(const StaState *sta) const override; Arrival targetClkWidth(const StaState *sta) const; - virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; + int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const override; void latchRequired(const StaState *sta, - // Return values. - Required &required, - Delay &borrow, - Arrival &adjusted_data_arrival, - Delay &time_given_to_startpoint) const; + // Return values. + Required &required, + Delay &borrow, + Arrival &adjusted_data_arrival, + Delay &time_given_to_startpoint) const; void latchBorrowInfo(const StaState *sta, - // Return values. - float &nom_pulse_width, - Delay &open_latency, - Delay &latency_diff, - float &open_uncertainty, - Crpr &open_crpr, - Crpr &crpr_diff, - Delay &max_borrow, - bool &borrow_limit_exists) const; - virtual bool ignoreClkLatency(const StaState *sta) const; + // Return values. + float &nom_pulse_width, + Delay &open_latency, + Delay &latency_diff, + float &open_uncertainty, + Crpr &open_crpr, + Crpr &crpr_diff, + Delay &max_borrow, + bool &borrow_limit_exists) const; + bool ignoreClkLatency(const StaState *sta) const override; protected: - PathEndLatchCheck(Path *path, - TimingArc *check_arc, - Edge *check_edge, - Path *clk_path, - Path *disable, - MultiCyclePath *mcp, - PathDelay *path_delay, - Delay src_clk_arrival, - Crpr crpr, - bool crpr_valid); - -private: Path *disable_path_; PathDelay *path_delay_; // Source clk arrival for set_max_delay -ignore_clk_latency. @@ -427,42 +401,36 @@ class PathEndOutputDelay : public PathEndClkConstrainedMcp { public: PathEndOutputDelay(OutputDelay *output_delay, - Path *path, - Path *clk_path, - MultiCyclePath *mcp, - const StaState *sta); - virtual PathEnd *copy() const; - virtual Type type() const; - virtual const char *typeName() const; - virtual void reportShort(const ReportPath *report) const; - virtual void reportFull(const ReportPath *report) const; - virtual bool isOutputDelay() const { return true; } - virtual ArcDelay margin(const StaState *sta) const; - virtual const TimingRole *checkRole(const StaState *sta) const; - virtual const ClockEdge *targetClkEdge(const StaState *sta) const; - virtual Arrival targetClkArrivalNoCrpr(const StaState *sta) const; - virtual Delay targetClkDelay(const StaState *sta) const; - virtual Delay targetClkInsertionDelay(const StaState *sta) const; - virtual Crpr crpr(const StaState *sta) const; - virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; + Path *path, + Path *clk_path, + MultiCyclePath *mcp, + const StaState *sta); + PathEnd *copy() const override; + Type type() const override; + const char *typeName() const override; + void reportShort(const ReportPath *report) const override; + void reportFull(const ReportPath *report) const override; + bool isOutputDelay() const override { return true; } + ArcDelay margin(const StaState *sta) const override; + const TimingRole *checkRole(const StaState *sta) const override; + const ClockEdge *targetClkEdge(const StaState *sta) const override; + Delay targetClkDelay(const StaState *sta) const override; + Delay targetClkInsertionDelay(const StaState *sta) const override; + Crpr crpr(const StaState *sta) const override; + int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const override; protected: - PathEndOutputDelay(OutputDelay *output_delay, - Path *path, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid); + Arrival targetClkArrivalNoCrpr(const StaState *sta) const override; Arrival tgtClkDelay(const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta) const; + const TimingRole *check_role, + const StaState *sta) const; void tgtClkDelay(const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta, - // Return values. - Arrival &insertion, - Arrival &latency) const; + const TimingRole *check_role, + const StaState *sta, + // Return values. + Arrival &insertion, + Arrival &latency) const; OutputDelay *output_delay_; }; @@ -472,31 +440,23 @@ class PathEndGatedClock : public PathEndClkConstrainedMcp { public: PathEndGatedClock(Path *gating_ref, - Path *clk_path, - const TimingRole *check_role, - MultiCyclePath *mcp, - ArcDelay margin, - const StaState *sta); - virtual PathEnd *copy() const; - virtual Type type() const; - virtual const char *typeName() const; - virtual void reportShort(const ReportPath *report) const; - virtual void reportFull(const ReportPath *report) const; - virtual bool isGatedClock() const { return true; } - virtual ArcDelay margin(const StaState *) const { return margin_; } - virtual const TimingRole *checkRole(const StaState *sta) const; - virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; + Path *clk_path, + const TimingRole *check_role, + MultiCyclePath *mcp, + ArcDelay margin, + const StaState *sta); + PathEnd *copy() const override; + Type type() const override; + const char *typeName() const override; + void reportShort(const ReportPath *report) const override; + void reportFull(const ReportPath *report) const override; + bool isGatedClock() const override { return true; } + ArcDelay margin(const StaState *) const override { return margin_; } + const TimingRole *checkRole(const StaState *sta) const override; + int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const override; protected: - PathEndGatedClock(Path *gating_ref, - Path *clk_path, - const TimingRole *check_role, - MultiCyclePath *mcp, - ArcDelay margin, - Crpr crpr, - bool crpr_valid); - const TimingRole *check_role_; ArcDelay margin_; }; @@ -505,38 +465,30 @@ class PathEndDataCheck : public PathEndClkConstrainedMcp { public: PathEndDataCheck(DataCheck *check, - Path *data_path, - Path *data_clk_path, - MultiCyclePath *mcp, - const StaState *sta); - virtual PathEnd *copy() const; - virtual Type type() const; - virtual const char *typeName() const; - virtual void reportShort(const ReportPath *report) const; - virtual void reportFull(const ReportPath *report) const; - virtual bool isDataCheck() const { return true; } - virtual const ClockEdge *targetClkEdge(const StaState *sta) const; - virtual const TimingRole *checkRole(const StaState *sta) const; - virtual ArcDelay margin(const StaState *sta) const; - virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; - virtual const Path *dataClkPath() const { return data_clk_path_; } + Path *data_path, + Path *data_clk_path, + MultiCyclePath *mcp, + const StaState *sta); + PathEnd *copy() const override; + Type type() const override; + const char *typeName() const override; + void reportShort(const ReportPath *report) const override; + void reportFull(const ReportPath *report) const override; + bool isDataCheck() const override { return true; } + const ClockEdge *targetClkEdge(const StaState *sta) const override; + const TimingRole *checkRole(const StaState *sta) const override; + ArcDelay margin(const StaState *sta) const override; + int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const override; + const Path *dataClkPath() const override { return data_clk_path_; } protected: - PathEndDataCheck(DataCheck *check, - Path *data_path, - Path *data_clk_path, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid); Path *clkPath(Path *path, const StaState *sta); - Arrival requiredTimeNoCrpr(const StaState *sta) const; + Arrival requiredTimeNoCrpr(const StaState *sta) const override; // setup uses zero cycle default - virtual int setupDefaultCycles() const { return 0; } + int setupDefaultCycles() const override { return 0; } -private: Path *data_clk_path_; DataCheck *check_; }; @@ -549,52 +501,43 @@ class PathEndPathDelay : public PathEndClkConstrained public: // Vanilla path delay. PathEndPathDelay(PathDelay *path_delay, - Path *path, - const StaState *sta); + Path *path, + const StaState *sta); // Path delay to timing check. PathEndPathDelay(PathDelay *path_delay, - Path *path, - Path *clk_path, - TimingArc *check_arc, - Edge *check_edge, - const StaState *sta); + Path *path, + Path *clk_path, + TimingArc *check_arc, + Edge *check_edge, + const StaState *sta); // Path delay to output with set_output_delay. PathEndPathDelay(PathDelay *path_delay, - Path *path, - OutputDelay *output_delay, - const StaState *sta); - virtual PathEnd *copy() const; - virtual Type type() const; - virtual const char *typeName() const; - virtual void reportShort(const ReportPath *report) const; - virtual void reportFull(const ReportPath *report) const; - virtual bool isPathDelay() const { return true; } - virtual const TimingRole *checkRole(const StaState *sta) const; - virtual bool pathDelayMarginIsExternal() const; - virtual PathDelay *pathDelay() const { return path_delay_; } - virtual ArcDelay margin(const StaState *sta) const; - virtual float sourceClkOffset(const StaState *sta) const; - virtual const ClockEdge *targetClkEdge(const StaState *sta) const; - virtual float targetClkTime(const StaState *sta) const; - virtual Arrival targetClkArrivalNoCrpr(const StaState *sta) const; - virtual float targetClkOffset(const StaState *sta) const; - virtual TimingArc *checkArc() const { return check_arc_; } - virtual Required requiredTime(const StaState *sta) const; - virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; - bool hasOutputDelay() const { return output_delay_ != nullptr; } - virtual bool ignoreClkLatency(const StaState *sta) const; + Path *path, + OutputDelay *output_delay, + const StaState *sta); + PathEnd *copy() const override; + Type type() const override; + const char *typeName() const override; + void reportShort(const ReportPath *report) const override; + void reportFull(const ReportPath *report) const override; + bool isPathDelay() const override { return true; } + const TimingRole *checkRole(const StaState *sta) const override; + bool pathDelayMarginIsExternal() const override; + PathDelay *pathDelay() const override { return path_delay_; } + ArcDelay margin(const StaState *sta) const override; + float sourceClkOffset(const StaState *sta) const override; + const ClockEdge *targetClkEdge(const StaState *sta) const override; + float targetClkTime(const StaState *sta) const override; + float targetClkOffset(const StaState *sta) const override; + TimingArc *checkArc() const override { return check_arc_; } + Required requiredTime(const StaState *sta) const override; + int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const override; + [[nodiscard]] bool hasOutputDelay() const { return output_delay_ != nullptr; } + bool ignoreClkLatency(const StaState *sta) const override; protected: - PathEndPathDelay(PathDelay *path_delay, - Path *path, - Path *clk_path, - TimingArc *check_arc, - Edge *check_edge, - OutputDelay *output_delay, - Arrival src_clk_arrival, - Crpr crpr, - bool crpr_valid); + Arrival targetClkArrivalNoCrpr(const StaState *sta) const override; void findSrcClkArrival(const StaState *sta); PathDelay *path_delay_; @@ -613,11 +556,13 @@ protected: class PathEndLess { public: - explicit PathEndLess(const StaState *sta); + PathEndLess(bool cmp_slack, + const StaState *sta); bool operator()(const PathEnd *path_end1, - const PathEnd *path_end2) const; + const PathEnd *path_end2) const; protected: + bool cmp_slack_; const StaState *sta_; }; @@ -625,23 +570,25 @@ protected: class PathEndSlackLess { public: - explicit PathEndSlackLess(const StaState *sta); + PathEndSlackLess(bool cmp_slack, + const StaState *sta); bool operator()(const PathEnd *path_end1, - const PathEnd *path_end2) const; + const PathEnd *path_end2) const; protected: + bool cmp_slack_; const StaState *sta_; }; class PathEndNoCrprLess { public: - explicit PathEndNoCrprLess(const StaState *sta); + PathEndNoCrprLess(const StaState *sta); bool operator()(const PathEnd *path_end1, - const PathEnd *path_end2) const; + const PathEnd *path_end2) const; protected: const StaState *sta_; }; -} // namespace +} // namespace sta diff --git a/include/sta/PathExpanded.hh b/include/sta/PathExpanded.hh index 784409bf8..2a54e2a83 100644 --- a/include/sta/PathExpanded.hh +++ b/include/sta/PathExpanded.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,11 +24,11 @@ #pragma once -#include "TimingArc.hh" #include "GraphClass.hh" -#include "SearchClass.hh" #include "Path.hh" +#include "SearchClass.hh" #include "StaState.hh" +#include "TimingArc.hh" namespace sta { @@ -38,13 +38,13 @@ public: PathExpanded(const StaState *sta); // Expand path for lookup by index. PathExpanded(const Path *path, - const StaState *sta); + const StaState *sta); PathExpanded(const Path *path, - // Expand generated clk source paths. - bool expand_genclks, - const StaState *sta); + // Expand generated clk source paths. + bool expand_genclks, + const StaState *sta); void expand(const Path *path, - bool expand_genclks); + bool expand_genclks); size_t size() const { return paths_.size(); } // path(0) is the startpoint. // path(size()-1) is the endpoint. @@ -59,9 +59,9 @@ public: size_t startIndex() const; const Path *clkPath() const; void latchPaths(// Return values. - const Path *&d_path, - const Path *&q_path, - Edge *&d_q_edge) const; + const Path *&d_path, + const Path *&q_path, + Edge *&d_q_edge) const; protected: void expandGenclk(const Path *clk_path); @@ -78,4 +78,4 @@ protected: const StaState *sta_; }; -} // namespace +} // namespace sta diff --git a/include/sta/PathGroup.hh b/include/sta/PathGroup.hh index cc84f9ea2..df1800168 100644 --- a/include/sta/PathGroup.hh +++ b/include/sta/PathGroup.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,78 +24,80 @@ #pragma once +#include #include +#include +#include +#include -#include "Map.hh" -#include "Vector.hh" +#include "PathEnd.hh" #include "SdcClass.hh" -#include "StaState.hh" #include "SearchClass.hh" +#include "StaState.hh" +#include "StringUtil.hh" namespace sta { class MinMax; class PathEndVisitor; -typedef PathEndSeq::Iterator PathGroupIterator; -typedef Map PathGroupClkMap; -typedef Map PathGroupNamedMap; -typedef std::vector PathGroupSeq; -typedef std::vector StdStringSeq; +using PathGroupIterator = PathEndSeq::iterator; +using PathGroupClkMap = std::map; +using PathGroupNamedMap = std::map; +using PathGroupSeq = std::vector; // A collection of PathEnds grouped and sorted for reporting. class PathGroup { public: - ~PathGroup(); // Path group that compares compare slacks. - static PathGroup *makePathGroupArrival(const char *name, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - const MinMax *min_max, - const StaState *sta); + static PathGroup *makePathGroupArrival(std::string_view name, + size_t group_path_count, + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + const MinMax *min_max, + const StaState *sta); // Path group that compares arrival time, sorted by min_max. - static PathGroup *makePathGroupSlack(const char *name, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float min_slack, - float max_slack, - const StaState *sta); - const char *name() const { return name_; } + static PathGroup *makePathGroupSlack(std::string_view name, + size_t group_path_count, + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + const StaState *sta); + ~PathGroup(); + const std::string &name() const { return name_; } const MinMax *minMax() const { return min_max_;} - const PathEndSeq &pathEnds() const { return path_ends_; } + PathEndSeq pathEnds() const { return path_ends_; } void insert(PathEnd *path_end); // Push group_path_count into path_ends. void pushEnds(PathEndSeq &path_ends); // Predicate to determine if a PathEnd is worth saving. bool saveable(PathEnd *path_end); bool enumMinSlackUnderMin(PathEnd *path_end); - int maxPaths() const { return group_path_count_; } - PathGroupIterator *iterator(); + size_t maxPaths() const { return group_path_count_; } // This does NOT delete the path ends. void clear(); static size_t group_path_count_max; protected: - PathGroup(const char *name, - size_t group_path_count, - size_t endpoint_path_count, - bool unique_pins, - bool unique_edges, - float min_slack, - float max_slack, - bool cmp_slack, - const MinMax *min_max, - const StaState *sta); + PathGroup(std::string_view name, + size_t group_path_count, + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + bool cmp_slack, + const MinMax *min_max, + const StaState *sta); void ensureSortedMaxPaths(); void prune(); void sort(); - const char *name_; + std::string name_; size_t group_path_count_; size_t endpoint_path_count_; bool unique_pins_; @@ -104,8 +106,9 @@ protected: float slack_max_; PathEndSeq path_ends_; const MinMax *min_max_; - bool compare_slack_; + bool cmp_slack_; float threshold_; + std::mutex lock_; const StaState *sta_; }; @@ -113,85 +116,91 @@ protected: class PathGroups : public StaState { public: - PathGroups(int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - PathGroupNameSet *group_names, - bool setup, - bool hold, - bool recovery, - bool removal, - bool clk_gating_setup, - bool clk_gating_hold, - bool unconstrained, - const StaState *sta); - ~PathGroups(); - // Use corner nullptr to make PathEnds for all corners. + PathGroups(size_t group_path_count, + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + StringSeq &group_names, + bool setup, + bool hold, + bool recovery, + bool removal, + bool clk_gating_setup, + bool clk_gating_hold, + bool unconstrained, + const Mode *mode); + ~PathGroups() override; + // Use scene nullptr to make PathEnds for all scenes. // The PathEnds in the vector are owned by the PathGroups. - PathEndSeq makePathEnds(ExceptionTo *to, - bool unconstrained_paths, - const Corner *corner, - const MinMaxAll *min_max, - bool sort_by_slack); - PathGroup *findPathGroup(const char *name, - const MinMax *min_max) const; + void makePathEnds(ExceptionTo *to, + const SceneSeq &scenes, + const MinMaxAll *min_max, + bool sort_by_slack, + bool unconstrained_paths, + // Return value. + PathEndSeq &path_ends); + PathGroup *findPathGroup(const std::string &name, + const MinMax *min_max) const; PathGroup *findPathGroup(const Clock *clock, - const MinMax *min_max) const; + const MinMax *min_max) const; PathGroupSeq pathGroups(const PathEnd *path_end) const; - static StdStringSeq pathGroupNames(const PathEnd *path_end, - const StaState *sta); - static const char *asyncPathGroupName() { return async_group_name_; } - static const char *pathDelayGroupName() { return path_delay_group_name_; } - static const char *gatedClkGroupName() { return gated_clk_group_name_; } - static const char *unconstrainedGroupName() { return unconstrained_group_name_; } + static StringSeq pathGroupNames(const PathEnd *path_end, + 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_; } + static std::string_view unconstrainedGroupName() { return unconstrained_group_name_; } protected: void makeGroupPathEnds(ExceptionTo *to, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - const Corner *corner, - const MinMaxAll *min_max); + size_t group_path_count, + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + const SceneSeq &scenes, + const MinMaxAll *min_max); void makeGroupPathEnds(ExceptionTo *to, - const Corner *corner, - const MinMaxAll *min_max, - PathEndVisitor *visitor); - void makeGroupPathEnds(VertexSet *endpoints, - const Corner *corner, - const MinMaxAll *min_max, - PathEndVisitor *visitor); + const SceneSeq &scenes, + const MinMaxAll *min_max, + PathEndVisitor *visitor); + void makeGroupPathEnds(VertexSet &endpoints, + const SceneSeq &scenes, + const MinMaxAll *min_max, + PathEndVisitor *visitor); void enumPathEnds(PathGroup *group, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - bool cmp_slack); + size_t group_path_count, + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + bool cmp_slack); - void pushGroupPathEnds(PathEndSeq &path_ends); + void pushEnds(PathEndSeq &path_ends); void pushUnconstrainedPathEnds(PathEndSeq &path_ends, - const MinMaxAll *min_max); - - void makeGroups(int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - PathGroupNameSet *group_names, - bool setup_hold, - bool async, - bool gated_clk, - bool unconstrained, - const MinMax *min_max); - bool reportGroup(const char *group_name, - PathGroupNameSet *group_names) const; - - int group_path_count_; - int endpoint_path_count_; + const MinMaxAll *min_max); + + void makeGroups(size_t group_path_count, + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + StringSet &group_names, + bool setup_hold, + bool async, + bool gated_clk, + bool unconstrained, + const MinMax *min_max); + bool reportGroup(const std::string &group_name, + StringSet &group_names) const; + static GroupPath *groupPathTo(const PathEnd *path_end, + const StaState *sta); + StringSeq pathGroupNames(); + + const Mode *mode_; + size_t group_path_count_; + size_t endpoint_path_count_; bool unique_pins_; bool unique_edges_; float slack_min_; @@ -211,10 +220,10 @@ protected: // Unconstrained paths. PathGroup *unconstrained_[MinMax::index_count]; - static const char *path_delay_group_name_; - static const char *gated_clk_group_name_; - static const char *async_group_name_; - static const char *unconstrained_group_name_; + static constexpr std::string_view path_delay_group_name_ = "path delay"; + static constexpr std::string_view gated_clk_group_name_ = "gated clock"; + static constexpr std::string_view async_group_name_ = "asynchronous"; + static constexpr std::string_view unconstrained_group_name_ = "unconstrained"; }; -} // namespace +} // namespace sta diff --git a/include/sta/PatternMatch.hh b/include/sta/PatternMatch.hh index 3531b8a41..0fbed7e97 100644 --- a/include/sta/PatternMatch.hh +++ b/include/sta/PatternMatch.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,12 +25,13 @@ #pragma once #include +#include #include "Error.hh" // Don't require all of tcl.h. -typedef struct Tcl_RegExp_ *Tcl_RegExp; -typedef struct Tcl_Interp Tcl_Interp; +using Tcl_RegExp = struct Tcl_RegExp_ *; +using Tcl_Interp = struct Tcl_Interp; namespace sta { @@ -45,20 +46,17 @@ public: // Regular expressions are always anchored. // If nocase is true, ignore case in the pattern. // Tcl_Interp is optional for reporting regexp compile errors. - PatternMatch(const char *pattern, - bool is_regexp, - bool nocase, - Tcl_Interp *interp); + PatternMatch(std::string_view pattern, + bool is_regexp, + bool nocase, + Tcl_Interp *interp); // Use unix glob style matching. - PatternMatch(const char *pattern); - PatternMatch(const char *pattern, - const PatternMatch *inherit_from); - PatternMatch(const std::string &pattern, - const PatternMatch *inherit_from); - bool match(const char *str) const; - bool match(const std::string &str) const; - bool matchNoCase(const char *str) const; - const char *pattern() const { return pattern_; } + PatternMatch(std::string_view pattern); + PatternMatch(std::string_view pattern, + const PatternMatch *inherit_from); + bool match(std::string_view str) const; + bool matchNoCase(std::string_view str) const; + const std::string &pattern() const { return pattern_; } bool isRegexp() const { return is_regexp_; } bool nocase() const { return nocase_; } Tcl_Interp *tclInterp() const { return interp_; } @@ -67,7 +65,7 @@ public: private: void compileRegexp(); - const char *pattern_; + std::string pattern_; bool is_regexp_; bool nocase_; Tcl_Interp *interp_; @@ -78,9 +76,9 @@ private: class RegexpCompileError : public Exception { public: - explicit RegexpCompileError(const char *pattern); - virtual ~RegexpCompileError() noexcept {} - virtual const char *what() const noexcept; + RegexpCompileError(std::string_view pattern); + ~RegexpCompileError() noexcept override = default; + const char *what() const noexcept override; private: std::string error_; @@ -90,14 +88,14 @@ private: // '*' matches zero or more characters // '?' matches any character bool -patternMatch(const char *pattern, - const char *str); +patternMatch(std::string_view pattern, + std::string_view str); bool -patternMatchNoCase(const char *pattern, - const char *str, - bool nocase); +patternMatchNoCase(std::string_view pattern, + std::string_view str, + bool nocase); // Predicate to find out if there are wildcard characters in the pattern. bool -patternWildcards(const char *pattern); +patternWildcards(std::string_view pattern); -} // namespace +} // namespace sta diff --git a/include/sta/PinPair.hh b/include/sta/PinPair.hh index 7d3f27116..69e759fec 100644 --- a/include/sta/PinPair.hh +++ b/include/sta/PinPair.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,27 +24,28 @@ #pragma once +#include + #include "Hash.hh" -#include "Set.hh" #include "NetworkClass.hh" namespace sta { -typedef std::pair PinPair; +using PinPair = std::pair; class PinPairLess { public: PinPairLess(const Network *network); bool operator()(const PinPair &pair1, - const PinPair &pair2) const; + const PinPair &pair2) const; private: const Network *network_; }; -class PinPairSet : public Set +class PinPairSet : public std::set { public: PinPairSet(const Network *network); @@ -64,7 +65,7 @@ class PinPairEqual { public: bool operator()(const PinPair &pair1, - const PinPair &pair2) const; + const PinPair &pair2) const; }; -} // namespace +} // namespace sta diff --git a/include/sta/StringSeq.hh b/include/sta/PocvMode.hh similarity index 81% rename from include/sta/StringSeq.hh rename to include/sta/PocvMode.hh index c12044696..d740234db 100644 --- a/include/sta/StringSeq.hh +++ b/include/sta/PocvMode.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,15 +24,15 @@ #pragma once -#include "StringUtil.hh" -#include "Vector.hh" +#include namespace sta { -typedef Vector StringSeq; -typedef std::vector StdStringSeq; +enum class PocvMode { scalar, normal, skew_normal }; -void -deleteContents(StringSeq *strings); +const std::string & +pocvModeName(PocvMode mode); +PocvMode +findPocvMode(std::string_view mode_name); -} // namespace +} // namespace sta diff --git a/include/sta/PortDelay.hh b/include/sta/PortDelay.hh index 4220bf7cc..ab71151a7 100644 --- a/include/sta/PortDelay.hh +++ b/include/sta/PortDelay.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,8 @@ #pragma once +#include + #include "RiseFallMinMax.hh" #include "SdcClass.hh" @@ -31,7 +33,7 @@ namespace sta { class PortDelay; -typedef Vector PortDelaySeq; +using PortDelaySeq = std::vector; // set_input_delay arrival, set_output_delay departure class PortDelay @@ -52,14 +54,14 @@ public: protected: PortDelay(const Pin *pin, - const ClockEdge *clk_edge, + const ClockEdge *clk_edge, const Network *network); const Pin *pin_; const ClockEdge *clk_edge_; - bool source_latency_included_; - bool network_latency_included_; - const Pin *ref_pin_; + bool source_latency_included_{false}; + bool network_latency_included_{false}; + const Pin *ref_pin_{nullptr}; RiseFallMinMax delays_; PinSet leaf_pins_; }; @@ -71,9 +73,9 @@ public: protected: InputDelay(const Pin *pin, - const ClockEdge *clk_edge, - int index, - const Network *network); + const ClockEdge *clk_edge, + int index, + const Network *network); private: int index_; @@ -87,8 +89,8 @@ public: protected: OutputDelay(const Pin *pin, - const ClockEdge *clk_edge, - const Network *network); + const ClockEdge *clk_edge, + const Network *network); private: friend class Sdc; @@ -98,12 +100,12 @@ private: class PortDelayLess { public: - explicit PortDelayLess(const Network *network); + PortDelayLess(const Network *network); bool operator()(const PortDelay *delay1, - const PortDelay *delay2) const; + const PortDelay *delay2) const; private: const Network *network_; }; -} // namespace +} // namespace sta diff --git a/include/sta/PortDirection.hh b/include/sta/PortDirection.hh index 186914a9b..72d4f074b 100644 --- a/include/sta/PortDirection.hh +++ b/include/sta/PortDirection.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -41,10 +41,11 @@ public: static PortDirection *internal() { return internal_; } static PortDirection *ground() { return ground_; } static PortDirection *power() { return power_; } + static PortDirection *well() { return well_; } static PortDirection *unknown() { return unknown_; } static PortDirection *find(const char *dir_name); - const char *name() const { return name_; } - int index() const { return index_; } + std::string_view name() const { return name_; } + size_t index() const { return index_; } bool isInput() const { return this == input_; } // Input or bidirect. bool isAnyInput() const; @@ -57,17 +58,18 @@ public: bool isAnyTristate() const; bool isGround() const { return this == ground_; } bool isPower() const { return this == power_; } - // Ground or power. + bool isWell() const { return this == well_; } + // Ground, power, or well. bool isPowerGround() const; bool isInternal() const { return this == internal_; } bool isUnknown() const { return this == unknown_; } private: PortDirection(const char *name, - int index); + size_t index); const char *name_; - int index_; + size_t index_; static PortDirection *input_; static PortDirection *output_; @@ -76,7 +78,8 @@ private: static PortDirection *internal_; static PortDirection *ground_; static PortDirection *power_; + static PortDirection *well_; static PortDirection *unknown_; }; -} // namespace +} // namespace sta diff --git a/include/sta/PortExtCap.hh b/include/sta/PortExtCap.hh index 2bad21c9a..189ac33e8 100644 --- a/include/sta/PortExtCap.hh +++ b/include/sta/PortExtCap.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,52 +25,54 @@ #pragma once #include "MinMax.hh" -#include "Transition.hh" -#include "RiseFallMinMax.hh" #include "MinMaxValues.hh" #include "NetworkClass.hh" +#include "RiseFallMinMax.hh" +#include "Transition.hh" namespace sta { -typedef MinMaxIntValues FanoutValues; +using FanoutValues = MinMaxIntValues; // Port external pin and wire capacitance (set_load -pin_load -wire_load). class PortExtCap { public: - PortExtCap(const Port *port); const Port *port() { return port_; } void pinCap(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &cap, - bool &exists); - RiseFallMinMax *pinCap() { return &pin_cap_; } - void setPinCap(float cap, - const RiseFall *rf, - const MinMax *min_max); + const MinMax *min_max, + // Return values. + float &cap, + bool &exists) const; + const RiseFallMinMax *pinCap() const { return &pin_cap_; } + void setPinCap(const Port *port, + float cap, + const RiseFall *rf, + const MinMax *min_max); void wireCap(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &cap, - bool &exists); - RiseFallMinMax *wireCap() { return &wire_cap_; } - void setWireCap(float cap, - const RiseFall *rf, - const MinMax *min_max); - void setFanout(int fanout, - const MinMax *min_max); + const MinMax *min_max, + // Return values. + float &cap, + bool &exists) const; + const RiseFallMinMax *wireCap() const { return &wire_cap_; } + void setWireCap(const Port *port, + float cap, + const RiseFall *rf, + const MinMax *min_max); + void setFanout(const Port *port, + int fanout, + const MinMax *min_max); void fanout(const MinMax *min_max, - // Return values. - int &fanout, - bool &exists); - FanoutValues *fanout() { return &fanout_; } + // Return values. + int &fanout, + bool &exists) const; + const FanoutValues *fanout() const { return &fanout_; } private: - const Port *port_; + const Port *port_{nullptr}; RiseFallMinMax pin_cap_; RiseFallMinMax wire_cap_; FanoutValues fanout_; }; -} // namespace +} // namespace sta diff --git a/include/sta/PowerClass.hh b/include/sta/PowerClass.hh index 357d8767d..f94cf6adb 100644 --- a/include/sta/PowerClass.hh +++ b/include/sta/PowerClass.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,9 +24,13 @@ #pragma once +#include +#include + namespace sta { class Power; +class Instance; enum class PwrActivityOrigin { @@ -38,17 +42,16 @@ enum class PwrActivityOrigin propagated, clock, constant, - defaulted, unknown }; class PwrActivity { public: - PwrActivity(); + PwrActivity() = default; PwrActivity(float density, - float duty, - PwrActivityOrigin origin); + float duty, + PwrActivityOrigin origin); void init(); float density() const { return density_; } void setDensity(float density); @@ -56,18 +59,18 @@ public: void setDuty(float duty); PwrActivityOrigin origin() const { return origin_; } void setOrigin(PwrActivityOrigin origin); - const char *originName() const; + const std::string &originName() const; void set(float density, - float duty, - PwrActivityOrigin origin); + float duty, + PwrActivityOrigin origin); bool isSet() const; private: void check(); - float density_; // transitions / second - float duty_; // probability signal is high - PwrActivityOrigin origin_; + float density_{0.0}; // transitions / second + float duty_{0.0}; // probability signal is high + PwrActivityOrigin origin_{PwrActivityOrigin::unknown}; static constexpr float min_density = 1E-10; }; @@ -75,7 +78,7 @@ private: class PowerResult { public: - PowerResult(); + PowerResult() = default; void clear(); float internal() const { return internal_; } float inputinternal() const { return inputinternal_; } @@ -91,11 +94,14 @@ public: void incrLeakage(float pwr); private: - float internal_; - float inputinternal_; - float outputinternal_; - float switching_; - float leakage_; + float internal_{0.0}; + float inputinternal_{0.0}; + float outputinternal_{0.0}; + float switching_{0.0}; + float leakage_{0.0}; }; -} // namespace +using InstPower = std::pair; +using InstPowers = std::vector; + +} // namespace sta diff --git a/include/sta/Property.hh b/include/sta/Property.hh index 0a22543fc..0e5d42440 100644 --- a/include/sta/Property.hh +++ b/include/sta/Property.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,15 +24,18 @@ #pragma once +#include #include #include -#include +#include +#include "Error.hh" +#include "Format.hh" #include "LibertyClass.hh" #include "NetworkClass.hh" -#include "SearchClass.hh" -#include "SdcClass.hh" #include "PowerClass.hh" +#include "SdcClass.hh" +#include "SearchClass.hh" namespace sta { @@ -40,108 +43,112 @@ class Sta; class PropertyValue; class Sta; + +class PropertyUnknown : public Exception +{ +public: + PropertyUnknown(std::string_view type, + std::string_view property) : + msg_(sta::format("{} objects do not have a {} property.", type, property)) + {} + const char *what() const noexcept override { return msg_.c_str(); } +private: + std::string msg_; +}; class PropertyValue; template class PropertyRegistry { public: - typedef std::function PropertyHandler; - void defineProperty(const std::string &property, + using PropertyHandler = std::function; + void defineProperty(std::string_view property, PropertyHandler handler); PropertyValue getProperty(TYPE object, - const std::string &property, + std::string_view property, + std::string_view type_name, Sta *sta); private: - std::map registry_; + std::map> registry_; }; class Properties { public: Properties(Sta *sta); - virtual ~Properties() {} PropertyValue getProperty(const Library *lib, - const std::string property); + std::string_view property); PropertyValue getProperty(const LibertyLibrary *lib, - const std::string property); + std::string_view property); PropertyValue getProperty(const Cell *cell, - const std::string property); + std::string_view property); PropertyValue getProperty(const LibertyCell *cell, - const std::string property); + std::string_view property); PropertyValue getProperty(const Port *port, - const std::string property); + std::string_view property); PropertyValue getProperty(const LibertyPort *port, - const std::string property); + std::string_view property); PropertyValue getProperty(const Instance *inst, - const std::string property); + std::string_view property); PropertyValue getProperty(const Pin *pin, - const std::string property); + std::string_view property); PropertyValue getProperty(const Net *net, - const std::string property); + std::string_view property); PropertyValue getProperty(Edge *edge, - const std::string property); + std::string_view property); PropertyValue getProperty(const Clock *clk, - const std::string property); + std::string_view property); PropertyValue getProperty(PathEnd *end, - const std::string property); + std::string_view property); PropertyValue getProperty(Path *path, - const std::string property); + std::string_view property); PropertyValue getProperty(TimingArcSet *arc_set, - const std::string property); + std::string_view property); // Define handler for external property. // properties->defineProperty("foo", // [] (const Instance *, Sta *) -> PropertyValue { // return PropertyValue("bar"); // }); - void defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, - 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); + 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); + 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); + void defineProperty(std::string_view property, + const PropertyRegistry::PropertyHandler &handler); protected: PropertyValue portSlew(const Port *port, - const MinMax *min_max); - PropertyValue portSlew(const Port *port, - const RiseFall *rf, + const RiseFallBoth *rf, const MinMax *min_max); PropertyValue portSlack(const Port *port, - const MinMax *min_max); - PropertyValue portSlack(const Port *port, - const RiseFall *rf, + const RiseFallBoth *rf, const MinMax *min_max); PropertyValue pinArrival(const Pin *pin, - const RiseFall *rf, + const RiseFallBoth *rf, const MinMax *min_max); PropertyValue pinSlack(const Pin *pin, - const MinMax *min_max); - PropertyValue pinSlack(const Pin *pin, - const RiseFall *rf, + const RiseFallBoth *rf, const MinMax *min_max); PropertyValue pinSlew(const Pin *pin, - const MinMax *min_max); - PropertyValue pinSlew(const Pin *pin, - const RiseFall *rf, + const RiseFallBoth *rf, const MinMax *min_max); PropertyValue delayPropertyValue(Delay delay); @@ -166,7 +173,7 @@ protected: }; // Adding a new property type -// value union +// value union (string values use std::string* so the union stays trivial) // enum Type // constructor // copy constructor switch clause @@ -180,18 +187,19 @@ protected: class PropertyValue { public: - enum Type { type_none, type_string, type_float, type_bool, - type_library, type_cell, type_port, - type_liberty_library, type_liberty_cell, type_liberty_port, - type_instance, type_pin, type_pins, type_net, - type_clk, type_clks, type_paths, type_pwr_activity }; + 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 }; static const char *type_name(Type type); PropertyValue(); PropertyValue(const char *value); - PropertyValue(std::string &value); + PropertyValue(std::string_view value); + PropertyValue(std::string value); PropertyValue(float value, const Unit *unit); - explicit PropertyValue(bool value); + PropertyValue(bool value); PropertyValue(const Library *value); PropertyValue(const Cell *value); PropertyValue(const Port *value); @@ -210,15 +218,15 @@ public: PropertyValue(ConstPathSeq *value); PropertyValue(PwrActivity *value); // Copy constructor. - PropertyValue(const PropertyValue &props); + PropertyValue(const PropertyValue &value); // Move constructor. - PropertyValue(PropertyValue &&props); + 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 char *stringValue() const; // valid for type string + 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_; } @@ -239,15 +247,18 @@ public: // Copy assignment. PropertyValue &operator=(const PropertyValue &); // Move assignment. - PropertyValue &operator=(PropertyValue &&); + PropertyValue &operator=(PropertyValue &&) noexcept; // Comparison int compare(const PropertyValue &rhs, const Network *network, bool natural = false) const; private: + void destroyActive(); + Type type_; union { - const char *string_; + // Use heap string to simplify initialization/destrucction. + std::string *string_; float float_; bool bool_; const Library *library_; @@ -268,4 +279,4 @@ private: const Unit *unit_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Report.hh b/include/sta/Report.hh index 7d2aebe91..c835a8eef 100644 --- a/include/sta/Report.hh +++ b/include/sta/Report.hh @@ -1,41 +1,49 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #pragma once -#include #include -#include #include #include +#include +#include +#include -#include "Machine.hh" // __attribute__ +#include "Format.hh" +#include "Machine.hh" // __attribute__ struct Tcl_Interp; namespace sta { +// Throws ExceptionMsg - implemented in Report.cc to avoid circular include with +// Error.hh +void +reportThrowExceptionMsg(const std::string &msg, + bool suppressed); + // Output streams used for printing. // This is a wrapper for all printing. It supports logging output to // a file and redirection of command output to a file. @@ -43,76 +51,138 @@ class Report { public: Report(); - virtual ~Report(); - - // Print line with return. - virtual void reportLine(const char *fmt, ...) - __attribute__((format (printf, 2, 3))); - virtual void reportLineString(const char *line); - virtual void reportLineString(const std::string &line); + virtual ~Report() = default; + virtual void reportLine(const std::string &line); virtual void reportBlankLine(); + // Print formatted line using std::format. + template + void report(std::string_view fmt, + Args &&...args) + { + reportMsg(sta::vformat(fmt, sta::make_format_args(args...))); + } + virtual void reportMsg(const std::string &formatted_msg) + { + reportLine(formatted_msg); + } + //////////////////////////////////////////////////////////////// // Report warning. - virtual void warn(int id, - const char *fmt, ...) - __attribute__((format (printf, 3, 4))); - virtual void vwarn(int id, - const char *fmt, - va_list args); + template + void warn(int id, + std::string_view fmt, + Args &&...args) + { + if (!isSuppressed(id)) + warnMsg(id, sta::vformat(fmt, sta::make_format_args(args...))); + } + virtual void warnMsg(int id, + const std::string &formatted_msg) { + reportLine(sta::format("Warning {}: {}", id, formatted_msg)); + } + // Report warning in a file. - virtual void fileWarn(int id, - const char *filename, - int line, - const char *fmt, ...) - __attribute__((format (printf, 5, 6))); - virtual void vfileWarn(int id, - const char *filename, - int line, - const char *fmt, - va_list args); - - virtual void error(int id, - const char *fmt, ...) - __attribute__((format (printf, 3, 4))); - virtual void verror(int id, - const char *fmt, - va_list args); + template + void fileWarn(int id, + std::string_view filename, + int line, + std::string_view fmt, + Args &&...args) + { + if (!isSuppressed(id)) { + fileWarnMsg(id, filename, line, + sta::vformat(fmt, sta::make_format_args(args...))); + } + } + virtual void + fileWarnMsg(int id, + std::string_view filename, + int line, + const std::string &formatted_msg) { + reportLine(sta::format("Warning {}: {} line {}, {}", + id, filename, line, formatted_msg)); + } + + template + void error(int id, + std::string_view fmt, + Args &&...args) + { + errorMsg(id, sta::vformat(fmt, sta::make_format_args(args...))); + } + virtual void errorMsg(int id, + const std::string &formatted_msg) + { + reportThrowExceptionMsg(sta::format("{} {}", id, formatted_msg), isSuppressed(id)); + } // Report error in a file. - virtual void fileError(int id, - const char *filename, - int line, - const char *fmt, ...) - __attribute__((format (printf, 5, 6))); - virtual void vfileError(int id, - const char *filename, - int line, - const char *fmt, - va_list args); - - // Critical. + template + void fileError(int id, + std::string_view filename, + int line, + std::string_view fmt, + Args &&...args) + { + fileErrorMsg(id, filename, line, + sta::vformat(fmt, sta::make_format_args(args...))); + } + virtual void fileErrorMsg(int id, + std::string_view filename, + int line, + const std::string &formatted_msg) + { + reportThrowExceptionMsg(sta::format("{} {} line {}, {}", + id, filename, line, formatted_msg), + isSuppressed(id)); + } + + // Critical. // Report error condition that should not be possible or that prevents execution. // The default handler prints msg to stderr and exits. - virtual void critical(int id, - const char *fmt, - ...) - __attribute__((format (printf, 3, 4))); - virtual void fileCritical(int id, - const char *filename, - int line, - const char *fmt, - ...) - __attribute__((format (printf, 5, 6))); + template + void critical(int id, + std::string_view fmt, + Args &&...args) + { + criticalMsg(id, sta::vformat(fmt, sta::make_format_args(args...))); + } + virtual void criticalMsg(int id, + const std::string &formatted_msg) + { + reportLine(sta::format("Critical {}: {}", id, formatted_msg)); + exit(1); + } + + template + void fileCritical(int id, + std::string_view filename, + int line, + std::string_view fmt, + Args &&...args) + { + fileCriticalMsg(id, filename, line, + sta::vformat(fmt, sta::make_format_args(args...))); + } + virtual void fileCriticalMsg(int id, + std::string_view filename, + int line, + const std::string &formatted_msg) + { + reportLine(sta::format("Critical {}: {} line {}, {}", id, filename, line, + formatted_msg)); + exit(1); + } // Log output to filename until logEnd is called. - virtual void logBegin(const char *filename); + virtual void logBegin(std::string_view filename); virtual void logEnd(); // Redirect output to filename until redirectFileEnd is called. - virtual void redirectFileBegin(const char *filename); + virtual void redirectFileBegin(std::string_view filename); // Redirect append output to filename until redirectFileEnd is called. - virtual void redirectFileAppendBegin(const char *filename); + virtual void redirectFileAppendBegin(std::string_view filename); virtual void redirectFileEnd(); // Redirect output to a string until redirectStringEnd is called. virtual void redirectStringBegin(); @@ -129,7 +199,7 @@ public: // Suppress message by id. void suppressMsgId(int id); void unsuppressMsgId(int id); - bool isSuppressed(int id); + [[nodiscard]] bool isSuppressed(int id); protected: // All sta print functions have an implicit return printed by this function. @@ -139,34 +209,17 @@ protected: // Return the number of characters written. virtual size_t printConsole(const char *buffer, size_t length); - void printToBuffer(const char *fmt, - ...) - __attribute__((format (printf, 2, 3))); - - void printToBuffer(const char *fmt, - va_list args); - void printToBufferAppend(const char *fmt, - ...); - void printToBufferAppend(const char *fmt, - va_list args); - void printBufferLine(); void redirectStringPrint(const char *buffer, size_t length); - FILE *log_stream_; - FILE *redirect_stream_; - bool redirect_to_string_; + FILE *log_stream_{nullptr}; + FILE *redirect_stream_{nullptr}; + bool redirect_to_string_{false}; std::string redirect_string_; - // Buffer to support printf style arguments. - size_t buffer_size_; - char *buffer_; - // Length of string in buffer. - size_t buffer_length_; - std::mutex buffer_lock_; static Report *default_; std::set suppressed_msg_ids_; friend class Debug; }; -} // namespace +} // namespace sta diff --git a/include/sta/ReportStd.hh b/include/sta/ReportStd.hh index a580b6288..b92821e84 100644 --- a/include/sta/ReportStd.hh +++ b/include/sta/ReportStd.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -33,4 +33,4 @@ class Report; Report * makeReportStd(); -} // namespace +} // namespace sta diff --git a/include/sta/ReportTcl.hh b/include/sta/ReportTcl.hh index 37ba416d3..9f48364c7 100644 --- a/include/sta/ReportTcl.hh +++ b/include/sta/ReportTcl.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -43,38 +43,38 @@ class ReportTcl : public Report { public: ReportTcl(); - virtual ~ReportTcl(); - virtual void logBegin(const char *filename); - virtual void logEnd(); - virtual void redirectFileBegin(const char *filename); - virtual void redirectFileAppendBegin(const char *filename); - virtual void redirectFileEnd(); - virtual void redirectStringBegin(); - virtual const char *redirectStringEnd(); + ~ReportTcl() override; + void logBegin(std::string_view filename) override; + void logEnd() override; + void redirectFileBegin(std::string_view filename) override; + void redirectFileAppendBegin(std::string_view filename) override; + void redirectFileEnd() override; + void redirectStringBegin() override; + const char *redirectStringEnd() override; // This must be called after the Tcl interpreter has been constructed. // It makes the encapsulated channels. - virtual void setTclInterp(Tcl_Interp *interp); + void setTclInterp(Tcl_Interp *interp) override; protected: - virtual size_t printConsole(const char *buffer, - size_t length); + size_t printConsole(const char *buffer, + size_t length) override; void flush(); private: Tcl_ChannelType *makeEncapChannelType(Tcl_Channel channel, - char *channel_name, - Tcl_DriverOutputProc output_proc); + char *channel_name, + Tcl_DriverOutputProc output_proc); size_t printTcl(Tcl_Channel channel, const char *buffer, size_t length); - Tcl_Interp *interp_; + Tcl_Interp *interp_{nullptr}; // The original tcl channels. - Tcl_Channel tcl_stdout_; - Tcl_Channel tcl_stderr_; + Tcl_Channel tcl_stdout_{nullptr}; + Tcl_Channel tcl_stderr_{nullptr}; // Encapsulated channels that print on this object. - Tcl_Channel tcl_encap_stdout_; - Tcl_Channel tcl_encap_stderr_; + Tcl_Channel tcl_encap_stdout_{nullptr}; + Tcl_Channel tcl_encap_stderr_{nullptr}; }; -} // namespace +} // namespace sta diff --git a/include/sta/RiseFallMinMax.hh b/include/sta/RiseFallMinMax.hh index 1aacbdd40..b3a8abcf8 100644 --- a/include/sta/RiseFallMinMax.hh +++ b/include/sta/RiseFallMinMax.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -35,41 +35,41 @@ class RiseFallMinMax public: RiseFallMinMax(); RiseFallMinMax(const RiseFallMinMax *rfmm); - explicit RiseFallMinMax(float init_value); + RiseFallMinMax(float init_value); float value(const RiseFall *rf, - const MinMax *min_max) const; + const MinMax *min_max) const; float value(const MinMax *min_max) const; void value(const RiseFall *rf, - const MinMax *min_max, - float &value, - bool &exists) const; + const MinMax *min_max, + float &value, + bool &exists) const; bool hasValue() const; void maxValue(// Return values - float &max_value, - bool &exists) const; - bool empty() const; + float &max_value, + bool &exists) const; + [[nodiscard]] bool empty() const; bool hasValue(const RiseFall *rf, - const MinMax *min_max) const; + const MinMax *min_max) const; void setValue(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float value); + const MinMaxAll *min_max, + float value); void setValue(const RiseFallBoth *rf, - const MinMax *min_max, - float value); + const MinMax *min_max, + float value); void setValue(const RiseFall *rf, - const MinMax *min_max, float value); + const MinMax *min_max, float value); void setValue(float value); void mergeValue(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float value); + const MinMaxAll *min_max, + float value); void mergeValue(const RiseFall *rf, - const MinMax *min_max, - float value); + const MinMax *min_max, + float value); void setValues(RiseFallMinMax *values); void removeValue(const RiseFallBoth *rf, - const MinMax *min_max); + const MinMax *min_max); void removeValue(const RiseFallBoth *rf, - const MinMaxAll *min_max); + const MinMaxAll *min_max); // Merge all values of rfmm. void mergeWith(RiseFallMinMax *rfmm); void clear(); @@ -77,12 +77,12 @@ public: bool isOneValue() const; bool isOneValue(float &value) const; bool isOneValue(const MinMax *min_max, - // Return values. - float &value) const; + // Return values. + float &value) const; private: float values_[RiseFall::index_count][MinMax::index_count]; bool exists_[RiseFall::index_count][MinMax::index_count]; }; -} // namespace +} // namespace sta diff --git a/include/sta/StringSet.hh b/include/sta/RiseFallMinMaxDelay.hh similarity index 62% rename from include/sta/StringSet.hh rename to include/sta/RiseFallMinMaxDelay.hh index ae643e351..50c803aad 100644 --- a/include/sta/StringSet.hh +++ b/include/sta/RiseFallMinMaxDelay.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -21,19 +21,32 @@ // misrepresented as being the original software. // // This notice may not be removed or altered from any source distribution. -#pragma once -#include +#pragma once -#include "StringUtil.hh" -#include "Set.hh" +#include "Delay.hh" +#include "MinMax.hh" +#include "Transition.hh" namespace sta { -typedef Set StringSet; -typedef std::set StdStringSet; +class RiseFallMinMaxDelay +{ +public: + RiseFallMinMaxDelay(); + [[nodiscard]] bool empty() const; + void value(const RiseFall *rf, + const MinMax *min_max, + Delay &value, + bool &exists) const; + void mergeValue(const RiseFall *rf, + const MinMax *min_max, + Delay &value, + const StaState *sta); -void -deleteContents(StringSet *strings); +private: + Delay values_[RiseFall::index_count][MinMax::index_count]; + bool exists_[RiseFall::index_count][MinMax::index_count]; +}; -} // namespace +} // namespace sta diff --git a/include/sta/RiseFallValues.hh b/include/sta/RiseFallValues.hh index e3ba0ca70..5a49a25e3 100644 --- a/include/sta/RiseFallValues.hh +++ b/include/sta/RiseFallValues.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -33,10 +33,10 @@ class RiseFallValues { public: RiseFallValues(); - explicit RiseFallValues(float init_value); + RiseFallValues(float init_value); float value(const RiseFall *rf) const; void value(const RiseFall *rf, - float &value, bool &exists) const; + float &value, bool &exists) const; bool hasValue(const RiseFall *rf) const; void setValue(const RiseFallBoth *rf, float value); void setValue(const RiseFall *rf, float value); @@ -49,4 +49,4 @@ private: bool exists_[RiseFall::index_count]; }; -} // namespace +} // namespace sta diff --git a/include/sta/Scene.hh b/include/sta/Scene.hh new file mode 100644 index 000000000..2e2041d39 --- /dev/null +++ b/include/sta/Scene.hh @@ -0,0 +1,98 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include +#include +#include + +#include "GraphClass.hh" +#include "SearchClass.hh" + +namespace sta { + +class Mode; +class Sdc; +class MinMax; +class Scene; +class LibertyLibrary; +class Parasitics; + +using SceneSet = std::set; +using SceneSeq = std::vector; +using LibertySeq = std::vector; +using ModeSeq = std::vector; +using ModeSet = std::set; + +class Scene +{ +public: + Scene(const std::string &name, + size_t index, + Mode *mode, + Parasitics *parasitics); + Scene(const std::string &name, + size_t index, + Mode *mode, + Parasitics *parasitics_min, + Parasitics *parasitics_max); + const std::string &name() const { return name_; } + size_t index() const { return index_; } + Mode *mode() { return mode_; } + Mode *mode() const { return mode_; } + void setMode(Mode *mode); + Sdc *sdc(); + Sdc *sdc() const; + Parasitics *parasitics(const MinMax *min_max) const; + void setParasitics(Parasitics *parasitics, + const MinMaxAll *min_max); + size_t pathIndex(const MinMax *min_max) const; + + DcalcAPIndex dcalcAnalysisPtIndex(const MinMax *min_max) const; + const MinMax *checkClkSlewMinMax(const MinMax *min_max) const; + // Slew index of timing check clock. + DcalcAPIndex checkClkSlewIndex(const MinMax *min_max) const; + + const LibertySeq &libertyLibraries(const MinMax *min_max) const; + size_t libertyIndex(const MinMax *min_max) const; + void addLiberty(LibertyLibrary *lib, + const MinMax *min_max); + + static SceneSet sceneSet(const SceneSeq &scenes); + static ModeSeq modes(const SceneSeq &scenes); + static ModeSet modeSet(const SceneSeq &scenes); + static ModeSeq modesSorted(const SceneSeq &scenes); + +protected: + std::string name_; + size_t index_; + Mode *mode_; + std::array liberty_; + std::array parasitics_; + + friend class Scenes; +}; + +} // namespace sta diff --git a/include/sta/Sdc.hh b/include/sta/Sdc.hh index e4f32afec..96c0fd8fc 100644 --- a/include/sta/Sdc.hh +++ b/include/sta/Sdc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,23 +24,25 @@ #pragma once +#include #include +#include +#include +#include -#include "StringUtil.hh" -#include "StringSet.hh" -#include "Map.hh" -#include "UnorderedMap.hh" -#include "MinMax.hh" -#include "StaState.hh" -#include "NetworkClass.hh" -#include "LibertyClass.hh" -#include "GraphClass.hh" -#include "SdcClass.hh" -#include "RiseFallValues.hh" #include "Clock.hh" -#include "DataCheck.hh" #include "CycleAccting.hh" +#include "DataCheck.hh" #include "ExceptionPath.hh" +#include "GraphClass.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "NetworkClass.hh" +#include "PinPair.hh" +#include "RiseFallValues.hh" +#include "SdcClass.hh" +#include "StaState.hh" +#include "StringUtil.hh" namespace sta { @@ -52,24 +54,24 @@ class DisabledPorts; class GraphLoop; class DeratingFactors; class DeratingFactorsGlobal; -class DeratingFactorsNet; +class DeratingFactors; class DeratingFactorsCell; class PatternMatch; class FindNetCaps; class ClkHpinDisable; class FindClkHpinDisables; -class Corner; +class Scene; class ClockPinIterator; class ClockIterator; -typedef std::pair PinClockPair; +using PinClockPair = std::pair; class ClockInsertionkLess { public: ClockInsertionkLess(const Network *network); bool operator()(const ClockInsertion *insert1, - const ClockInsertion *insert2) const; + const ClockInsertion *insert2) const; private: const Network *network_; @@ -80,7 +82,7 @@ class ClockLatencyLess public: ClockLatencyLess(const Network *network); bool operator()(const ClockLatency *latency1, - const ClockLatency *latency2) const; + const ClockLatency *latency2) const; private: const Network *network_; @@ -93,7 +95,7 @@ class ClockPairLess { public: bool operator()(const ClockPair &pair1, - const ClockPair &pair2) const; + const ClockPair &pair2) const; }; class PinClockPairLess @@ -101,7 +103,7 @@ class PinClockPairLess public: PinClockPairLess(const Network *network); bool operator()(const PinClockPair &pin_clk1, - const PinClockPair &pin_clk2) const; + const PinClockPair &pin_clk2) const; protected: const Network *network_; @@ -112,7 +114,7 @@ class ClkHpinDisableLess public: ClkHpinDisableLess(const Network *network); bool operator()(const ClkHpinDisable *disable1, - const ClkHpinDisable *disable2) const; + const ClkHpinDisable *disable2) const; private: const Network *network_; @@ -121,90 +123,89 @@ private: class NetWireCaps : public MinMaxFloatValues { public: - NetWireCaps(); bool subtractPinCap(const MinMax *min_max); void setSubtractPinCap(bool subtrace_pin_cap, const MinMax *min_max); private: - bool subtract_pin_cap_[MinMax::index_count]; + bool subtract_pin_cap_[MinMax::index_count]{false, false}; }; -typedef Map ClockNameMap; -typedef UnorderedMap ClockPinMap; -typedef Set InputDelaySet; -typedef Map InputDelaysPinMap; -typedef Set OutputDelaySet; -typedef Map OutputDelaysPinMap; -typedef UnorderedMap PinExceptionsMap; -typedef UnorderedMap ClockExceptionsMap; -typedef UnorderedMap InstanceExceptionsMap; -typedef UnorderedMap NetExceptionsMap; -typedef UnorderedMap EdgeExceptionsMap; -typedef Vector ExceptionThruSeq; -typedef Map InputDriveMap; -typedef Map> ExceptionPathPtHash; -typedef Set ClockLatencies; -typedef Map PinClockUncertaintyMap; -typedef Set InterClockUncertaintySet; -typedef Map ClockGatingCheckMap; -typedef Map InstanceClockGatingCheckMap; -typedef Map PinClockGatingCheckMap; -typedef Set ClockInsertions; -typedef Map PinLatchBorrowLimitMap; -typedef Map InstLatchBorrowLimitMap; -typedef Map ClockLatchBorrowLimitMap; -typedef Set DataCheckSet; -typedef Map DataChecksMap; -typedef Map NetResistanceMap; -typedef Map PortSlewLimitMap; -typedef Map PinSlewLimitMap; -typedef Map CellSlewLimitMap; -typedef Map CellCapLimitMap; -typedef Map PortCapLimitMap; -typedef Map PinCapLimitMap; -typedef Map PortFanoutLimitMap; -typedef Map CellFanoutLimitMap; -typedef Map PortExtCapMap; -typedef Map NetWireCapMap; -typedef Map PinWireCapMap; -typedef Map InstancePvtMap; -typedef Map EdgeClockLatencyMap; -typedef Map PinMinPulseWidthMap; -typedef Map ClockMinPulseWidthMap; -typedef Map InstMinPulseWidthMap; -typedef Map NetDeratingFactorsMap; -typedef Map InstDeratingFactorsMap; -typedef Map CellDeratingFactorsMap; -typedef Set ClockGroupsSet; -typedef Map ClockGroupsClkMap; -typedef Map ClockGroupsNameMap; -typedef Map ClockSenseMap; -typedef Set ClkHpinDisables; -typedef Set GroupPathSet; -typedef Map GroupPathMap; -typedef Set ClockPairSet; -typedef Map NetVoltageMap; +using ClockNameMap = std::map>; +using ClockPinMap = std::unordered_map; +using InputDelaySet = std::set; +using InputDelaysPinMap = std::map; +using OutputDelaySet = std::set; +using OutputDelaysPinMap = std::map; +using PinExceptionsMap = std::unordered_map; +using ClockExceptionsMap = std::unordered_map; +using InstanceExceptionsMap = std::unordered_map; +using NetExceptionsMap = std::unordered_map; +using EdgeExceptionsMap = std::unordered_map; +using InputDriveMap = std::map; +using ExceptionPathPtHash = std::map; +using ClockLatencies = std::set; +using EdgeClockLatencyMap = std::map; +using PinClockUncertaintyMap = std::map; +using InterClockUncertaintySet=std::set; +using ClockGatingCheckMap = std::map; +using InstanceClockGatingCheckMap = std::map; +using PinClockGatingCheckMap = std::map; +using ClockInsertions = std::set; +using PinLatchBorrowLimitMap = std::map; +using InstLatchBorrowLimitMap = std::map; +using ClockLatchBorrowLimitMap = std::map; +using DataCheckSet = std::set; +using DataChecksMap = std::map; +using NetResistanceMap = std::map; +using PortSlewLimitMap = std::map; +using PinSlewLimitMap = std::map; +using CellSlewLimitMap = std::map; +using CellCapLimitMap = std::map; +using PortCapLimitMap = std::map; +using PinCapLimitMap = std::map; +using PortFanoutLimitMap = std::map; +using CellFanoutLimitMap = std::map; +using PortExtCapMap = std::map; +using NetWireCapMap = std::map; +using PinWireCapMap = std::map; +using InstancePvtMap = std::map; +using PinMinPulseWidthMap = std::map; +using ClockMinPulseWidthMap = std::map; +using InstMinPulseWidthMap = std::map; +using NetDeratingFactorsMap = std::map; +using InstDeratingFactorsMap = std::map; +using CellDeratingFactorsMap = std::map; +using ClockGroupsSet = std::set; +using ClockGroupsClkMap = std::map; +using ClockGroupsNameMap = std::map>; +using ClockSenseMap = std::map; +using ClkHpinDisables = std::set; +using GroupPathSet = std::set; +using GroupPathMap = std::map>; +using ClockPairSet = std::set; +using NetVoltageMap = std::map; void findLeafLoadPins(const Pin *pin, - const Network *network, - PinSet *leaf_pins); + const Network *network, + PinSet *leaf_pins); void findLeafDriverPins(const Pin *pin, - const Network *network, - PinSet *leaf_pins); + const Network *network, + PinSet *leaf_pins); class Sdc : public StaState { public: - Sdc(StaState *sta); - ~Sdc(); + Sdc(Mode *mode, + StaState *sta); + ~Sdc() override; + Mode *mode() const { return mode_; } // Note that Search may reference a Filter exception removed by clear(). void clear(); - void makeCornersBefore(); - void makeCornersAfter(Corners *corners); + void makeSceneBefore(); // Return true if pin is referenced by any constraint. bool isConstrained(const Pin *pin) const; // Return true if inst is referenced by any constraint. @@ -219,161 +220,161 @@ public: void deleteInstanceBefore(const Instance *inst); // SWIG sdc interface. - PortSeq allInputs(bool no_clks); - PortSeq allOutputs(); - AnalysisType analysisType() { return analysis_type_; } + PortSeq allInputs(bool no_clks) const; + PortSeq allOutputs() const; + AnalysisType analysisType() const { return analysis_type_; } void setAnalysisType(AnalysisType analysis_type); void setOperatingConditions(OperatingConditions *op_cond, - const MinMaxAll *min_max); + const MinMaxAll *min_max); void setOperatingConditions(OperatingConditions *op_cond, - const MinMax *min_max); + const MinMax *min_max); void setTimingDerate(TimingDerateType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate); + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate); // Delay type is always net for net derating. void setTimingDerate(const Net *net, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate); + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate); void setTimingDerate(const Instance *inst, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate); + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate); void setTimingDerate(const LibertyCell *cell, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate); + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate); float timingDerateInstance(const Pin *pin, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late) const; + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late) const; float timingDerateNet(const Pin *pin, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late) const; + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late) const; void unsetTimingDerate(); static void swapDeratingFactors(Sdc *sdc1, Sdc *sdc2); void setInputSlew(const Port *port, const RiseFallBoth *rf, - const MinMaxAll *min_max, + const MinMaxAll *min_max, float slew); // Set the rise/fall drive resistance on design port. void setDriveResistance(const Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float res); + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float res); // Set the drive on design port using external cell timing arcs of // cell driven by from_slews between from_port and to_port. void setDriveCell(const LibertyLibrary *library, - const LibertyCell *cell, - const Port *port, - const LibertyPort *from_port, - float *from_slews, - const LibertyPort *to_port, - const RiseFallBoth *rf, - const MinMaxAll *min_max); + const LibertyCell *cell, + const Port *port, + const LibertyPort *from_port, + const DriveCellSlews &from_slews, + const LibertyPort *to_port, + const RiseFallBoth *rf, + const MinMaxAll *min_max); void setLatchBorrowLimit(const Pin *pin, - float limit); + float limit); void setLatchBorrowLimit(const Instance *inst, - float limit); + float limit); void setLatchBorrowLimit(const Clock *clk, - float limit); + float limit); // Return the latch borrow limit respecting precedence if multiple // limits apply. void latchBorrowLimit(const Pin *data_pin, - const Pin *enable_pin, - const Clock *clk, - // Return values. - float &limit, - bool &exists); + const Pin *enable_pin, + const Clock *clk, + // Return values. + float &limit, + bool &exists); void setMinPulseWidth(const RiseFallBoth *rf, - float min_width); + float min_width); void setMinPulseWidth(const Pin *pin, - const RiseFallBoth *rf, - float min_width); + const RiseFallBoth *rf, + float min_width); void setMinPulseWidth(const Instance *inst, - const RiseFallBoth *rf, - float min_width); + const RiseFallBoth *rf, + float min_width); void setMinPulseWidth(const Clock *clk, - const RiseFallBoth *rf, - float min_width); + const RiseFallBoth *rf, + float min_width); // Return min pulse with respecting precedence. void minPulseWidth(const Pin *pin, - const Clock *clk, - const RiseFall *hi_low, - float &min_width, - bool &exists) const; + const Clock *clk, + const RiseFall *hi_low, + float &min_width, + bool &exists) const; void setSlewLimit(Clock *clk, - const RiseFallBoth *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - float slew); + const RiseFallBoth *rf, + PathClkOrData clk_data, + const MinMax *min_max, + float slew); bool haveClkSlewLimits() const; - void slewLimit(Clock *clk, - const RiseFall *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - float &slew, - bool &exists); + void slewLimit(const Clock *clk, + const RiseFall *rf, + PathClkOrData clk_data, + const MinMax *min_max, + float &slew, + bool &exists) const; void slewLimit(Port *port, - const MinMax *min_max, - float &slew, - bool &exists); + const MinMax *min_max, + float &slew, + bool &exists) const; void setSlewLimit(Port *port, - const MinMax *min_max, - float slew); + const MinMax *min_max, + float slew); void slewLimit(Cell *cell, - const MinMax *min_max, - float &slew, - bool &exists); + const MinMax *min_max, + float &slew, + bool &exists) const; void setSlewLimit(Cell *cell, - const MinMax *min_max, - float slew); + const MinMax *min_max, + float slew); void capacitanceLimit(Port *port, - const MinMax *min_max, - float &cap, - bool &exists); + const MinMax *min_max, + float &cap, + bool &exists) const; void capacitanceLimit(Pin *pin, - const MinMax *min_max, - float &cap, - bool &exists); + const MinMax *min_max, + float &cap, + bool &exists) const; void capacitanceLimit(Cell *cell, - const MinMax *min_max, - float &cap, - bool &exists); + const MinMax *min_max, + float &cap, + bool &exists) const; void setCapacitanceLimit(Port *port, - const MinMax *min_max, - float cap); + const MinMax *min_max, + float cap); void setCapacitanceLimit(Pin *pin, - const MinMax *min_max, - float cap); + const MinMax *min_max, + float cap); void setCapacitanceLimit(Cell *cell, - const MinMax *min_max, - float cap); + const MinMax *min_max, + float cap); void fanoutLimit(Port *port, - const MinMax *min_max, - float &fanout, - bool &exists); + const MinMax *min_max, + float &fanout, + bool &exists) const; void setFanoutLimit(Port *port, - const MinMax *min_max, - float fanout); + const MinMax *min_max, + float fanout); void fanoutLimit(Cell *cell, - const MinMax *min_max, - float &fanout, - bool &exists); + const MinMax *min_max, + float &fanout, + bool &exists) const; void setFanoutLimit(Cell *cell, - const MinMax *min_max, - float fanout); + const MinMax *min_max, + float fanout); void setMaxArea(float area); float maxArea() const; void setMaxDynamicPower(float power); @@ -381,15 +382,15 @@ public: void setMaxLeakagePower(float power); float maxLeakagePower() const; void createLibertyGeneratedClocks(Clock *clk); - Clock *makeClock(const char *name, - PinSet *pins, + Clock *makeClock(std::string_view name, + const PinSet &pins, bool add_to_pins, float period, - FloatSeq *waveform, - const char *comment); + const FloatSeq &waveform, + std::string_view comment); // edges size must be 3. - Clock *makeGeneratedClock(const char *name, - PinSet *pins, + Clock *makeGeneratedClock(std::string_view name, + const PinSet &pins, bool add_to_pins, Pin *src_pin, Clock *master_clk, @@ -398,9 +399,9 @@ public: float duty_cycle, bool invert, bool combinational, - IntSeq *edges, - FloatSeq *edge_shifts, - const char *comment); + const IntSeq &edges, + const FloatSeq &dge_shifts, + std::string_view comment); // Invalidate all generated clock waveforms. void invalidateGeneratedClks() const; void removeClock(Clock *clk); @@ -412,79 +413,79 @@ public: void removePropagatedClock(Clock *clk); void setPropagatedClock(Pin *pin); void removePropagatedClock(Pin *pin); - bool isPropagatedClock(const Pin *pin); + bool isPropagatedClock(const Pin *pin) const; void setClockSlew(Clock *clk, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew); + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew); void removeClockSlew(Clock *clk); // Latency can be on a clk, pin, or clk/pin combination. void setClockLatency(Clock *clk, - Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float delay); + Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float delay); void removeClockLatency(const Clock *clk, - const Pin *pin); + const Pin *pin); ClockLatency *clockLatency(Edge *edge) const; bool hasClockLatency(const Pin *pin) const; void clockLatency(Edge *edge, - const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &latency, - bool &exists) const; + const RiseFall *rf, + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) const; ClockLatencies *clockLatencies() { return &clk_latencies_; } const ClockLatencies *clockLatencies() const { return &clk_latencies_; } // Clock latency on pin with respect to clk. // This does NOT check for latency on clk (without pin). void clockLatency(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &latency, - bool &exists) const; + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) const; void clockLatency(const Clock *clk, - const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &latency, - bool &exists) const; + const RiseFall *rf, + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) const; float clockLatency(const Clock *clk, - const RiseFall *rf, - const MinMax *min_max) const; + const RiseFall *rf, + const MinMax *min_max) const; // Clock insertion delay (set_clk_latency -source). // Insertion delay can be on a clk, pin, or clk/pin combination. void setClockInsertion(const Clock *clk, - const Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - const EarlyLateAll *early_late, - float delay); + const Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay); void setClockInsertion(const Clock *clk, const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late, - float delay); + const RiseFall *rf, + const MinMax *min_max, + const EarlyLate *early_late, + float delay); void removeClockInsertion(const Clock *clk, - const Pin *pin); + const Pin *pin); static void swapClockInsertions(Sdc *sdc1, Sdc *sdc2); bool hasClockInsertion(const Pin *pin) const; float clockInsertion(const Clock *clk, - const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late) const; + const RiseFall *rf, + const MinMax *min_max, + const EarlyLate *early_late) const; // Respects precedence of pin/clk and set_input_delay on clk pin. void clockInsertion(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late, - // Return values. - float &insertion, - bool &exists) const; + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + const EarlyLate *early_late, + // Return values. + float &insertion, + bool &exists) const; const ClockInsertions &clockInsertions() const { return clk_insertions_; } // Clock uncertainty. void setClockUncertainty(Pin *pin, @@ -503,159 +504,154 @@ public: Clock *to_clk, const RiseFallBoth *to_rf, const SetupHoldAll *setup_hold); - ClockGroups *makeClockGroups(const char *name, - bool logically_exclusive, - bool physically_exclusive, - bool asynchronous, - bool allow_paths, - const char *comment); + ClockGroups *makeClockGroups(std::string_view name, + bool logically_exclusive, + bool physically_exclusive, + bool asynchronous, + bool allow_paths, + std::string_view comment); void makeClockGroup(ClockGroups *clk_groups, - ClockSet *clks); - void removeClockGroups(const char *name); - // nullptr name removes all. - void removeClockGroupsLogicallyExclusive(const char *name); - void removeClockGroupsPhysicallyExclusive(const char *name); - void removeClockGroupsAsynchronous(const char *name); + ClockSet *clks); + void removeClockGroups(const std::string &name); + void removeClockGroupsLogicallyExclusive(); + void removeClockGroupsLogicallyExclusive(const std::string &name); + void removeClockGroupsPhysicallyExclusive(); + void removeClockGroupsPhysicallyExclusive(const std::string &name); + void removeClockGroupsAsynchronous(); + void removeClockGroupsAsynchronous(const std::string &name); bool sameClockGroup(const Clock *clk1, - const Clock *clk2); + const Clock *clk2) const; // Clocks explicitly excluded by set_clock_group. bool sameClockGroupExplicit(const Clock *clk1, - const Clock *clk2); + const Clock *clk2); void setClockSense(PinSet *pins, - ClockSet *clks, - ClockSense sense); + ClockSet *clks, + ClockSense sense); bool clkStopPropagation(const Pin *pin, - const Clock *clk) const; + const Clock *clk) const; bool clkStopPropagation(const Clock *clk, - const Pin *from_pin, - const RiseFall *from_rf, - const Pin *to_pin, - const RiseFall *to_rf) const; + const Pin *from_pin, + const RiseFall *from_rf, + const Pin *to_pin, + const RiseFall *to_rf) const; void setClockGatingCheck(const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin); + const SetupHold *setup_hold, + float margin); void setClockGatingCheck(Instance *inst, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value); + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value); void setClockGatingCheck(Clock *clk, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin); + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin); void setClockGatingCheck(const Pin *pin, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value); + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value); void setDataCheck(Pin *from, - const RiseFallBoth *from_rf, - Pin *to, - const RiseFallBoth *to_rf, - Clock *clk, - const SetupHoldAll *setup_hold, - float margin); + const RiseFallBoth *from_rf, + Pin *to, + const RiseFallBoth *to_rf, + Clock *clk, + const SetupHoldAll *setup_hold, + float margin); void removeDataCheck(Pin *from, - const RiseFallBoth *from_rf, - Pin *to, - const RiseFallBoth *to_rf, - Clock *clk, - const SetupHoldAll *setup_hold); + const RiseFallBoth *from_rf, + Pin *to, + const RiseFallBoth *to_rf, + Clock *clk, + const SetupHoldAll *setup_hold); DataCheckSet *dataChecksFrom(const Pin *from) const; DataCheckSet *dataChecksTo(const Pin *to) const; void setInputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const Pin *ref_pin, - bool source_latency_included, - bool network_latency_included, - const MinMaxAll *min_max, - bool add, + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, float delay); void removeInputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMaxAll *min_max); + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMaxAll *min_max); void setOutputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_tr, - const Pin *ref_pin, - bool source_latency_included, - bool network_latency_included, - const MinMaxAll *min_max, - bool add, + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, float delay); void removeOutputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMaxAll *min_max); + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMaxAll *min_max); static void swapPortDelays(Sdc *sdc1, Sdc *sdc2); // Set port external pin load (set_load -pin_load port). void setPortExtPinCap(const Port *port, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float cap); + const RiseFall *rf, + const MinMax *min_max, + float cap); // Set port external wire load (set_load -wire port). void setPortExtWireCap(const Port *port, - bool subtract_pin_cap, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float cap); + const RiseFall *rf, + const MinMax *min_max, + float cap); static void swapPortExtCaps(Sdc *sdc1, Sdc *sdc2); // Remove all "set_load net" annotations. void removeNetLoadCaps(); void setNetWireCap(const Net *net, - bool subtract_pin_cap, - const Corner *corner, - const MinMax *min_max, - float wire_cap); + bool subtract_pin_cap, + const MinMax *min_max, + float wire_cap); bool hasNetWireCap(const Net *net) const; // True if driver pin net has wire capacitance. - bool drvrPinHasWireCap(const Pin *pin, - const Corner *corner); + bool drvrPinHasWireCap(const Pin *pin) const; // Net wire capacitance (set_load -wire net). void drvrPinWireCap(const Pin *drvr_pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &cap, - bool &exists, + const MinMax *min_max, + // Return values. + float &cap, + bool &exists, bool &subtract_pin_cap) const; // Pin capacitance derated by operating conditions and instance pvt. float pinCapacitance(const Pin *pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max); + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) const; void setResistance(const Net *net, - const MinMaxAll *min_max, - float res); + const MinMaxAll *min_max, + float res); void resistance(const Net *net, - const MinMax *min_max, - float &res, - bool &exists); - NetResistanceMap &netResistances() { return net_res_map_; } + const MinMax *min_max, + float &res, + bool &exists) const; + const NetResistanceMap &netResistances() const { return net_res_map_; } void setPortExtFanout(const Port *port, - const Corner *corner, - const MinMax *min_max, - int fanout); + const MinMax *min_max, + int fanout); // set_disable_timing cell [-from] [-to] // Disable all edges thru cell if from/to are null. // Bus and bundle ports are NOT supported. void disable(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to); + LibertyPort *from, + LibertyPort *to); void removeDisable(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to); + LibertyPort *from, + LibertyPort *to); // set_disable_timing liberty port. // Bus and bundle ports are NOT supported. void disable(LibertyPort *port); @@ -668,11 +664,11 @@ public: // Disable all edges thru instance if from/to are null. // Bus and bundle ports are NOT supported. void disable(Instance *inst, - LibertyPort *from, - LibertyPort *to); + LibertyPort *from, + LibertyPort *to); void removeDisable(Instance *inst, - LibertyPort *from, - LibertyPort *to); + LibertyPort *from, + LibertyPort *to); // set_disable_timing pin void disable(const Pin *pin); void removeDisable(Pin *pin); @@ -684,34 +680,40 @@ public: void removeDisable(TimingArcSet *arc_set); // Disable a wire edge. From/to pins musts be on the same net. // There is no SDC equivalent to this. - void disable(Pin *from, Pin *to); - void removeDisable(Pin *from, Pin *to); - bool isDisabled(const Pin *pin) const; + void disableWire(const Pin *from, + const Pin *to); + void removeDisableWire(Pin *from, + Pin *to); + [[nodiscard]] bool isDisabledWire(const Pin *from, + const Pin *to) const; + [[nodiscard]] bool isDisabledConstraint(const Pin *pin) const; // Edge disabled by hierarchical pin disable or instance/cell port pair. // Disables do NOT apply to timing checks. // inst can be either the from_pin or to_pin instance because it // is only referenced when they are the same (non-wire edge). - bool isDisabled(const Instance *inst, - const Pin *from_pin, - const Pin *to_pin, - const TimingRole *role) const; - bool isDisabled(Edge *edge); - bool isDisabled(TimingArcSet *arc_set) const; - DisabledCellPortsMap *disabledCellPorts(); + [[nodiscard]] bool isDisabled(const Instance *inst, + const Pin *from_pin, + const Pin *to_pin, + const TimingRole *role) const; + [[nodiscard]] bool isDisabled(const Edge *edge) const; + [[nodiscard]] bool isDisabled(TimingArcSet *arc_set) const; + const DisabledCellPortsMap *disabledCellPorts() const; const DisabledInstancePortsMap *disabledInstancePorts() const; const PinSet *disabledPins() const { return &disabled_pins_; } const PortSet *disabledPorts() const { return &disabled_ports_; } const LibertyPortSet *disabledLibPorts() const { return &disabled_lib_ports_; } const EdgeSet *disabledEdges() const { return &disabled_edges_; } + [[nodiscard]] bool isDisabledConstraint(const Edge *edge) const; + void disableClockGatingCheck(Instance *inst); void disableClockGatingCheck(Pin *pin); void disableClockGatingCheck(LibertyCell *cell); void removeDisableClockGatingCheck(Instance *inst); void removeDisableClockGatingCheck(Pin *pin); void removeDisableClockGatingCheck(LibertyCell *cell); - bool isDisableClockGatingCheck(const Pin *pin); - bool isDisableClockGatingCheck(const Instance *inst); - bool isDisableClockGatingCheck(const LibertyCell *cell); + bool isDisableClockGatingCheck(const Pin *pin) const; + bool isDisableClockGatingCheck(const Instance *inst) const; + bool isDisableClockGatingCheck(const LibertyCell *cell) const; const InstanceSet *disabledClockGatingChecksInst() const { return &disabled_clk_gating_checks_inst_; } const PinSet *disabledClockGatingChecksPin() const @@ -720,100 +722,107 @@ public: { return &disabled_clk_gating_checks_lib_cell_; } // set_LogicValue::zero, set_LogicValue::one, set_logic_dc void setLogicValue(const Pin *pin, - LogicValue value); + LogicValue value); // set_case_analysis void setCaseAnalysis(const Pin *pin, - LogicValue value); + LogicValue value); void removeCaseAnalysis(const Pin *pin); void logicValue(const Pin *pin, - LogicValue &value, - bool &exists); + LogicValue &value, + bool &exists) const; void caseLogicValue(const Pin *pin, LogicValue &value, - bool &exists); + bool &exists) const; // Pin has set_case_analysis or set_logic constant value. - bool hasLogicValue(const Pin *pin); + bool hasLogicValue(const Pin *pin) const; + // The from/thrus/to arguments passed into the following functions // that make exceptions are owned by the constraints once they are // passed in. The constraint internals may change or delete them do // to exception merging. void makeFalsePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - const char *comment); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + std::string_view comment); // Loop paths are false paths used to disable paths around // combinational loops when dynamic loop breaking is enabled. void makeLoopExceptions(); void makeLoopExceptions(GraphLoop *loop); void deleteLoopExceptions(); void makeMulticyclePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool use_end_clk, - int path_multiplier, - const char *comment); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, + std::string_view comment); void makePathDelay(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMax *min_max, - bool ignore_clk_latency, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMax *min_max, + bool ignore_clk_latency, bool break_path, - float delay, - const char *comment); + float delay, + std::string_view comment); bool pathDelaysWithoutTo() const { return path_delays_without_to_; } // Delete matching false/multicycle/path_delay exceptions. // Caller owns from, thrus, to exception points (and must delete them). void resetPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max); - void makeGroupPath(const char *name, - bool is_default, - ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const char *comment); - bool isGroupPathName(const char *group_name); - GroupPathMap &groupPaths() { return group_path_map_; } + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max); + void makeGroupPath(std::string_view name, + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + std::string_view comment); + bool isGroupPathName(std::string_view group_name) const; + const GroupPathMap &groupPaths() const { return group_path_map_; } void addException(ExceptionPath *exception); // The pin/clk/instance/net set arguments passed into the following // functions that make exception from/thru/to's are owned by the // constraints once they are passed in. ExceptionFrom *makeExceptionFrom(PinSet *from_pins, - ClockSet *from_clks, - InstanceSet *from_insts, - const RiseFallBoth *from_rf); + ClockSet *from_clks, + InstanceSet *from_insts, + const RiseFallBoth *from_rf) const; bool isExceptionStartpoint(const Pin *pin) const; // Make an exception -through specification. ExceptionThru *makeExceptionThru(PinSet *pins, - NetSet *nets, - InstanceSet *insts, - const RiseFallBoth *rf); - bool isExceptionEndpoint(const Pin *pin); + NetSet *nets, + InstanceSet *insts, + const RiseFallBoth *rf) const; + bool isExceptionEndpoint(const Pin *pin) const; // Make an exception -to specification. ExceptionTo *makeExceptionTo(PinSet *pins, - ClockSet *clks, - InstanceSet *insts, - const RiseFallBoth *rf, - const RiseFallBoth *end_rf); + ClockSet *clks, + InstanceSet *insts, + const RiseFallBoth *rf, + const RiseFallBoth *end_rf) const; + FilterPath *makeFilterPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to); - Wireload *wireload(const MinMax *min_max); + ExceptionThruSeq *thrus, + ExceptionTo *to); + void makeFilter(ExceptionFrom *from, + ExceptionThruSeq *thrus); + FilterPath *filter() const { return filter_; } + void deleteFilter(); + + Wireload *wireload(const MinMax *min_max) const; void setWireload(Wireload *wireload, - const MinMaxAll *min_max); - WireloadMode wireloadMode(); + const MinMaxAll *min_max); + WireloadMode wireloadMode() const { return wireload_mode_; }; void setWireloadMode(WireloadMode mode); const WireloadSelection *wireloadSelection(const MinMax *min_max); - void setWireloadSelection(WireloadSelection *selection, - const MinMaxAll *min_max); + void setWireloadSelection(const WireloadSelection *selection, + const MinMaxAll *min_max); // STA interface. InputDelaySet *refPinInputDelays(const Pin *ref_pin) const; - LogicValueMap &logicValues() { return logic_value_map_; } - LogicValueMap &caseLogicValues() { return case_value_map_; } + const LogicValueMap &logicValues() const { return logic_value_map_; } + const LogicValueMap &caseLogicValues() const { return case_value_map_; } // Returns nullptr if set_operating_conditions has not been called. OperatingConditions *operatingConditions(const MinMax *min_max) const; // Instance specific process/voltage/temperature. @@ -821,24 +830,24 @@ public: const MinMax *min_max) const; // Pvt may be shared among multiple instances. void setPvt(const Instance *inst, - const MinMaxAll *min_max, - const Pvt &pvt); + const MinMaxAll *min_max, + const Pvt &pvt); void voltage(const MinMax *min_max, // Return values. float &voltage, - bool &exists); + bool &exists) const; void voltage(const Net *net, const MinMax *min_max, // Return values. float &voltage, - bool &exists); + bool &exists) const; void setVoltage(const MinMax *min_max, float voltage); void setVoltage(const Net *net, const MinMax *min_max, float voltage); - InputDrive *findInputDrive(Port *port); - Clock *findClock(const char *name) const; + InputDrive *findInputDrive(Port *port) const; + Clock *findClock(std::string_view name) const; ClockSeq findClocksMatching(PatternMatch *pattern) const; // True if pin is defined as a clock source (pin may be hierarchical). bool isClock(const Pin *pin) const; @@ -849,51 +858,63 @@ public: // Find the clocks defined for pin. ClockSet *findClocks(const Pin *pin) const; ClockSet *findLeafPinClocks(const Pin *pin) const; - void sortedClocks(ClockSeq &clks); - ClockSeq *clocks() { return &clocks_; } - ClockSeq &clks() { return clocks_; } + const ClockSeq &clocks() const { return clocks_; } + ClockSeq sortedClocks() const; bool clkDisabledByHpinThru(const Clock *clk, - const Pin *from_pin, - const Pin *to_pin); + const Pin *from_pin, + const Pin *to_pin) const; void clkHpinDisablesInvalid(); - ClockUncertainties *clockUncertainties(const Pin *pin); + const ClockUncertainties *clockUncertainties(const Pin *pin) const; void clockUncertainty(const Pin *pin, - const SetupHold *setup_hold, - float &uncertainty, - bool &exists); + const SetupHold *setup_hold, + // Return values. + float &uncertainty, + bool &exists); // Inter-clock uncertainty. void clockUncertainty(const Clock *src_clk, - const RiseFall *src_rf, - const Clock *tgt_clk, - const RiseFall *tgt_rf, - const SetupHold *setup_hold, - float &uncertainty, bool &exists); + const RiseFall *src_rf, + const Clock *tgt_clk, + const RiseFall *tgt_rf, + const SetupHold *setup_hold, + // Return values. + float &uncertainty, + bool &exists) const; void clockGatingMarginEnablePin(const Pin *enable_pin, - const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, float &margin); + const RiseFall *enable_rf, + const SetupHold *setup_hold, + // Return values. + bool &exists, + float &margin) const; void clockGatingMarginInstance(Instance *inst, - const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, float &margin); + const RiseFall *enable_rf, + const SetupHold *setup_hold, + // Return values. + bool &exists, + float &margin) const; void clockGatingMarginClkPin(const Pin *clk_pin, - const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, float &margin); + const RiseFall *enable_rf, + const SetupHold *setup_hold, + // Return values. + bool &exists, + float &margin) const; void clockGatingMarginClk(const Clock *clk, - const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, float &margin); + const RiseFall *enable_rf, + const SetupHold *setup_hold, + // Return values. + bool &exists, + float &margin) const; void clockGatingMargin(const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, float &margin); + const SetupHold *setup_hold, + // Return values. + bool &exists, + float &margin) const; // Gated clock active (non-controlling) logic value. LogicValue clockGatingActiveValue(const Pin *clk_pin, - const Pin *enable_pin); + const Pin *enable_pin) const; // Find the cycle accounting info for paths that start at src clock // edge and end at target clock edge. CycleAccting *cycleAccting(const ClockEdge *src, - const ClockEdge *tgt); + const ClockEdge *tgt); // Report clock to clock relationships that exceed max_cycle_count. void reportClkToClkMaxCycleWarnings(); @@ -901,7 +922,7 @@ public: // Pin -> input delays. const InputDelaysPinMap &inputDelayPinMap() const { return input_delay_pin_map_; } // Input delays on leaf_pin. - InputDelaySet *inputDelaysLeafPin(const Pin *leaf_pin); + InputDelaySet *inputDelaysLeafPin(const Pin *leaf_pin) const; bool hasInputDelay(const Pin *leaf_pin) const; // Pin is internal (not top level port) and has an input arrival. bool isInputDelayInternal(const Pin *pin) const; @@ -910,99 +931,94 @@ public: // Pin -> output delays. const OutputDelaysPinMap &outputDelaysPinMap() const { return output_delay_pin_map_; } // Output delays on leaf_pin. - OutputDelaySet *outputDelaysLeafPin(const Pin *leaf_pin); - bool hasOutputDelay(const Pin *leaf_pin) const; + OutputDelaySet *outputDelaysLeafPin(const Pin *leaf_pin) const; + [[nodiscard]] bool hasOutputDelay(const Pin *leaf_pin) const; - PortExtCap *portExtCap(const Port *port, - const Corner *corner) const; + const PortExtCap *portExtCap(const Port *port) const; bool hasPortExtCap(const Port *port) const; void portExtCap(const Port *port, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &pin_cap, - bool &has_pin_cap, - float &wire_cap, - bool &has_wire_cap, - int &fanout, - bool &has_fanout) const; + const RiseFall *rf, + const MinMax *min_max, + // Return values. + float &pin_cap, + bool &has_pin_cap, + float &wire_cap, + bool &has_wire_cap, + int &fanout, + bool &has_fanout) const; float portExtCap(const Port *port, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max) const; + const RiseFall *rf, + const MinMax *min_max) const; // Connected total capacitance. // pin_cap = pin capacitance + port external pin // wire_cap = port external wire capacitance + net wire capacitance void connectedCap(const Pin *pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float &pin_cap, - float &wire_cap, - float &fanout, - bool &has_net_load) const; + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_net_load) const; void pinCaps(const Pin *pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float &pin_cap, - float &wire_cap, - float &fanout) const; + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + float &pin_cap, + float &wire_cap, + float &fanout) const; void portExtFanout(const Port *port, - const Corner *corner, - const MinMax *min_max, - // Return values. - int &fanout, - bool &exists); + const MinMax *min_max, + // Return values. + int &fanout, + bool &exists) const; int portExtFanout(Port *port, - const Corner *corner, - const MinMax *min_max); + const MinMax *min_max) const; // Return true if search should proceed from pin/clk (no false paths // start at pin/clk). When thru is true, consider -thru exceptions // that start at pin/net/instance also). Transition tr applies to // pin, not clk. bool exceptionFromStates(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - ExceptionStateSet *&states) const; + const RiseFall *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMax *min_max, + ExceptionStateSet *&states); bool exceptionFromStates(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - bool include_filter, - ExceptionStateSet *&states) const; + const RiseFall *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMax *min_max, + bool include_filter, + ExceptionStateSet *&states); void exceptionFromClkStates(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - ExceptionStateSet *&states) const; + const RiseFall *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMax *min_max, + ExceptionStateSet *&states); void filterRegQStates(const Pin *to_pin, - const RiseFall *to_rf, - const MinMax *min_max, - ExceptionStateSet *&states) const; + const RiseFall *to_rf, + const MinMax *min_max, + ExceptionStateSet *&states) const; // Return hierarchical -thru exceptions that start between // from_pin and to_pin. void exceptionThruStates(const Pin *from_pin, const Pin *to_pin, const RiseFall *to_rf, const MinMax *min_max, - ExceptionStateSet *&states) const; + ExceptionStateSet *&states); // Find the highest priority exception with first exception pt at // pin/clk end. void exceptionTo(ExceptionPathType type, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - // Return values. - ExceptionPath *&hi_priority_exception, - int &hi_priority) const; + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + // Return values. + ExceptionPath *&hi_priority_exception, + int &hi_priority); bool exceptionMatchesTo(ExceptionPath *exception, const Pin *pin, const RiseFall *rf, @@ -1011,18 +1027,18 @@ public: bool match_min_max_exactly, bool require_to_pin) const; void groupPathsTo(const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - // Return value. - ExceptionPathSeq &group_paths) const; + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + // Return value. + ExceptionPathSeq &group_paths); bool isCompleteTo(ExceptionState *state, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - bool require_to_pin) const; + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + bool require_to_pin) const; bool isCompleteTo(ExceptionState *state, const Pin *pin, const RiseFall *rf, @@ -1032,38 +1048,46 @@ public: const PinSet &pathDelayInternalFrom() const; bool isPathDelayInternalTo(const Pin *pin) const; bool isPathDelayInternalToBreak(const Pin *pin) const; - ExceptionPathSet &exceptions() { return exceptions_; } + const ExceptionPathSet &exceptions() const { return exceptions_; } void deleteExceptions(); void deleteException(ExceptionPath *exception); void recordException(ExceptionPath *exception); void unrecordException(ExceptionPath *exception); - void annotateGraph(); - void removeGraphAnnotations(); // Network edit before/after methods. void deletePinBefore(const Pin *pin); void connectPinAfter(const Pin *pin); void clkHpinDisablesChanged(const Pin *pin); void makeClkHpinDisable(const Clock *clk, - const Pin *drvr, - const Pin *load); + const Pin *drvr, + const Pin *load); void ensureClkHpinDisables(); + //////////////////////////////////////////////////////////////// + // + // Sdc/Mode dependewnt state + // + //////////////////////////////////////////////////////////////// + + // Vertices are constrained if they have one or more of the + // following timing constraints: + // output delay constraints + // data check constraints + // path delay constraints + bool isConstrainedEnd(const Pin *pin) const; + protected: void portMembers(const Port *port, - PortSeq &ports); + PortSeq &ports) const; void initVariables(); void clearCycleAcctings(); - void removeLibertyAnnotations(); void deleteExceptionsReferencing(Clock *clk); void deleteClkPinMappings(Clock *clk); void makeClkPinMappings(Clock *clk); void deletePinClocks(Clock *defining_clk, - PinSet *pins); + const PinSet &pins); void makeDefaultArrivalClock(); InputDrive *ensureInputDrive(const Port *port); - PortExtCap *ensurePortExtPinCap(const Port *port, - const Corner *corner); ExceptionPath *findMergeMatch(ExceptionPath *exception); void addException1(ExceptionPath *exception); void addException2(ExceptionPath *exception); @@ -1077,140 +1101,140 @@ protected: bool hasLibertyCheckTo(const Pin *pin); void deleteMatchingExceptions(ExceptionPath *exception); void findMatchingExceptions(ExceptionPath *exception, - ExceptionPathSet &matches); + ExceptionPathSet &matches); void checkForThruHpins(ExceptionPath *exception); void findMatchingExceptionsFirstFrom(ExceptionPath *exception, - ExceptionPathSet &matches); + ExceptionPathSet &matches); void findMatchingExceptionsFirstThru(ExceptionPath *exception, - ExceptionPathSet &matches); + ExceptionPathSet &matches); void findMatchingExceptionsFirstTo(ExceptionPath *exception, - ExceptionPathSet &matches); + ExceptionPathSet &matches); void findMatchingExceptionsClks(ExceptionPath *exception, - ClockSet *clks, - ClockExceptionsMap &exception_map, - ExceptionPathSet &matches); + ClockSet *clks, + ClockExceptionsMap &exception_map, + ExceptionPathSet &matches); void findMatchingExceptionsPins(ExceptionPath *exception, - PinSet *pins, - PinExceptionsMap &exception_map, - ExceptionPathSet &matches); + PinSet *pins, + PinExceptionsMap &exception_map, + ExceptionPathSet &matches); void findMatchingExceptionsInsts(ExceptionPath *exception, - InstanceSet *insts, - InstanceExceptionsMap &exception_map, - ExceptionPathSet &matches); + InstanceSet *insts, + InstanceExceptionsMap &exception_map, + ExceptionPathSet &matches); void findMatchingExceptions(ExceptionPath *exception, - ExceptionPathSet *potential_matches, - ExceptionPathSet &matches); + ExceptionPathSet *potential_matches, + ExceptionPathSet &matches); void expandExceptionExcluding(ExceptionPath *exception, - ExceptionPath *excluding, - ExceptionPathSet &expanded_matches); + ExceptionPath *excluding, + ExceptionPathSet &expansions); void recordException1(ExceptionPath *exception); void recordExceptionFirstPts(ExceptionPath *exception); void recordExceptionFirstFrom(ExceptionPath *exception); void recordExceptionFirstThru(ExceptionPath *exception); void recordExceptionFirstTo(ExceptionPath *exception); void recordExceptionClks(ExceptionPath *exception, - ClockSet *clks, - ClockExceptionsMap &exception_map); + ClockSet *clks, + ClockExceptionsMap &exception_map); void recordExceptionInsts(ExceptionPath *exception, - InstanceSet *insts, - InstanceExceptionsMap &exception_map); + InstanceSet *insts, + InstanceExceptionsMap &exception_map); void recordExceptionPins(ExceptionPath *exception, - PinSet *pins, - PinExceptionsMap &exception_map); + PinSet *pins, + PinExceptionsMap &exception_map); void recordExceptionNets(ExceptionPath *exception, - NetSet *nets, - NetExceptionsMap &exception_map); + NetSet *nets, + NetExceptionsMap &exception_map); void recordExceptionHpin(ExceptionPath *exception, - Pin *pin, - PinExceptionsMap &exception_map); + Pin *pin, + PinExceptionsMap &exception_map); void recordExceptionEdges(ExceptionPath *exception, - EdgePinsSet *edges, - EdgeExceptionsMap &exception_map); + EdgePinsSet *edges, + EdgeExceptionsMap &exception_map); void recordMergeHash(ExceptionPath *exception, ExceptionPt *missing_pt); void recordMergeHashes(ExceptionPath *exception); void unrecordExceptionFirstPts(ExceptionPath *exception); void unrecordExceptionPins(ExceptionPath *exception); void unrecordExceptionClks(ExceptionPath *exception, - ClockSet *clks, - ClockExceptionsMap &exception_map); + ClockSet *clks, + ClockExceptionsMap &exception_map); void unrecordExceptionPins(ExceptionPath *exception, - PinSet *pins, - PinExceptionsMap &exception_map); + PinSet *pins, + PinExceptionsMap &exception_map); void unrecordExceptionInsts(ExceptionPath *exception, - InstanceSet *insts, - InstanceExceptionsMap &exception_map); + InstanceSet *insts, + InstanceExceptionsMap &exception_map); void unrecordExceptionEdges(ExceptionPath *exception, - EdgePinsSet *edges, - EdgeExceptionsMap &exception_map); + EdgePinsSet *edges, + EdgeExceptionsMap &exception_map); void unrecordExceptionNets(ExceptionPath *exception, - NetSet *nets, - NetExceptionsMap &exception_map); + NetSet *nets, + NetExceptionsMap &exception_map); void unrecordExceptionHpin(ExceptionPath *exception, - Pin *pin, - PinExceptionsMap &exception_map); + Pin *pin, + PinExceptionsMap &exception_map); void unrecordMergeHashes(ExceptionPath *exception); void unrecordMergeHash(ExceptionPath *exception, - ExceptionPt *missing_pt); + ExceptionPt *missing_pt); void mergeException(ExceptionPath *exception); void expandException(ExceptionPath *exception, - ExceptionPathSet &expansions); + ExceptionPathSet &expansions); bool exceptionFromStates(const ExceptionPathSet *exceptions, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - bool include_filter, - ExceptionStateSet *&states) const; + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + bool include_filter, + ExceptionStateSet *&states) const; void exceptionThruStates(const ExceptionPathSet *exceptions, - const RiseFall *to_rf, - const MinMax *min_max, - // Return value. - ExceptionStateSet *&states) const; + const RiseFall *to_rf, + const MinMax *min_max, + // Return value. + ExceptionStateSet *&states) const; void exceptionTo(const ExceptionPathSet *to_exceptions, - ExceptionPathType type, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - // Return values. - ExceptionPath *&hi_priority_exception, - int &hi_priority) const; + ExceptionPathType type, + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + // Return values. + ExceptionPath *&hi_priority_exception, + int &hi_priority) const; void exceptionTo(ExceptionPath *exception, - ExceptionPathType type, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - // Return values. - ExceptionPath *&hi_priority_exception, - int &hi_priority) const; + ExceptionPathType type, + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + // Return values. + ExceptionPath *&hi_priority_exception, + int &hi_priority) const; void groupPathsTo(const ExceptionPathSet *to_exceptions, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - // Return value. - ExceptionPathSeq &group_paths) const; + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + // Return value. + ExceptionPathSeq &group_paths) const; void makeLoopPath(ExceptionThruSeq *thrus); void makeLoopException(const Pin *loop_input_pin, - const Pin *loop_pin, - const Pin *loop_prev_pin); + const Pin *loop_pin, + const Pin *loop_prev_pin); void makeLoopExceptionThru(const Pin *pin, - ExceptionThruSeq *thrus); + ExceptionThruSeq *thrus); void deleteConstraints(); InputDelay *findInputDelay(const Pin *pin, - const ClockEdge *clk_edge); + const ClockEdge *clk_edge); InputDelay *makeInputDelay(const Pin *pin, - const ClockEdge *clk_edge); + const ClockEdge *clk_edge); void deleteInputDelays(const Pin *pin, - InputDelay *except); + InputDelay *except); void deleteInputDelaysReferencing(const Clock *clk); void deleteInputDelay(InputDelay *input_delay); OutputDelay *findOutputDelay(const Pin *pin, - const ClockEdge *clk_edge); + const ClockEdge *clk_edge); OutputDelay *makeOutputDelay(const Pin *pin, - const ClockEdge *clk_edge); + const ClockEdge *clk_edge); void deleteOutputDelays(const Pin *pin, OutputDelay *except); void deleteOutputDelaysReferencing(const Clock *clk); @@ -1225,44 +1249,32 @@ protected: // Liberty library to look for defaults. LibertyLibrary *defaultLibertyLibrary(); void annotateGraphConstrainOutputs(); - void annotateDisables(); - void annotateGraphDisabled(const Pin *pin); - void setEdgeDisabledInstPorts(DisabledInstancePorts *disabled_inst); - void setEdgeDisabledInstFrom(Pin *from_pin, - bool disable_checks); - void setEdgeDisabledInstPorts(DisabledPorts *disabled_port, - Instance *inst); void deleteClockLatenciesReferencing(Clock *clk); void deleteClockLatency(ClockLatency *latency); void deleteDeratingFactors(); - void annotateGraphOutputDelays(); - void annotateGraphDataChecks(); - void annotateGraphConstrained(const PinSet *pins); - void annotateGraphConstrained(const InstanceSet *insts); - void annotateGraphConstrained(const Instance *inst); - void annotateGraphConstrained(const Pin *pin); + void annotateHierClkLatency(); void annotateHierClkLatency(const Pin *hpin, - ClockLatency *latency); + ClockLatency *latency); void netCaps(const Pin *drvr_pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &pin_cap, - float &wire_cap, - float &fanout, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, bool &has_net_load) const; // connectedCap pin_cap. float connectedPinCap(const Pin *pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max); + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max); float portCapacitance(Instance *inst, LibertyPort *port, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max) const; + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) const; void removeClockGroups(ClockGroups *groups); void ensureClkGroupExclusions(); void makeClkGroupExclusions(ClockGroups *clk_groups); @@ -1270,25 +1282,26 @@ protected: void makeClkGroupExclusions(ClockGroupSet *groups); void makeClkGroupSame(ClockGroup *group); void clearClkGroupExclusions(); - char *makeClockGroupsName(); + std::string makeClockGroupsName(); void setClockSense(const Pin *pin, - const Clock *clk, - ClockSense sense); + const Clock *clk, + ClockSense sense); bool clkStopSense(const Pin *to_pin, - const Clock *clk, - const RiseFall *from_rf, - const RiseFall *to_rf) const; + const Clock *clk, + const RiseFall *from_rf, + const RiseFall *to_rf) const; void disconnectPinBefore(const Pin *pin, - ExceptionPathSet *exceptions); + ExceptionPathSet *exceptions); void clockGroupsDeleteClkRefs(Clock *clk); void clearGroupPathMap(); + Mode *mode_; AnalysisType analysis_type_; OperatingConditions *operating_conditions_[MinMax::index_count]; InstancePvtMap instance_pvt_maps_[MinMax::index_count]; MinMaxFloatValues voltages_; NetVoltageMap net_voltage_map_; - DeratingFactorsGlobal *derating_factors_; + DeratingFactorsGlobal *derating_factors_{nullptr}; NetDeratingFactorsMap net_derating_factors_; InstDeratingFactorsMap inst_derating_factors_; CellDeratingFactorsMap cell_derating_factors_; @@ -1297,7 +1310,7 @@ protected: // which iterating over the name map can't provide. ClockSeq clocks_; // Clocks are assigned an index. - int clk_index_; + int clk_index_{0}; // Default clock used for unclocked input arrivals. Clock *default_arrival_clk_; ClockNameMap clock_name_map_; @@ -1308,6 +1321,8 @@ protected: bool clk_hpin_disables_valid_; PinSet propagated_clk_pins_; ClockLatencies clk_latencies_; + PinSet clk_latency_pins_; + EdgeClockLatencyMap edge_clk_latency_map_; ClockInsertions clk_insertions_; PinClockUncertaintyMap pin_clk_uncertainty_map_; InterClockUncertaintySet inter_clk_uncertainties_; @@ -1318,7 +1333,7 @@ protected: // clks in the same set_clock_group set. ClockPairSet clk_group_same_; ClockSenseMap clk_sense_map_; - ClockGatingCheck *clk_gating_check_; + ClockGatingCheck *clk_gating_check_{nullptr}; ClockGatingCheckMap clk_gating_check_map_; InstanceClockGatingCheckMap inst_clk_gating_check_map_; PinClockGatingCheckMap pin_clk_gating_check_map_; @@ -1333,7 +1348,7 @@ protected: // Input delays on hierarchical pins are indexed by the load pins. InputDelaysPinMap input_delay_leaf_pin_map_; InputDelaysPinMap input_delay_internal_pin_map_; - int input_delay_index_; + int input_delay_index_{0}; OutputDelaySet output_delays_; OutputDelaysPinMap output_delay_pin_map_; @@ -1352,13 +1367,10 @@ protected: // External parasitics on top level ports. // set_load port // set_fanout_load port - // Indexed by corner_index. - std::vector port_ext_cap_maps_; + PortExtCapMap port_ext_cap_map_; // set_load net - // Indexed by corner_index. - std::vector net_wire_cap_maps_; - // Indexed by corner_index. - std::vector drvr_pin_wire_cap_maps_; + NetWireCapMap net_wire_cap_map_; + PinWireCapMap drvr_pin_wire_cap_map_; NetResistanceMap net_res_map_; PinSet disabled_pins_; PortSet disabled_ports_; @@ -1371,9 +1383,9 @@ protected: PinSet disabled_clk_gating_checks_pin_; LibertyCellSet disabled_clk_gating_checks_lib_cell_; ExceptionPathSet exceptions_; - size_t exception_id_; // Unique ID for exceptions. + size_t exception_id_{0}; // Unique ID for exceptions. - bool have_thru_hpin_exceptions_; + bool have_thru_hpin_exceptions_{false}; // First pin/clock/instance/net/edge exception point to exception set map. PinExceptionsMap first_from_pin_exceptions_; ClockExceptionsMap first_from_clk_exceptions_; @@ -1402,6 +1414,12 @@ protected: bool path_delays_without_to_; // Group path exception names. GroupPathMap group_path_map_; + + // Filter exception to tag arrivals for + // report_timing -from pin|inst -through. + // -to is always nullptr. + FilterPath *filter_{nullptr}; + InputDriveMap input_drive_map_; // set_LogicValue::one/zero/dc LogicValueMap logic_value_map_; @@ -1419,11 +1437,7 @@ protected: float max_leakage_power_; Wireload *wireload_[MinMax::index_count]; WireloadMode wireload_mode_; - WireloadSelection *wireload_selection_[MinMax::index_count]; - - // Annotations on graph objects that are stored in constraints - // rather on the graph itself. - EdgeClockLatencyMap edge_clk_latency_; + const WireloadSelection *wireload_selection_[MinMax::index_count]; private: friend class WriteSdc; @@ -1431,4 +1445,4 @@ private: friend class GroupPathIterator; }; -} // namespace +} // namespace sta diff --git a/include/sta/SdcClass.hh b/include/sta/SdcClass.hh index 2fece7b36..570650897 100644 --- a/include/sta/SdcClass.hh +++ b/include/sta/SdcClass.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,13 +24,15 @@ #pragma once -#include "Map.hh" -#include "Set.hh" -#include "Vector.hh" +#include +#include +#include + #include "LibertyClass.hh" -#include "NetworkClass.hh" #include "MinMaxValues.hh" +#include "NetworkClass.hh" #include "PinPair.hh" +#include "Transition.hh" namespace sta { @@ -67,11 +69,11 @@ class PortDelay; enum class AnalysisType { single, bc_wc, ocv }; enum class ExceptionPathType { false_path, loop, multi_cycle, path_delay, - group_path, filter, any}; + group_path, filter, any}; enum class ClockSense { positive, negative, stop }; -typedef std::pair ClockPair; +using ClockPair = std::pair; class ClockIndexLess { @@ -80,26 +82,26 @@ public: const Clock *clk2) const; }; -typedef Vector FloatSeq; -typedef Vector IntSeq; -typedef Vector ClockSeq; -typedef ClockSeq::ConstIterator ClockSeqIterator; -typedef std::vector ConstClockSeq; -typedef Set ClockSet; -typedef std::set ConstClockSet; -typedef ClockSet ClockGroup; -typedef Vector PinSetSeq; -typedef MinMax SetupHold; -typedef MinMaxAll SetupHoldAll; -typedef Vector ExceptionThruSeq; -typedef Set LibertyPortPairSet; -typedef Map DisabledInstancePortsMap; -typedef Map DisabledCellPortsMap; -typedef MinMaxValues ClockUncertainties; -typedef std::set ExceptionPathSet; -typedef PinPair EdgePins; -typedef PinPairSet EdgePinsSet; -typedef Map LogicValueMap; +using FloatSeq = std::vector; +using IntSeq = std::vector; +using ClockSeq = std::vector; +using ClockSeqIterator = VectorIterator; +using ConstClockSeq = std::vector; +using ClockSet = std::set; +using ConstClockSet = std::set; +using ClockGroup = ClockSet; +using PinSetSeq = std::vector; +using SetupHold = MinMax; +using SetupHoldAll = MinMaxAll; +using ExceptionThruSeq = std::vector; +using LibertyPortPairSet = std::set; +using DisabledInstancePortsMap = std::map; +using DisabledCellPortsMap = std::map; +using ClockUncertainties = MinMaxValues; +using ExceptionPathSet = std::set; +using EdgePins = PinPair; +using EdgePinsSet = PinPairSet; +using LogicValueMap = std::map; class ClockSetLess { @@ -108,7 +110,7 @@ public: const ClockSet *set2) const; }; -typedef Set ClockGroupSet; +using ClockGroupSet = std::set; // For Search. class ExceptionState; @@ -121,16 +123,18 @@ public: }; class ExceptionPath; -typedef Set ExceptionStateSet; +using ExceptionStateSet = std::set; // Constraint applies to clock or data paths. enum class PathClkOrData { clk, data }; -const int path_clk_or_data_count = 2; +const size_t path_clk_or_data_count = 2; enum class TimingDerateType { cell_delay, cell_check, net_delay }; -constexpr int timing_derate_type_count = 3; +constexpr size_t timing_derate_type_count = 3; enum class TimingDerateCellType { cell_delay, cell_check }; -constexpr int timing_derate_cell_type_count = 2; +constexpr size_t timing_derate_cell_type_count = 2; + +using DriveCellSlews = std::array; -} // namespace +} // namespace sta diff --git a/include/sta/SdcCmdComment.hh b/include/sta/SdcCmdComment.hh index 0ad099f1c..65b6b6e66 100644 --- a/include/sta/SdcCmdComment.hh +++ b/include/sta/SdcCmdComment.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,22 +24,26 @@ #pragma once +#include +#include + namespace sta { class SdcCmdComment { public: - SdcCmdComment(); - SdcCmdComment(const char *comment); - const char *comment() { return comment_; } - void setComment(const char *comment); + SdcCmdComment() = default; + SdcCmdComment(std::string_view comment); + const std::string &comment() { return comment_; } + const std::string &comment() const { return comment_; } + void setComment(std::string_view comment); protected: // Destructor is protected to prevent deletion of a derived // class with a pointer to this base class. - ~SdcCmdComment(); + ~SdcCmdComment() = default; - char *comment_; + std::string comment_; }; -} // namespace +} // namespace sta diff --git a/include/sta/SdcNetwork.hh b/include/sta/SdcNetwork.hh index a3473759b..ea4c83045 100644 --- a/include/sta/SdcNetwork.hh +++ b/include/sta/SdcNetwork.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,6 +25,8 @@ #pragma once #include +#include +#include #include "Network.hh" @@ -36,26 +38,26 @@ class NetworkNameAdapter : public NetworkEdit { public: NetworkNameAdapter(Network *network); - bool linkNetwork(const char *top_cell_name, + bool linkNetwork(std::string_view top_cell_name, bool make_black_boxes, Report *report) override; - const char *name(const Library *library) const override; + std::string name(const Library *library) const override; ObjectId id(const Library *library) const override; LibertyLibrary *defaultLibertyLibrary() const override; LibraryIterator *libraryIterator() const override; LibertyLibraryIterator *libertyLibraryIterator() const override; - Library *findLibrary(const char *name) override; - LibertyLibrary *findLibertyFilename(const char *filename) override; - LibertyLibrary *findLiberty(const char *name) override; + Library *findLibrary(std::string_view name) override; + LibertyLibrary *findLibertyFilename(std::string_view filename) override; + LibertyLibrary *findLiberty(std::string_view name) override; Cell *findCell(const Library *library, - const char *name) const override; + std::string_view name) const override; CellSeq findCellsMatching(const Library *library, const PatternMatch *pattern) const override; - const char *name(const Cell *cell) const override; + std::string name(const Cell *cell) const override; std::string getAttribute(const Cell *cell, - const std::string &key) const override; + std::string_view key) const override; const AttributeMap &attributeMap(const Cell *cell) const override; ObjectId id(const Cell *cell) const override; Library *library(const Cell *cell) const override; @@ -63,9 +65,9 @@ public: const LibertyCell *libertyCell(const Cell *cell) const override; Cell *cell(LibertyCell *cell) const override; const Cell *cell(const LibertyCell *cell) const override; - const char *filename(const Cell *cell) override; + std::string_view filename(const Cell *cell) const override; Port *findPort(const Cell *cell, - const char *name) const override; + std::string_view name) const override; PortSeq findPortsMatching(const Cell *cell, const PatternMatch *pattern) const override; bool isLeaf(const Cell *cell) const override; @@ -73,7 +75,7 @@ public: CellPortBitIterator *portBitIterator(const Cell *cell) const override; int portBitCount(const Cell *cell) const override; - const char *name(const Port *port) const override; + std::string name(const Port *port) const override; ObjectId id(const Port *port) const override; Cell *cell(const Port *port) const override; LibertyPort *libertyPort(const Port *port) const override; @@ -81,7 +83,7 @@ public: bool isBundle(const Port *port) const override; bool isBus(const Port *port) const override; int size(const Port *port) const override; - const char *busName(const Port *port) const override; + std::string busName(const Port *port) const override; Port *findBusBit(const Port *port, int index) const override; int fromIndex(const Port *port) const override; @@ -93,7 +95,7 @@ public: ObjectId id(const Instance *instance) const override; std::string getAttribute(const Instance *inst, - const std::string &key) const override; + std::string_view key) const override; const AttributeMap &attributeMap(const Instance *inst) const override; Instance *topInstance() const override; Cell *cell(const Instance *instance) const override; @@ -145,15 +147,15 @@ public: void setPathEscape(char escape) override; bool isEditable() const override; - LibertyLibrary *makeLibertyLibrary(const char *name, - const char *filename) override; + LibertyLibrary *makeLibertyLibrary(std::string_view name, + std::string_view filename) override; Instance *makeInstance(LibertyCell *cell, - const char *name, + std::string_view name, Instance *parent) override; void makePins(Instance *inst) override; void replaceCell(Instance *inst, Cell *to_cell) override; - Net *makeNet(const char *name, + Net *makeNet(std::string_view name, Instance *parent) override; Pin *connect(Instance *inst, Port *port, @@ -195,47 +197,47 @@ public: SdcNetwork(Network *network); Port *findPort(const Cell *cell, - const char *name) const override; + std::string_view name) const override; PortSeq findPortsMatching(const Cell *cell, const PatternMatch *pattern) const override; - const char *name(const Port *port) const override; - const char *busName(const Port *port) const override; + std::string name(const Port *port) const override; + std::string busName(const Port *port) const override; - const char *name(const Instance *instance) const override; - const char *pathName(const Instance *instance) const override; - const char *pathName(const Pin *pin) const override; - const char *portName(const Pin *pin) const override; + std::string name(const Instance *instance) const override; + std::string pathName(const Instance *instance) const override; + std::string pathName(const Pin *pin) const override; + std::string portName(const Pin *pin) const override; - const char *name(const Net *net) const override; - const char *pathName(const Net *net) const override; + std::string name(const Net *net) const override; + std::string pathName(const Net *net) const override; - Instance *findInstance(const char *path_name) const override; + Instance *findInstance(std::string_view path_name) const override; Instance *findInstanceRelative(const Instance *inst, - const char *path_name) const override; + std::string_view path_name) const override; InstanceSeq findInstancesMatching(const Instance *context, const PatternMatch *pattern) const override; - Net *findNet(const char *path_name) const override; - Net *findNetRelative(const Instance *instance, - const char *net_name) const override; + Net *findNet(std::string_view path_name) const override; + Net *findNetRelative(const Instance *inst, + std::string_view path_name) const override; Net *findNet(const Instance *instance, - const char *net_name) const override; + std::string_view net_name) const override; NetSeq findNetsMatching(const Instance *parent, const PatternMatch *pattern) const override; void findInstNetsMatching(const Instance *instance, const PatternMatch *pattern, - NetSeq &nets) const override; + NetSeq &matches) const override; Instance *findChild(const Instance *parent, - const char *name) const override; - Pin *findPin(const char *path_name) const override; + std::string_view name) const override; + Pin *findPin(std::string_view path_name) const override; Pin *findPin(const Instance *instance, - const char *port_name) const override; + std::string_view port_name) const override; PinSeq findPinsMatching(const Instance *instance, const PatternMatch *pattern) const override; Instance *makeInstance(LibertyCell *cell, - const char *name, + std::string_view name, Instance *parent) override; - Net *makeNet(const char *name, + Net *makeNet(std::string_view name, Instance *parent) override; // The following member functions are inherited from the @@ -247,38 +249,45 @@ public: using Network::findPin; protected: - void parsePath(const char *path, - // Return values. - Instance *&inst, - const char *&path_tail) const; - void scanPath(const char *path, - // Return values. - // Unescaped divider count. - int ÷r_count, - int &path_length) const; - void parsePath(const char *path, - int divider_count, - int path_length, - // Return values. - Instance *&inst, - const char *&path_tail) const; + void parsePath(std::string_view path, + // Return values. + Instance *&inst, + std::string &path_tail) const; + void scanPath(std::string_view path, + // Return values. + // Unescaped divider count. + int ÷r_count, + int &path_length) const; + void parsePath(std::string_view path, + int divider_count, + int path_length, + // Return values. + Instance *&inst, + std::string &path_tail) const; bool visitMatches(const Instance *parent, - const PatternMatch *pattern, - std::function - visit_tail) const; + const PatternMatch *pattern, + const std::function + &visit_tail) const; bool visitPinTail(const Instance *instance, - const PatternMatch *tail, - PinSeq &matches) const; + const PatternMatch *tail, + PinSeq &matches) const; void findInstancesMatching1(const Instance *context, const PatternMatch *pattern, InstanceSeq &matches) const; - const char *staToSdc(const char *sta_name) const; + std::string staToSdc(std::string_view sta_name) const; }; // Encapsulate a network to map names to/from the sdc namespace. Network * makeSdcNetwork(Network *network); -} // namespace +std::string +escapeDividers(std::string_view name, + const Network *network); +std::string +escapeBrackets(std::string_view name, + const Network *network); + +} // namespace sta diff --git a/include/sta/Search.hh b/include/sta/Search.hh index 679fd15e1..442dbd00a 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -1,5 +1,5 @@ // opensta, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,23 +24,24 @@ #pragma once -#include #include +#include +#include #include -#include "MinMax.hh" -#include "UnorderedSet.hh" -#include "Transition.hh" +#include "Delay.hh" +#include "GraphClass.hh" #include "LibertyClass.hh" +#include "MinMax.hh" #include "NetworkClass.hh" -#include "GraphClass.hh" -#include "Delay.hh" +#include "Path.hh" #include "SdcClass.hh" -#include "StaState.hh" #include "SearchClass.hh" #include "SearchPred.hh" +#include "StaState.hh" +#include "StringUtil.hh" +#include "Transition.hh" #include "VertexVisitor.hh" -#include "Path.hh" namespace sta { @@ -48,6 +49,7 @@ class BfsFwdIterator; class BfsBkwdIterator; class SearchPred; class SearchThru; +class SearchAdj; class ClkInfoLess; class PathEndVisitor; class ArrivalVisitor; @@ -56,68 +58,64 @@ class ClkPathIterator; class EvalPred; class TagGroup; class TagGroupBldr; -class PathGroups; class WorstSlacks; -class DcalcAnalysisPt; class VisitPathEnds; class GatedClk; class CheckCrpr; -class Genclks; -class Corner; - -typedef Set ClkInfoSet; -typedef UnorderedSet TagSet; -typedef UnorderedSet TagGroupSet; -typedef Map VertexSlackMap; -typedef Vector VertexSlackMapSeq; -typedef Vector WorstSlacksSeq; -typedef std::vector DelayDblSeq; -typedef Vector ExceptionPathSeq; -typedef std::vector PathGroupSeq; +class Scene; + +using ClkInfoSet = std::set; +using TagSet = std::unordered_set; +using TagGroupSet = std::unordered_set; +using VertexSlackMap = std::map; +using VertexSlackMapSeq = std::vector; +using WorstSlacksSeq = std::vector; +using DelayDblSeq = std::vector; +using ExceptionPathSeq = std::vector; class Search : public StaState { public: - explicit Search(StaState *sta); - virtual ~Search(); - virtual void copyState(const StaState *sta); + Search(StaState *sta); + ~Search() override; + void copyState(const StaState *sta) override; // Reset to virgin state. void clear(); // When enabled, non-critical path arrivals are pruned to improve // run time and reduce memory. - bool crprPathPruningEnabled() const; + [[nodiscard]] bool crprPathPruningEnabled() const; void setCrprpathPruningEnabled(bool enabled); // When path pruning is enabled required times for non-critical paths // that have been pruned require additional search. This option // disables additional search to returns approximate required times. - bool crprApproxMissingRequireds() const; + [[nodiscard]] bool crprApproxMissingRequireds() const; void setCrprApproxMissingRequireds(bool enabled); - bool unconstrainedPaths() const { return unconstrained_paths_; } + [[nodiscard]] bool unconstrainedPaths() const { return unconstrained_paths_; } // from/thrus/to are owned and deleted by Search. - // Use corner nullptr to report timing for all corners. - // PathEnds are owned by Search PathGroups and deleted on next call. + // Use scene nullptr to report timing for all scenes. + // PathEnds are owned by Mode PathGroups and deleted on next call. PathEndSeq findPathEnds(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, bool unconstrained, - const Corner *corner, + const SceneSeq &scenes, const MinMaxAll *min_max, - size_t group_path_count, - size_t endpoint_path_count, + int group_path_count, + int endpoint_path_count, bool unique_pins, bool unique_edges, float slack_min, float slack_max, bool sort_by_slack, - PathGroupNameSet *group_names, + StringSeq &group_names, bool setup, bool hold, bool recovery, bool removal, bool clk_gating_setup, bool clk_gating_hold); - bool arrivalsValid(); + [[nodiscard]] bool arrivalsValid(); // Invalidate all arrival and required times. void arrivalsInvalid(); // Invalidate vertex arrival time. @@ -141,83 +139,60 @@ public: void findRequireds(); // Find required times down thru level. void findRequireds(Level level); - bool requiredsSeeded() const { return requireds_seeded_; } - bool requiredsExist() const { return requireds_exist_; } + [[nodiscard]] bool requiredsSeeded() const { return requireds_seeded_; } + [[nodiscard]] bool requiredsExist() const { return requireds_exist_; } // The sum of all negative endpoints slacks. // Incrementally updated. Slack totalNegativeSlack(const MinMax *min_max); - Slack totalNegativeSlack(const Corner *corner, - const MinMax *min_max); + Slack totalNegativeSlack(const Scene *scene, + const MinMax *min_max); // Worst endpoint slack and vertex. // Incrementally updated. void worstSlack(const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex); - void worstSlack(const Corner *corner, - const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex); + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); + void worstSlack(const Scene *scene, + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); // Clock arrival respecting ideal clock insertion delay and latency. Arrival clkPathArrival(const Path *clk_path) const; Arrival clkPathArrival(const Path *clk_path, - const ClkInfo *clk_info, - const ClockEdge *clk_edge, - const MinMax *min_max, - const PathAnalysisPt *path_ap) const; + const ClkInfo *clk_info, + const ClockEdge *clk_edge, + const MinMax *min_max) const; // Clock arrival at the path source/launch point. Arrival pathClkPathArrival(const Path *path) const; - PathGroupSeq pathGroups(const PathEnd *path_end) const; void deletePathGroups(); - void makePathGroups(int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float min_slack, - float max_slack, - PathGroupNameSet *group_names, - bool setup, - bool hold, - bool recovery, - bool removal, - bool clk_gating_setup, - bool clk_gating_hold); - virtual ExceptionPath *exceptionTo(ExceptionPathType type, - const Path *path, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - bool require_to_pin) const; + ExceptionPath *exceptionTo(ExceptionPathType type, + const Path *path, + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + bool require_to_pin, + Sdc *sdc) const; ExceptionPathSeq groupPathsTo(const PathEnd *path_end) const; - FilterPath *filter() const { return filter_; } void deleteFilter(); void deleteFilteredArrivals(); - VertexSet *endpoints(); + VertexSet &endpoints(); void endpointsInvalid(); - // Clock tree vertices between the clock source pin and register clk pins. - // This does NOT include generated clock source paths. - bool isClock(const Vertex *vertex) const; - // Vertices on propagated generated clock source paths. - bool isGenClkSrc(const Vertex *vertex) const; // The set of clocks that arrive at vertex in the clock network. - ClockSet clocks(const Vertex *vertex) const; - ClockSet clocks(const Pin *pin) const; + ClockSet clocks(const Pin *pin, + const Mode *mode) const; + ClockSet clocks(const Vertex *vertex, + const Mode *mode) const; // Clock domains for a vertex. - ClockSet clockDomains(const Vertex *vertex) const; - ClockSet clockDomains(const Pin *pin) const; - void visitStartpoints(VertexVisitor *visitor); - void visitEndpoints(VertexVisitor *visitor); - bool havePathGroups() const; - PathGroup *findPathGroup(const char *name, - const MinMax *min_max) const; - PathGroup *findPathGroup(const Clock *clk, - const MinMax *min_max) const; + ClockSet clockDomains(const Vertex *vertex, + const Mode *mode) const; + ClockSet clockDomains(const Pin *pin, + const Mode *mode) const; //////////////////////////////////////////////////////////////// // @@ -233,37 +208,42 @@ public: bool isClkGateVertex(Vertex *vertex); void seedArrival(Vertex *vertex); EvalPred *evalPred() const { return eval_pred_; } - SearchPred *searchAdj() const { return search_adj_; } + SearchPred *searchAdj() const { return search_thru_; } Tag *tag(TagIndex index) const; TagIndex tagCount() const; TagGroupIndex tagGroupCount() const; void reportTagGroups() const; void reportPathCountHistogram() const; - virtual int clkInfoCount() const; - virtual bool isEndpoint(Vertex *vertex) const; - virtual bool isEndpoint(Vertex *vertex, - SearchPred *pred) const; + int clkInfoCount() const; + // Endpoint for any mode. + [[nodiscard]] bool isEndpoint(Vertex *vertex) const; + // Endpoint for one mode. + [[nodiscard]] bool isEndpoint(Vertex *vertex, + const Mode *mode) const; + [[nodiscard]] bool isEndpoint(Vertex *vertex, + const ModeSeq &modes) const; + [[nodiscard]] bool isEndpoint(Vertex *vertex, + SearchPred *pred, + const Mode *mode) const; void endpointInvalid(Vertex *vertex); Tag *fromUnclkedInputTag(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - const PathAnalysisPt *path_ap, - bool is_segment_start, - bool require_exception); + const RiseFall *rf, + const MinMax *min_max, + bool is_segment_start, + bool require_exception, + Scene *scene); Tag *fromRegClkTag(const Pin *from_pin, - const RiseFall *from_rf, - const Clock *clk, - const RiseFall *clk_rf, - const ClkInfo *clk_info, - const Pin *to_pin, - const RiseFall *to_rf, - const MinMax *min_max, - const PathAnalysisPt *path_ap); + const RiseFall *from_rf, + const Clock *clk, + const RiseFall *clk_rf, + const ClkInfo *clk_info, + const Pin *to_pin, + const RiseFall *to_rf, + const MinMax *min_max, + Scene *scene); Tag *thruTag(Tag *from_tag, Edge *edge, const RiseFall *to_rf, - const MinMax *min_max, - const PathAnalysisPt *path_ap, TagSet *tag_cache); Tag *thruClkTag(Path *from_path, Vertex *from_vertex, @@ -273,70 +253,74 @@ public: const RiseFall *to_rf, bool arc_delay_min_max_eq, const MinMax *min_max, - const PathAnalysisPt *path_ap); + Scene *scene); const ClkInfo *thruClkInfo(Path *from_path, - Vertex *from_vertex, - const ClkInfo *from_clk_info, - bool from_is_clk, - Edge *edge, - Vertex *to_vertex, - const Pin *to_pin, - bool to_is_clk, - bool arc_delay_min_max_eq, - const MinMax *min_max, - const PathAnalysisPt *path_ap); + Vertex *from_vertex, + const ClkInfo *from_clk_info, + bool from_is_clk, + Edge *edge, + Vertex *to_vertex, + const Pin *to_pin, + bool to_is_clk, + bool arc_delay_min_max_eq, + const MinMax *min_max, + Scene *scene); const ClkInfo *clkInfoWithCrprClkPath(const ClkInfo *from_clk_info, - Path *from_path, - const PathAnalysisPt *path_ap); + Path *from_path); void seedClkArrivals(const Pin *pin, - Vertex *vertex, - TagGroupBldr *tag_bldr); + const Mode *mode, + TagGroupBldr *tag_bldr); void setVertexArrivals(Vertex *vertex, - TagGroupBldr *group_bldr); + TagGroupBldr *tag_bldr); void tnsInvalid(Vertex *vertex); - bool arrivalsChanged(Vertex *vertex, - TagGroupBldr *tag_bldr); + [[nodiscard]] bool arrivalsChanged(Vertex *vertex, + TagGroupBldr *tag_bldr); BfsFwdIterator *arrivalIterator() const { return arrival_iter_; } BfsBkwdIterator *requiredIterator() const { return required_iter_; } - bool arrivalsAtEndpointsExist()const{return arrivals_at_endpoints_exist_;} // Used by OpenROAD. bool makeUnclkedPaths(Vertex *vertex, - bool is_segment_start, + bool is_segment_start, bool require_exception, - TagGroupBldr *tag_bldr); + TagGroupBldr *tag_bldr, + const Mode *mode); bool makeUnclkedPaths2(Vertex *vertex, TagGroupBldr *tag_bldr); - bool isSegmentStart(const Pin *pin); - bool isInputArrivalSrchStart(Vertex *vertex); + [[nodiscard]] bool isInputArrivalSrchStart(Vertex *vertex); void seedInputSegmentArrival(const Pin *pin, - Vertex *vertex, - TagGroupBldr *tag_bldr); + Vertex *vertex, + const Mode *mode, + TagGroupBldr *tag_bldr); void enqueueLatchDataOutputs(Vertex *vertex); void enqueueLatchOutput(Vertex *vertex); - virtual void seedRequired(Vertex *vertex); - virtual void seedRequiredEnqueueFanin(Vertex *vertex); + void enqueuePendingClkFanouts(); + void postponeClkFanouts(Vertex *vertex); + void seedRequired(Vertex *vertex); + void seedRequiredEnqueueFanin(Vertex *vertex); void seedInputDelayArrival(const Pin *pin, - Vertex *vertex, - InputDelay *input_delay); + Vertex *vertex, + InputDelay *input_delay, + const Mode *mode); void seedInputDelayArrival(const Pin *pin, - Vertex *vertex, - InputDelay *input_delay, - bool is_segment_start, - TagGroupBldr *tag_bldr); + Vertex *vertex, + InputDelay *input_delay, + bool is_segment_start, + const Mode *mode, + TagGroupBldr *tag_bldr); // Insertion delay for regular or generated clock. Arrival clockInsertion(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap) const; - bool propagateClkSense(const Pin *from_pin, - Path *from_path, - const RiseFall *to_rf); - - Tag *findTag(const RiseFall *rf, - const PathAnalysisPt *path_ap, - const ClkInfo *tag_clk, + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + const EarlyLate *early_late, + const Mode *mode) const; + [[nodiscard]] bool propagateClkSense(const Pin *from_pin, + Path *from_path, + const RiseFall *to_rf); + + Tag *findTag(Scene *scene, + const RiseFall *rf, + const MinMax *min_max, + const ClkInfo *clk_info, bool is_clk, InputDelay *input_delay, bool is_segment_start, @@ -345,47 +329,51 @@ public: TagSet *tag_cache); void reportTags() const; void reportClkInfos() const; - const ClkInfo *findClkInfo(const ClockEdge *clk_edge, - const Pin *clk_src, - bool is_propagated, - const Pin *gen_clk_src, - bool gen_clk_src_path, - const RiseFall *pulse_clk_sense, - Arrival insertion, - float latency, - ClockUncertainties *uncertainties, - const PathAnalysisPt *path_ap, - Path *crpr_clk_path); - const ClkInfo *findClkInfo(const ClockEdge *clk_edge, - const Pin *clk_src, - bool is_propagated, - Arrival insertion, - const PathAnalysisPt *path_ap); + const ClkInfo *findClkInfo(Scene *scene, + const ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, + const Pin *gen_clk_src, + bool gen_clk_src_path, + const RiseFall *pulse_clk_sense, + Arrival insertion, + float latency, + const ClockUncertainties *uncertainties, + const MinMax *min_max, + Path *crpr_clk_path); + const ClkInfo *findClkInfo(Scene *scene, + const ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, + Arrival insertion, + const MinMax *min_max); // Timing derated arc delay for a path analysis point. ArcDelay deratedDelay(const Vertex *from_vertex, - const TimingArc *arc, - const Edge *edge, - bool is_clk, - const PathAnalysisPt *path_ap); + const TimingArc *arc, + const Edge *edge, + bool is_clk, + const MinMax *min_max, + DcalcAPIndex dcalc_ap, + const Sdc *sdc); TagGroup *tagGroup(const Vertex *vertex) const; TagGroup *tagGroup(TagGroupIndex index) const; void reportArrivals(Vertex *vertex, - bool report_tag_index) const; + bool report_tag_index) const; Slack wnsSlack(Vertex *vertex, - PathAPIndex path_ap_index); + PathAPIndex path_ap_index); void levelsChangedBefore(); void levelChangedBefore(Vertex *vertex); void seedInputArrival(const Pin *pin, - Vertex *vertex, - TagGroupBldr *tag_bldr); + Vertex *vertex, + const Mode *mode, + TagGroupBldr *tag_bldr); void ensureDownstreamClkPins(); - bool matchesFilter(Path *path, - const ClockEdge *to_clk_edge); + [[nodiscard]] bool matchesFilter(Path *path, + const ClockEdge *to_clk_edge); CheckCrpr *checkCrpr() { return check_crpr_; } VisitPathEnds *visitPathEnds() { return visit_path_ends_; } GatedClk *gatedClk() { return gated_clk_; } - Genclks *genclks() { return genclks_; } void findClkVertexPins(PinSet &clk_pins); void findFilteredArrivals(ExceptionFrom *from, ExceptionThruSeq *thrus, @@ -396,10 +384,10 @@ public: Arrival *arrivals(const Vertex *vertex) const; Arrival *makeArrivals(const Vertex *vertex, - uint32_t count); + uint32_t count); void deleteArrivals(const Vertex *vertex); Required *requireds(const Vertex *vertex) const; - bool hasRequireds(const Vertex *vertex) const; + [[nodiscard]] bool hasRequireds(const Vertex *vertex) const; Required *makeRequireds(const Vertex *vertex, uint32_t count); void deleteRequireds(const Vertex *vertex); @@ -409,11 +397,11 @@ public: Path *makePrevPaths(const Vertex *vertex, uint32_t count); void deletePrevPaths(Vertex *vertex); - bool crprPathPruningDisabled(const Vertex *vertex) const; + [[nodiscard]] bool crprPathPruningDisabled(const Vertex *vertex) const; void setCrprPathPruningDisabled(const Vertex *vertex, bool disabled); - bool bfsInQueue(const Vertex *vertex, - BfsIndex index) const; + [[nodiscard]] bool bfsInQueue(const Vertex *vertex, + BfsIndex index) const; void setBfsInQueue(const Vertex *vertex, BfsIndex index, bool value); @@ -425,16 +413,16 @@ public: void deleteTagGroup(TagGroup *group); bool postponeLatchOutputs() const { return postpone_latch_outputs_; } void saveEnumPath(Path *path); + bool isSrchRoot(Vertex *vertex, + const Mode *mode) const; + DelaysWrtClks arrivalsWrtClks(Vertex *vertex, + const Scene *scene); + DelaysWrtClks delaysWrtClks(Vertex *vertex, + const Scene *scene, + const PathDelayFunc &get_path_delay); protected: - void init(StaState *sta); void initVars(); - void makeAnalysisPts(AnalysisType analysis_type); - void makeAnalysisPts(bool swap_clk_min_max, - bool report_min, - bool report_max, - DcalcAnalysisPt *dcalc_ap_min, - DcalcAnalysisPt *dcalc_ap_max); void deleteTags(); void deleteTagsPrev(); void deleteUnusedTagGroups(); @@ -442,90 +430,90 @@ protected: void seedArrivals(); void findClockVertices(VertexSet &vertices); void seedClkDataArrival(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const ClockEdge *clk_edge, - const MinMax *min_max, - const PathAnalysisPt *path_ap, - Arrival insertion, - TagGroupBldr *tag_bldr); + const RiseFall *rf, + const Clock *clk, + const ClockEdge *clk_edge, + const MinMax *min_max, + Arrival insertion, + Scene *scene, + TagGroupBldr *tag_bldr); void seedClkArrival(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const ClockEdge *clk_edge, - const MinMax *min_max, - const PathAnalysisPt *path_ap, - Arrival insertion, - TagGroupBldr *tag_bldr); + const RiseFall *rf, + const Clock *clk, + const ClockEdge *clk_edge, + const MinMax *min_max, + Arrival insertion, + Scene *scene, + TagGroupBldr *tag_bldr); Tag *clkDataTag(const Pin *pin, - const Clock *clk, - const RiseFall *rf, - const ClockEdge *clk_edge, - Arrival insertion, - const MinMax *min_max, - const PathAnalysisPt *path_ap); + const Clock *clk, + const RiseFall *rf, + const ClockEdge *clk_edge, + Arrival insertion, + const MinMax *min_max, + Scene *scene); void findInputArrivalVertices(VertexSet &vertices); - void seedInputArrivals(ClockSet *clks); void findRootVertices(VertexSet &vertices); void findInputDrvrVertices(VertexSet &vertices); void seedInputArrival1(const Pin *pin, - Vertex *vertex, - bool is_segment_start, - TagGroupBldr *tag_bldr); + Vertex *vertex, + bool is_segment_start, + const Mode *mode, + TagGroupBldr *tag_bldr); void seedInputArrival(const Pin *pin, - Vertex *vertex, - ClockSet *wrt_clks); + Vertex *vertex, + ClockSet *wrt_clks); void seedInputDelayArrival(const Pin *pin, - InputDelay *input_delay, - const ClockEdge *clk_edge, - float clk_arrival, - float clk_insertion, - float clk_latency, - bool is_segment_start, - const MinMax *min_max, - PathAnalysisPt *path_ap, - TagGroupBldr *tag_bldr); + InputDelay *input_delay, + const ClockEdge *clk_edge, + float clk_arrival, + float clk_insertion, + float clk_latency, + bool is_segment_start, + const MinMax *min_max, + Scene *scene, + TagGroupBldr *tag_bldr); void seedInputDelayArrival(const Pin *pin, - const RiseFall *rf, - float arrival, - InputDelay *input_delay, - const ClockEdge *clk_edge, - float clk_insertion, - float clk_latency, - bool is_segment_start, - const MinMax *min_max, - PathAnalysisPt *path_ap, - TagGroupBldr *tag_bldr); + const RiseFall *rf, + float arrival, + InputDelay *input_delay, + const ClockEdge *clk_edge, + float clk_insertion, + float clk_latency, + bool is_segment_start, + const MinMax *min_max, + Scene *scene, + TagGroupBldr *tag_bldr); void inputDelayClkArrival(InputDelay *input_delay, - const ClockEdge *clk_edge, - const MinMax *min_max, - const PathAnalysisPt *path_ap, - // Return values. - float &clk_arrival, - float &clk_insertion, - float &clk_latency); + const ClockEdge *clk_edge, + const MinMax *min_max, + const Mode *mode, + // Return values. + float &clk_arrival, + float &clk_insertion, + float &clk_latency); void inputDelayRefPinArrival(Path *ref_path, - const ClockEdge *clk_edge, - const MinMax *min_max, - // Return values. - float &ref_arrival, - float &ref_insertion, - float &ref_latency); + const ClockEdge *clk_edge, + const MinMax *min_max, + const Sdc *sdc, + // Return values. + float &ref_arrival, + float &ref_insertion, + float &ref_latency); Tag *inputDelayTag(const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - float clk_insertion, - float clk_latency, - InputDelay *input_delay, - bool is_segment_start, - const MinMax *min_max, - const PathAnalysisPt *path_ap); + const RiseFall *rf, + const ClockEdge *clk_edge, + float clk_insertion, + float clk_latency, + InputDelay *input_delay, + bool is_segment_start, + const MinMax *min_max, + Scene *scene); void seedClkVertexArrivals(); - void seedClkVertexArrivals(const Pin *pin, - Vertex *vertex); void findClkArrivals1(); - void findAllArrivals(bool thru_latches); + void findAllArrivals(bool thru_latches, + bool clks_only); void findArrivals1(Level level); Tag *mutateTag(Tag *from_tag, const Pin *from_pin, @@ -539,32 +527,32 @@ protected: bool to_is_segment_start, const ClkInfo *to_clk_info, InputDelay *to_input_delay, - const MinMax *min_max, - const PathAnalysisPt *path_ap, TagSet *tag_cache); ExceptionPath *exceptionTo(const Path *path, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max) const; + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max) const; void seedRequireds(); void seedInvalidRequireds(); - bool havePendingLatchOutputs(); + [[nodiscard]] bool havePendingLatchOutputs(); void clearPendingLatchOutputs(); void enqueuePendingLatchOutputs(); void findFilteredArrivals(bool thru_latches); void findArrivalsSeed(); void seedFilterStarts(); - bool hasEnabledChecks(Vertex *vertex) const; - virtual float timingDerate(const Vertex *from_vertex, - const TimingArc *arc, - const Edge *edge, - bool is_clk, - const PathAnalysisPt *path_ap); + [[nodiscard]] bool hasEnabledChecks(Vertex *vertex, + const Mode *mode) const; + float timingDerate(const Vertex *from_vertex, + const TimingArc *arc, + const Edge *edge, + bool is_clk, + const Sdc *sdc, + const MinMax *min_max); void deletePaths(); // Delete with incremental tns/wns update. void deletePathsIncr(Vertex *vertex); - TagGroup *findTagGroup(TagGroupBldr *group_bldr); + TagGroup *findTagGroup(TagGroupBldr *tag_bldr); void deleteFilterTags(); void deleteFilterTagGroups(); void deleteFilterClkInfos(); @@ -574,203 +562,214 @@ protected: void updateInvalidTns(); void clearWorstSlack(); void wnsSlacks(Vertex *vertex, - // Return values. - SlackSeq &slacks); + // Return values. + SlackSeq &slacks); void wnsTnsPreamble(); void worstSlackPreamble(); void deleteWorstSlacks(); void updateWorstSlacks(Vertex *vertex, - Slack slacks); + Slack slacks); void updateTns(Vertex *vertex, - SlackSeq &slacks); + SlackSeq &slacks); void tnsIncr(Vertex *vertex, - Slack slack, - PathAPIndex path_ap_index); + Slack slack, + PathAPIndex path_ap_index); void tnsDecr(Vertex *vertex, - PathAPIndex path_ap_index); + PathAPIndex path_ap_index); void tnsNotifyBefore(Vertex *vertex); - bool matchesFilterTo(Path *path, - const ClockEdge *to_clk_edge) const; + [[nodiscard]] bool matchesFilterTo(Path *path, + const ClockEdge *to_clk_edge) const; const Path *pathClkPathArrival1(const Path *path) const; void deletePathsState(const Vertex *vertex) const; void clocks(const Vertex *vertex, + const Mode *mode, // Return value. ClockSet &clks) const; void clockDomains(const Vertex *vertex, + const Mode *mode, // Return value. ClockSet &clks) const; //////////////////////////////////////////////////////////////// // findPathEnds arg. - bool unconstrained_paths_; - bool crpr_path_pruning_enabled_; - bool crpr_approx_missing_requireds_; + bool unconstrained_paths_{false}; + bool crpr_path_pruning_enabled_{true}; + bool crpr_approx_missing_requireds_{true}; + // Search predicates. - SearchPred *search_adj_; - SearchPred *search_clk_; + SearchPred *search_thru_; + SearchAdj *search_adj_; EvalPred *eval_pred_; - ArrivalVisitor *arrival_visitor_; + // Clock arrivals are known. bool clk_arrivals_valid_; // Per-vertex cache of whether the vertex is clock gated. std::vector clk_gated_; // Some arrivals exist. - bool arrivals_exist_; - // Arrivals at end points exist (but may be invalid). - bool arrivals_at_endpoints_exist_; + bool arrivals_exist_{false}; // Arrivals at start points have been initialized. - bool arrivals_seeded_; - // Some requireds exist. - bool requireds_exist_; - // Requireds have been seeded by searching arrivals to all endpoints. - bool requireds_seeded_; + bool arrivals_seeded_{false}; // Vertices with invalid arrival times to update and search from. - VertexSet *invalid_arrivals_; + VertexSet invalid_arrivals_; std::mutex invalid_arrivals_lock_; BfsFwdIterator *arrival_iter_; + ArrivalVisitor *arrival_visitor_; + + // Some requireds exist. + bool requireds_exist_{false}; + // Requireds have been seeded by searching arrivals to all endpoints. + bool requireds_seeded_{false}; // Vertices with invalid required times to update and search from. - VertexSet *invalid_requireds_; + VertexSet invalid_requireds_; BfsBkwdIterator *required_iter_; - bool tns_exists_; + + bool tns_exists_{false}; // Endpoint vertices with slacks that have changed since tns was found. - VertexSet *invalid_tns_; + VertexSet invalid_tns_; // Indexed by path_ap->index(). DelayDblSeq tns_; // Indexed by path_ap->index(). VertexSlackMapSeq tns_slacks_; std::mutex tns_lock_; + // Indexed by path_ap->index(). - WorstSlacks *worst_slacks_; + WorstSlacks *worst_slacks_{nullptr}; + // Use pointer to clk_info set so Tag.hh does not need to be included. ClkInfoSet *clk_info_set_; std::mutex clk_info_lock_; - // Use pointer to tag set so Tag.hh does not need to be included. - TagSet *tag_set_; + // Entries in tags_ may be missing where previous filter tags were deleted. - TagIndex tag_capacity_; + TagIndex tag_capacity_{128}; std::atomic tags_; + // Use pointer to tag set so Tag.hh does not need to be included. + TagSet *tag_set_; std::vector tags_prev_; - TagIndex tag_next_; - // Holes in tags_ left by deleting filter tags. - std::vector tag_free_indices_; + TagIndex tag_next_{0}; std::mutex tag_lock_; - TagGroupSet *tag_group_set_; + + // Capacity of tag_groups_. + TagGroupIndex tag_group_capacity_; std::atomic tag_groups_; + TagGroupSet *tag_group_set_; std::vector tag_groups_prev_; - TagGroupIndex tag_group_next_; + TagGroupIndex tag_group_next_{0}; // Holes in tag_groups_ left by deleting filter tag groups. std::vector tag_group_free_indices_; - // Capacity of tag_groups_. - TagGroupIndex tag_group_capacity_; std::mutex tag_group_lock_; + // Latches data outputs to queue on the next search pass. - VertexSet *pending_latch_outputs_; + VertexSet pending_latch_outputs_; std::mutex pending_latch_outputs_lock_; - VertexSet *endpoints_; - VertexSet *invalid_endpoints_; - // Filter exception to tag arrivals for - // report_timing -from pin|inst -through. - // -to is always nullptr. - FilterPath *filter_; - // filter_from_ is owned by filter_ if it exists. - ExceptionFrom *filter_from_; - ExceptionTo *filter_to_; - VertexSet *filtered_arrivals_; + // Clock network endpoints where arrival search was suppended by findClkArrivals(). + VertexSet pending_clk_endpoints_; + std::mutex pending_clk_endpoints_lock_; + + VertexSet endpoints_; + bool endpoints_initialized_{false}; + VertexSet invalid_endpoints_; + + bool have_filter_{false}; + ExceptionFrom *filter_from_{nullptr}; + ExceptionThruSeq *filter_thrus_{nullptr}; + ExceptionTo *filter_to_{nullptr}; + VertexSet filtered_arrivals_; std::mutex filtered_arrivals_lock_; - bool found_downstream_clk_pins_; - bool postpone_latch_outputs_; - PathGroups *path_groups_; - VisitPathEnds *visit_path_ends_; + + bool found_downstream_clk_pins_{false}; + bool postpone_latch_outputs_{false}; std::vector enum_paths_; + + VisitPathEnds *visit_path_ends_; GatedClk *gated_clk_; CheckCrpr *check_crpr_; - Genclks *genclks_; }; // Eval across latch D->Q edges. // SearchPred0 unless -// timing check edge // disabled loop // disabled converging clock edge (Xilinx) // clk source pin class EvalPred : public SearchPred0 { public: - explicit EvalPred(const StaState *sta); - virtual bool searchThru(Edge *edge); + EvalPred(const StaState *sta); + bool searchThru(Edge *edge, + const Mode *mode) const override; void setSearchThruLatches(bool thru_latches); - virtual bool searchTo(const Vertex *to_vertex); + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; -protected: - bool search_thru_latches_; -}; + using SearchPred::searchFrom; + using SearchPred::searchThru; + using SearchPred::searchTo; -class ClkArrivalSearchPred : public EvalPred -{ -public: - ClkArrivalSearchPred(const StaState *sta); - virtual bool searchThru(Edge *edge); +protected: + bool search_thru_latches_{true}; }; // Class for visiting fanin/fanout paths of a vertex. // This used by forward/backward search to find arrival/required path times. -class PathVisitor : public VertexVisitor, public StaState +class PathVisitor : public VertexVisitor, + public StaState { public: // Uses search->evalPred() for search predicate. PathVisitor(const StaState *sta); PathVisitor(SearchPred *pred, - bool make_tag_cache, - const StaState *sta); - virtual ~PathVisitor(); + bool make_tag_cache, + const StaState *sta); + ~PathVisitor() override; virtual void visitFaninPaths(Vertex *to_vertex); virtual void visitFanoutPaths(Vertex *from_vertex); + // Return false to stop visiting. + virtual bool visitFromToPath(const Pin *from_pin, + Vertex *from_vertex, + const RiseFall *from_rf, + Tag *from_tag, + Path *from_path, + const Arrival &from_arrival, + Edge *edge, + TimingArc *arc, + ArcDelay arc_delay, + Vertex *to_vertex, + const RiseFall *to_rf, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max) = 0; protected: // Return false to stop visiting. - virtual bool visitEdge(const Pin *from_pin, Vertex *from_vertex, - Edge *edge, const Pin *to_pin, Vertex *to_vertex); + virtual bool visitEdge(const Pin *from_pin, + Vertex *from_vertex, + Edge *edge, + const Pin *to_pin, + Vertex *to_vertex); // Return false to stop visiting. - bool visitArc(const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - Path *from_path, - Edge *edge, - TimingArc *arc, - const Pin *to_pin, - Vertex *to_vertex, - const MinMax *min_max, - PathAnalysisPt *path_ap); + [[nodiscard]] bool visitArc(const Pin *from_pin, + Vertex *from_vertex, + const RiseFall *from_rf, + Path *from_path, + Edge *edge, + TimingArc *arc, + const Pin *to_pin, + Vertex *to_vertex, + const MinMax *min_max, + const Mode *mode); // This calls visit below with everything required to make to_path. // Return false to stop visiting. virtual bool visitFromPath(const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - Path *from_path, - Edge *edge, - TimingArc *arc, - const Pin *to_pin, - Vertex *to_vertex, - const RiseFall *to_rf, - const MinMax *min_max, - const PathAnalysisPt *path_ap); - // Return false to stop visiting. - virtual bool visitFromToPath(const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - Tag *from_tag, - Path *from_path, - const Arrival &from_arrival, - Edge *edge, - TimingArc *arc, - ArcDelay arc_delay, - Vertex *to_vertex, - const RiseFall *to_rf, - Tag *to_tag, - Arrival &to_arrival, - const MinMax *min_max, - const PathAnalysisPt *path_ap) = 0; + Vertex *from_vertex, + const RiseFall *from_rf, + Path *from_path, + Edge *edge, + TimingArc *arc, + const Pin *to_pin, + Vertex *to_vertex, + const RiseFall *to_rf, + const MinMax *min_max); + SearchPred *pred_; TagSet *tag_cache_; }; @@ -781,47 +780,46 @@ class ArrivalVisitor : public PathVisitor { public: ArrivalVisitor(const StaState *sta); - virtual ~ArrivalVisitor(); + ~ArrivalVisitor() override; // Initialize the visitor. - // Defaults pred to search->eval_pred_. - void init(bool always_to_endpoints); void init(bool always_to_endpoints, - SearchPred *pred); - virtual void visit(Vertex *vertex); - virtual VertexVisitor *copy() const; + bool clks_only, + SearchPred *pred); + void copyState(const StaState *sta) override; + void visit(Vertex *vertex) override; + VertexVisitor *copy() const override; // Return false to stop visiting. - virtual bool visitFromToPath(const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - Tag *from_tag, - Path *from_path, - const Arrival &from_arrival, - Edge *edge, - TimingArc *arc, - ArcDelay arc_delay, - Vertex *to_vertex, - const RiseFall *to_rf, - Tag *to_tag, - Arrival &to_arrival, - const MinMax *min_max, - const PathAnalysisPt *path_ap); + bool visitFromToPath(const Pin *from_pin, + Vertex *from_vertex, + const RiseFall *from_rf, + Tag *from_tag, + Path *from_path, + const Arrival &from_arrival, + Edge *edge, + TimingArc *arc, + ArcDelay arc_delay, + Vertex *to_vertex, + const RiseFall *to_rf, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max) override; void setAlwaysToEndpoints(bool to_endpoints); TagGroupBldr *tagBldr() const { return tag_bldr_; } protected: ArrivalVisitor(bool always_to_endpoints, - SearchPred *pred, - const StaState *sta); + SearchPred *pred, + const StaState *sta); void init0(); - void enqueueRefPinInputDelays(const Pin *ref_pin); - void seedInputDelayArrival(const Pin *pin, - Vertex *vertex, - InputDelay *input_delay); + void enqueueRefPinInputDelays(const Pin *ref_pin, + const Sdc *sdc); + void seedArrivals(Vertex *vertex); void pruneCrprArrivals(); void constrainedRequiredsInvalid(Vertex *vertex, - bool is_clk); + bool is_clk); bool always_to_endpoints_; bool always_save_prev_paths_; + bool clks_only_; TagGroupBldr *tag_bldr_; TagGroupBldr *tag_bldr_no_crpr_; SearchPred *adj_pred_; @@ -832,21 +830,20 @@ protected: class RequiredCmp { public: - RequiredCmp(); void requiredsInit(Vertex *vertex, - const StaState *sta); + const StaState *sta); void requiredSet(size_t path_index, - Required &required, - const MinMax *min_max, - const StaState *sta); + Required &required, + const MinMax *min_max, + const StaState *sta); // Return true if the requireds changed. bool requiredsSave(Vertex *vertex, - const StaState *sta); + const StaState *sta); Required required(size_t path_index); protected: - ArrivalSeq requireds_; - bool have_requireds_; + ArrivalSeq requireds_{10}; + bool have_requireds_{false}; }; // Visitor called during backward search to record a @@ -855,52 +852,31 @@ class RequiredVisitor : public PathVisitor { public: RequiredVisitor(const StaState *sta); - virtual ~RequiredVisitor(); - virtual VertexVisitor *copy() const; - virtual void visit(Vertex *vertex); + ~RequiredVisitor() override; + VertexVisitor *copy() const override; + void visit(Vertex *vertex) override; + // Return false to stop visiting. + bool visitFromToPath(const Pin *from_pin, + Vertex *from_vertex, + const RiseFall *from_rf, + Tag *from_tag, + Path *from_path, + const Arrival &from_arrival, + Edge *edge, + TimingArc *arc, + ArcDelay arc_delay, + Vertex *to_vertex, + const RiseFall *to_rf, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max) override; protected: RequiredVisitor(bool make_tag_cache, - const StaState *sta); - // Return false to stop visiting. - virtual bool visitFromToPath(const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - Tag *from_tag, - Path *from_path, - const Arrival &from_arrival, - Edge *edge, - TimingArc *arc, - ArcDelay arc_delay, - Vertex *to_vertex, - const RiseFall *to_rf, - Tag *to_tag, - Arrival &to_arrival, - const MinMax *min_max, - const PathAnalysisPt *path_ap); + const StaState *sta); RequiredCmp *required_cmp_; VisitPathEnds *visit_path_ends_; }; -// This does not use SearchPred as a base class to avoid getting -// two sets of StaState variables when multiple inheritance is used -// to add the functions in this class to another. -class DynLoopSrchPred -{ -public: - DynLoopSrchPred(TagGroupBldr *tag_bldr); - -protected: - bool loopEnabled(Edge *edge, - bool dynamic_loop_breaking_enabled, - const Graph *graph, - Search *search); - bool hasPendingLoopPaths(Edge *edge, - const Graph *graph, - Search *search); - - TagGroupBldr *tag_bldr_; -}; - -} // namespace +} // namespace sta diff --git a/include/sta/SearchClass.hh b/include/sta/SearchClass.hh index d596fb170..8e3b578cf 100644 --- a/include/sta/SearchClass.hh +++ b/include/sta/SearchClass.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,22 +24,24 @@ #pragma once +#include #include +#include +#include -#include "Vector.hh" -#include "Set.hh" -#include "Map.hh" -#include "UnorderedMap.hh" -#include "StringSet.hh" -#include "MinMaxValues.hh" #include "Delay.hh" -#include "NetworkClass.hh" #include "GraphClass.hh" +#include "LibertyClass.hh" +#include "MinMaxValues.hh" +#include "NetworkClass.hh" +#include "RiseFallMinMaxDelay.hh" +#include "SdcClass.hh" +#include "VectorMap.hh" namespace sta { class Search; -class Corner; +class Scene; class Path; class PathEnd; class PathGroup; @@ -56,12 +58,9 @@ class ClkInfo; class ClkInfoHash; class ClkInfoEqual; class VertexPathIterator; -class PathAnalysisPt; -class PathAnalysisPtIterator; class MinPulseWidthCheck; class MinPeriodCheck; class MaxSkewCheck; -class CharPtrLess; class SearchPred; class BfsFwdIterator; class ClkDelays; @@ -70,10 +69,10 @@ class ClkDelays; class TagMatchLess { public: - explicit TagMatchLess(bool match_crpr_clk_pin, - const StaState *sta); + TagMatchLess(bool match_crpr_clk_pin, + const StaState *sta); bool operator()(const Tag *tag1, - const Tag *tag2) const; + const Tag *tag2) const; protected: bool match_crpr_clk_pin_; @@ -84,7 +83,7 @@ class TagMatchHash { public: TagMatchHash(bool match_crpr_clk_pin, - const StaState *sta); + const StaState *sta); size_t operator()(const Tag *tag) const; protected: @@ -96,39 +95,38 @@ class TagMatchEqual { public: TagMatchEqual(bool match_crpr_clk_pin, - const StaState *sta); + const StaState *sta); bool operator()(const Tag *tag1, - const Tag *tag2) const; + const Tag *tag2) const; protected: bool match_crpr_clk_pin_; const StaState *sta_; }; -typedef int PathAPIndex; -typedef uint32_t TagIndex; -typedef Vector TagSeq; -typedef Vector MinPulseWidthCheckSeq; -typedef Vector MinPeriodCheckSeq; -typedef Vector MaxSkewCheckSeq; -typedef StringSet PathGroupNameSet; -typedef Vector PathEndSeq; -typedef Vector ArrivalSeq; -typedef Map VertexPathCountMap; -typedef Map PathIndexMap; -typedef Vector SlackSeq; -typedef Delay Crpr; -typedef Vector PathSeq; -typedef std::vector ConstPathSeq; +using PathAPIndex = int; +using TagIndex = uint32_t; +using TagSeq = std::vector; +using PathEndSeq = std::vector; +using ArrivalSeq = std::vector; +using VertexPathCountMap = std::map; +using PathIndexMap = VectorMap; +using SlackSeq = std::vector; +using Crpr = Delay; +using PathSeq = std::vector; +using ConstPathSeq = std::vector; +// Path::slack/arrival/required function. +using PathDelayFunc = std::function; +using DelaysWrtClks = std::map; enum class ReportPathFormat { full, - full_clock, - full_clock_expanded, - shorter, - endpoint, - summary, - slack_only, - json + full_clock, + full_clock_expanded, + shorter, + endpoint, + summary, + slack_only, + json }; enum class ReportDeduplicationMode { none, @@ -140,7 +138,7 @@ static const TagIndex tag_index_bit_count = 28; static const TagIndex tag_index_max = (1 << tag_index_bit_count) - 1; static const TagIndex tag_index_null = tag_index_max; static const int path_ap_index_bit_count = 8; -// One path analysis point per corner min/max. -static const int corner_count_max = (1 << path_ap_index_bit_count) / 2; +// One path analysis point per scene min/max. +static const int scene_count_max = (1 << path_ap_index_bit_count) / 2; -} // namespace +} // namespace sta diff --git a/include/sta/SearchPred.hh b/include/sta/SearchPred.hh index 618dd2ea2..5cea34f9c 100644 --- a/include/sta/SearchPred.hh +++ b/include/sta/SearchPred.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,49 +24,60 @@ #pragma once -#include "NetworkClass.hh" #include "GraphClass.hh" #include "LibertyClass.hh" +#include "NetworkClass.hh" #include "StaState.hh" namespace sta { // Class hierarchy: // SearchPred -// SearchPred0 (unless disabled or constant) -// EvalPred (unless timing check) -// SearchThru (unless latch D->Q, outside vertex subset) +// SearchAdj (unless loop disabled, latch D->Q, timing check, dynamic loop) +// SearchPred0 (unless timing check, disabled or constant) +// EvalPred (unless dynamic loop breaking) +// SearchThru (unless latch D->Q) +// GenClkInsertionSearchPred // SearchPred1 (unless loop disabled) -// ClkTreeSearchPred (only wire or combinational) -// SearchPred2 (unless timing check) -// SearchPredNonLatch2 (unless latch D->Q) -// SearchPredNonReg2 (unless reg CLK->Q, latch D->Q) +// FanOutSrchPred +// ClkTreeSearchPred (only wire or combinational) // Virtual base class for search predicates. class SearchPred { public: - SearchPred() {} - virtual ~SearchPred() {} + SearchPred(const StaState *sta); + virtual ~SearchPred() = default; // Search is allowed from from_vertex. - virtual bool searchFrom(const Vertex *from_vertex) = 0; + virtual bool searchFrom(const Vertex *from_vertex, + const Mode *mode) const = 0; + bool searchFrom(const Vertex *from_vertex) const; // Search is allowed through edge. // from/to pins are NOT checked. // inst can be either the from_pin or to_pin instance because it // is only referenced when they are the same (non-wire edge). - virtual bool searchThru(Edge *edge) = 0; + virtual bool searchThru(Edge *edge, + const Mode *mode) const = 0; + bool searchThru(Edge *edge) const; // Search is allowed to to_pin. - virtual bool searchTo(const Vertex *to_vertex) = 0; + virtual bool searchTo(const Vertex *to_vertex, + const Mode *mode) const = 0; + bool searchTo(const Vertex *to_vertex) const; + void copyState(const StaState *sta); + +protected: + const StaState *sta_; }; class SearchPred0 : public SearchPred { public: - explicit SearchPred0(const StaState *sta); + SearchPred0(const StaState *sta); // Search from a vertex unless // disabled by constraint // constant logic zero/one - virtual bool searchFrom(const Vertex *from_vertex); + bool searchFrom(const Vertex *from_vertex, + const Mode *mode) const override; // Search thru an edge unless // traverses disabled from/to pin pair // disabled by condition expression @@ -75,13 +86,12 @@ public: // cond expression is disabled // non-controlling constant values on other pins that disable the // edge (such as a mux select) - virtual bool searchThru(Edge *edge); + bool searchThru(Edge *edge, + const Mode *mode) const override; // Search to a vertex unless // constant logic zero/one - virtual bool searchTo(const Vertex *to_vertex); - -protected: - const StaState *sta_; + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; }; // SearchPred0 unless @@ -89,75 +99,66 @@ protected: class SearchPred1 : public SearchPred0 { public: - explicit SearchPred1(const StaState *sta); - virtual bool searchThru(Edge *edge); -}; - -// SearchPred1 unless -// timing check edge -class SearchPred2 : public SearchPred1 -{ -public: - explicit SearchPred2(const StaState *sta); - virtual bool searchThru(Edge *edge); -}; - -// SearchPred2 unless -// latch D->Q edge -class SearchPredNonLatch2 : public SearchPred2 -{ -public: - explicit SearchPredNonLatch2(const StaState *sta); - virtual bool searchThru(Edge *edge); -}; + SearchPred1(const StaState *sta); + bool searchThru(Edge *edge, + const Mode *mode) const override; -// SearchPred2 unless -// register/latch CLK->Q edges. -class SearchPredNonReg2 : public SearchPred2 -{ -public: - explicit SearchPredNonReg2(const StaState *sta); - virtual bool searchThru(Edge *edge); + using SearchPred::searchFrom; + using SearchPred::searchThru; + using SearchPred::searchTo; }; // Predicate for BFS search to stop at the end of the clock tree. // Search only thru combinational gates and wires. -class ClkTreeSearchPred : public SearchPred1 +class ClkTreeSearchPred : public SearchPred { public: - explicit ClkTreeSearchPred(const StaState *sta); - virtual bool searchThru(Edge *edge); + ClkTreeSearchPred(const StaState *sta); + bool searchFrom(const Vertex *from_vertex, + const Mode *mode) const override; + bool searchThru(Edge *edge, + const Mode *mode) const override; + using SearchPred::searchFrom; + using SearchPred::searchThru; + using SearchPred::searchTo; + // The variable part of searchThru used by descendents. + virtual bool searchThruAllow(const TimingRole *role) const; + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; }; bool -isClkEnd(Vertex *vertex, Graph *graph); +isClkEnd(Vertex *vertex, + const Mode *mode); // Predicate to see if arc/edge is disabled by constants on other pins // that effect the unateness of the edge. bool searchThru(const Edge *edge, - const TimingArc *arc, - const Graph *graph); + const TimingArc *arc, + const Mode *mode); bool searchThru(Vertex *from_vertex, - const RiseFall *from_rf, - const Edge *edge, - Vertex *to_vertex, - const RiseFall *to_rf); - + const RiseFall *from_rf, + const Edge *edge, + Vertex *to_vertex, + const RiseFall *to_rf, + const Mode *mode); //////////////////////////////////////////////////////////////// bool hasFanin(Vertex *vertex, - SearchPred *pred, - const Graph *graph); + SearchPred *pred, + const Graph *graph, + const Mode *mode); // Vertices with no fanout have at no enabled (non-disabled) edges // leaving them. bool hasFanout(Vertex *vertex, - SearchPred *pred, - const Graph *graph); + SearchPred *pred, + const Graph *graph, + const Mode *mode); -} // namespace +} // namespace sta diff --git a/include/sta/Sequential.hh b/include/sta/Sequential.hh index cba09523b..1b07075a6 100644 --- a/include/sta/Sequential.hh +++ b/include/sta/Sequential.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -55,8 +55,8 @@ enum class StateInternalValue { class StatetableRow; -typedef std::vector StateInputValues; -typedef std::vector StateInternalValues; +using StateInputValues = std::vector; +using StateInternalValues = std::vector; // Register/Latch class Sequential @@ -76,20 +76,24 @@ public: LibertyPort *output() const { return output_; } LibertyPort *outputInv() const { return output_inv_; } -protected: // clock/data are: // clocked_on/next_state for registers // enable/data for latches Sequential(bool is_register, - FuncExpr *clock, - FuncExpr *data, - FuncExpr *clear, - FuncExpr *preset, - LogicValue clr_preset_out, - LogicValue clr_preset_out_inv, - LibertyPort *output, - LibertyPort *output_inv); + FuncExpr *clock, + FuncExpr *data, + FuncExpr *clear, + FuncExpr *preset, + LogicValue clr_preset_out, + LogicValue clr_preset_out_inv, + LibertyPort *output, + LibertyPort *output_inv); + Sequential(Sequential &&other) noexcept; + Sequential(const Sequential &) = delete; + Sequential &operator=(Sequential &&) noexcept; + Sequential &operator=(const Sequential &) = delete; +protected: bool is_register_; FuncExpr *clock_; FuncExpr *data_; @@ -137,4 +141,4 @@ private: StateInternalValues next_values_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Set.hh b/include/sta/Set.hh deleted file mode 100644 index 7e08c3b17..000000000 --- a/include/sta/Set.hh +++ /dev/null @@ -1,220 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include - -namespace sta { - -// Add convenience functions around STL container. -template > -class Set : public std::set -{ -public: - Set() : std::set() {} - explicit Set(const CMP &cmp) : std::set(cmp) {} - - // Find the entry corresponding to key. - KEY findKey(const KEY key) const - { - auto find_iter = this->find(key); - if (find_iter != this->end()) - return *find_iter; - else - return nullptr; - } - // Find out if key is in the set. - bool hasKey(const KEY key) const - { - auto find_iter = this->find(key); - return find_iter != this->end(); - } - - // Slowaris STL doesn't support operator== on sets. - static bool equal(const std::set *set1, - const std::set *set2); - - // True if set2 is a subset of this set. - bool isSubset(const std::set *set2); - - void insertSet(const std::set *set2); - - void - deleteContents() - { - Iterator iter(this); - while (iter.hasNext()) - delete iter.next(); - } - - void - deleteContentsClear() - { - deleteContents(); - this->clear(); - } - - static bool - intersects(const std::set *set1, - const std::set *set2, - CMP key_less); - - // Java style container itererator - // Set::Iterator iter(set); - // while (iter.hasNext()) { - // Key *v = iter.next(); - // } - class Iterator - { - public: - Iterator() : container_(nullptr) {} - explicit Iterator(std::set *container) : - container_(container) - { if (container_ != nullptr) iter_ = container_->begin(); } - explicit Iterator(std::set &container) : - container_(&container) - { if (container_ != nullptr) iter_ = container_->begin(); } - void init(std::set *container) - { container_ = container; if (container_ != nullptr) iter_=container_->begin();} - void init(std::set &container) - { container_ = &container; if (container_ != nullptr) iter_=container_->begin();} - bool hasNext() { return container_ != nullptr && iter_ != container_->end(); } - KEY next() { return *iter_++; } - std::set *container() { return container_; } - - private: - std::set *container_; - typename std::set::iterator iter_; - }; - - class ConstIterator - { - public: - ConstIterator() : container_(nullptr) {} - explicit ConstIterator(const std::set *container) : - container_(container) - { if (container_ != nullptr) iter_ = container_->begin(); } - explicit ConstIterator(const std::set &container) : - container_(&container) - { if (container_ != nullptr) iter_ = container_->begin(); } - void init(const std::set *container) - { container_ = container; if (container_ != nullptr) iter_=container_->begin();} - void init(const std::set &container) - { container_ = &container; if (container_ != nullptr) iter_=container_->begin();} - - bool hasNext() { return container_ != nullptr && iter_ != container_->end(); } - KEY next() { return *iter_++; } - const std::set *container() { return container_; } - - private: - const std::set *container_; - typename std::set::const_iterator iter_; - }; -}; - -template -bool -Set::equal(const std::set *set1, - const std::set *set2) -{ - if ((set1 == nullptr || set1->empty()) - && (set2 == nullptr || set2->empty())) - return true; - else if (set1 && set2) { - if (set1->size() == set2->size()) { - typename Set::ConstIterator iter1(set1); - typename Set::ConstIterator iter2(set2); - while (iter1.hasNext() && iter2.hasNext()) { - if (iter1.next() != iter2.next()) - return false; - } - return true; - } - else - return false; - } - else - return false; -} - -template -bool -Set::isSubset(const std::set *set2) -{ - if (this->empty() && set2->empty()) - return true; - else { - typename Set::ConstIterator iter2(set2); - while (iter2.hasNext()) { - const KEY key2 = iter2.next(); - if (!hasKey(key2)) - return false; - } - return true; - } -} - -template -bool -Set::intersects(const std::set *set1, - const std::set *set2, - CMP key_less) -{ - if (set1 && set2) { - auto iter1 = set1->begin(); - auto end1 = set1->end(); - auto iter2 = set2->begin(); - auto end2 = set2->end(); - while (iter1 != end1 && iter2 != end2) { - if (key_less(*iter1, *iter2)) - iter1++; - else if (key_less(*iter2, *iter1)) - iter2++; - else - return true; - } - } - return false; -} - -// A complicated way to call the base class operator<. -template -bool -operator<(const Set &set1, const Set &set2) -{ - const std::set &set1_base = set1; - const std::set &set2_base = set2; - return set1_base < set2_base; -} - -template -void -Set::insertSet(const std::set *set2) -{ - if (set2) - this->insert(set2->begin(), set2->end()); -} - -} // namespace diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index eff131a4b..97b752f9f 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,22 +24,27 @@ #pragma once +#include #include +#include +#include -#include "StringSeq.hh" +#include "ArcDelayCalc.hh" +#include "CircuitSim.hh" +#include "GraphClass.hh" #include "LibertyClass.hh" #include "NetworkClass.hh" -#include "SdcClass.hh" -#include "GraphClass.hh" #include "ParasiticsClass.hh" -#include "StaState.hh" -#include "VertexVisitor.hh" -#include "SearchClass.hh" #include "PowerClass.hh" -#include "ArcDelayCalc.hh" -#include "CircuitSim.hh" -#include "Variables.hh" #include "Property.hh" +#include "RiseFallMinMaxDelay.hh" +#include "Scene.hh" +#include "SdcClass.hh" +#include "SearchClass.hh" +#include "StaState.hh" +#include "StringUtil.hh" +#include "Variables.hh" +#include "VertexVisitor.hh" struct Tcl_Interp; @@ -53,10 +58,9 @@ class RiseFall; class VerilogReader; class ReportPath; class CheckTiming; -class DcalcAnalysisPt; -class CheckSlewLimits; -class CheckFanoutLimits; -class CheckCapacitanceLimits; +class CheckSlews; +class CheckFanouts; +class CheckCapacitances; class CheckMinPulseWidths; class CheckMinPeriods; class CheckMaxSkews; @@ -64,18 +68,21 @@ class PatternMatch; class CheckPeriods; class LibertyReader; class SearchPred; -class Corner; +class Scene; class ClkSkews; class ReportField; class EquivCells; +class StaSimObserver; +class GraphLoop; -typedef InstanceSeq::Iterator SlowDrvrIterator; -typedef Vector CheckError; -typedef Vector CheckErrorSeq; -typedef Vector CornerSeq; -typedef std::vector StdStringSeq; - +using ModeNameMap = std::map>; +using SceneNameMap = std::map; +using SlowDrvrIterator = Iterator; +using CheckError = StringSeq; +using CheckErrorSeq = std::vector; enum class CmdNamespace { sta, sdc }; +using ParasiticsNameMap = std::map>; +using GraphLoopSeq = std::vector; // Initialize sta functions that are not part of the Sta class. void initSta(); @@ -94,7 +101,6 @@ deleteAllMemory(); class Sta : public StaState { public: - Sta(); // The Sta is a FACTORY for the components. // makeComponents calls the make{Component} virtual functions. // Ideally this would be called by the Sta constructor, but a @@ -105,7 +111,7 @@ public: // pointers to some components have changed. // This must be called after changing any of the StaState components. virtual void updateComponentsState(); - virtual ~Sta(); + ~Sta() override; // Singleton accessor used by tcl command interpreter. static Sta *sta(); @@ -115,11 +121,39 @@ public: virtual int defaultThreadCount() const; void setThreadCount(int thread_count); - virtual LibertyLibrary *readLiberty(const char *filename, - Corner *corner, - const MinMaxAll *min_max, - bool infer_latches); - bool readVerilog(const char *filename); + // define_corners compatibility. + void makeScenes(const StringSeq &scene_names); + void makeScene(const std::string &name, + const std::string &mode_name, + const StringSeq &liberty_min_files, + const StringSeq &liberty_max_files, + const std::string &spef_min_file, + const std::string &spef_max_file); + Scene *findScene(const std::string &name) const; + // Pattern match name. + SceneSeq findScenes(const std::string &name) const; + SceneSeq findScenes(const std::string &name, + ModeSeq &modes) const; + Scene *cmdScene() const; + void setCmdScene(Scene *scene); + SceneSeq makeSceneSeq(Scene *scene) const; + + Mode *cmdMode() const { return cmd_scene_->mode(); } + const std::string &cmdModeName(); + void setCmdMode(std::string_view mode_name); + Mode *findMode(std::string_view mode_name) const; + ModeSeq findModes(const std::string &mode_name) const; + Sdc *cmdSdc() const; + + virtual LibertyLibrary *readLiberty(std::string_view filename, + Scene *scene, + const MinMaxAll *min_max, + bool infer_latches); + // tmp public + void readLibertyAfter(LibertyLibrary *liberty, + Scene *scene, + const MinMaxAll *min_max); + bool readVerilog(std::string_view filename); // Network readers call this to notify the Sta to delete any previously // linked network. void readNetlistBefore(); @@ -127,744 +161,852 @@ public: bool linkDesign(const char *top_cell_name, bool make_black_boxes); + bool readSdf(std::string_view filename, + std::string_view path, + Scene *scene, + bool unescaped_dividers, + bool incremental_only, + MinMaxAll *cond_use); + // SDC Swig API. Instance *currentInstance() const; void setCurrentInstance(Instance *inst); - virtual void setAnalysisType(AnalysisType analysis_type); + virtual void setAnalysisType(AnalysisType analysis_type, + Sdc *sdc); void setOperatingConditions(OperatingConditions *op_cond, - const MinMaxAll *min_max); + const MinMaxAll *min_max, + Sdc *sdc); void setTimingDerate(TimingDerateType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate); + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate, + Sdc *sdc); // Delay type is always net for net derating. void setTimingDerate(const Net *net, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate); + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate, + Sdc *sdc); void setTimingDerate(const Instance *inst, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate); + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate, + Sdc *sdc); void setTimingDerate(const LibertyCell *cell, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate); - void unsetTimingDerate(); + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate, + Sdc *sdc); + void unsetTimingDerate(Sdc *sdc); void setInputSlew(const Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew); + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew, + Sdc *sdc); // Set port external pin load (set_load -pin port). void setPortExtPinCap(const Port *port, - const RiseFallBoth *rf, - const Corner *corner, - const MinMaxAll *min_max, - float cap); + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float cap, + Sdc *sdc); void portExtCaps(const Port *port, - const Corner *corner, const MinMax *min_max, + const Sdc *sdc, float &pin_cap, float &wire_cap, int &fanout); // Set port external wire load (set_load -wire port). void setPortExtWireCap(const Port *port, - bool subtract_pin_cap, - const RiseFallBoth *rf, - const Corner *corner, - const MinMaxAll *min_max, - float cap); + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float cap, + Sdc *sdc); // Set net wire capacitance (set_load -wire net). void setNetWireCap(const Net *net, - bool subtract_pin_load, - const Corner *corner, - const MinMaxAll *min_max, - float cap); + bool subtract_pin_cap, + const MinMaxAll *min_max, + float cap, + Sdc *sdc); // Remove all "set_load net" annotations. - void removeNetLoadCaps() const; + void removeNetLoadCaps(Sdc *sdc) const; // Set port external fanout (used by wireload models). void setPortExtFanout(const Port *port, - int fanout, - const Corner *corner, - const MinMaxAll *min_max); + int fanout, + const MinMaxAll *min_max, + Sdc *sdc); // Liberty port capacitance. float capacitance(const LibertyPort *port, - Corner *corner, + Scene *scene, const MinMax *min_max); // pin_cap = net pin capacitances + port external pin capacitance, // wire_cap = annotated net capacitance + port external wire capacitance. void connectedCap(const Pin *drvr_pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float &pin_cap, - float &wire_cap) const; + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + float &pin_cap, + float &wire_cap) const; void connectedCap(const Net *net, - Corner *corner, - const MinMax *min_max, - float &pin_cap, - float &wire_cap) const; + Scene *scene, + const MinMax *min_max, + float &pin_cap, + float &wire_cap) const; void setResistance(const Net *net, - const MinMaxAll *min_max, - float res); + const MinMaxAll *min_max, + float res, + Sdc *sdc); void setDriveCell(const LibertyLibrary *library, - const LibertyCell *cell, - const Port *port, - const LibertyPort *from_port, - float *from_slews, - const LibertyPort *to_port, - const RiseFallBoth *rf, - const MinMaxAll *min_max); + const LibertyCell *cell, + const Port *port, + const LibertyPort *from_port, + const DriveCellSlews &from_slews, + const LibertyPort *to_port, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + Sdc *sdc); void setDriveResistance(const Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float res); + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float res, + Sdc *sdc); void setLatchBorrowLimit(const Pin *pin, - float limit); + float limit, + Sdc *sdc); void setLatchBorrowLimit(const Instance *inst, - float limit); + float limit, + Sdc *sdc); void setLatchBorrowLimit(const Clock *clk, - float limit); + float limit, + Sdc *sdc); void setMinPulseWidth(const RiseFallBoth *rf, - float min_width); + float min_width, + Sdc *sdc); void setMinPulseWidth(const Pin *pin, - const RiseFallBoth *rf, - float min_width); + const RiseFallBoth *rf, + float min_width, + Sdc *sdc); void setMinPulseWidth(const Instance *inst, - const RiseFallBoth *rf, - float min_width); + const RiseFallBoth *rf, + float min_width, + Sdc *sdc); void setMinPulseWidth(const Clock *clk, - const RiseFallBoth *rf, - float min_width); + const RiseFallBoth *rf, + float min_width, + Sdc *sdc); void setWireload(Wireload *wireload, - const MinMaxAll *min_max); - void setWireloadMode(WireloadMode mode); + const MinMaxAll *min_max, + Sdc *sdc); + void setWireloadMode(WireloadMode mode, + Sdc *sdc); void setWireloadSelection(WireloadSelection *selection, - const MinMaxAll *min_max); + const MinMaxAll *min_max, + Sdc *sdc); void setSlewLimit(Clock *clk, - const RiseFallBoth *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - float slew); + const RiseFallBoth *rf, + PathClkOrData clk_data, + const MinMax *min_max, + float slew, + Sdc *sdc); void setSlewLimit(Port *port, - const MinMax *min_max, - float slew); + const MinMax *min_max, + float slew, + Sdc *sdc); void setSlewLimit(Cell *cell, - const MinMax *min_max, - float slew); + const MinMax *min_max, + float slew, + Sdc *sdc); void setCapacitanceLimit(Cell *cell, - const MinMax *min_max, - float cap); + const MinMax *min_max, + float cap, + Sdc *sdc); void setCapacitanceLimit(Port *port, - const MinMax *min_max, - float cap); + const MinMax *min_max, + float cap, + Sdc *sdc); void setCapacitanceLimit(Pin *pin, - const MinMax *min_max, - float cap); + const MinMax *min_max, + float cap, + Sdc *sdc); void setFanoutLimit(Cell *cell, - const MinMax *min_max, - float fanout); + const MinMax *min_max, + float fanout, + Sdc *sdc); void setFanoutLimit(Port *port, - const MinMax *min_max, - float fanout); - void setMaxArea(float area); - void setMaxDynamicPower(float power); - void setMaxLeakagePower(float power); - - void makeClock(const char *name, - PinSet *pins, - bool add_to_pins, - float period, - FloatSeq *waveform, - char *comment); + const MinMax *min_max, + float fanout, + Sdc *sdc); + void setMaxArea(float area, + Sdc *sdc); + void setMaxDynamicPower(float power, + Sdc *sdc); + void setMaxLeakagePower(float power, + Sdc *sdc); + + void makeClock(std::string_view name, + const PinSet &pins, + bool add_to_pins, + float period, + const FloatSeq &waveform, + std::string_view comment, + const Mode *mode); // edges size must be 3. - void makeGeneratedClock(const char *name, - PinSet *pins, - bool add_to_pins, - Pin *src_pin, - Clock *master_clk, - int divide_by, - int multiply_by, - float duty_cycle, - bool invert, - bool combinational, - IntSeq *edges, - FloatSeq *edge_shifts, - char *comment); - void removeClock(Clock *clk); + void makeGeneratedClock(std::string_view name, + const PinSet &pins, + bool add_to_pins, + Pin *src_pin, + Clock *master_clk, + int divide_by, + int multiply_by, + float duty_cycle, + bool invert, + bool combinational, + const IntSeq &edges, + const FloatSeq &edge_shifts, + std::string_view comment, + const Mode *mode); + void removeClock(Clock *clk, + Sdc *sdc); // Update period/waveform for generated clocks from source pin clock. void updateGeneratedClks(); // Mark that generated clocks need to be updated. void setUpdateGenclks(); // True if pin is defined as a clock source (pin may be hierarchical). - bool isClockSrc(const Pin *pin) const; + bool isClockSrc(const Pin *pin, + const Sdc *sdc) const; // Propagated (non-ideal) clocks. - void setPropagatedClock(Clock *clk); - void removePropagatedClock(Clock *clk); - void setPropagatedClock(Pin *pin); - void removePropagatedClock(Pin *pin); - void setClockSlew(Clock *clock, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew); - void removeClockSlew(Clock *clk); + void setPropagatedClock(Clock *clk, + const Mode *mode); + void removePropagatedClock(Clock *clk, + const Mode *mode); + void setPropagatedClock(Pin *pin, + const Mode *mode); + void removePropagatedClock(Pin *pin, + const Mode *mode); + void setClockSlew(Clock *clk, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew, + Sdc *sdc); + void removeClockSlew(Clock *clk, + Sdc *sdc); // Clock latency. // Latency can be on a clk, pin, or clk/pin combination. void setClockLatency(Clock *clk, - Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float delay); + Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float delay, + Sdc *sdc); void removeClockLatency(const Clock *clk, - const Pin *pin); + const Pin *pin, + Sdc *sdc); // Clock insertion delay (source latency). void setClockInsertion(const Clock *clk, - const Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - const EarlyLateAll *early_late, - float delay); + const Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay, + Sdc *sdc); void removeClockInsertion(const Clock *clk, - const Pin *pin); + const Pin *pin, + Sdc *sdc); // Clock uncertainty. - virtual void setClockUncertainty(Clock *clk, - const SetupHoldAll *setup_hold, - float uncertainty); - virtual void removeClockUncertainty(Clock *clk, - const SetupHoldAll *setup_hold); - virtual void setClockUncertainty(Pin *pin, - const SetupHoldAll *setup_hold, - float uncertainty); - virtual void removeClockUncertainty(Pin *pin, - const SetupHoldAll *setup_hold); + void setClockUncertainty(Clock *clk, + const SetupHoldAll *setup_hold, + float uncertainty); + void removeClockUncertainty(Clock *clk, + const SetupHoldAll *setup_hold); + void setClockUncertainty(Pin *pin, + const SetupHoldAll *setup_hold, + float uncertainty, + Sdc *sdc); + void removeClockUncertainty(Pin *pin, + const SetupHoldAll *setup_hold, + Sdc *sdc); // Inter-clock uncertainty. - virtual void setClockUncertainty(Clock *from_clk, - const RiseFallBoth *from_rf, - Clock *to_clk, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold, - float uncertainty); - virtual void removeClockUncertainty(Clock *from_clk, - const RiseFallBoth *from_rf, - Clock *to_clk, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold); - ClockGroups *makeClockGroups(const char *name, - bool logically_exclusive, - bool physically_exclusive, - bool asynchronous, - bool allow_paths, - const char *comment); - // nullptr name removes all. - void removeClockGroupsLogicallyExclusive(const char *name); - void removeClockGroupsPhysicallyExclusive(const char *name); - void removeClockGroupsAsynchronous(const char *name); + void setClockUncertainty(Clock *from_clk, + const RiseFallBoth *from_rf, + Clock *to_clk, + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold, + float uncertainty, + Sdc *sdc); + void removeClockUncertainty(Clock *from_clk, + const RiseFallBoth *from_rf, + Clock *to_clk, + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold, + Sdc *sdc); + ClockGroups *makeClockGroups(std::string_view name, + bool logically_exclusive, + bool physically_exclusive, + bool asynchronous, + bool allow_paths, + std::string_view comment, + Sdc *sdc); + void removeClockGroupsLogicallyExclusive(Sdc *sdc); + void removeClockGroupsLogicallyExclusive(const std::string &name, + Sdc *sdc); + void removeClockGroupsPhysicallyExclusive(Sdc *sdc); + void removeClockGroupsPhysicallyExclusive(const std::string &name, + Sdc *sdc); + void removeClockGroupsAsynchronous(Sdc *sdc); + void removeClockGroupsAsynchronous(const std::string &name, + Sdc *sdc); void makeClockGroup(ClockGroups *clk_groups, - ClockSet *clks); + ClockSet *clks, + Sdc *sdc); void setClockSense(PinSet *pins, - ClockSet *clks, - ClockSense sense); + ClockSet *clks, + ClockSense sense, + Sdc *sdc); void setClockGatingCheck(const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin); + const SetupHold *setup_hold, + float margin, + Sdc *sdc); void setClockGatingCheck(Clock *clk, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin); + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + Sdc *sdc); void setClockGatingCheck(Instance *inst, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value); + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value, + Sdc *sdc); void setClockGatingCheck(Pin *pin, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value); + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value, + Sdc *sdc); void setDataCheck(Pin *from, - const RiseFallBoth *from_rf, - Pin *to, - const RiseFallBoth *to_rf, - Clock *clk, - const SetupHoldAll *setup_hold, - float margin); + const RiseFallBoth *from_rf, + Pin *to, + const RiseFallBoth *to_rf, + Clock *clk, + const SetupHoldAll *setup_hold, + float margin, + Sdc *sdc); void removeDataCheck(Pin *from, - const RiseFallBoth *from_rf, - Pin *to, - const RiseFallBoth *to_rf, - Clock *clk, - const SetupHoldAll *setup_hold); + const RiseFallBoth *from_rf, + Pin *to, + const RiseFallBoth *to_rf, + Clock *clk, + const SetupHoldAll *setup_hold, + Sdc *sdc); // set_disable_timing cell [-from] [-to] // Disable all edges thru cell if from/to are null. // Bus and bundle ports are NOT supported. void disable(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to); + LibertyPort *from, + LibertyPort *to, + Sdc *sdc); void removeDisable(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to); + LibertyPort *from, + LibertyPort *to, + Sdc *sdc); // set_disable_timing liberty port. // Bus and bundle ports are NOT supported. - void disable(LibertyPort *port); - void removeDisable(LibertyPort *port); + void disable(LibertyPort *port, + Sdc *sdc); + void removeDisable(LibertyPort *port, + Sdc *sdc); // set_disable_timing port (top level instance port). // Bus and bundle ports are NOT supported. - void disable(Port *port); - void removeDisable(Port *port); + void disable(Port *port, + Sdc *sdc); + void removeDisable(Port *port, + Sdc *sdc); // set_disable_timing instance [-from] [-to]. // Disable all edges thru instance if from/to are null. // Bus and bundle ports are NOT supported. // Hierarchical instances are NOT supported. void disable(Instance *inst, - LibertyPort *from, - LibertyPort *to); + LibertyPort *from, + LibertyPort *to, + Sdc *sdc); void removeDisable(Instance *inst, - LibertyPort *from, - LibertyPort *to); + LibertyPort *from, + LibertyPort *to, + Sdc *sdc); // set_disable_timing pin - void disable(Pin *pin); - void removeDisable(Pin *pin); + void disable(Pin *pin, + Sdc *sdc); + void removeDisable(Pin *pin, + Sdc *sdc); // set_disable_timing [get_timing_arc -of_objects instance]] - void disable(Edge *edge); - void removeDisable(Edge *edge); + void disable(Edge *edge, + Sdc *sdc); + void removeDisable(Edge *edge, + Sdc *sdc); // set_disable_timing [get_timing_arc -of_objects lib_cell]] - void disable(TimingArcSet *arc_set); - void removeDisable(TimingArcSet *arc_set); + void disable(TimingArcSet *arc_set, + Sdc *sdc); + void removeDisable(TimingArcSet *arc_set, + Sdc *sdc); + [[nodiscard]] bool isConstant(const Pin *pin, + const Mode *mode) const; // Edge is disabled by constant. - bool isDisabledConstant(Edge *edge); + [[nodiscard]] bool isDisabledConstant(Edge *edge, + const Mode *mode); // Return a set of constant pins that disabled edge. // Caller owns the returned set. - PinSet disabledConstantPins(Edge *edge); + PinSet disabledConstantPins(Edge *edge, + const Mode *mode); // Edge timing sense with propagated constants. - TimingSense simTimingSense(Edge *edge); + TimingSense simTimingSense(Edge *edge, + const Mode *mode); // Edge is disabled by set_disable_timing constraint. - bool isDisabledConstraint(Edge *edge); + [[nodiscard]] bool isDisabledConstraint(Edge *edge, + const Sdc *sdc); // Edge is disabled to break combinational loops. - bool isDisabledLoop(Edge *edge) const; + [[nodiscard]] bool isDisabledLoop(Edge *edge) const; // Edge is disabled internal bidirect output path. - bool isDisabledBidirectInstPath(Edge *edge) const; + [[nodiscard]] bool isDisabledBidirectInstPath(Edge *edge) const; // Edge is disabled bidirect net path. - bool isDisabledBidirectNetPath(Edge *edge) const; - bool isDisabledPresetClr(Edge *edge) const; + [[nodiscard]] bool isDisabledBidirectNetPath(Edge *edge) const; + [[nodiscard]] bool isDisabledPresetClr(Edge *edge) const; // Return a vector of graph edges that are disabled, sorted by // from/to vertex names. Caller owns the returned vector. - EdgeSeq disabledEdges(); - EdgeSeq disabledEdgesSorted(); - void disableClockGatingCheck(Instance *inst); - void disableClockGatingCheck(Pin *pin); - void disableClockGatingCheck(LibertyCell *cell); - void removeDisableClockGatingCheck(Instance *inst); - void removeDisableClockGatingCheck(Pin *pin); - void removeDisableClockGatingCheck(LibertyCell *cell); + EdgeSeq disabledEdges(const Mode *mode); + EdgeSeq disabledEdgesSorted(const Mode *mode); + void disableClockGatingCheck(Instance *inst, + Sdc *sdc); + void disableClockGatingCheck(Pin *pin, + Sdc *sdc); + void removeDisableClockGatingCheck(Instance *inst, + Sdc *sdc); + void removeDisableClockGatingCheck(Pin *pin, + Sdc *sdc); + void disableClockGatingCheck(LibertyCell *cell, + Sdc *sdc); + void removeDisableClockGatingCheck(LibertyCell *cell, + Sdc *sdc); void setLogicValue(Pin *pin, - LogicValue value); + LogicValue value, + Mode *mode); void setCaseAnalysis(Pin *pin, - LogicValue value); - void removeCaseAnalysis(Pin *pin); + LogicValue value, + Mode *mode); + void removeCaseAnalysis(Pin *pin, + Mode *mode); void setInputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const Pin *ref_pin, - bool source_latency_included, - bool network_latency_included, - const MinMaxAll *min_max, - bool add, - float delay); + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, + float delay, + Sdc *sdc); void removeInputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMaxAll *min_max); + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMaxAll *min_max, + Sdc *sdc); void setOutputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const Pin *ref_pin, - bool source_latency_included, - bool network_latency_included, - const MinMaxAll *min_max, - bool add, - float delay); + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, + float delay, + Sdc *sdc); void removeOutputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMaxAll *min_max); + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMaxAll *min_max, + Sdc *sdc); void makeFalsePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - const char *comment); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + std::string_view comment, + Sdc *sdc); void makeMulticyclePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool use_end_clk, - int path_multiplier, - const char *comment); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, + std::string_view comment, + Sdc *sdc); void makePathDelay(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMax *min_max, - bool ignore_clk_latency, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMax *min_max, + bool ignore_clk_latency, bool break_path, - float delay, - const char *comment); - void makeGroupPath(const char *name, - bool is_default, - ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const char *comment); + float delay, + std::string_view comment, + Sdc *sdc); + void makeGroupPath(std::string_view name, + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + std::string_view comment, + Sdc *sdc); // Deprecated 10/24/2025 - bool isGroupPathName(const char *group_name) __attribute__ ((deprecated)); - bool isPathGroupName(const char *group_name) const; - StdStringSeq pathGroupNames() const; + bool isGroupPathName(std::string_view group_name, + const Sdc *sdc) __attribute__ ((deprecated)); + bool isPathGroupName(std::string_view group_name, + const Sdc *sdc) const; + StringSeq pathGroupNames(const Sdc *sdc) const; void resetPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + Sdc *sdc); // Make an exception -from specification. ExceptionFrom *makeExceptionFrom(PinSet *from_pins, - ClockSet *from_clks, - InstanceSet *from_insts, - const RiseFallBoth *from_rf); + ClockSet *from_clks, + InstanceSet *from_insts, + const RiseFallBoth *from_rf, + const Sdc *sdc); void checkExceptionFromPins(ExceptionFrom *from, - const char *file, - int line) const; + std::string_view filename, + int line, + const Sdc *sdc) const; void deleteExceptionFrom(ExceptionFrom *from); // Make an exception -through specification. ExceptionThru *makeExceptionThru(PinSet *pins, - NetSet *nets, - InstanceSet *insts, - const RiseFallBoth *rf); + NetSet *nets, + InstanceSet *insts, + const RiseFallBoth *rf, + const Sdc *sdc); void deleteExceptionThru(ExceptionThru *thru); // Make an exception -to specification. ExceptionTo *makeExceptionTo(PinSet *to_pins, - ClockSet *to_clks, - InstanceSet *to_insts, - const RiseFallBoth *rf, - const RiseFallBoth *end_rf); + ClockSet *to_clks, + InstanceSet *to_insts, + const RiseFallBoth *rf, + const RiseFallBoth *end_rf, + const Sdc *sdc); void checkExceptionToPins(ExceptionTo *to, - const char *file, int) const; + const char *file, + int line, + const Sdc *sdc) const; void deleteExceptionTo(ExceptionTo *to); + InstanceSet findRegisterInstances(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches); + bool registers, + bool latches, + const Mode *mode); PinSet findRegisterDataPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool registers, - bool latches); + bool latches, + const Mode *mode); PinSet findRegisterClkPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool registers, - bool latches); + bool latches, + const Mode *mode); PinSet findRegisterAsyncPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool registers, - bool latches); + bool latches, + const Mode *mode); PinSet findRegisterOutputPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool registers, - bool latches); + bool latches, + const Mode *mode); PinSet findFaninPins(PinSeq *to, bool flat, bool startpoints_only, int inst_levels, int pin_levels, bool thru_disabled, - bool thru_constants); + bool thru_constants, + const Mode *mode); InstanceSet findFaninInstances(PinSeq *to, - bool flat, - bool startpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants); + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants, + const Mode *mode); PinSet findFanoutPins(PinSeq *from, - bool flat, - bool endpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants); + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants, + const Mode *mode); InstanceSet findFanoutInstances(PinSeq *from, - bool flat, - bool endpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants); + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants, + const Mode *mode); + // Registers whose clock pin is in the fanout of an ICG cell InstanceSeq clockGatedRegisters(); bool isClkGatedRegister(const Instance *inst); // The set of clocks that arrive at vertex in the clock network. - ClockSet clocks(const Pin *pin); + ClockSet clocks(const Pin *pin, + const Mode *mode); // Clock domains for a pin. - ClockSet clockDomains(const Pin *pin); + ClockSet clockDomains(const Pin *pin, + const Mode *mode); - void checkSlewLimitPreamble(); - // Return pins with the min/max slew limit slack. + //////////////////////////////////////////////////////////////// // net=null check all nets - // corner=nullptr checks all corners. - PinSeq checkSlewLimits(Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max); - void reportSlewLimitShortHeader(); - void reportSlewLimitShort(Pin *pin, - const Corner *corner, - const MinMax *min_max); - void reportSlewLimitVerbose(Pin *pin, - const Corner *corner, - const MinMax *min_max); - // requires checkSlewLimitPreamble() + void reportSlewChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes, + const MinMax *min_max); + void checkSlewsPreamble(); + // requires checkSlewsPreamble() void checkSlew(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - bool check_clks, - // Return values. - const Corner *&corner1, - const RiseFall *&tr, - Slew &slew, - float &limit, - float &slack); + const SceneSeq &scenes, + const MinMax *min_max, + bool check_clks, + // Return values. + Slew &slew, + float &limit, + float &slack, + const RiseFall *&rf, + const Scene *&Scene); void maxSlewCheck(// Return values. const Pin *&pin, Slew &slew, float &slack, float &limit); void findSlewLimit(const LibertyPort *port, - const Corner *corner, + const Scene *scene, const MinMax *min_max, // Return values. float &limit, bool &exists); + size_t maxSlewViolationCount(); - void checkFanoutLimitPreamble(); - // Return pins with the min/max fanout limit slack. - // net=null check all nets - // corner=nullptr checks all corners. - PinSeq checkFanoutLimits(Net *net, - bool violators, - const MinMax *min_max); - void reportFanoutLimitShortHeader(); - void reportFanoutLimitShort(Pin *pin, - const MinMax *min_max); - void reportFanoutLimitVerbose(Pin *pin, - const MinMax *min_max); - // requires checkFanoutLimitPreamble() + //////////////////////////////////////////////////////////////// + // net == nullptr to check all. + void reportFanoutChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes, + const MinMax *min_max); + void checkFanoutPreamble(); + // requires checkFanoutPreamble() void checkFanout(const Pin *pin, - const MinMax *min_max, - // Return values. - float &fanout, - float &limit, - float &slack); - void maxFanoutCheck(// Return values. - const Pin *&pin, - float &fanout, - float &slack, - float &limit); - - void checkCapacitanceLimitPreamble(); - // Return pins with the min/max slew limit slack. + const Mode *mode, + const MinMax *min_max, + // Return values. + float &fanout, + float &limit, + float &slack); + // Return the pin etc with max fanout check min slack. + void maxFanoutMinSlackPin(const ModeSeq &modes, + // Return values. + const Pin *&pin, + float &fanout, + float &limit, + float &slack, + const Mode *&mode); + size_t fanoutViolationCount(const MinMax *min_max, + const ModeSeq &modes); + + //////////////////////////////////////////////////////////////// // net=null check all nets - // corner=nullptr checks all corners. - PinSeq checkCapacitanceLimits(Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max); - void reportCapacitanceLimitShortHeader(); - void reportCapacitanceLimitShort(Pin *pin, - const Corner *corner, - const MinMax *min_max); - void reportCapacitanceLimitVerbose(Pin *pin, - const Corner *corner, - const MinMax *min_max); + void reportCapacitanceChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes, + const MinMax *min_max); + size_t maxCapacitanceViolationCount(); + void checkCapacitancesPreamble(const SceneSeq &scenes); // requires checkCapacitanceLimitPreamble() void checkCapacitance(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - const Corner *&corner1, - const RiseFall *&tr, - float &capacitance, - float &limit, - float &slack); + const SceneSeq &scenes, + const MinMax *min_max, + // Return values. + float &capacitance, + float &limit, + float &slack, + const RiseFall *&rf, + const Scene *&scene); void maxCapacitanceCheck(// Return values. const Pin *&pin, float &capacitance, float &slack, float &limit); - // Min pulse width check with the least slack. - // corner=nullptr checks all corners. - MinPulseWidthCheck *minPulseWidthSlack(const Corner *corner); - // All violating min pulse width checks. - // corner=nullptr checks all corners. - MinPulseWidthCheckSeq &minPulseWidthViolations(const Corner *corner); - // Min pulse width checks for pins. - // corner=nullptr checks all corners. - MinPulseWidthCheckSeq &minPulseWidthChecks(PinSeq *pins, - const Corner *corner); - // All min pulse width checks. - // corner=nullptr checks all corners. - MinPulseWidthCheckSeq &minPulseWidthChecks(const Corner *corner); - void reportMpwChecks(MinPulseWidthCheckSeq *checks, - bool verbose); - void reportMpwCheck(MinPulseWidthCheck *check, - bool verbose); - - // Min period check with the least slack. - MinPeriodCheck *minPeriodSlack(); - // All violating min period checks. - MinPeriodCheckSeq &minPeriodViolations(); - void reportChecks(MinPeriodCheckSeq *checks, - bool verbose); - void reportCheck(MinPeriodCheck *check, - bool verbose); - - // Max skew check with the least slack. - MaxSkewCheck *maxSkewSlack(); - // All violating min period checks. - MaxSkewCheckSeq &maxSkewViolations(); - void reportChecks(MaxSkewCheckSeq *checks, - bool verbose); - void reportCheck(MaxSkewCheck *check, - bool verbose); + //////////////////////////////////////////////////////////////// + void reportMinPulseWidthChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes); + + //////////////////////////////////////////////////////////////// + void reportMinPeriodChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes); + //////////////////////////////////////////////////////////////// + void reportMaxSkewChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes); //////////////////////////////////////////////////////////////// // User visible but non SDC commands. - // Instance specific process/voltage/temperature. - // Defaults to operating condition if instance is not annotated. - const Pvt *pvt(Instance *inst, - const MinMax *min_max); - void setPvt(Instance *inst, - const MinMaxAll *min_max, - float process, - float voltage, - float temperature); - // Pvt may be shared among multiple instances. - void setPvt(const Instance *inst, - const MinMaxAll *min_max, - const Pvt &pvt); - void setVoltage(const MinMax *min_max, - float voltage); - void setVoltage(const Net *net, - const MinMax *min_max, - float voltage); - // Clear all state except network. - virtual void clear(); - // Remove all constraints. - virtual void removeConstraints(); - // Notify the sta that the constraints have changed directly rather - // than thru this sta API. - virtual void constraintsChanged(); + // Clear all state except network, scenes and liberty libraries. + void clear(); + // Clear all state except network, scenes liberty libraries, and sdc. + void clearNonSdc(); // Namespace used by command interpreter. CmdNamespace cmdNamespace(); void setCmdNamespace(CmdNamespace namespc); - OperatingConditions *operatingConditions(const MinMax *min_max) const; + OperatingConditions *operatingConditions(const MinMax *min_max, + const Sdc *sdc) const; // Set the delay on a timing arc. // Required/arrival times are incrementally updated. void setArcDelay(Edge *edge, - TimingArc *arc, - const Corner *corner, - const MinMaxAll *min_max, - ArcDelay delay); + TimingArc *arc, + const Scene *scene, + const MinMaxAll *min_max, + ArcDelay delay); // Set annotated slew on a vertex for delay calculation. void setAnnotatedSlew(Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - const RiseFallBoth *rf, - float slew); - void writeSdf(const char *filename, - const Corner *corner, - char divider, + const Scene *scene, + const MinMaxAll *min_max, + const RiseFallBoth *rf, + float slew); + void writeSdf(std::string_view filename, + const Scene *scene, + char divider, bool include_typ, - int digits, - bool gzip, - bool no_timestamp, - bool no_version); + int digits, + bool gzip, + bool no_timestamp, + bool no_version); // Remove all delay and slew annotations. void removeDelaySlewAnnotations(); - virtual CheckErrorSeq &checkTiming(bool no_input_delay, - bool no_output_delay, - bool reg_multiple_clks, - bool reg_no_clks, - bool unconstrained_endpoints, - bool loops, - bool generated_clks); + // Instance specific process/voltage/temperature. + // Defaults to operating condition if instance is not annotated. + const Pvt *pvt(Instance *inst, + const MinMax *min_max, + Sdc *sdc); + void setPvt(Instance *inst, + const MinMaxAll *min_max, + float process, + float voltage, + float temperature, + Sdc *sdc); + // Pvt may be shared among multiple instances. + void setPvt(const Instance *inst, + const MinMaxAll *min_max, + const Pvt &pvt, + Sdc *sdc); + void setVoltage(const MinMax *min_max, + float voltage, + Sdc *sdc); + void setVoltage(const Net *net, + const MinMax *min_max, + float voltage, + Sdc *sdc); + + virtual CheckErrorSeq &checkTiming(const Mode *mode, + bool no_input_delay, + bool no_output_delay, + bool reg_multiple_clks, + bool reg_no_clks, + bool unconstrained_endpoints, + bool loops, + bool generated_clks); // Path from/thrus/to filter. // from/thrus/to are owned and deleted by Search. // PathEnds in the returned PathEndSeq are owned by Search PathGroups // and deleted on next call. - virtual PathEndSeq findPathEnds(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool unconstrained, - // Use corner nullptr to report timing - // for all corners. - const Corner *corner, - // max for setup checks. - // min for hold checks. - // min_max for setup and hold checks. - const MinMaxAll *min_max, - // Number of path ends to report in - // each group. - int group_path_count, - // Number of paths to report for - // each endpoint. - int endpoint_path_count, - // endpoint_path_count paths report paths with - // unique pins. - bool unique_pins, - // endpoint_path_count paths report paths with - // unique pins and rise/fall edges. - bool unique_edges, - // Min/max bounds for slack of - // returned path ends. - float slack_min, - float slack_max, - // Sort path ends by slack ignoring path groups. - bool sort_by_slack, - // Path groups to report. - // Null or empty list reports all groups. - PathGroupNameSet *group_names, - // Predicates to filter the type of path - // ends returned. - bool setup, - bool hold, - bool recovery, - bool removal, - bool clk_gating_setup, - bool clk_gating_hold); + PathEndSeq findPathEnds(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool unconstrained, + const SceneSeq &scenes, + // max for setup checks. + // min for hold checks. + // min_max for setup and hold checks. + const MinMaxAll *min_max, + // Number of path ends to report in + // each group. + int group_path_count, + // Number of paths to report for + // each endpoint. + int endpoint_path_count, + // endpoint_path_count paths report unique pins + // without rise/fall variations. + bool unique_pins, + // endpoint_path_count paths report paths with + // unique pins and rise/fall edges. + bool unique_edges, + // Min/max bounds for slack of + // returned path ends. + float slack_min, + float slack_max, + // Sort path ends by slack ignoring path groups. + bool sort_by_slack, + // Path groups to report. + // Empty list reports all groups. + StringSeq &group_names, + // Predicates to filter the type of path + // ends returned. + bool setup, + bool hold, + bool recovery, + bool removal, + bool clk_gating_setup, + bool clk_gating_hold); void setReportPathFormat(ReportPathFormat format); - void setReportPathFieldOrder(StringSeq *field_names); + void setReportPathFieldOrder(const StringSeq &field_names); void setReportPathFields(bool report_input_pin, bool report_hier_pins, - bool report_net, - bool report_cap, - bool report_slew, - bool report_fanout, - bool report_src_attr); - ReportField *findReportPathField(const char *name); + bool report_net, + bool report_cap, + bool report_slew, + bool report_fanout, + bool report_variation, + bool report_src_attr); + ReportField *findReportPathField(std::string_view name); void setReportPathDigits(int digits); void setReportPathNoSplit(bool no_split); - void setReportPathSigmas(bool report_sigmas); void setReportDedupByWord(bool dedup_by_word); void setReportDedupSameDelay(bool dedup_same_delay); void setSilimateDedupEndpointRegex(std::string_view silimate_dedup_endpoints_rx); // SILIMATE: Custom regex-based deduplication by removal of matching parts from endpoints @@ -877,7 +1019,8 @@ public: // - detect path group changes so headers are reported by group. // - JSON format: if not first, add a comma before appending new path void reportPathEnd(PathEnd *end, - PathEnd *prev_end); + PathEnd *prev_end, + bool last); void reportPathEnd(PathEnd *end); void reportPathEnds(PathEndSeq *ends); ReportPath *reportPath() { return report_path_; } @@ -885,19 +1028,20 @@ public: // Report clk skews for clks. void reportClkSkew(ConstClockSeq &clks, - const Corner *corner, - const SetupHold *setup_hold, + const SceneSeq &scenes, + const SetupHold *setup_hold, bool include_internal_latency, - int digits); - float findWorstClkSkew(const SetupHold *setup_hold, + int digits); + Delay findWorstClkSkew(const SetupHold *setup_hold, bool include_internal_latency); void reportClkLatency(ConstClockSeq &clks, - const Corner *corner, + const SceneSeq &scenes, bool include_internal_latency, int digits); // Find min/max/rise/fall delays for clk. ClkDelays findClkDelays(const Clock *clk, + const Scene *scene, bool include_internal_latency); // Update arrival times for all pins. @@ -916,46 +1060,41 @@ public: void arrivalsInvalid(); PinSet startpointPins(); PinSet endpointPins(); - VertexSet *endpoints(); + VertexSet &endpoints(); int endpointViolationCount(const MinMax *min_max); // Find all required times after updateTiming(). void findRequireds(); std::string reportDelayCalc(Edge *edge, TimingArc *arc, - const Corner *corner, + const Scene *scene, const MinMax *min_max, int digits); - void writeSdc(const char *filename, - // Map hierarchical pins and instances to leaf pins and instances. - bool leaf, - // Replace non-sdc get functions with OpenSTA equivalents. - bool native, - int digits, + void writeSdc(const Sdc *sdc, + std::string_view filename, + // Map hierarchical pins and instances to leaf pins and instances. + bool leaf, + // Replace non-sdc get functions with OpenSTA equivalents. + bool native, + int digits, bool gzip, - bool no_timestamp); + bool no_timestamp); // The sum of all negative endpoints slacks. // Incrementally updated. Slack totalNegativeSlack(const MinMax *min_max); - Slack totalNegativeSlack(const Corner *corner, - const MinMax *min_max); + Slack totalNegativeSlack(const Scene *scene, + const MinMax *min_max); // Worst endpoint slack and vertex. // Incrementally updated. Slack worstSlack(const MinMax *min_max); void worstSlack(const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex); - void worstSlack(const Corner *corner, - const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex); - VertexPathIterator *vertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap); - VertexPathIterator *vertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max); + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); + void worstSlack(const Scene *scene, + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); Path *vertexWorstArrivalPath(Vertex *vertex, const RiseFall *rf, const MinMax *min_max); @@ -982,93 +1121,78 @@ public: // update timing to the level of the vertex. They do NOT do multiple // passes required propagate arrivals around latch loops. // See Sta::updateTiming() to propagate arrivals around latch loops. - Arrival vertexArrival(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap, - const MinMax *min_max); - // Min/max across all clock tags. - Arrival vertexArrival(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap); - Arrival vertexArrival(Vertex *vertex, - const MinMax *min_max); - Arrival pinArrival(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max); - Required vertexRequired(Vertex *vertex, - const MinMax *min_max); - Required vertexRequired(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max); - // Min/max across all clock tags. - Required vertexRequired(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap); - Required vertexRequired(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap); - - Slack netSlack(const Net *net, - const MinMax *min_max); - Slack pinSlack(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max); - Slack pinSlack(const Pin *pin, - const MinMax *min_max); + Arrival arrival(const Pin *pin, + const RiseFallBoth *rf, + const MinMax *min_max); + Arrival arrival(Vertex *vertex, + const RiseFallBoth *rf, + const SceneSeq &scenes, + const MinMax *min_max); + + Required required(Vertex *vertex, + const RiseFallBoth *rf, + const SceneSeq &scenes, + const MinMax *min_max); + + Slack slack(const Net *net, + const MinMax *min_max); + Slack slack(const Pin *pin, + const RiseFallBoth *rf, + const SceneSeq &scenes, + const MinMax *min_max); + + Slack slack(Vertex *vertex, + const MinMax *min_max); + Slack slack(Vertex *vertex, + const RiseFall *rf, + const MinMax *min_max); + Slack slack(Vertex *vertex, + const RiseFallBoth *rf, + const SceneSeq &scenes, + const MinMax *min_max); + + void slacks(Vertex *vertex, + Slack (&slacks)[RiseFall::index_count][MinMax::index_count]); // Worst slack for an endpoint in a path group. Slack endpointSlack(const Pin *pin, - const std::string &path_group_name, - const MinMax *min_max); - Slack vertexSlack(Vertex *vertex, - const MinMax *min_max); - Slack vertexSlack(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max); - // Slack with respect to clk_edge. - Slack vertexSlack(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap); - // Min slack across all clock tags. - Slack vertexSlack(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap); - void vertexSlacks(Vertex *vertex, - Slack (&slacks)[RiseFall::index_count][MinMax::index_count]); - // Slew for one corner. - Slew vertexSlew(Vertex *vertex, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max); - // Slew for one delay calc analysis pt (corner min/max). - Slew vertexSlew(Vertex *vertex, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap); - // Slew across all corners. - Slew vertexSlew(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max); - Slew vertexSlew(Vertex *vertex, - const MinMax *min_max); + std::string_view path_group_name, + const MinMax *min_max); + + void reportArrivalWrtClks(const Pin *pin, + const Scene *scene, + bool report_variance, + int digits); + void reportRequiredWrtClks(const Pin *pin, + const Scene *scene, + bool report_variance, + int digits); + void reportSlackWrtClks(const Pin *pin, + const Scene *scene, + bool report_variance, + int digits); + + Slew slew(Vertex *vertex, + const RiseFallBoth *rf, + const SceneSeq &scenes, + const MinMax *min_max); + ArcDelay arcDelay(Edge *edge, - TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap); + TimingArc *arc, + DcalcAPIndex ap_index); // True if the timing arc has been back-annotated. bool arcDelayAnnotated(Edge *edge, - TimingArc *arc, - DcalcAnalysisPt *dcalc_ap); + TimingArc *arc, + const Scene *scene, + const MinMax *min_max); // Set/unset the back-annotation flag for a timing arc. void setArcDelayAnnotated(Edge *edge, - TimingArc *arc, - DcalcAnalysisPt *dcalc_ap, - bool annotated); + TimingArc *arc, + const Scene *scene, + const MinMax *min_max, + bool annotated); // Make sure levels are up to date and return vertex level. Level vertexLevel(Vertex *vertex); GraphLoopSeq &graphLoops(); - PathAnalysisPt *pathAnalysisPt(Path *path); - DcalcAnalysisPt *pathDcalcAnalysisPt(Path *path); TagIndex tagCount() const; TagGroupIndex tagGroupCount() const; int clkInfoCount() const; @@ -1076,90 +1200,91 @@ public: int vertexPathCount(Vertex *vertex) const; Vertex *maxPathCountVertex() const; - LogicValue simLogicValue(const Pin *pin); // Propagate liberty constant functions and pins tied high/low through - // combinational logic and registers. + // combinational logic and registers. This is mode/sdc independent. + // Used by OpenROAD/Restructure.cpp void findLogicConstants(); - // Clear the constants found by findLogicConstants so they do not interfere - // with normal constant propagate for timing. + LogicValue simLogicValue(const Pin *pin, + const Mode *mode); + // Clear propagated sim constants. void clearLogicConstants(); // Instances sorted by max driver pin slew. InstanceSeq slowDrivers(int count); - // Make parasitic analysis points. - // per_corner ap_count - // false 2 - // true corners*2 - void setParasiticAnalysisPts(bool per_corner); + Parasitics *makeConcreteParasitics(std::string_view name, + std::string_view filename); // Annotate hierarchical "instance" with parasitics. // The parasitic analysis point is ap_name. // The parasitic memory footprint is much smaller if parasitic // networks (dspf) are reduced and deleted after reading each net // with reduce_to and delete_after_reduce. // Return true if successful. - bool readSpef(const char *filename, - Instance *instance, - const Corner *corner, + bool readSpef(std::string_view name, + std::string_view filename, + Instance *instance, + Scene *scene, const MinMaxAll *min_max, - bool pin_cap_included, - bool keep_coupling_caps, - float coupling_cap_factor, - bool reduce); - void reportParasiticAnnotation(bool report_unannotated, - const Corner *corner); + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + bool reduce); + Parasitics *findParasitics(const std::string &name); + void reportParasiticAnnotation(const std::string &spef_name, + bool report_unannotated); // Parasitics. void findPiElmore(Pin *drvr_pin, - const RiseFall *rf, - const MinMax *min_max, - float &c2, - float &rpi, - float &c1, - bool &exists) const; + const RiseFall *rf, + const MinMax *min_max, + float &c2, + float &rpi, + float &c1, + bool &exists) const; void findElmore(Pin *drvr_pin, - Pin *load_pin, - const RiseFall *rf, - const MinMax *min_max, - float &elmore, - bool &exists) const; + Pin *load_pin, + const RiseFall *rf, + const MinMax *min_max, + float &elmore, + bool &exists) const; void makePiElmore(Pin *drvr_pin, - const RiseFall *rf, - const MinMaxAll *min_max, - float c2, - float rpi, - float c1); + const RiseFall *rf, + const MinMaxAll *min_max, + float c2, + float rpi, + float c1); void setElmore(Pin *drvr_pin, - Pin *load_pin, - const RiseFall *rf, - const MinMaxAll *min_max, - float elmore); + Pin *load_pin, + const RiseFall *rf, + const MinMaxAll *min_max, + float elmore); void deleteParasitics(); Parasitic *makeParasiticNetwork(const Net *net, bool includes_pin_caps, - const ParasiticAnalysisPt *ap); + const Scene *scene, + const MinMax *min_max); //////////////////////////////////////////////////////////////// // TCL network edit function support. virtual Instance *makeInstance(const char *name, - LibertyCell *cell, - Instance *parent); + LibertyCell *cell, + Instance *parent); virtual void deleteInstance(Instance *inst); // replace_cell virtual void replaceCell(Instance *inst, - Cell *to_cell); + Cell *to_cell); virtual void replaceCell(Instance *inst, - LibertyCell *to_lib_cell); + LibertyCell *to_lib_cell); virtual Net *makeNet(const char *name, - Instance *parent); + Instance *parent); virtual void deleteNet(Net *net); // connect_net virtual void connectPin(Instance *inst, - Port *port, - Net *net); + Port *port, + Net *net); virtual void connectPin(Instance *inst, - LibertyPort *port, - Net *net); + LibertyPort *port, + Net *net); // disconnect_net virtual void disconnectPin(Pin *pin); virtual void makePortPin(const char *port_name, @@ -1168,6 +1293,8 @@ public: // editing API. For example, reading a netlist without using the // builtin network readers. void networkChanged(); + // Network changed but all SDC references to instance/net/pin/port are preserved. + void networkChangedNonSdc(); void deleteLeafInstanceBefore(const Instance *inst); void deleteInstancePinsBefore(const Instance *inst); @@ -1176,12 +1303,12 @@ public: // Replace the instance cell with to_cell. // equivCells(from_cell, to_cell) must be true. virtual void replaceEquivCellBefore(const Instance *inst, - const LibertyCell *to_cell); + const LibertyCell *to_cell); virtual void replaceEquivCellAfter(const Instance *inst); // Replace the instance cell with to_cell. // equivCellPorts(from_cell, to_cell) must be true. virtual void replaceCellBefore(const Instance *inst, - const LibertyCell *to_cell); + const LibertyCell *to_cell); virtual void replaceCellAfter(const Instance *inst); virtual void makePortPinAfter(Pin *pin); virtual void connectPinAfter(const Pin *pin); @@ -1192,14 +1319,19 @@ public: //////////////////////////////////////////////////////////////// - void ensureClkNetwork(); - void clkPinsInvalid(); + void ensureClkNetwork(const Mode *mode); + void clkPinsInvalid(const Mode *mode); // The following functions assume ensureClkNetwork() has been called. - bool isClock(const Pin *pin) const; - bool isClock(const Net *net) const; - bool isIdealClock(const Pin *pin) const; - bool isPropagatedClock(const Pin *pin) const; - const PinSet *pins(const Clock *clk); + bool isClock(const Pin *pin, + const Mode *mode); + bool isClock(const Net *net, + const Mode *mode); + bool isIdealClock(const Pin *pin, + const Mode *mode); + bool isPropagatedClock(const Pin *pin, + const Mode *mode); + const PinSet *pins(const Clock *clk, + const Mode *mode); //////////////////////////////////////////////////////////////// @@ -1213,11 +1345,7 @@ public: // Ensure that the timing graph has been built. Graph *ensureGraph(); void ensureClkArrivals(); - Corner *cmdCorner() const; - void setCmdCorner(Corner *corner); - Corner *findCorner(const char *corner_name); - bool multiCorner(); - virtual void makeCorners(StringSet *corner_names); + // Find all arc delays and vertex slews with delay calculator. virtual void findDelays(); // Find arc delays and vertex slews thru to level of to_vertex. @@ -1232,10 +1360,10 @@ public: void searchPreamble(); // Define the delay calculator implementation. - void setArcDelayCalc(const char *delay_calc_name); + void setArcDelayCalc(std::string_view delay_calc_name); void setDebugLevel(const char *what, - int level); + int level); // Delays and arrivals downsteam from inst are invalid. void delaysInvalidFrom(const Instance *inst); @@ -1248,43 +1376,57 @@ public: void delaysInvalidFromFanin(const Pin *pin); void delaysInvalidFromFanin(Vertex *vertex); void replaceCellPinInvalidate(const LibertyPort *from_port, - Vertex *vertex, - const LibertyCell *to_cell); + Vertex *vertex, + const LibertyCell *to_cell); // Power API. + void reportPowerDesign(const Scene *scene, + int digits); + void reportPowerInsts(const InstanceSeq &insts, + const Scene *scene, + int digits); + void reportPowerHighestInsts(size_t count, + const Scene *scene, + int digits); + void reportPowerDesignJson(const Scene *scene, + int digits); + void reportPowerInstsJson(const InstanceSeq &insts, + const Scene *scene, + int digits); Power *power() { return power_; } const Power *power() const { return power_; } - void power(const Corner *corner, - // Return values. - PowerResult &total, - PowerResult &sequential, - PowerResult &combinational, - PowerResult &clock, - PowerResult ¯o, - PowerResult &pad); + void power(const Scene *scene, + // Return values. + PowerResult &total, + PowerResult &sequential, + PowerResult &combinational, + PowerResult &clock, + PowerResult ¯o, + PowerResult &pad); PowerResult power(const Instance *inst, - const Corner *corner); - PwrActivity activity(const Pin *pin); + const Scene *scene); + PwrActivity activity(const Pin *pin, + const Scene *scene); - void writeTimingModel(const char *lib_name, - const char *cell_name, - const char *filename, - const Corner *corner, - const bool scalar); + void writeTimingModel(std::string_view lib_name, + std::string_view cell_name, + std::string_view filename, + const Scene *scene, + const bool scalar); // Find equivalent cells in equiv_libs. // Optionally add mappings for cells in map_libs. void makeEquivCells(LibertyLibrarySeq *equiv_libs, - LibertyLibrarySeq *map_libs); + LibertyLibrarySeq *map_libs); LibertyCellSeq *equivCells(LibertyCell *cell); - void writePathSpice(Path *path, - const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, + void writePathSpice(const Path *path, + std::string_view spice_filename, + std::string_view subckt_filename, + std::string_view lib_subckt_filename, + std::string_view model_filename, + std::string_view power_name, + std::string_view gnd_name, CircuitSim ckt_sim); //////////////////////////////////////////////////////////////// @@ -1299,12 +1441,13 @@ public: // TCL variable sta_crpr_mode. CrprMode crprMode() const; void setCrprMode(CrprMode mode); - // TCL variable sta_pocv_enabled. + // TCL variable sta_pocv_mode. // Parametric on chip variation (statisical sta). - bool pocvEnabled() const; - void setPocvEnabled(bool enabled); + PocvMode pocvMode() const; + void setPocvMode(PocvMode mode); // Number of std deviations from mean to use for normal distributions. - void setSigmaFactor(float factor); + float pocvQuantile(); + void setPocvQuantile(float quantile); // TCL variable sta_propagate_gated_clock_enable. // Propagate gated clock enable arrivals. bool propagateGatedClockEnable() const; @@ -1321,10 +1464,6 @@ public: // Enable/disable timing from bidirect pins back into the instance. bool bidirectInstPathsEnabled() const; void setBidirectInstPathsEnabled(bool enabled); - // TCL variable sta_bidirect_net_paths_enabled. - // Enable/disable timing from bidirect driver pins to their own loads. - bool bidirectNetPathsEnabled() const; - void setBidirectNetPathsEnabled(bool enabled); // TCL variable sta_recovery_removal_checks_enabled. bool recoveryRemovalChecksEnabled() const; void setRecoveryRemovalChecksEnabled(bool enabled); @@ -1382,21 +1521,17 @@ protected: virtual void makeUnits(); virtual void makeNetwork(); virtual void makeSdcNetwork(); - virtual void makeSdc(); virtual void makeGraph(); - virtual void makeCorners(); + virtual void makeDefaultScene(); virtual void makeLevelize(); - virtual void makeParasitics(); virtual void makeArcDelayCalc(); virtual void makeGraphDelayCalc(); - virtual void makeSim(); virtual void makeSearch(); virtual void makeLatches(); - virtual void makeClkNetwork(); virtual void makeCheckTiming(); - virtual void makeCheckSlewLimits(); - virtual void makeCheckFanoutLimits(); - virtual void makeCheckCapacitanceLimits(); + virtual void makeCheckSlews(); + virtual void makeCheckFanouts(); + virtual void makeCheckCapacitances(); virtual void makeCheckMinPulseWidths(); virtual void makeCheckMinPeriods(); virtual void makeCheckMaxSkews(); @@ -1406,119 +1541,148 @@ protected: virtual void makeObservers(); NetworkEdit *networkCmdEdit(); - LibertyLibrary *readLibertyFile(const char *filename, - Corner *corner, - const MinMaxAll *min_max, - bool infer_latches); - // Allow external Liberty reader to parse forms not used by Sta. - virtual LibertyLibrary *readLibertyFile(const char *filename, - bool infer_latches); + LibertyLibrary *readLibertyFile(std::string_view filename, + Scene *scene, + const MinMaxAll *min_max, + bool infer_latches); void delayCalcPreamble(); void delaysInvalidFrom(const Port *port); void delaysInvalidFromFanin(const Port *port); void deleteEdge(Edge *edge); void netParasiticCaps(Net *net, - const RiseFall *rf, - const MinMax *min_max, - float &pin_cap, - float &wire_cap) const; + const RiseFall *rf, + const MinMax *min_max, + float &pin_cap, + float &wire_cap) const; const Pin *findNetParasiticDrvrPin(const Net *net) const; void exprConstantPins(FuncExpr *expr, - const Instance *inst, - PinSet &pins); - Slack vertexSlack1(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap); + const Instance *inst, + const Mode *mode, + // Return value. + PinSet &pins); void findRequired(Vertex *vertex); - Required vertexRequired(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap, - const MinMax *min_max); + + void reportDelaysWrtClks(const Pin *pin, + const Scene *scene, + bool report_variance, + int digits, + bool find_required, + const PathDelayFunc &get_path_delay); + void reportDelaysWrtClks(Vertex *vertex, + const Scene *scene, + bool report_variance, + int digits, + bool find_required, + const PathDelayFunc &get_path_delay); + void reportDelaysWrtClks(const ClockEdge *clk_edge, + bool report_variance, + int digits, + DelaysWrtClks &clk_delays); + std::string formatDelay(const RiseFall *rf, + const MinMax *min_max, + const RiseFallMinMaxDelay &delays, + bool report_variance, + int digits); + void connectDrvrPinAfter(Vertex *vertex); void connectLoadPinAfter(Vertex *vertex); Path *latchEnablePath(Path *q_path, - Edge *d_q_edge, - const ClockEdge *en_clk_edge); + Edge *d_q_edge, + const ClockEdge *en_clk_edge); void clockSlewChanged(Clock *clk); - void minPulseWidthPreamble(); - void minPeriodPreamble(); void maxSkewPreamble(); bool idealClockMode(); void disableAfter(); void findFaninPins(Vertex *vertex, - bool flat, - bool startpoints_only, - int inst_levels, - int pin_levels, - PinSet &fanin, - SearchPred &pred); + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + PinSet &fanin, + SearchPred &pred, + const Mode *mode); void findFaninPins(Vertex *to, - bool flat, - int inst_levels, - int pin_levels, - VertexSet &visited, - SearchPred *pred, - int inst_level, - int pin_level); + bool flat, + int inst_levels, + int pin_levels, + VertexSet &visited, + SearchPred *pred, + int inst_level, + int pin_level, + const Mode *mode); void findFanoutPins(Vertex *vertex, - bool flat, - bool endpoints_only, - int inst_levels, - int pin_levels, - PinSet &fanout, - SearchPred &pred); + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + PinSet &fanout, + SearchPred &pred, + const Mode *mode); void findFanoutPins(Vertex *from, - bool flat, - int inst_levels, - int pin_levels, - VertexSet &visited, - SearchPred *pred, - int inst_level, - int pin_level); - void findRegisterPreamble(); + bool flat, + int inst_levels, + int pin_levels, + VertexSet &visited, + SearchPred *pred, + int inst_level, + int pin_level, + const Mode *mode); + void findRegisterPreamble(const Mode *mode); bool crossesHierarchy(Edge *edge) const; - void readLibertyAfter(LibertyLibrary *liberty, - Corner *corner, - const MinMax *min_max); void powerPreamble(); + void powerPreamble(const Scene *scene); virtual void replaceCell(Instance *inst, Cell *to_cell, LibertyCell *to_lib_cell); - void sdcChangedGraph(); - void ensureGraphSdcAnnotated(); - CornerSeq makeCornerSeq(Corner *corner) const; - void makeParasiticAnalysisPts(); void clkSkewPreamble(); void setCmdNamespace1(CmdNamespace namespc); void setThreadCount1(int thread_count); + void updateLibertyScenes(); + void updateSceneLiberty(Scene *scene, + const StringSeq &liberty_min_files, + const StringSeq &liberty_max_files); + void updateSceneLiberty(Scene *scene, + const StringSeq &liberty_files, + const MinMaxAll *min_max); + + Scene *makeScene(const std::string &name, + Mode *mode, + Parasitics *parasitics_min, + Parasitics *parasitics_max); + Scene *makeScene(const std::string &name, + Mode *mode, + Parasitics *parasitics); + void deleteScenes(); + void checkLibrarayPocv(); - CmdNamespace cmd_namespace_; - Instance *current_instance_; - Corner *cmd_corner_; - VerilogReader *verilog_reader_; - CheckTiming *check_timing_; - CheckSlewLimits *check_slew_limits_; - CheckFanoutLimits *check_fanout_limits_; - CheckCapacitanceLimits *check_capacitance_limits_; - CheckMinPulseWidths *check_min_pulse_widths_; - CheckMinPeriods *check_min_periods_; - CheckMaxSkews *check_max_skews_; - ClkSkews *clk_skews_; - ReportPath *report_path_; - Power *power_; - Tcl_Interp *tcl_interp_; - bool update_genclks_; - EquivCells *equiv_cells_; - bool graph_sdc_annotated_; - bool parasitics_per_corner_; - bool parasitics_per_min_max_; - bool liberty_line_debug_; - Properties properties_; + Scene *cmd_scene_{nullptr}; + CmdNamespace cmd_namespace_{CmdNamespace::sdc}; + Instance *current_instance_{nullptr}; + SceneNameMap scene_name_map_; + ModeNameMap mode_name_map_; + ParasiticsNameMap parasitics_name_map_; + VerilogReader *verilog_reader_{nullptr}; + CheckTiming *check_timing_{nullptr}; + CheckSlews *check_slews_{nullptr}; + CheckFanouts *check_fanouts_{nullptr}; + CheckCapacitances *check_capacitances_{nullptr}; + CheckMinPulseWidths *check_min_pulse_widths_{nullptr}; + CheckMinPeriods *check_min_periods_{nullptr}; + CheckMaxSkews *check_max_skews_{nullptr}; + ClkSkews *clk_skews_{nullptr}; + ReportPath *report_path_{nullptr}; + Power *power_{nullptr}; + Tcl_Interp *tcl_interp_{nullptr}; + bool update_genclks_{false}; + EquivCells *equiv_cells_{nullptr}; + Properties properties_{this}; + bool graph_sdc_annotated_{false}; + bool parasitics_per_corner_{false}; + bool parasitics_per_min_max_{false}; + bool liberty_line_debug_{false}; // Singleton sta used by tcl command interpreter. - static Sta *sta_; + inline static Sta *sta_{nullptr}; }; -} // namespace +} // namespace sta diff --git a/include/sta/StaMain.hh b/include/sta/StaMain.hh index 92a9bbe46..e588ab265 100644 --- a/include/sta/StaMain.hh +++ b/include/sta/StaMain.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,8 @@ #pragma once +#include + struct Tcl_Interp; namespace sta { @@ -33,16 +35,16 @@ class Sta; // Parse command line argument int staTclAppInit(int argc, - char *argv[], - const char *init_filename, - Tcl_Interp *interp); + char *argv[], + const char *init_filename, + Tcl_Interp *interp); // Sta initialization. // Makes the Sta object and registers TCL commands. void initSta(int argc, - char *argv[], - Tcl_Interp *interp); + char *argv[], + Tcl_Interp *interp); // TCL init files are encoded into the string init using the three // digit decimal equivalent for each ascii character. This function @@ -51,28 +53,26 @@ initSta(int argc, // separate files that have to be located and loaded at run time. void evalTclInit(Tcl_Interp *interp, - const char *inits[]); + const char *inits[]); char * unencode(const char *inits[]); bool findCmdLineFlag(int &argc, - char *argv[], - const char *flag); + char *argv[], + std::string_view flag); char * findCmdLineKey(int &argc, - char *argv[], - const char *key); + char *argv[], + std::string_view key); int parseThreadsArg(int &argc, - char *argv[]); + char *argv[]); int sourceTclFile(const char *filename, - bool echo, - bool verbose, - Tcl_Interp *interp); -bool -is_regular_file(const char *filename); + bool echo, + bool verbose, + Tcl_Interp *interp); -} // namespace +} // namespace sta diff --git a/include/sta/StaState.hh b/include/sta/StaState.hh index cce271be2..c0da5998e 100644 --- a/include/sta/StaState.hh +++ b/include/sta/StaState.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,10 @@ #pragma once +#include + +#include "Scene.hh" + namespace sta { class Report; @@ -32,8 +36,6 @@ class Units; class Network; class NetworkEdit; class NetworkReader; -class Sdc; -class Corners; class Graph; class Edge; class Levelize; @@ -43,9 +45,12 @@ class Parasitics; class ArcDelayCalc; class GraphDelayCalc; class Latches; -class ClkNetwork; class DispatchQueue; class Variables; +class DelayOps; + +using ModeSeq = std::vector; +using ModeSet = std::set; // Most STA components use functionality in other components. // This class simplifies the process of copying pointers to the @@ -60,7 +65,7 @@ public: // Copy the state from sta. This is virtual so that a component // can notify sub-components. virtual void copyState(const StaState *sta); - virtual ~StaState() {} + virtual ~StaState() = default; Report *report() { return report_; } Report *report() const { return report_; } void setReport(Report *report); @@ -81,35 +86,38 @@ public: // Command network uses the SDC namespace. Network *cmdNetwork() { return cmd_network_; } Network *cmdNetwork() const { return cmd_network_; } - Sdc *sdc() { return sdc_; } - Sdc *sdc() const { return sdc_; } - Corners *corners() { return corners_; } - Corners *corners() const { return corners_; } Graph *graph() { return graph_; } Graph *graph() const { return graph_; } + Graph *&graphRef() { return graph_; } Levelize *levelize() { return levelize_; } Levelize *levelize() const { return levelize_; } - Parasitics *parasitics() { return parasitics_; } - Parasitics *parasitics() const { return parasitics_; } ArcDelayCalc *arcDelayCalc() { return arc_delay_calc_; } ArcDelayCalc *arcDelayCalc() const { return arc_delay_calc_; } GraphDelayCalc *graphDelayCalc() { return graph_delay_calc_; } GraphDelayCalc *graphDelayCalc() const { return graph_delay_calc_; } - Sim *sim() { return sim_; } - Sim *sim() const { return sim_; } Search *search() { return search_; } Search *search() const { return search_; } + const DelayOps *delayOps() const { return delay_ops_; } Latches *latches() { return latches_; } Latches *latches() const { return latches_; } - ClkNetwork *clkNetwork() { return clk_network_; } - ClkNetwork *clkNetwork() const { return clk_network_; } unsigned threadCount() const { return thread_count_; } - float sigmaFactor() const { return sigma_factor_; } - bool crprActive() const; + bool crprActive(const Mode *mode) const; Variables *variables() { return variables_; } const Variables *variables() const { return variables_; } // Edge is default cond disabled by timing_disable_cond_default_arcs var. - bool isDisabledCondDefault(Edge *edge) const; + [[nodiscard]] bool isDisabledCondDefault(const Edge *edge) const; + + const SceneSeq &scenes() { return scenes_; } + const SceneSeq &scenes() const { return scenes_; } + bool multiScene() const { return scenes_.size() > 1; } + size_t scenePathCount() const; + DcalcAPIndex dcalcAnalysisPtCount() const; + + SceneSet scenesSet(); + + ModeSeq &modes() { return modes_; } + const ModeSeq &modes() const { return modes_; } + bool multiMode() const { return modes_.size() > 1; } protected: Report *report_; @@ -119,21 +127,18 @@ protected: Network *sdc_network_; // Network used by command interpreter (SdcNetwork). Network *cmd_network_; - Sdc *sdc_; - Corners *corners_; + SceneSeq scenes_; + ModeSeq modes_; Graph *graph_; Levelize *levelize_; - Parasitics *parasitics_; ArcDelayCalc *arc_delay_calc_; GraphDelayCalc *graph_delay_calc_; - Sim *sim_; Search *search_; + DelayOps *delay_ops_; Latches *latches_; - ClkNetwork *clk_network_; Variables *variables_; int thread_count_; DispatchQueue *dispatch_queue_; - float sigma_factor_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Stats.hh b/include/sta/Stats.hh index 7f10effeb..79cd3ef41 100644 --- a/include/sta/Stats.hh +++ b/include/sta/Stats.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -35,17 +35,17 @@ class Report; class Stats { public: - explicit Stats(Debug *debug, - Report *report); + Stats(Debug *debug, + Report *report); void report(const char *step); private: - double elapsed_begin_; - double user_begin_; - double system_begin_; - size_t memory_begin_; + double elapsed_begin_{0.0}; + double user_begin_{0.0}; + double system_begin_{0.0}; + size_t memory_begin_{0}; Debug *debug_; Report *report_; }; -} // namespace +} // namespace sta diff --git a/include/sta/StringUtil.hh b/include/sta/StringUtil.hh index b2f3af8b9..3f3464300 100644 --- a/include/sta/StringUtil.hh +++ b/include/sta/StringUtil.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,18 +24,25 @@ #pragma once +#include +#include #include #include +#include #include +#include +#include #include "Machine.hh" // __attribute__ -#include "Vector.hh" namespace sta { +using StringSeq = std::vector; +using StringSet = std::set; + inline bool stringEq(const char *str1, - const char *str2) + const char *str2) { return strcmp(str1, str2) == 0; } @@ -43,155 +50,53 @@ stringEq(const char *str1, // Compare the first length characters. inline bool stringEq(const char *str1, - const char *str2, - size_t length) + const char *str2, + size_t length) { return strncmp(str1, str2, length) == 0; } -inline bool -stringEqIf(const char *str1, - const char *str2) -{ - return (str1 == nullptr && str2 == nullptr) - || (str1 && str2 && strcmp(str1, str2) == 0); -} - // Case sensitive compare the beginning of str1 to str2. inline bool stringBeginEq(const char *str1, - const char *str2) + const char *str2) { return strncmp(str1, str2, strlen(str2)) == 0; } -// Case insensitive compare the beginning of str1 to str2. -inline bool -stringBeginEqual(const char *str1, - const char *str2) -{ - return strncasecmp(str1, str2, strlen(str2)) == 0; -} - -// Case insensitive compare. -inline bool -stringEqual(const char *str1, - const char *str2) -{ - return strcasecmp(str1, str2) == 0; -} - inline bool -stringEqualIf(const char *str1, - const char *str2) +charEqual(unsigned char c1, + unsigned char c2) { - return (str1 == nullptr && str2 == nullptr) - || (str1 && str2 && strcasecmp(str1, str2) == 0); + return std::tolower(c1) == std::tolower(c2); } +// Case insensitive compare the beginning of str1 to str2. inline bool -stringLess(const char *str1, - const char *str2) +stringBeginEqual(std::string_view str, + std::string_view prefix) { - return strcmp(str1, str2) < 0; + if (str.size() < prefix.size()) + return false; + return std::ranges::equal(str.substr(0, prefix.size()), prefix, charEqual); } +// Case insensitive compare. inline bool -stringLessIf(const char *str1, - const char *str2) -{ - return (str1 == nullptr && str2 != nullptr) - || (str1 != nullptr && str2 != nullptr && strcmp(str1, str2) < 0); -} - -class CharPtrLess +stringEqual(std::string_view s1, + std::string_view s2) { -public: - bool operator()(const char *string1, - const char *string2) const - { - return stringLess(string1, string2); - } -}; - -// Case insensitive comparision. -class CharPtrCaseLess -{ -public: - bool operator()(const char *string1, - const char *string2) const - { - return strcasecmp(string1, string2) < 0; - } -}; - -class StringLessIf -{ -public: - bool operator()(const char *string1, - const char *string2) const - { - return stringLessIf(string1, string2); - } -}; - -// strdup using new instead of malloc so delete can be used on the strings. -char * -stringCopy(const char *str); - -inline void -stringAppend(char *&str1, - const char *str2) -{ - strcpy(str1, str2); - str1 += strlen(str2); + return std::ranges::equal(s1, s2, charEqual); } -void -stringDeleteCheck(const char *str); - -// Delete for strings allocated with new char[]. -inline void -stringDelete(const char *str) -{ - delete [] str; -} +std::pair +stringFloat(const std::string &str); bool isDigits(const char *str); -// Print to a new string. -// Caller owns returned string. -char * -stringPrint(const char *fmt, - ...) __attribute__((format (printf, 1, 2))); -std::string -stdstrPrint(const char *fmt, - ...) __attribute__((format (printf, 1, 2))); -char * -stringPrintArgs(const char *fmt, - va_list args); -void -stringPrint(std::string &str, - const char *fmt, - ...) __attribute__((format (printf, 2, 3))); -// Formated append to std::string. -void -stringAppend(std::string &str, - const char *fmt, - ...) __attribute__((format (printf, 2, 3))); - -// Print to a temporary string. -char * -stringPrintTmp(const char *fmt, - ...) __attribute__((format (printf, 1, 2))); - -char * -makeTmpString(size_t length); -char * -makeTmpString(std::string &str); bool -isTmpString(const char *str); +isDigits(std::string_view str); //////////////////////////////////////////////////////////////// @@ -207,12 +112,9 @@ trimLeft(std::string &str); void trim(std::string &str); -typedef Vector StringVector; - -void -split(const std::string &text, - const std::string &delims, - // Return values. - StringVector &tokens); +// Parse text into delimiter separated tokens and skip whitepace. +StringSeq +parseTokens(const std::string &text, + std::string_view delims = " \t"); -} // namespace +} // namespace sta diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index 40dd60c68..40855a75c 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,95 +24,104 @@ #pragma once -#include +#include #include +#include +#include +#include -#include "MinMax.hh" -#include "Vector.hh" -#include "Transition.hh" #include "LibertyClass.hh" +#include "MinMax.hh" #include "TimingModel.hh" +#include "Transition.hh" +#include "Variables.hh" namespace sta { class Unit; class Units; class Report; +class TableModels; class Table; +class TableModel; +class TableAxis; class OutputWaveforms; -class Table1; -typedef Vector FloatSeq; -typedef Vector FloatTable; -typedef Vector Table1Seq; -typedef Table1 Waveform; +using FloatSeq = std::vector; +using FloatTable = std::vector; +// Sequence of 1D tables (order 1). +using Table1Seq = std::vector; +using Waveform = Table; +using TableModelsEarlyLate = std::array; TableAxisVariable -stringTableAxisVariable(const char *variable); -const char * +stringTableAxisVariable(std::string_view variable); +std::string_view tableVariableString(TableAxisVariable variable); const Unit * tableVariableUnit(TableAxisVariable variable, - const Units *units); + const Units *units); class GateTableModel : public GateTimingModel { public: GateTableModel(LibertyCell *cell, - TableModel *delay_model, - TableModel *delay_sigma_models[EarlyLate::index_count], - TableModel *slew_model, - TableModel *slew_sigma_models[EarlyLate::index_count], + TableModels *delay_models, + TableModels *slew_models, ReceiverModelPtr receiver_model, OutputWaveforms *output_waveforms); - virtual ~GateTableModel(); + GateTableModel(LibertyCell *cell, + TableModels *delay_models, + TableModels *slew_models); void gateDelay(const Pvt *pvt, float in_slew, float load_cap, - bool pocv_enabled, // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew) const override; - // deprecated 2024-01-07 - // related_out_cap arg removed. - void gateDelay(const Pvt *pvt, - float in_slew, - float load_cap, - float related_out_cap, - bool pocv_enabled, - ArcDelay &gate_delay, - Slew &drvr_slew) const __attribute__ ((deprecated)); + float &gate_delay, + float &drvr_slew) const override; + // Fill in pocv parameters in gate_delay, drvr_slew. + void gateDelayPocv(const Pvt *pvt, + float in_slew, + float load_cap, + const MinMax *min_max, + PocvMode pocv_mode, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) const override; std::string reportGateDelay(const Pvt *pvt, float in_slew, float load_cap, - bool pocv_enabled, + const MinMax *min_max, + PocvMode pocv_mode, int digits) const override; float driveResistance(const Pvt *pvt) const override; + void setIsScaled(bool is_scaled) override; - const TableModel *delayModel() const { return delay_model_; } - const TableModel *slewModel() const { return slew_model_; } + const TableModels *delayModels() const { return delay_models_.get(); } + const TableModel *delayModel() const; + const TableModels *slewModels() const { return slew_models_.get(); } + const TableModel *slewModel() const; const ReceiverModel *receiverModel() const { return receiver_model_.get(); } - OutputWaveforms *outputWaveforms() const { return output_waveforms_; } + OutputWaveforms *outputWaveforms() const { return output_waveforms_.get(); } // Check the axes before making the model. // Return true if the model axes are supported. - static bool checkAxes(const TablePtr &table); + static bool checkAxes(const TableModel *table); protected: void maxCapSlew(float in_slew, - const Pvt *pvt, - float &slew, - float &cap) const; - void setIsScaled(bool is_scaled) override; + const Pvt *pvt, + float &slew, + float &cap) const; float axisValue(const TableAxis *axis, - float load_cap, - float in_slew, - float related_out_cap) const; + float in_slew, + float load_cap, + float related_out_cap) const; float findValue(const Pvt *pvt, - const TableModel *model, - float in_slew, - float load_cap, - float related_out_cap) const; - std::string reportTableLookup(const char *result_name, + const TableModel *model, + float in_slew, + float load_cap, + float related_out_cap) const; + std::string reportTableLookup(std::string_view result_name, const Pvt *pvt, const TableModel *model, float in_slew, @@ -120,357 +129,308 @@ protected: float related_out_cap, int digits) const; void findAxisValues(const TableModel *model, - float in_slew, - float load_cap, - float related_out_cap, - // Return values. - float &axis_value1, - float &axis_value2, - float &axis_value3) const; + float in_slew, + float load_cap, + float related_out_cap, + // Return values. + float &axis_value1, + float &axis_value2, + float &axis_value3) const; static bool checkAxis(const TableAxis *axis); - TableModel *delay_model_; - TableModel *delay_sigma_models_[EarlyLate::index_count]; - TableModel *slew_model_; - TableModel *slew_sigma_models_[EarlyLate::index_count]; + std::unique_ptr delay_models_; + std::unique_ptr slew_models_; ReceiverModelPtr receiver_model_; - OutputWaveforms *output_waveforms_; + std::unique_ptr output_waveforms_; }; class CheckTableModel : public CheckTimingModel { public: - explicit CheckTableModel(LibertyCell *cell, - TableModel *model, - TableModel *sigma_models[EarlyLate::index_count]); - virtual ~CheckTableModel(); + CheckTableModel(LibertyCell *cell, + TableModels *check_models); ArcDelay checkDelay(const Pvt *pvt, float from_slew, float to_slew, float related_out_cap, - bool pocv_enabled) const override; + const MinMax *min_max, + PocvMode pocv_mode) const override; std::string reportCheckDelay(const Pvt *pvt, float from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, float to_slew, float related_out_cap, - bool pocv_enabled, + const MinMax *min_max, + PocvMode pocv_mode, int digits) const override; - const TableModel *model() const { return model_; } + const TableModels *checkModels() const { return check_models_.get(); } + const TableModel *checkModel() const; + void setIsScaled(bool is_scaled) override; // Check the axes before making the model. // Return true if the model axes are supported. - static bool checkAxes(const TablePtr table); + static bool checkAxes(const TableModel *table); protected: - void setIsScaled(bool is_scaled) override; float findValue(const Pvt *pvt, - const TableModel *model, - float from_slew, - float to_slew, - float related_out_cap) const; + const TableModel *model, + float from_slew, + float to_slew, + float related_out_cap) const; void findAxisValues(float from_slew, - float to_slew, - float related_out_cap, - // Return values. - float &axis_value1, - float &axis_value2, - float &axis_value3) const; + float to_slew, + float related_out_cap, + // Return values. + float &axis_value1, + float &axis_value2, + float &axis_value3) const; float axisValue(const TableAxis *axis, - float load_cap, - float in_slew, - float related_out_cap) const; - std::string reportTableDelay(const char *result_name, + float from_slew, + float to_slew, + float related_out_cap) const; + std::string reportTableDelay(std::string_view result_name, const Pvt *pvt, const TableModel *model, float from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, float to_slew, float related_out_cap, int digits) const; static bool checkAxis(const TableAxis *axis); - TableModel *model_; - TableModel *sigma_models_[EarlyLate::index_count]; + std::unique_ptr check_models_; }; -// Wrapper class for Table to apply scale factors. -class TableModel +class TableAxis { public: - TableModel(TablePtr table, - TableTemplate *tbl_template, - ScaleFactorType scale_factor_type, - const RiseFall *rf); - void setScaleFactorType(ScaleFactorType type); - int order() const; - TableTemplate *tblTemplate() const { return tbl_template_; } - const TableAxis *axis1() const; - const TableAxis *axis2() const; - const TableAxis *axis3() const; - void setIsScaled(bool is_scaled); - float value(size_t index1, - size_t index2, - size_t index3) const; - // Table interpolated lookup. - float findValue(float value1, - float value2, - float value3) const; - // Table interpolated lookup with scale factor. - float findValue(const LibertyCell *cell, - const Pvt *pvt, - float value1, - float value2, - float value3) const; - std::string reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *pvt, - float value1, - const char *comment1, - float value2, - float value3, - const Unit *table_unit, - int digits) const; - std::string report(const Units *units, - Report *report) const; - -protected: - float scaleFactor(const LibertyCell *cell, - const Pvt *pvt) const; - std::string reportPvtScaleFactor(const LibertyCell *cell, - const Pvt *pvt, - int digits) const; + TableAxis(TableAxisVariable variable, + FloatSeq &&values); + TableAxisVariable variable() const { return variable_; } + std::string_view variableString() const; + const Unit *unit(const Units *units); + size_t size() const { return values_.size(); } + bool inBounds(float value) const; + float axisValue(size_t index) const { return values_[index]; } + // Find the index for value such that axis[index] <= value < axis[index+1]. + size_t findAxisIndex(float value) const; + void findAxisIndex(float value, + // Return values. + size_t &index, + bool &exists) const; + size_t findAxisClosestIndex(float value) const; + const FloatSeq &values() const { return values_; } + float min() const; + float max() const; - TablePtr table_; - TableTemplate *tbl_template_; - // ScaleFactorType gcc barfs if this is dcl'd. - unsigned scale_factor_type_:scale_factor_bits; - unsigned rf_index_:RiseFall::index_bit_count; - bool is_scaled_:1; +private: + TableAxisVariable variable_; + FloatSeq values_; }; -// Abstract base class for 0, 1, 2, or 3 dimesnion float tables. +// 0, 1, 2, or 3 dimension float tables. class Table { public: - Table() {} - virtual ~Table() {} - void setScaleFactorType(ScaleFactorType type); - virtual int order() const = 0; - virtual const TableAxis *axis1() const { return nullptr; } - virtual const TableAxis *axis2() const { return nullptr; } - virtual const TableAxis *axis3() const { return nullptr; } - void setIsScaled(bool is_scaled); - virtual float value(size_t axis_idx1, - size_t axis_idx2, - size_t axis_idx3) const = 0; - // Table interpolated lookup. - virtual float findValue(float axis_value1, - float axis_value2, - float axis_value3) const = 0; - // Table interpolated lookup with scale factor. - float findValue(const LibertyLibrary *library, - const LibertyCell *cell, - const Pvt *pvt, - float axis_value1, - float axis_value2, - float axis_value3) const; - virtual std::string reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *pvt, - float value1, - const char *comment1, - float value2, - float value3, - const Unit *table_unit, - int digits) const = 0; - virtual void report(const Units *units, - Report *report) const = 0; -}; + Table(); + explicit Table(float value); + Table(FloatSeq *values, + TableAxisPtr axis1); + Table(FloatSeq &&values, + TableAxisPtr axis1); + Table(FloatTable &&values, + TableAxisPtr axis1, + TableAxisPtr axis2); + Table(FloatTable &&values, + TableAxisPtr axis1, + TableAxisPtr axis2, + TableAxisPtr axis3); + Table(Table &&table) noexcept; + Table(const Table &table); + Table &operator=(Table &&table) noexcept; -// Zero dimension (scalar) table. -class Table0 : public Table -{ -public: - Table0(float value); - int order() const override { return 0; } + void setIsScaled(bool is_scaled); + void setScaleFactorType(ScaleFactorType type); + int order() const { return order_; } + const TableAxis *axis1() const { return axis1_.get(); } + const TableAxis *axis2() const { return axis2_.get(); } + const TableAxis *axis3() const { return axis3_.get(); } + TableAxisPtr axis1ptr() const { return axis1_; } + TableAxisPtr axis2ptr() const { return axis2_; } + TableAxisPtr axis3ptr() const { return axis3_; } + + float value(size_t axis_idx1, + size_t axis_idx2, + size_t axis_idx3) const; + // Single-index value (order 1 only). + float value(size_t index1) const; + // Two-index value (order 2 and 3). float value(size_t axis_index1, - size_t axis_index2, - size_t axis_index3) const override; + size_t axis_index2) const; + + // Table interpolated lookup. float findValue(float axis_value1, float axis_value2, - float axis_value3) const override; - std::string reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *pvt, - float value1, - const char *comment1, - float value2, - float value3, - const Unit *table_unit, - int digits) const override; - void report(const Units *units, - Report *report) const override; - using Table::findValue; - -private: - float value_; -}; - -// One dimensional table. -class Table1 : public Table -{ -public: - Table1(); - Table1(FloatSeq *values, - TableAxisPtr axis1); - virtual ~Table1(); - Table1(Table1 &&table); - Table1(const Table1 &table); - Table1 &operator= (Table1 &&table); - int order() const override { return 1; } - const TableAxis *axis1() const override { return axis1_.get(); } - const TableAxisPtr axis1ptr() const { return axis1_; } - float value(size_t axis_index1, - size_t axis_index2, - size_t axis_index3) const override; - float findValue(float value1, - float value2, - float value3) const override; - std::string reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *pvt, - float value1, - const char *comment1, - float value2, - float value3, - const Unit *table_unit, - int digits) const override; - void report(const Units *units, - Report *report) const override; - - // Table1 specific functions. - float value(size_t index1) const; + float axis_value3) const; + // One-argument lookup (order 1). void findValue(float axis_value1, - // Return values. float &value, bool &extrapolated) const; float findValue(float axis_value1) const; float findValueClip(float axis_value1) const; - FloatSeq *values() const { return values_; } - using Table::findValue; - -private: - FloatSeq *values_; - TableAxisPtr axis1_; -}; + // Table interpolated lookup with scale factor. + float findValue(const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + float axis_value1, + float axis_value2, + float axis_value3) const; -// Two dimensional table. -class Table2 : public Table -{ -public: - Table2(FloatTable *values, - TableAxisPtr axis1, - TableAxisPtr axis2); - virtual ~Table2(); - int order() const override { return 2; } - const TableAxis *axis1() const override { return axis1_.get(); } - const TableAxis *axis2() const override { return axis2_.get(); } - float value(size_t axis_index1, - size_t axis_index2, - size_t axis_index3) const override; - float findValue(float value1, - float value2, - float value3) const override; - std::string reportValue(const char *result_name, + std::string reportValue(std::string_view result_name, const LibertyCell *cell, const Pvt *pvt, float value1, - const char *comment1, + std::string_view comment1, float value2, float value3, const Unit *table_unit, - int digits) const override; + int digits) const; void report(const Units *units, - Report *report) const override; + Report *report) const; - // Table2 specific functions. - float value(size_t axis_index1, - size_t axis_index2) const; - FloatTable *values3() { return values_; } + // Order 1: pointer to value sequence (nullptr if not order 1). + FloatSeq *values() const; + // Order 2 and 3: pointer to value table (nullptr otherwise). + FloatTable *values3(); + const FloatTable *values3() const; - using Table::findValue; +private: + void clear(); + float findValueOrder2(float axis_value1, float axis_value2) const; + float findValueOrder3(float axis_value1, float axis_value2, float axis_value3) const; + std::string reportValueOrder0(std::string_view result_name, + std::string_view comment1, + const Unit *table_unit, + int digits) const; + std::string reportValueOrder1(std::string_view result_name, + const LibertyCell *cell, + float value1, + std::string_view comment1, + float value2, + float value3, + const Unit *table_unit, + int digits) const; + std::string reportValueOrder2(std::string_view result_name, + const LibertyCell *cell, + float value1, + std::string_view comment1, + float value2, + float value3, + const Unit *table_unit, + int digits) const; + std::string reportValueOrder3(std::string_view result_name, + const LibertyCell *cell, + float value1, + std::string_view comment1, + float value2, + float value3, + const Unit *table_unit, + int digits) const; -protected: - FloatTable *values_; - // Row. + int order_; + float value_; // order 0 only + FloatSeq values1_; // order 1 only + FloatTable values_table_; // order 2 and 3 TableAxisPtr axis1_; - // Column. TableAxisPtr axis2_; + TableAxisPtr axis3_; }; -// Three dimensional table. -class Table3 : public Table2 +// Wrapper class for Table to apply scale factors. +class TableModel { public: - Table3(FloatTable *values, - TableAxisPtr axis1, - TableAxisPtr axis2, - TableAxisPtr axis3); - virtual ~Table3() {} - int order() const override { return 3; } - const TableAxis *axis1() const override { return axis1_.get(); } - const TableAxis *axis2() const override { return axis2_.get(); } - const TableAxis *axis3() const override { return axis3_.get(); } - float value(size_t axis_index1, - size_t axis_index2, - size_t axis_index3) const override; + TableModel(); + TableModel(TablePtr table, + TableTemplate *tbl_template, + ScaleFactorType scale_factor_type, + const RiseFall *rf); + void setScaleFactorType(ScaleFactorType type); + int order() const; + TableTemplate *tblTemplate() const { return tbl_template_; } + const TablePtr &table() const { return table_; } + ScaleFactorType scaleFactorType() const; + int rfIndex() const { return rf_index_; } + const TableAxis *axis1() const; + const TableAxis *axis2() const; + const TableAxis *axis3() const; + void setIsScaled(bool is_scaled); + float value(size_t index1, + size_t index2, + size_t index3) const; + // Table interpolated lookup. float findValue(float value1, float value2, - float value3) const override; - std::string reportValue(const char *result_name, + float value3) const; + // Table interpolated lookup with scale factor. + float findValue(const LibertyCell *cell, + const Pvt *pvt, + float value1, + float value2, + float value3) const; + std::string reportValue(std::string_view result_name, const LibertyCell *cell, const Pvt *pvt, float value1, - const char *comment1, + std::string_view comment1, float value2, float value3, const Unit *table_unit, - int digits) const override; - void report(const Units *units, - Report *report) const override; - using Table::findValue; + int digits) const; + std::string report(const Units *units, + Report *report) const; -private: - TableAxisPtr axis3_; +protected: + float scaleFactor(const LibertyCell *cell, + const Pvt *pvt) const; + std::string reportPvtScaleFactor(const LibertyCell *cell, + const Pvt *pvt, + int digits) const; + + TablePtr table_; + TableTemplate *tbl_template_; + // ScaleFactorType gcc barfs if this is dcl'd. + unsigned scale_factor_type_:scale_factor_bits; + unsigned rf_index_:RiseFall::index_bit_count; + bool is_scaled_:1; }; -class TableAxis +// cell/transition/check nldm/ocv/lvf models for one rise/fall edge. +class TableModels { public: - TableAxis(TableAxisVariable variable, - FloatSeq *values); - ~TableAxis(); - TableAxisVariable variable() const { return variable_; } - const char *variableString() const; - const Unit *unit(const Units *units); - size_t size() const { return values_->size(); } - bool inBounds(float value) const; - float axisValue(size_t index) const { return (*values_)[index]; } - // Find the index for value such that axis[index] <= value < axis[index+1]. - size_t findAxisIndex(float value) const; - void findAxisIndex(float value, - // Return values. - size_t &index, - bool &exists) const; - size_t findAxisClosestIndex(float value) const; - FloatSeq *values() const { return values_; } - float min() const; - float max() const; + TableModels(); + TableModels(TableModel *model); + ~TableModels(); + TableModel *model() const { return model_.get(); } + void setModel(TableModel *model); + TableModel *sigma(const EarlyLate *early_late) const; + void setSigma(TableModel *table, + const EarlyLate *early_late); + TableModel *meanShift() const { return mean_shift_.get(); } + void setMeanShift(TableModel *table); + TableModel *skewness() const { return skewness_.get(); } + void setSkewness(TableModel *table); + TableModel *stdDev() const { return std_dev_.get(); } + void setStdDev(TableModel *table); -private: - TableAxisVariable variable_; - FloatSeq *values_; +protected: + std::unique_ptr model_; + // Note early/late can point to the same model. + std::array sigma_; + std::unique_ptr std_dev_; + std::unique_ptr mean_shift_; + std::unique_ptr skewness_; }; //////////////////////////////////////////////////////////////// @@ -478,14 +438,13 @@ private: class ReceiverModel { public: - ~ReceiverModel(); - void setCapacitanceModel(TableModel *table_model, + void setCapacitanceModel(TableModel table_model, size_t segment, const RiseFall *rf); - static bool checkAxes(TablePtr table); + static bool checkAxes(const TableModel *table); private: - std::vector capacitance_models_; + std::vector capacitance_models_; }; // Two dimensional (slew/cap) table of one dimensional time/current tables. @@ -496,7 +455,7 @@ public: TableAxisPtr cap_axis, const RiseFall *rf, Table1Seq ¤t_waveforms, - Table1 *ref_times); + Table ref_times); ~OutputWaveforms(); const RiseFall *rf() const { return rf_; } const TableAxis *slewAxis() const { return slew_axis_.get(); } @@ -523,18 +482,18 @@ public: float cap); static bool checkAxes(const TableTemplate *tbl_template); - Table1 currentWaveform(float slew, - float cap); + Table currentWaveform(float slew, + float cap); // Waveform closest to slew/cap; no interpolation. - const Table1 *currentWaveformRaw(float slew, - float cap); - Table1 voltageWaveform(float in_slew, - float load_cap); + const Table *currentWaveformRaw(float slew, + float cap); + Table voltageWaveform(float in_slew, + float load_cap); // Waveform closest to slew/cap; no interpolation. - const Table1 *voltageWaveformRaw(float slew, - float cap); - Table1 voltageCurrentWaveform(float slew, - float cap); + const Table *voltageWaveformRaw(float slew, + float cap); + Table voltageCurrentWaveform(float slew, + float cap); // V/I for last segment of min slew/max cap. float finalResistance(); @@ -563,25 +522,25 @@ private: // Column. TableAxisPtr cap_axis_; const RiseFall *rf_; - Table1Seq current_waveforms_; // from liberty + Table1Seq current_waveforms_; // from liberty (1D tables) Table1Seq voltage_waveforms_; Table1Seq voltage_currents_; - Table1 *ref_times_; - float vdd_; + Table ref_times_; + float vdd_{0.0F}; static constexpr size_t voltage_waveform_step_count_ = 100; }; class DriverWaveform { public: - DriverWaveform(const std::string &name, + DriverWaveform(std::string name, TablePtr waveforms); - const char *name() const { return name_.c_str(); } - Table1 waveform(float slew); + std::string_view name() const { return name_; } + Table waveform(float slew); private: std::string name_; TablePtr waveforms_; }; -} // namespace +} // namespace sta diff --git a/include/sta/TclTypeHelpers.hh b/include/sta/TclTypeHelpers.hh index c4e6ec481..628ee099b 100644 --- a/include/sta/TclTypeHelpers.hh +++ b/include/sta/TclTypeHelpers.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,33 +22,26 @@ // // This notice may not be removed or altered from any source distribution. -#include "ArcDelayCalc.hh" -#include "StringSet.hh" -#include "StringSeq.hh" - #include +#include "ArcDelayCalc.hh" +#include "StringUtil.hh" + namespace sta { #if TCL_MAJOR_VERSION < 9 - typedef int Tcl_Size; + using Tcl_Size = int ; #endif -StringSet * -tclListSetConstChar(Tcl_Obj *const source, - Tcl_Interp *interp); - -int -tclListSeqConstCharCheck(Tcl_Obj *const source, - Tcl_Interp *interp); - +StringSeq +tclListStringSeq(Tcl_Obj *source, + Tcl_Interp *interp); StringSeq * -tclListSeqConstChar(Tcl_Obj *const source, - Tcl_Interp *interp); - -StdStringSet * -tclListSetStdString(Tcl_Obj *const source, - Tcl_Interp *interp); +tclListStringSeqPtr(Tcl_Obj *source, + Tcl_Interp *interp); +StringSet * +tclListStringSet(Tcl_Obj *source, + Tcl_Interp *interp); void tclArgError(Tcl_Interp *interp, @@ -58,10 +51,10 @@ tclArgError(Tcl_Interp *interp, void objectListNext(const char *list, - const char *type, - // Return values. - bool &type_match, - const char *&next); + const char *type, + // Return values. + bool &type_match, + const char *&next); Tcl_Obj * tclArcDcalcArg(ArcDcalcArg &gate, @@ -71,4 +64,4 @@ ArcDcalcArg arcDcalcArgTcl(Tcl_Obj *obj, Tcl_Interp *interp); -} // namespace +} // namespace sta diff --git a/include/sta/TimingArc.hh b/include/sta/TimingArc.hh index 3884cbd30..b2f562308 100644 --- a/include/sta/TimingArc.hh +++ b/include/sta/TimingArc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,23 +24,24 @@ #pragma once +#include #include +#include +#include -#include "Vector.hh" -#include "Transition.hh" #include "Delay.hh" #include "LibertyClass.hh" +#include "Transition.hh" namespace sta { class TimingArcAttrs; class WireTimingArc; class GateTableModel; -class DcalcAnalysisPt; +class Scene; -typedef int TimingArcIndex; -typedef Vector TimingArcSeq; -typedef Map ScaledTimingModelMap; +using TimingArcSeq = std::vector; +using ScaledTimingModelMap = std::map; enum class TimingType { clear, @@ -82,10 +83,10 @@ enum class TimingType { unknown }; -const char * +std::string_view to_string(TimingType type); TimingType -findTimingType(const char *string); +findTimingType(std::string_view type_name); bool timingTypeIsCheck(TimingType type); ScaleFactorType @@ -98,26 +99,26 @@ class TimingArcAttrs public: TimingArcAttrs(); TimingArcAttrs(TimingSense sense); - virtual ~TimingArcAttrs(); + ~TimingArcAttrs(); TimingType timingType() const { return timing_type_; } void setTimingType(TimingType type); TimingSense timingSense() const { return timing_sense_; } void setTimingSense(TimingSense sense); FuncExpr *cond() const { return cond_; } void setCond(FuncExpr *cond); - const char *sdfCond() const { return sdf_cond_; } - void setSdfCond(const char *cond); - const char *sdfCondStart() const { return sdf_cond_start_; } - void setSdfCondStart(const char *cond); - const char *sdfCondEnd() const { return sdf_cond_end_; } - void setSdfCondEnd(const char *cond); - const char *modeName() const { return mode_name_; } - void setModeName(const char *name); - const char *modeValue() const { return mode_value_; } - void setModeValue(const char *value); + const std::string &sdfCond() const { return sdf_cond_; } + void setSdfCond(std::string_view cond); + const std::string &sdfCondStart() const { return sdf_cond_start_; } + void setSdfCondStart(std::string_view cond); + const std::string &sdfCondEnd() const { return sdf_cond_end_; } + void setSdfCondEnd(std::string_view cond); + const std::string &modeName() const { return mode_name_; } + void setModeName(std::string_view name); + const std::string &modeValue() const { return mode_value_; } + void setModeValue(std::string_view value); TimingModel *model(const RiseFall *rf) const; void setModel(const RiseFall *rf, - TimingModel *model); + TimingModel *model); float ocvArcDepth() const { return ocv_arc_depth_; } void setOcvArcDepth(float depth); @@ -125,11 +126,11 @@ protected: TimingType timing_type_; TimingSense timing_sense_; FuncExpr *cond_; - const char *sdf_cond_; - const char *sdf_cond_start_; - const char *sdf_cond_end_; - const char *mode_name_; - const char *mode_value_; + std::string sdf_cond_; + std::string sdf_cond_start_; + std::string sdf_cond_end_; + std::string mode_name_; + std::string mode_value_; float ocv_arc_depth_; TimingModel *models_[RiseFall::index_count]; }; @@ -141,33 +142,32 @@ protected: // See ~LibertyCell for delete of TimingArcSet members. class TimingArcSet { + friend class LibertyCell; + public: - TimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - const TimingRole *role, - TimingArcAttrsPtr attrs); - virtual ~TimingArcSet(); + ~TimingArcSet(); + std::string to_string() const; LibertyCell *libertyCell() const; LibertyPort *from() const { return from_; } LibertyPort *to() const { return to_; } bool isWire() const; LibertyPort *relatedOut() const { return related_out_; } const TimingRole *role() const { return role_; }; + TimingType timingType() const { return attrs_->timingType(); } TimingSense sense() const; + TimingModel *model(const RiseFall *rf) const { return attrs_->model(rf); } // Rise/fall if the arc set is rising_edge or falling_edge. const RiseFall *isRisingFallingEdge() const; size_t arcCount() const { return arcs_.size(); } TimingArcSeq &arcs() { return arcs_; } // Return 1 or 2 arcs matching from transition. void arcsFrom(const RiseFall *from_rf, - // Return values. - TimingArc *&arc1, - TimingArc *&arc2) const; + // Return values. + TimingArc *&arc1, + TimingArc *&arc2) const; TimingArc *arcTo(const RiseFall *to_rf) const; const TimingArcSeq &arcs() const { return arcs_; } - TimingArcIndex addTimingArc(TimingArc *arc); + size_t addTimingArc(TimingArc *arc); void deleteTimingArc(TimingArc *arc); TimingArc *findTimingArc(unsigned arc_index); void setRole(const TimingRole *role); @@ -176,25 +176,25 @@ public: // other conditional timing arcs between the same pins. bool isCondDefault() const { return is_cond_default_; } void setIsCondDefault(bool is_default); + const FuncExpr *when() const { return attrs_->cond(); } // SDF IOPATHs match sdfCond. // sdfCond (IOPATH) reuses sdfCondStart (timing check) variable. - const char *sdfCond() const { return attrs_->sdfCondStart(); } + const std::string &sdfCond() const { return attrs_->sdfCondStart(); } // SDF timing checks match sdfCondStart/sdfCondEnd. - const char *sdfCondStart() const { return attrs_->sdfCondStart(); } - const char *sdfCondEnd() const { return attrs_->sdfCondEnd(); } - const char *modeName() const { return attrs_->modeName(); } - const char *modeValue() const { return attrs_->modeValue(); } + const std::string &sdfCondStart() const { return attrs_->sdfCondStart(); } + const std::string &sdfCondEnd() const { return attrs_->sdfCondEnd(); } + const std::string &modeName() const { return attrs_->modeName(); } + const std::string &modeValue() const { return attrs_->modeValue(); } // Timing arc set index in cell. - TimingArcIndex index() const { return index_; } - bool isDisabledConstraint() const { return is_disabled_constraint_; } - void setIsDisabledConstraint(bool is_disabled); + size_t index() const { return index_; } + void setIndex(size_t index); // OCV arc depth from timing/cell/library. float ocvArcDepth() const; static bool equiv(const TimingArcSet *set1, - const TimingArcSet *set2); + const TimingArcSet *set2); static bool less(const TimingArcSet *set1, - const TimingArcSet *set2); + const TimingArcSet *set2); static void init(); static void destroy(); @@ -207,6 +207,13 @@ public: protected: TimingArcSet(const TimingRole *role, TimingArcAttrsPtr attrs); + TimingArcSet(LibertyCell *cell, + LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + const TimingRole *role, + TimingArcAttrsPtr attrs, + size_t index); LibertyPort *from_; LibertyPort *to_; @@ -216,8 +223,7 @@ protected: TimingArcAttrsPtr attrs_; TimingArcSeq arcs_; bool is_cond_default_; - unsigned index_; - bool is_disabled_constraint_; + size_t index_; TimingArc *from_arc1_[RiseFall::index_count]; TimingArc *from_arc2_[RiseFall::index_count]; TimingArc *to_arc_[RiseFall::index_count]; @@ -232,9 +238,9 @@ class TimingArc { public: TimingArc(TimingArcSet *set, - const Transition *from_rf, - const Transition *to_rf, - TimingModel *model); + const Transition *from_rf, + const Transition *to_rf, + TimingModel *model); ~TimingArc(); std::string to_string() const; LibertyPort *from() const { return set_->from(); } @@ -245,34 +251,38 @@ public: TimingArcSet *set() const { return set_; } TimingSense sense() const; // Index in TimingArcSet. - unsigned index() const { return index_; } + size_t index() const { return index_; } TimingModel *model() const { return model_; } - GateTimingModel *gateModel(const DcalcAnalysisPt *dcalc_ap) const; - CheckTimingModel *checkModel(const DcalcAnalysisPt *dcalc_ap) const; + GateTimingModel *gateModel(const Scene *scene, + const MinMax *min_max) const; + CheckTimingModel *checkModel(const Scene *scene, + const MinMax *min_max) const; GateTableModel *gateTableModel() const; - GateTableModel *gateTableModel(const DcalcAnalysisPt *dcalc_ap) const; - const TimingArc *cornerArc(int ap_index) const; - void setCornerArc(TimingArc *corner_arc, - int ap_index); + GateTableModel *gateTableModel(const Scene *scene, + const MinMax *min_max) const; + const TimingArc *sceneArc(size_t lib_ap_index) const; + void setSceneArc(TimingArc *scene_arc, + size_t lib_ap_index); float driveResistance() const; ArcDelay intrinsicDelay() const; static bool equiv(const TimingArc *arc1, - const TimingArc *arc2); + const TimingArc *arc2); protected: - TimingModel *model(const DcalcAnalysisPt *dcalc_ap) const; - void setIndex(unsigned index); + TimingModel *model(const Scene *scene, + const MinMax *min_max) const; + void setIndex(size_t index); void addScaledModel(const OperatingConditions *op_cond, - TimingModel *scaled_model); + TimingModel *scaled_model); TimingArcSet *set_; const Transition *from_rf_; const Transition *to_rf_; unsigned index_; TimingModel *model_; - ScaledTimingModelMap *scaled_models_; - Vector corner_arcs_; + ScaledTimingModelMap *scaled_models_{nullptr}; + std::vector scene_arcs_; private: friend class LibertyLibrary; @@ -280,4 +290,4 @@ private: friend class TimingArcSet; }; -} // namespace +} // namespace sta diff --git a/include/sta/TimingModel.hh b/include/sta/TimingModel.hh index 3ee8cab89..36e25a36d 100644 --- a/include/sta/TimingModel.hh +++ b/include/sta/TimingModel.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,9 +25,11 @@ #pragma once #include +#include #include "Delay.hh" #include "LibertyClass.hh" +#include "Variables.hh" namespace sta { @@ -36,7 +38,7 @@ class TimingModel { public: TimingModel(LibertyCell *cell); - virtual ~TimingModel() {} + virtual ~TimingModel() = default; virtual void setIsScaled(bool is_scaled) = 0; protected: @@ -50,16 +52,25 @@ public: GateTimingModel(LibertyCell *cell); // Gate delay calculation. virtual void gateDelay(const Pvt *pvt, - float in_slew, - float load_cap, - bool pocv_enabled, - // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew) const = 0; + float in_slew, + float load_cap, + // Return values. + float &gate_delay, + float &drvr_slew) const = 0; + // Fill in pocv parameters in gate_delay, drvr_slew. + virtual void gateDelayPocv(const Pvt *pvt, + float in_slew, + float load_cap, + const MinMax *min_max, + PocvMode pocv_mode, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) const = 0; virtual std::string reportGateDelay(const Pvt *pvt, float in_slew, float load_cap, - bool pocv_enabled, + const MinMax *min_max, + PocvMode pocv_mode, int digits) const = 0; virtual float driveResistance(const Pvt *pvt) const = 0; }; @@ -74,14 +85,16 @@ public: float from_slew, float to_slew, float related_out_cap, - bool pocv_enabled) const = 0; + const MinMax *min_max, + PocvMode pocv_mode) const = 0; virtual std::string reportCheckDelay(const Pvt *pvt, float from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, float to_slew, float related_out_cap, - bool pocv_enabled, + const MinMax *min_max, + PocvMode pocv_mode, int digits) const = 0; }; -} // namespace +} // namespace sta diff --git a/include/sta/TimingRole.hh b/include/sta/TimingRole.hh index b9456cc15..f07d6ed6a 100644 --- a/include/sta/TimingRole.hh +++ b/include/sta/TimingRole.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -33,7 +33,7 @@ namespace sta { class TimingRole; -typedef std::map TimingRoleMap; +using TimingRoleMap = std::map; class TimingRole { @@ -74,10 +74,11 @@ public: bool isTimingCheck() const { return is_timing_check_; } // TIming check but not width or period. bool isTimingCheckBetween() const; - bool isAsyncTimingCheck() const; - bool isNonSeqTimingCheck() const { return is_non_seq_check_; } - bool isDataCheck() const; - bool isLatchDtoQ() const; + [[nodiscard]] bool isAsyncTimingCheck() const; + [[nodiscard]] bool isNonSeqTimingCheck() const { return is_non_seq_check_; } + [[nodiscard]] bool isDataCheck() const; + [[nodiscard]] bool isLatchDtoQ() const; + [[nodiscard]] bool isLatchEnToQ() const; const TimingRole *genericRole() const; const TimingRole *sdfRole() const; // Timing check data path min/max. @@ -88,18 +89,18 @@ public: // Pseudo role to match sdf IOPATH. static const TimingRole *sdfIopath() { return &sdf_iopath_; } static bool less(const TimingRole *role1, - const TimingRole *role2); + const TimingRole *role2); static const int index_max = 26; private: TimingRole(const char *name, - bool is_sdf_iopath, - bool is_timing_check, - bool is_non_seq_check, - const MinMax *path_min_max, - // generic_type = nullptr means type is the same as this. - const TimingRole *generic_role, - int index); + bool is_sdf_iopath, + bool is_timing_check, + bool is_non_seq_check, + const MinMax *path_min_max, + // generic_type = nullptr means type is the same as this. + const TimingRole *generic_role, + int index); const std::string name_; bool is_timing_check_; @@ -143,4 +144,4 @@ private: friend class TimingRoleLess; }; -} // namespace +} // namespace sta diff --git a/include/sta/TokenParser.hh b/include/sta/TokenParser.hh deleted file mode 100644 index f5f1bb20a..000000000 --- a/include/sta/TokenParser.hh +++ /dev/null @@ -1,52 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -namespace sta { - -// Iterate over the tokens in str separated by character sep. -// Similar in functionality to strtok, but does not leave the string -// side-effected. This is preferable to using strtok because it leaves -// string terminators where the separators were. -// Using STL string functions to parse tokens is messy and extremely slow -// on the RogueWave/Solaris implementation, apparently because of mutexes -// on temporary strings. -class TokenParser -{ -public: - TokenParser(const char *str, - const char *delimiters); - bool hasNext(); - char *next(); - -private: - const char *delimiters_; - char *token_; - char *token_end_; - char token_delimiter_; - bool first_; -}; - -} // namespace diff --git a/include/sta/Transition.hh b/include/sta/Transition.hh index c7a490a8b..16ccb0255 100644 --- a/include/sta/Transition.hh +++ b/include/sta/Transition.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,10 +25,11 @@ #pragma once #include +#include +#include #include #include "Iterator.hh" -#include "Map.hh" #include "StringUtil.hh" namespace sta { @@ -37,7 +38,7 @@ class Transition; class RiseFall; class RiseFallBoth; -typedef Map TransitionMap; +using TransitionMap = std::map>; // Rise/fall transition. class RiseFall @@ -46,43 +47,43 @@ public: // Singleton accessors. static const RiseFall *rise() { return &rise_; } static const RiseFall *fall() { return &fall_; } - static int riseIndex() { return rise_.sdf_triple_index_; } - static int fallIndex() { return fall_.sdf_triple_index_; } - const std::string &to_string() const { return short_name_; } - const char *name() const { return name_.c_str(); } - const char *shortName() const { return short_name_.c_str(); } - int index() const { return sdf_triple_index_; } + static size_t riseIndex() { return rise_.sdf_triple_index_; } + static size_t fallIndex() { return fall_.sdf_triple_index_; } + const std::string &to_string(bool use_short = false) const; + const std::string &name() const { return name_; } + const std::string &shortName() const { return short_name_; } + size_t index() const { return sdf_triple_index_; } const RiseFallBoth *asRiseFallBoth(); const RiseFallBoth *asRiseFallBoth() const; const Transition *asTransition() const; // Find transition corresponding to rf_str. - static const RiseFall *find(const char *rf_str); + static const RiseFall *find(std::string_view rf_name); // Find transition from index. - static const RiseFall *find(int index); + static const RiseFall *find(size_t index); const RiseFall *opposite() const; // for range support. // for (auto rf : RiseFall::range()) {} static const std::array &range() { return range_; } // for (auto rf_index : RiseFall::rangeIndex()) {} - static const std::array &rangeIndex() { return range_index_; } - static const int index_count = 2; - static const int index_max = (index_count - 1); - static const int index_bit_count = 1; + static const std::array &rangeIndex() { return range_index_; } + static const size_t index_count = 2; + static const size_t index_max = (index_count - 1); + static const size_t index_bit_count = 1; protected: - RiseFall(const char *name, - const char *short_name, - int sdf_triple_index); + RiseFall(std::string_view name, + std::string_view short_name, + size_t sdf_triple_index); const std::string name_; const std::string short_name_; - const int sdf_triple_index_; + const size_t sdf_triple_index_; static const RiseFall rise_; static const RiseFall fall_; static const std::array range_; - static const std::array range_index_; + static const std::array range_index_; }; // Rise/fall/risefall transition. @@ -93,38 +94,38 @@ public: static const RiseFallBoth *rise() { return &rise_; } static const RiseFallBoth *fall() { return &fall_; } static const RiseFallBoth *riseFall() { return &rise_fall_; } - const std::string &to_string() const { return short_name_; } - const char *name() const { return name_.c_str(); } - const char *shortName() const { return short_name_.c_str(); } - int index() const { return sdf_triple_index_; } + const std::string &to_string(bool use_short = false) const; + const std::string &name() const { return name_; } + const std::string &shortName() const { return short_name_; } + size_t index() const { return sdf_triple_index_; } bool matches(const RiseFall *rf) const; bool matches(const Transition *tr) const; const RiseFall *asRiseFall() const { return as_rise_fall_; } // Find transition corresponding to string. - static const RiseFallBoth *find(const char *tr_str); + static const RiseFallBoth *find(std::string_view rf_name); // for (const auto rf : rf->range()) {} const std::vector &range() const { return range_; } // for (const auto rf_index : rf->rangeIndex()) {} - const std::vector &rangeIndex() const { return range_index_; } + const std::vector &rangeIndex() const { return range_index_; } - static const int index_count = 3; - static const int index_max = (index_count - 1); - static const int index_bit_count = 2; + static const size_t index_count = 3; + static const size_t index_max = (index_count - 1); + static const size_t index_bit_count = 2; protected: - RiseFallBoth(const char *name, - const char *short_name, - int sdf_triple_index, + RiseFallBoth(std::string_view name, + std::string_view short_name, + size_t sdf_triple_index, const RiseFall *as_rise_fall, - std::vector range, - std::vector range_index); + const std::vector &range, + const std::vector &range_index); const std::string name_; const std::string short_name_; - const int sdf_triple_index_; + const size_t sdf_triple_index_; const RiseFall *as_rise_fall_; const std::vector range_; - const std::vector range_index_; + const std::vector range_index_; static const RiseFallBoth rise_; static const RiseFallBoth fall_; @@ -152,26 +153,26 @@ public: static const Transition *riseFall() { return &rise_fall_; } const std::string &to_string() const { return name_; } // As initial/final value pair. - const char *asInitFinalString() const { return init_final_.c_str(); } - int sdfTripleIndex() const { return sdf_triple_index_; } - int index() const { return sdf_triple_index_; } + const std::string &asInitFinalString() const { return init_final_; } + size_t sdfTripleIndex() const { return sdf_triple_index_; } + size_t index() const { return sdf_triple_index_; } const RiseFall *asRiseFall() const { return as_rise_fall_; } const RiseFallBoth *asRiseFallBoth() const; bool matches(const Transition *tr) const; // Find transition corresponding to string. - static const Transition *find(const char *tr_str); - static int maxIndex() { return max_index_; } + static const Transition *find(std::string_view tr_name); + static size_t maxIndex() { return max_index_; } private: - Transition(const char *name, - const char *init_final, - const RiseFall *as_rise_fall, - int sdf_triple_index); + Transition(std::string_view name, + std::string_view init_final, + const RiseFall *as_rise_fall, + size_t sdf_triple_index); const std::string name_; const std::string init_final_; const RiseFall *as_rise_fall_; - const int sdf_triple_index_; + const size_t sdf_triple_index_; static const Transition rise_; static const Transition fall_; @@ -186,12 +187,12 @@ private: static const Transition tr_XZ_; static const Transition tr_ZX_; static const Transition rise_fall_; - static const int index_count = 13; - static const int index_max = (index_count - 1); - static const int index_bit_count = 4; + static const size_t index_count = 13; + static const size_t index_max = (index_count - 1); + static const size_t index_bit_count = 4; static TransitionMap transition_map_; - static int max_index_; + static size_t max_index_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Units.hh b/include/sta/Units.hh index 8b54be5f8..c3868f992 100644 --- a/include/sta/Units.hh +++ b/include/sta/Units.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -39,7 +39,7 @@ public: double staToUser(double value); // Convert from user interface units to sta units. double userToSta(double value); - void operator=(const Unit &unit); + Unit &operator=(const Unit &unit) = default; float scale() const { return scale_; } void setScale(float scale); // Mkmunpf abbreviation for scale. @@ -56,10 +56,9 @@ public: void setDigits(int digits); // Does not include suffix. int width() const; - const char *asString(float value) const; - const char *asString(double value) const; - const char *asString(float value, - int digits) const; + std::string asString(float value) const; + std::string asString(float value, + int digits) const; private: void setScaleAbbrevSuffix(); @@ -76,8 +75,8 @@ class Units { public: Units(); - Unit *find(const char *unit_name); - void operator=(const Units &units); + Unit *find(std::string_view unit_name); + Units &operator=(const Units &units); Unit *timeUnit() { return &time_unit_; } const Unit *timeUnit() const { return &time_unit_; } Unit *capacitanceUnit() { return &capacitance_unit_; } @@ -106,4 +105,4 @@ private: Unit scalar_unit_; }; -} // namespace +} // namespace sta diff --git a/include/sta/UnorderedMap.hh b/include/sta/UnorderedMap.hh deleted file mode 100644 index 316a254fa..000000000 --- a/include/sta/UnorderedMap.hh +++ /dev/null @@ -1,203 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include -#include - -namespace sta { - -// Add convenience functions around STL container. -template , class EQUAL = std::equal_to> -class UnorderedMap : public std::unordered_map -{ -public: - UnorderedMap() : - std::unordered_map() - { - } - - explicit UnorderedMap(const HASH &hash) : - std::unordered_map(0, hash, std::equal_to()) - { - } - - explicit UnorderedMap(size_t size, - const HASH &hash, - const EQUAL &equal) : - std::unordered_map(size, hash, equal) - { - } - - // Find out if key is in the set. - bool - hasKey(const KEY key) const - { - return this->find(key) != this->end(); - } - - // Find the value corresponding to key. - VALUE - findKey(const KEY key) const - { - auto find_iter = this->find(key); - if (find_iter != this->end()) - return find_iter->second; - else - return nullptr; - } - void - findKey(const KEY key, - // Return Values. - VALUE &value, - bool &exists) const - { - auto find_iter = this->find(key); - if (find_iter != this->end()) { - value = find_iter->second; - exists = true; - } - else - exists = false; - } - void - findKey(const KEY &key, - // Return Values. - KEY &map_key, - VALUE &value, - bool &exists) const - { - auto find_iter = this->find(key); - if (find_iter != this->end()) { - map_key = find_iter->first; - value = find_iter->second; - exists = true; - } - else - exists = false; - } - - void - insert(const KEY &key, - VALUE value) - { - this->operator[](key) = value; - } - - void - deleteContents() - { - Iterator iter(this); - while (iter.hasNext()) - delete iter.next(); - } - - void - deleteKeysContents() - { - Iterator iter(this); - while (iter.hasNext()) { - KEY key; - VALUE value; - iter.next(key, value); - delete key; - delete value; - } - } - - void - deleteArrayContents() - { - Iterator iter(this); - while (iter.hasNext()) - delete [] iter.next(); - } - - void - deleteContentsClear() - { - deleteContents(); - std::unordered_map::clear(); - } - - // Java style container itererator - // Map::Iterator iter(map); - // while (iter.hasNext()) { - // Value *v = iter.next(); - // } - class Iterator - { - public: - Iterator() : container_(nullptr) {} - explicit Iterator(std::unordered_map *container) : - container_(container) - { if (container_ != nullptr) iter_ = container_->begin(); } - explicit Iterator(std::unordered_map &container) : - container_(&container) - { if (container_ != nullptr) iter_ = container_->begin(); } - void init(std::unordered_map *container) - { container_ = container; if (container_ != nullptr) iter_=container_->begin();} - void init(std::unordered_map &container) - { container_ = &container; if (container_ != nullptr) iter_=container_->begin();} - bool hasNext() { return container_ != nullptr && iter_ != container_->end(); } - VALUE next() { return iter_++->second; } - void next(KEY &key, - VALUE &value) - { key = iter_->first; value = iter_->second; iter_++; } - std::unordered_map *container() { return container_; } - - private: - std::unordered_map *container_; - typename std::unordered_map::iterator iter_; - }; - - class ConstIterator - { - public: - ConstIterator() : container_(nullptr) {} - explicit ConstIterator(const std::unordered_map *container) : - container_(container) - { if (container_ != nullptr) iter_ = container_->begin(); } - explicit ConstIterator(const std::unordered_map &container) : - container_(&container) - { if (container_ != nullptr) iter_ = container_->begin(); } - void init(const std::unordered_map *container) - { container_ = container; if (container_ != nullptr) iter_=container_->begin();} - void init(const std::unordered_map &container) - { container_ = &container; if (container_ != nullptr) iter_=container_->begin();} - bool hasNext() { return container_ != nullptr && iter_ != container_->end(); } - VALUE next() { return iter_++->second; } - void next(KEY &key, - VALUE &value) - { key = iter_->first; value = iter_->second; iter_++; } - const std::unordered_map *container() { return container_; } - - private: - const std::unordered_map *container_; - typename std::unordered_map::const_iterator iter_; - }; -}; - -} // namespace diff --git a/include/sta/UnorderedSet.hh b/include/sta/UnorderedSet.hh deleted file mode 100644 index 2d8598152..000000000 --- a/include/sta/UnorderedSet.hh +++ /dev/null @@ -1,139 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include -#include - -namespace sta { - -// Add convenience functions around STL container. -template , class EQUAL = std::equal_to > -class UnorderedSet : public std::unordered_set -{ -public: - UnorderedSet() : - std::unordered_set() - { - } - - explicit UnorderedSet(size_t size) : - std::unordered_set(size) - { - } - - explicit UnorderedSet(size_t size, - const HASH &hash, - const EQUAL &equal) : - std::unordered_set(size, hash, equal) - { - } - - // Find out if key is in the set. - bool - hasKey(const KEY key) const - { - return this->find(key) != this->end(); - } - - // Find the value corresponding to key. - KEY - findKey(const KEY key) const - { - auto find_iter = this->find(key); - if (find_iter != this->end()) - return *find_iter; - else - return nullptr; - } - - void - deleteContents() - { - Iterator iter(this); - while (iter.hasNext()) - delete iter.next(); - } - - void - deleteContentsClear() - { - deleteContents(); - std::unordered_set::clear(); - } - - // Java style container itererator - // Set::Iterator iter(set); - // while (iter.hasNext()) { - // Value *v = iter.next(); - // } - class Iterator - { - public: - Iterator() : container_(nullptr) {} - explicit Iterator(std::unordered_set *container) : - container_(container) - { if (container_ != nullptr) iter_ = container_->begin(); } - explicit Iterator(std::unordered_set &container) : - container_(&container) - { if (container_ != nullptr) iter_ = container_->begin(); } - void init(std::unordered_set *container) - { container_ = container; if (container_ != nullptr) iter_=container_->begin();} - void init(std::unordered_set &container) - { container_ = &container; if (container_ != nullptr) iter_=container_->begin();} - bool hasNext() { return container_ != nullptr && iter_ != container_->end(); } - KEY next() { return *iter_++; } - std::unordered_set *container() { return container_; } - - private: - std::unordered_set *container_; - typename std::unordered_set::iterator iter_; - }; - - class ConstIterator - { - public: - ConstIterator() : container_(nullptr) {} - explicit ConstIterator(const std::unordered_set *container) : - container_(container) - { if (container_ != nullptr) iter_ = container_->begin(); } - explicit ConstIterator(const std::unordered_set &container) : - container_(&container) - { if (container_ != nullptr) iter_ = container_->begin(); } - void init(const std::unordered_set *container) - { container_ = container; if (container_ != nullptr) iter_=container_->begin();} - void init(const std::unordered_set &container) - { container_ = &container; if (container_ != nullptr) iter_=container_->begin();} - bool hasNext() { return container_ != nullptr && iter_ != container_->end(); } - KEY next() { return iter_++->second; } - const std::unordered_set *container() { return container_; } - - private: - const std::unordered_set *container_; - typename std::unordered_set::const_iterator iter_; - }; -}; - -} // namespace diff --git a/include/sta/Variables.hh b/include/sta/Variables.hh index ef4c5d0ff..938a94aba 100644 --- a/include/sta/Variables.hh +++ b/include/sta/Variables.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,8 @@ #pragma once +#include "PocvMode.hh" + namespace sta { enum class CrprMode { same_pin, same_transition }; @@ -32,7 +34,6 @@ enum class CrprMode { same_pin, same_transition }; class Variables { public: - Variables(); // TCL variable sta_crpr_enabled. bool crprEnabled() const { return crpr_enabled_; } void setCrprEnabled(bool enabled); @@ -54,10 +55,6 @@ public: // Enable/disable timing from bidirect pins back into the instance. bool bidirectInstPathsEnabled() const { return bidirect_inst_paths_enabled_; } void setBidirectInstPathsEnabled(bool enabled); - // TCL variable sta_bidirect_net_paths_enabled. - // Enable/disable timing from bidirect driver pins to their own loads. - bool bidirectNetPathsEnabled() const { return bidirect_net_paths_enabled_; } - void setBidirectNetPathsEnabled(bool enabled); // TCL variable sta_recovery_removal_checks_enabled. bool recoveryRemovalChecksEnabled() const { return recovery_removal_checks_enabled_; } void setRecoveryRemovalChecksEnabled(bool enabled); @@ -76,8 +73,11 @@ public: // TCL variable sta_input_port_default_clock. bool useDefaultArrivalClock() { return use_default_arrival_clock_; } void setUseDefaultArrivalClock(bool enable); - bool pocvEnabled() const { return pocv_enabled_; } - void setPocvEnabled(bool enabled); + bool pocvEnabled() const; + PocvMode pocvMode() const { return pocv_mode_; } + void setPocvMode(PocvMode mode); + float pocvQuantile() const { return pocv_quantile_; } + void setPocvQuantile(float quantile); // SILIMATE ADDITIONS // TCL variable sta_boolean_props_as_int. @@ -104,29 +104,29 @@ public: private: - bool crpr_enabled_; - CrprMode crpr_mode_; - bool propagate_gated_clock_enable_; - bool preset_clr_arcs_enabled_; - bool cond_default_arcs_enabled_; - bool bidirect_net_paths_enabled_; - bool bidirect_inst_paths_enabled_; - bool recovery_removal_checks_enabled_; - bool gated_clk_checks_enabled_; - bool clk_thru_tristate_enabled_; - bool dynamic_loop_breaking_; - bool propagate_all_clks_; - bool use_default_arrival_clock_; - bool pocv_enabled_; + bool crpr_enabled_{true}; + CrprMode crpr_mode_{CrprMode::same_pin}; + bool propagate_gated_clock_enable_{true}; + bool preset_clr_arcs_enabled_{false}; + bool cond_default_arcs_enabled_{true}; + bool bidirect_inst_paths_enabled_{false}; + bool recovery_removal_checks_enabled_{true}; + bool gated_clk_checks_enabled_{true}; + bool clk_thru_tristate_enabled_{false}; + bool dynamic_loop_breaking_{false}; + bool propagate_all_clks_{false}; + bool use_default_arrival_clock_{false}; + PocvMode pocv_mode_{PocvMode::scalar}; + float pocv_quantile_{3.0}; - // SILIMATE ADDITIONS - bool boolean_props_as_int_; - bool direction_props_short_; - bool liberty_line_debug_; - bool no_inv_delay_calc_; - bool no_inv_power_calc_; - bool strip_escaped_bus_; - bool enable_collections_; + // SILIMATE: + bool boolean_props_as_int_{true}; + bool direction_props_short_{false}; + bool liberty_line_debug_{false}; + bool no_inv_delay_calc_{false}; + bool no_inv_power_calc_{false}; + bool strip_escaped_bus_{false}; + bool enable_collections_{false}; }; -} // namespace +} // namespace sta diff --git a/include/sta/Vector.hh b/include/sta/Vector.hh deleted file mode 100644 index 001b37131..000000000 --- a/include/sta/Vector.hh +++ /dev/null @@ -1,146 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include -#include - -namespace sta { - -// Add convenience functions around STL container. -template -class Vector : public std::vector -{ -public: - Vector() : std::vector() {} - Vector(size_t n) : std::vector(n) {} - Vector(size_t n, const OBJ &obj) : std::vector(n, obj) {} - - // Erase an object from the vector (slow). - void - eraseObject(OBJ obj) - { - auto find_iter = std::find(this->begin(), this->end(), obj); - if (find_iter != this->end()) - this->erase(find_iter); - } - - void - deleteContents() - { - Iterator iter(this); - while (iter.hasNext()) - delete iter.next(); - } - - void - deleteContentsClear() - { - deleteContents(); - this->clear(); - } - - void - deleteArrayContentsClear() - { - Iterator iter(this); - while (iter.hasNext()) - delete [] iter.next(); - this->clear(); - } - - // Java style container itererator - // Vector::Iterator iter(vector); - // while (iter.hasNext()) { - // Object *v = iter.next(); - // } - class Iterator - { - public: - Iterator() : container_(nullptr) {} - Iterator(std::vector *container) : - container_(container) - { if (container) iter_ = container->begin(); } - Iterator(std::vector &container) : - container_(&container) - { if (container_) iter_ = container_->begin(); } - void init() { iter_ = container_->begin(); } - void init(std::vector *container) - { container_ = container; if (container_) iter_=container_->begin(); } - void init(std::vector &container) - { container_ = &container; iter_ = container_->begin(); } - bool hasNext() { return container_ && iter_ != container_->end(); } - OBJ& next() { return *iter_++; } - std::vector *container() { return container_; } - - private: - std::vector *container_; - typename std::vector::iterator iter_; - }; - - class ConstIterator - { - public: - ConstIterator() : container_(nullptr) {} - ConstIterator(const std::vector *container) : - container_(container) - { if (container_) iter_ = container_->begin(); } - ConstIterator(const std::vector &container) : - container_(&container) - { iter_ = container_->begin(); } - void init() { iter_ = container_->begin(); } - void init(const std::vector *container) - { container_ = container; if (container_) iter_=container_->begin();} - void init(const std::vector &container) - { container_ = &container; if (container_) iter_=container_->begin();} - bool hasNext() { return container_ && iter_ != container_->end(); } - const OBJ& next() { return *iter_++; } - const std::vector *container() { return container_; } - - private: - const std::vector *container_; - typename std::vector::const_iterator iter_; - }; -}; - -template -void -sort(Vector &seq, SortCmp cmp) -{ - // For some strange reason std::sort goes off into never never land - // when optimization is turned on in gcc. - std::stable_sort(seq.begin(), seq.end(), cmp); -} - -template -void -sort(Vector *seq, SortCmp cmp) -{ - // For some strange reason std::sort goes off into never never land - // when optimization is turned on in gcc. - std::stable_sort(seq->begin(), seq->end(), cmp); -} - -} // namespace sta diff --git a/include/sta/VectorMap.hh b/include/sta/VectorMap.hh new file mode 100644 index 000000000..5fa6990cc --- /dev/null +++ b/include/sta/VectorMap.hh @@ -0,0 +1,487 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace sta { + +// Proxy reference type that mimics std::pair +// and supports structured bindings via tuple interface +template +class VectorMapReferenceProxy { +public: + // Constructor that stores pointer to underlying pair (non-const) + VectorMapReferenceProxy(std::pair* ptr) : pair_ptr_(ptr) {} + + // Constructor that stores pointer to underlying pair (const) + VectorMapReferenceProxy(const std::pair* ptr) + : pair_ptr_(const_cast*>(ptr)) {} + + // Access members like std::pair (for compatibility) + const Key& first() const { return pair_ptr_->first; } + Value& second() { return pair_ptr_->second; } + const Value& second() const { return pair_ptr_->second; } + + // Tuple-like access for structured bindings + template + auto get() const { + if constexpr (I == 0) return pair_ptr_->first; + if constexpr (I == 1) return pair_ptr_->second; + } + + template + auto get() { + if constexpr (I == 0) return pair_ptr_->first; + if constexpr (I == 1) return pair_ptr_->second; + } + + // Assignment (only second can be assigned) + VectorMapReferenceProxy& operator=(const std::pair& other) { + pair_ptr_->second = other.second; + return *this; + } + +private: + std::pair* pair_ptr_; +}; + +// VectorMap: A map implementation using a sorted vector with binary search. +// This provides O(log n) lookup, O(n) insertion/deletion, but better cache +// locality than std::map for small to medium-sized maps. +template > +class VectorMap { +public: + using key_type = Key; + using mapped_type = Value; + using value_type = std::pair; + using key_compare = Compare; + using size_type = size_t; + using difference_type = ptrdiff_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + +private: + // Internal storage: vector of pairs + // We use std::pair internally, but expose it as + // std::pair through iterators + using storage_type = std::vector>; + storage_type data_; + Compare comp_; + + // Helper to find insertion point using binary search + storage_type::iterator findInsertPos(const Key& key) { + return std::lower_bound(data_.begin(), data_.end(), key, + [this](const auto& pair, const Key& k) { + return comp_(pair.first, k); + }); + } + + storage_type::const_iterator findInsertPos(const Key& key) const { + return std::lower_bound(data_.begin(), data_.end(), key, + [this](const auto& pair, const Key& k) { + return comp_(pair.first, k); + }); + } + + // Iterator adapter to expose const Key in pair + // Uses a proxy reference to safely expose std::pair + template + class IteratorAdapter { + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = std::pair; + using difference_type = ptrdiff_t; + using proxy_type = VectorMapReferenceProxy; + + using reference = proxy_type; + using pointer = proxy_type*; + + explicit IteratorAdapter(BaseIter it) : it_(it) {} + + // Conversion constructor: allow conversion from non-const to const iterator + template + requires std::is_convertible_v + IteratorAdapter(const IteratorAdapter& other) + : it_(other.base()) {} + + reference operator*() { + return proxy_type(&*it_); + } + + reference operator*() const { + return proxy_type(&*it_); + } + + // For operator->, we return a pointer to a temporary + // This is not ideal but necessary for compatibility + class arrow_proxy { + public: + explicit arrow_proxy(const proxy_type& ref) + : value_(ref.first(), ref.second()) {} + value_type* operator->() { return &value_; } + private: + value_type value_; + }; + + arrow_proxy operator->() { + return arrow_proxy(operator*()); + } + + arrow_proxy operator->() const { + return arrow_proxy(operator*()); + } + + // Assignment operator: allow assignment from non-const to const iterator + template + requires std::is_convertible_v + IteratorAdapter& operator=(const IteratorAdapter& other) { + it_ = other.base(); + return *this; + } + + IteratorAdapter& operator++() { + ++it_; + return *this; + } + + IteratorAdapter operator++(int) { + IteratorAdapter tmp = *this; + ++it_; + return tmp; + } + + IteratorAdapter& operator--() { + --it_; + return *this; + } + + IteratorAdapter operator--(int) { + IteratorAdapter tmp = *this; + --it_; + return tmp; + } + + IteratorAdapter& operator+=(difference_type n) { + it_ += n; + return *this; + } + + IteratorAdapter& operator-=(difference_type n) { + it_ -= n; + return *this; + } + + IteratorAdapter operator+(difference_type n) const { + return IteratorAdapter(it_ + n); + } + + IteratorAdapter operator-(difference_type n) const { + return IteratorAdapter(it_ - n); + } + + difference_type operator-(const IteratorAdapter& other) const { + return it_ - other.it_; + } + + reference operator[](difference_type n) { + return proxy_type(&it_[n]); + } + + reference operator[](difference_type n) const { + return proxy_type(&it_[n]); + } + + bool operator==(const IteratorAdapter& other) const { + return it_ == other.it_; + } + + bool operator!=(const IteratorAdapter& other) const { + return it_ != other.it_; + } + + bool operator<(const IteratorAdapter& other) const { + return it_ < other.it_; + } + + bool operator<=(const IteratorAdapter& other) const { + return it_ <= other.it_; + } + + bool operator>(const IteratorAdapter& other) const { + return it_ > other.it_; + } + + bool operator>=(const IteratorAdapter& other) const { + return it_ >= other.it_; + } + + BaseIter base() const { return it_; } + + private: + BaseIter it_; + }; + +public: + using iterator = IteratorAdapter; + using const_iterator = IteratorAdapter; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + explicit VectorMap(const Compare& comp) : comp_(comp) {} + + template + VectorMap(InputIt first, InputIt last, const Compare& comp = Compare()) + : comp_(comp) { + for (; first != last; ++first) { + insert(*first); + } + } + + VectorMap(std::initializer_list init, + const Compare& comp = Compare()) + : comp_(comp) { + for (const auto& pair : init) { + insert(pair); + } + } + + // Element access + Value& operator[](const Key& key) { + auto it = findInsertPos(key); + if (it != data_.end() && !comp_(key, it->first)) { + // Key exists + return it->second; + } else { + // Key doesn't exist, insert it + return data_.insert(it, std::make_pair(key, Value{}))->second; + } + } + + Value& at(const Key& key) { + auto it = findInsertPos(key); + if (it != data_.end() && !comp_(key, it->first)) { + return it->second; + } + throw std::out_of_range("VectorMap::at"); + } + + const Value& at(const Key& key) const { + auto it = findInsertPos(key); + if (it != data_.end() && !comp_(key, it->first)) { + return it->second; + } + throw std::out_of_range("VectorMap::at"); + } + + // Lookup + bool contains(const Key& key) const { + auto it = findInsertPos(key); + return it != data_.end() && !comp_(key, it->first); + } + + iterator find(const Key& key) { + auto it = findInsertPos(key); + if (it != data_.end() && !comp_(key, it->first)) { + return iterator(it); + } + return end(); + } + + const_iterator find(const Key& key) const { + auto it = findInsertPos(key); + if (it != data_.end() && !comp_(key, it->first)) { + return const_iterator(it); + } + return end(); + } + + size_type count(const Key& key) const { + return contains(key) ? 1 : 0; + } + + // Modifiers + std::pair insert(const value_type& value) { + auto it = findInsertPos(value.first); + if (it != data_.end() && !comp_(value.first, it->first)) { + // Key already exists + return std::make_pair(iterator(it), false); + } else { + // Insert new key-value pair + it = data_.insert(it, std::make_pair(value.first, value.second)); + return std::make_pair(iterator(it), true); + } + } + + std::pair insert(value_type&& value) { + auto it = findInsertPos(value.first); + if (it != data_.end() && !comp_(value.first, it->first)) { + // Key already exists + return std::make_pair(iterator(it), false); + } else { + // Insert new key-value pair + it = data_.insert(it, std::make_pair(std::move(value.first), + std::move(value.second))); + return std::make_pair(iterator(it), true); + } + } + + template + std::pair insert(P&& value) { + return insert(value_type(std::forward

(value))); + } + + template + void insert(InputIt first, InputIt last) { + for (; first != last; ++first) { + insert(*first); + } + } + + void insert(std::initializer_list ilist) { + for (const auto& pair : ilist) { + insert(pair); + } + } + + iterator erase(const_iterator pos) { + return iterator(data_.erase(pos.base())); + } + + iterator erase(const_iterator first, const_iterator last) { + return iterator(data_.erase(first.base(), last.base())); + } + + size_type erase(const Key& key) { + auto it = findInsertPos(key); + if (it != data_.end() && !comp_(key, it->first)) { + data_.erase(it); + return 1; + } + return 0; + } + + void clear() { + data_.clear(); + } + + // Capacity + bool empty() const { + return data_.empty(); + } + + size_type size() const { + return data_.size(); + } + + size_type max_size() const { + return data_.max_size(); + } + + // Iterators + iterator begin() { + return iterator(data_.begin()); + } + + const_iterator begin() const { + return const_iterator(data_.begin()); + } + + const_iterator cbegin() const { + return const_iterator(data_.begin()); + } + + iterator end() { + return iterator(data_.end()); + } + + const_iterator end() const { + return const_iterator(data_.end()); + } + + const_iterator cend() const { + return const_iterator(data_.end()); + } + + reverse_iterator rbegin() { + return reverse_iterator(end()); + } + + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + + const_reverse_iterator crbegin() const { + return const_reverse_iterator(end()); + } + + reverse_iterator rend() { + return reverse_iterator(begin()); + } + + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + const_reverse_iterator crend() const { + return const_reverse_iterator(begin()); + } + + // Observers + key_compare key_comp() const { + return comp_; + } +}; + +} // namespace sta + +// Specializations for structured bindings support +namespace std { + +template +struct tuple_size> + : std::integral_constant {}; + +template +struct tuple_element> { + static_assert(I < 2, "Index out of bounds for pair"); + using type = std::conditional_t; +}; + +template +struct tuple_element> { + static_assert(I < 2, "Index out of bounds for pair"); + using type = std::conditional_t; +}; + +} // namespace std + diff --git a/include/sta/VerilogNamespace.hh b/include/sta/VerilogNamespace.hh index 835bd45e1..441b6ba24 100644 --- a/include/sta/VerilogNamespace.hh +++ b/include/sta/VerilogNamespace.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,25 +25,26 @@ #pragma once #include +#include namespace sta { std::string -cellVerilogName(const char *sta_name); +cellVerilogName(std::string_view sta_name); std::string -instanceVerilogName(const char *sta_name); +instanceVerilogName(std::string_view sta_name); std::string -netVerilogName(const char *sta_name); +netVerilogName(std::string_view sta_name); std::string -portVerilogName(const char *sta_name); +portVerilogName(std::string_view sta_name); std::string -moduleVerilogToSta(const std::string *sta_name); +moduleVerilogToSta(std::string_view module_name); std::string -instanceVerilogToSta(const std::string *sta_name); +instanceVerilogToSta(std::string_view inst_name); std::string -netVerilogToSta(const std::string *sta_name); +netVerilogToSta(std::string_view net_name); std::string -portVerilogToSta(const std::string *sta_name); +portVerilogToSta(std::string_view port_name); -} // namespace +} // namespace sta diff --git a/include/sta/VerilogReader.hh b/include/sta/VerilogReader.hh index dc76ccc6e..5236e7ef0 100644 --- a/include/sta/VerilogReader.hh +++ b/include/sta/VerilogReader.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,12 +24,15 @@ #pragma once +#include #include +#include +#include -#include "StringSet.hh" -#include "Vector.hh" -#include "Map.hh" +#include "Format.hh" #include "NetworkClass.hh" +#include "Report.hh" +#include "StringUtil.hh" namespace sta { @@ -59,30 +62,54 @@ class StringRegistry; class VerilogBindingTbl; class VerilogNetNameIterator; class VerilogNetPortRef; -class VerilogError; class LibertyCell; +class VerilogErrorCmp; -typedef Map VerilogModuleMap; -typedef Vector VerilogStmtSeq; -typedef Vector VerilogNetSeq; -typedef Vector VerilogDclArgSeq; -typedef Vector VerilogAttrStmtSeq; -typedef Vector VerilogAttrEntrySeq; -typedef Vector VerilogErrorSeq; +class VerilogError +{ +public: + VerilogError(int id, + std::string_view filename, + int line, + std::string_view msg, + bool warn); + const char *msg() const { return msg_.c_str(); } + std::string_view filename() const { return filename_; } + int id() const { return id_; } + int line() const { return line_; } + bool warn() const { return warn_; } + +private: + int id_; + std::string filename_; + int line_; + std::string msg_; + bool warn_; + + friend class VerilogErrorCmp; +}; + +using VerilogModuleMap = std::map; +using VerilogStmtSeq = std::vector; +using VerilogNetSeq = std::vector; +using VerilogDclArgSeq = std::vector; +using VerilogAttrStmtSeq = std::vector; +using VerilogAttrEntrySeq = std::vector; +using VerilogErrorSeq = std::vector; class VerilogReader { public: VerilogReader(NetworkReader *network); ~VerilogReader(); - bool read(const char *filename); + bool read(std::string_view filename); - void makeModule(const std::string *module_name, + void makeModule(std::string_view module_vname, VerilogNetSeq *ports, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, int line); - void makeModule(const std::string *module_name, + void makeModule(std::string_view module_vname, VerilogStmtSeq *port_dcls, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, @@ -95,8 +122,8 @@ public: VerilogDclArg *arg, VerilogAttrStmtSeq *attr_stmts, int line); - VerilogDclArg *makeDclArg(const std::string *net_name); - VerilogDclArg*makeDclArg(VerilogAssign *assign); + VerilogDclArg *makeDclArg(std::string_view net_vname); + VerilogDclArg *makeDclArg(VerilogAssign *assign); VerilogDclBus *makeDclBus(PortDirection *dir, int from_index, int to_index, @@ -109,189 +136,185 @@ public: VerilogDclArgSeq *args, VerilogAttrStmtSeq *attr_stmts, int line); - VerilogInst *makeModuleInst(const std::string *module_name, - const std::string *inst_name, + VerilogInst *makeModuleInst(std::string_view module_vname, + std::string_view inst_vname, VerilogNetSeq *pins, VerilogAttrStmtSeq *attr_stmts, - const int line); + int line); VerilogAssign *makeAssign(VerilogNet *lhs, - VerilogNet *rhs, - int line); - VerilogNetScalar *makeNetScalar(const std::string *name); - VerilogNetPortRef *makeNetNamedPortRefScalarNet(const std::string *port_vname); - VerilogNetPortRef *makeNetNamedPortRefScalarNet(const std::string *port_name, - const std::string *net_name); - VerilogNetPortRef *makeNetNamedPortRefBitSelect(const std::string *port_name, - const std::string *bus_name, - int index); - VerilogNetPortRef *makeNetNamedPortRefScalar(const std::string *port_name, - VerilogNet *net); - VerilogNetPortRef *makeNetNamedPortRefBit(const std::string *port_name, - int index, - VerilogNet *net); - VerilogNetPortRef *makeNetNamedPortRefPart(const std::string *port_name, - int from_index, - int to_index, - VerilogNet *net); + VerilogNet *rhs, + int line); + VerilogNetScalar *makeNetScalar(std::string_view name); + VerilogNetPortRef *makeNetNamedPortRefScalarNet(std::string_view port_vname); + VerilogNetPortRef *makeNetNamedPortRefScalarNet(std::string_view port_vname, + std::string_view net_vname); + VerilogNetPortRef *makeNetNamedPortRefBitSelect(std::string_view port_vname, + std::string_view bus_vname, + int index); + VerilogNetPortRef *makeNetNamedPortRefScalar(std::string_view port_vname, + VerilogNet *net); + VerilogNetPortRef *makeNetNamedPortRefBit(std::string_view port_vname, + int index, + VerilogNet *net); + VerilogNetPortRef *makeNetNamedPortRefPart(std::string_view port_vname, + int from_index, + int to_index, + VerilogNet *net); VerilogNetConcat *makeNetConcat(VerilogNetSeq *nets); - VerilogNetConstant *makeNetConstant(const std::string *constant, - int line); - VerilogNetBitSelect *makeNetBitSelect(const std::string *name, - int index); - VerilogNetPartSelect *makeNetPartSelect(const std::string *name, - int from_index, - int to_index); + VerilogNetConstant *makeNetConstant(std::string_view constant, + int line); + VerilogNetBitSelect *makeNetBitSelect(std::string_view name, + int index); + VerilogNetPartSelect *makeNetPartSelect(std::string_view name, + int from_index, + int to_index); VerilogModule *module(Cell *cell); - Instance *linkNetwork(const char *top_cell_name, - bool make_black_boxes, + Instance *linkNetwork(std::string_view top_cell_name, + bool make_black_boxes, bool delete_modules); - const char *filename() const { return filename_.c_str(); } + std::string_view filename() const { return filename_; } void incrLine(); Report *report() const { return report_; } + template void error(int id, - const char *filename, - int line, - const char *fmt, ...); + std::string_view filename, + int line, + std::string_view fmt, + Args &&...args) + { + report_->fileError(id, filename, line, fmt, std::forward(args)...); + } + template void warn(int id, - const char *filename, - int line, - const char *fmt, ...); + std::string_view filename, + int line, + std::string_view fmt, + Args &&...args) + { + report_->fileWarn(id, filename, line, fmt, std::forward(args)...); + } const std::string &zeroNetName() const { return zero_net_name_; } const std::string &oneNetName() const { return one_net_name_; } void deleteModules(); - void reportStmtCounts(); const std::string &constant10Max() const { return constant10_max_; } protected: - void init(const char *filename); + void init(std::string_view filename); void makeCellPorts(Cell *cell, - VerilogModule *module, - VerilogNetSeq *ports); + VerilogModule *module, + VerilogNetSeq *ports); Port *makeCellPort(Cell *cell, - VerilogModule *module, - const std::string &port_name); + VerilogModule *module, + const std::string &port_name); void makeNamedPortRefCellPorts(Cell *cell, - VerilogModule *module, - VerilogNet *mod_port, - StdStringSet &port_names); + VerilogModule *module, + VerilogNet *mod_port, + StringSet &port_names); void checkModuleDcls(VerilogModule *module, - std::set &port_names); + std::set &port_names); void makeModuleInstBody(VerilogModule *module, - Instance *inst, - VerilogBindingTbl *bindings, - bool make_black_boxes); + Instance *inst, + VerilogBindingTbl *bindings, + bool make_black_boxes); + void makeGeneratedClocks(LibertyCell *lib_cell, Instance *inst); void makeModuleInstNetwork(VerilogModuleInst *mod_inst, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool make_black_boxes); + Instance *parent, + VerilogModule *parent_module, + VerilogBindingTbl *parent_bindings, + bool make_black_boxes); void makeLibertyInst(VerilogLibertyInst *lib_inst, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings); + Instance *parent, + VerilogModule *parent_module, + VerilogBindingTbl *parent_bindings); void bindGlobalNets(VerilogBindingTbl *bindings); void makeNamedInstPins1(Cell *cell, - Instance *inst, - VerilogModuleInst *mod_inst, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogBindingTbl *parent_bindings, - bool is_leaf); + Instance *inst, + VerilogModuleInst *mod_inst, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogBindingTbl *parent_bindings, + bool is_leaf); void makeNamedInstPins(Cell *cell, - Instance *inst, - VerilogModuleInst *mod_inst, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool is_leaf); + Instance *inst, + VerilogModuleInst *mod_inst, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogModule *parent_module, + VerilogBindingTbl *parent_bindings, + bool is_leaf); void makeOrderedInstPins(Cell *cell, - Instance *inst, - VerilogModuleInst *mod_inst, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool is_leaf); + Instance *inst, + VerilogModuleInst *mod_inst, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogModule *parent_module, + VerilogBindingTbl *parent_bindings, + bool is_leaf); void mergeAssignNet(VerilogAssign *assign, - VerilogModule *module, - Instance *inst, - VerilogBindingTbl *bindings); + VerilogModule *module, + Instance *inst, + VerilogBindingTbl *bindings); void makeInstPin(Instance *inst, - Port *port, - VerilogNetNameIterator *net_name_iter, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogBindingTbl *parent_bindings, - bool is_leaf); + Port *port, + VerilogNetNameIterator *net_name_iter, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogBindingTbl *parent_bindings, + bool is_leaf); void makeInstPin(Instance *inst, - Port *port, - const std::string &net_name, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogBindingTbl *parent_bindings, - bool is_leaf); + Port *port, + const std::string &net_name, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogBindingTbl *parent_bindings, + bool is_leaf); + template void linkWarn(int id, - const char *filename, - int line, - const char *msg, ...) - __attribute__((format (printf, 5, 6))); + std::string_view filename, + int line, + std::string_view msg, + Args &&...args) + { + std::string msg_str = sta::formatRuntime(msg, std::forward(args)...); + link_errors_.push_back(new VerilogError(id, filename, line, msg_str, true)); + } + template void linkError(int id, - const char *filename, - int line, - const char *msg, ...) - __attribute__((format (printf, 5, 6))); + std::string_view filename, + int line, + std::string_view msg, + Args &&...args) + { + std::string msg_str = sta::formatRuntime(msg, std::forward(args)...); + link_errors_.push_back(new VerilogError(id, filename, line, msg_str, false)); + } bool reportLinkErrors(); bool haveLinkErrors(); Cell *makeBlackBox(VerilogModuleInst *mod_inst, - VerilogModule *parent_module); + VerilogModule *parent_module); void makeBlackBoxNamedPorts(Cell *cell, - VerilogModuleInst *mod_inst, - VerilogModule *parent_module); + VerilogModuleInst *mod_inst, + VerilogModule *parent_module); void makeBlackBoxOrderedPorts(Cell *cell, - VerilogModuleInst *mod_inst, - VerilogModule *parent_module); + VerilogModuleInst *mod_inst, + VerilogModule *parent_module); bool isBlackBox(Cell *cell); bool hasScalarNamedPortRefs(LibertyCell *liberty_cell, - VerilogNetSeq *pins); + VerilogNetSeq *pins); std::string filename_; Report *report_; Debug *debug_; NetworkReader *network_; - Library *library_; - int black_box_index_; + Library *library_{nullptr}; + int black_box_index_{0}; VerilogModuleMap module_map_; VerilogErrorSeq link_errors_; const std::string zero_net_name_; const std::string one_net_name_; std::string constant10_max_; ViewType *view_type_; - bool report_stmt_stats_; - int module_count_; - int inst_mod_count_; - int inst_lib_count_; - int inst_lib_net_arrays_; - int port_names_; - int inst_module_names_; - int inst_names_; - int dcl_count_; - int dcl_bus_count_; - int dcl_arg_count_; - int net_scalar_count_; - int net_scalar_names_; - int net_bus_names_; - int net_part_select_count_; - int net_bit_select_count_; - int net_port_ref_scalar_count_; - int net_port_ref_scalar_net_count_; - int net_port_ref_bit_count_; - int net_port_ref_part_count_; - int net_constant_count_; - int assign_count_; - int concat_count_; }; } // namespace sta - diff --git a/include/sta/VerilogWriter.hh b/include/sta/VerilogWriter.hh index b4a08e8cb..7e4cf6e06 100644 --- a/include/sta/VerilogWriter.hh +++ b/include/sta/VerilogWriter.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -30,8 +30,8 @@ namespace sta { void writeVerilog(const char *filename, - bool include_pwr_gnd, - CellSeq *remove_cells, - Network *network); + bool include_pwr_gnd, + CellSeq *remove_cells, + Network *network); -} // namespace +} // namespace sta diff --git a/include/sta/VertexId.hh b/include/sta/VertexId.hh index b56b8b7c3..8e6181b7b 100644 --- a/include/sta/VertexId.hh +++ b/include/sta/VertexId.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -30,8 +30,8 @@ namespace sta { -typedef ObjectId VertexId; +using VertexId = ObjectId; static constexpr VertexId vertex_id_null = object_id_null; -} // namespace +} // namespace sta diff --git a/include/sta/VertexVisitor.hh b/include/sta/VertexVisitor.hh index b371ef8ec..e7aa7d363 100644 --- a/include/sta/VertexVisitor.hh +++ b/include/sta/VertexVisitor.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,8 +24,8 @@ #pragma once -#include "NetworkClass.hh" #include "GraphClass.hh" +#include "NetworkClass.hh" namespace sta { @@ -33,12 +33,10 @@ namespace sta { class VertexVisitor { public: - VertexVisitor() {} - virtual ~VertexVisitor() {} + virtual ~VertexVisitor() = default; virtual VertexVisitor *copy() const = 0; virtual void visit(Vertex *vertex) = 0; void operator()(Vertex *vertex) { visit(vertex); } - virtual void levelFinished() {} }; // Collect visited pins into a PinSet. @@ -47,11 +45,11 @@ class VertexPinCollector : public VertexVisitor public: VertexPinCollector(PinSet &pins); const PinSet &pins() const { return pins_; } - void visit(Vertex *vertex); - virtual VertexVisitor *copy() const; + void visit(Vertex *vertex) override; + VertexVisitor *copy() const override; protected: PinSet &pins_; }; -} // namespace +} // namespace sta diff --git a/include/sta/VisitPathEnds.hh b/include/sta/VisitPathEnds.hh index f6114b3f6..8ac723f51 100644 --- a/include/sta/VisitPathEnds.hh +++ b/include/sta/VisitPathEnds.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,8 +24,8 @@ #pragma once -#include "SdcClass.hh" #include "GraphClass.hh" +#include "SdcClass.hh" #include "SearchClass.hh" #include "StaState.hh" @@ -38,116 +38,111 @@ class VisitPathEnds : public StaState { public: VisitPathEnds(const StaState *sta); - // All corners, unfiltered. + // All scenes, unfiltered. void visitPathEnds(Vertex *vertex, - PathEndVisitor *visitor); - // Use corner nullptr to visit PathEnds for all corners. + PathEndVisitor *visitor); void visitPathEnds(Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - bool filtered, - PathEndVisitor *visitor); - bool checkEdgeEnabled(Edge *edge) const; + const SceneSet &scenes, + const MinMaxAll *min_max, + bool filtered, + PathEndVisitor *visitor); + bool checkEdgeEnabled(const Edge *edge, + const Mode *mode) const; protected: void visitClkedPathEnds(const Pin *pin, - Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained); + Vertex *vertex, + const SceneSet &scenes, + const MinMaxAll *min_max, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); virtual void visitCheckEnd(const Pin *pin, - Vertex *vertex, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained); + Vertex *vertex, + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); void visitCheckEndUnclked(const Pin *pin, - Vertex *vertex, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained); + Vertex *vertex, + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); void visitOutputDelayEnd(const Pin *pin, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained); + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); virtual void visitOutputDelayEnd1(OutputDelay *output_delay, - const Pin *pin, - Path *path, - const RiseFall *end_rf, - const ClockEdge *tgt_clk_edge, - Path *ref_path, - const MinMax *min_max, - PathEndVisitor *visitor, - bool &is_constrained); + const Pin *pin, + Path *path, + const RiseFall *end_rf, + const ClockEdge *tgt_clk_edge, + Path *ref_path, + const MinMax *min_max, + PathEndVisitor *visitor, + bool &is_constrained); virtual void visitGatedClkEnd(const Pin *pin, - Vertex *vertex, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained); + Vertex *vertex, + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); float clockGatingMargin(const Clock *clk, - const Pin *clk_pin, - const Pin *enable_pin, - const RiseFall *enable_rf, - const SetupHold *setup_hold); + const Pin *clk_pin, + const Pin *enable_pin, + const RiseFall *enable_rf, + const SetupHold *setup_hold, + const Sdc *sdc); void visitDataCheckEnd(const Pin *pin, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained); + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); bool visitDataCheckEnd1(DataCheck *check, - const Pin *pin, - Path *path, - const Clock *src_clk, - const RiseFall *end_rf, - const MinMax *min_max, - const PathAnalysisPt *clk_ap, - const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained); + const Pin *pin, + Path *path, + const Clock *src_clk, + const RiseFall *end_rf, + const MinMax *min_max, + const Pin *from_pin, + Vertex *from_vertex, + const RiseFall *from_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); virtual void visitUnconstrainedPathEnds(const Pin *pin, - Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - bool filtered, - PathEndVisitor *visitor); + Vertex *vertex, + const SceneSet &scenes, + const MinMaxAll *min_max, + bool filtered, + PathEndVisitor *visitor); bool falsePathTo(Path *path, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max); + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max); PathDelay *pathDelayTo(Path *path, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max); + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max); ExceptionPath *exceptionTo(const Path *path, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max) const; + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max) const; }; // Abstract base class used by visitPathEnds to visit vertex path ends. class PathEndVisitor { public: - virtual ~PathEndVisitor() {} + virtual ~PathEndVisitor() = default; virtual PathEndVisitor *copy() const = 0; // Begin visiting the path ends for a vertex / path_index. virtual void vertexBegin(Vertex *) {} @@ -157,4 +152,4 @@ public: virtual void vertexEnd(Vertex *) {} }; -} // namespace +} // namespace sta diff --git a/include/sta/Wireload.hh b/include/sta/Wireload.hh index d0ea001c0..c0dc90765 100644 --- a/include/sta/Wireload.hh +++ b/include/sta/Wireload.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,54 +24,58 @@ #pragma once -#include "Vector.hh" +#include +#include +#include +#include + #include "LibertyClass.hh" namespace sta { class WireloadForArea; -typedef std::pair FanoutLength; -typedef Vector FanoutLengthSeq; -typedef Vector WireloadForAreaSeq; +using FanoutLength = std::pair; +using FanoutLengthSeq = std::vector; +using WireloadForAreaSeq = std::vector; const char * wireloadTreeString(WireloadTree tree); WireloadTree -stringWireloadTree(const char *tree); +stringWireloadTree(std::string_view wire_load_type); const char * wireloadModeString(WireloadMode wire_load_mode); WireloadMode -stringWireloadMode(const char *wire_load_mode); +stringWireloadMode(std::string_view wire_load_mode); class Wireload { public: - Wireload(const char *name, - LibertyLibrary *library); - Wireload(const char *name, - LibertyLibrary *library, - float area, - float resistance, - float capacitance, - float slope); + Wireload(std::string name, + LibertyLibrary *library); + Wireload(std::string name, + LibertyLibrary *library, + float area, + float resistance, + float capacitance, + float slope); virtual ~Wireload(); - const char *name() const { return name_; } + const std::string &name() const { return name_; } void setArea(float area); void setResistance(float res); void setCapacitance(float cap); void setSlope(float slope); void addFanoutLength(float fanout, - float length); + float length); // Find wireload resistance/capacitance for fanout. virtual void findWireload(float fanout, - const OperatingConditions *op_cond, - float &cap, - float &res) const; + const OperatingConditions *op_cond, + float &cap, + float &res) const; protected: - const char *name_; + std::string name_; LibertyLibrary *library_; float area_; float resistance_; @@ -84,17 +88,17 @@ protected: class WireloadSelection { public: - explicit WireloadSelection(const char *name); + WireloadSelection(std::string name); ~WireloadSelection(); - const char *name() const { return name_; } + const std::string &name() const { return name_; } void addWireloadFromArea(float min_area, - float max_area, - const Wireload *wireload); + float max_area, + const Wireload *wireload); const Wireload *findWireload(float area) const; private: - const char *name_; + const std::string name_; WireloadForAreaSeq wireloads_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Zlib.hh b/include/sta/Zlib.hh index 7a9399de0..e7ca6fe8f 100644 --- a/include/sta/Zlib.hh +++ b/include/sta/Zlib.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -47,7 +47,7 @@ #define Z_NULL nullptr namespace gzstream { -typedef std::ifstream igzstream; +using igzstream = std::ifstream; } #endif // ZLIB_FOUND diff --git a/liberty/EquivCells.cc b/liberty/EquivCells.cc index f8910791e..3c40ca4d3 100644 --- a/liberty/EquivCells.cc +++ b/liberty/EquivCells.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,21 +24,19 @@ #include "EquivCells.hh" -#include "Hash.hh" -#include "MinMax.hh" -#include "PortDirection.hh" -#include "Transition.hh" -#include "TimingRole.hh" +#include + +#include "ContainerHelpers.hh" #include "FuncExpr.hh" -#include "TimingArc.hh" +#include "Hash.hh" #include "Liberty.hh" -#include "TableModel.hh" +#include "LibertyClass.hh" +#include "PortDirection.hh" #include "Sequential.hh" +#include "TimingArc.hh" namespace sta { -using std::max; - static unsigned hashCell(const LibertyCell *cell); static unsigned @@ -49,7 +47,7 @@ static unsigned hashSequential(const Sequential *seq); bool equivCellStatetables(const LibertyCell *cell1, - const LibertyCell *cell2); + const LibertyCell *cell2); static bool equivCellPortSeq(const LibertyPortSeq &ports1, const LibertyPortSeq &ports2); @@ -86,77 +84,75 @@ class CellDriveResistanceGreater { public: bool operator()(const LibertyCell *cell1, - const LibertyCell *cell2) const + const LibertyCell *cell2) const { return cellDriveResistance(cell1) > cellDriveResistance(cell2); } }; EquivCells::EquivCells(LibertyLibrarySeq *equiv_libs, - LibertyLibrarySeq *map_libs) + LibertyLibrarySeq *map_libs) { LibertyCellHashMap hash_matches; for (auto lib : *equiv_libs) findEquivCells(lib, hash_matches); // Sort the equiv sets by drive resistance. for (auto cell : unique_equiv_cells_) { - auto equivs = equiv_cells_.findKey(cell); + auto equivs = findKey(equiv_cells_, cell); sort(equivs, CellDriveResistanceGreater()); } if (map_libs) { for (auto lib : *map_libs) mapEquivCells(lib, hash_matches); } - hash_matches.deleteContents(); + deleteContents(hash_matches); } EquivCells::~EquivCells() { for (auto cell : unique_equiv_cells_) - delete equiv_cells_.findKey(cell); + delete findKey(equiv_cells_, cell); } LibertyCellSeq * EquivCells::equivs(LibertyCell *cell) { - return equiv_cells_.findKey(cell); + return findKey(equiv_cells_, cell); } // Use a comprehensive hash on cell properties to segregate // cells into groups of potential matches. void EquivCells::findEquivCells(const LibertyLibrary *library, - LibertyCellHashMap &hash_matches) + LibertyCellHashMap &hash_matches) { LibertyCellIterator cell_iter(library); while (cell_iter.hasNext()) { LibertyCell *cell = cell_iter.next(); if (!cell->dontUse()) { unsigned hash = hashCell(cell); - LibertyCellSeq *matches = hash_matches.findKey(hash); + LibertyCellSeq *matches = findKey(hash_matches, hash); if (matches) { - LibertyCellSeq::Iterator match_iter(matches); - while (match_iter.hasNext()) { - LibertyCell *match = match_iter.next(); - if (equivCells(match, cell)) { - LibertyCellSeq *equivs = equiv_cells_.findKey(match); - if (equivs == nullptr) { - equivs = new LibertyCellSeq; - equivs->push_back(match); - unique_equiv_cells_.push_back(match); - equiv_cells_[match] = equivs; - } - equivs->push_back(cell); - equiv_cells_[cell] = equivs; - break; - } - } - matches->push_back(cell); + for (LibertyCell *match : *matches) { + if (equivCells(match, cell)) { + LibertyCellSeq *equivs = findKey(equiv_cells_, match); + if (equivs == nullptr) { + equivs = new LibertyCellSeq; + equivs->push_back(match); + unique_equiv_cells_.push_back(match); + equiv_cells_[match] = equivs; + } + equivs->push_back(cell); + equiv_cells_[cell] = equivs; + break; + } + } + matches->push_back(cell); } else { - matches = new LibertyCellSeq; - hash_matches[hash] = matches; - matches->push_back(cell); + matches = new LibertyCellSeq; + hash_matches[hash] = matches; + matches->push_back(cell); } } } @@ -165,7 +161,7 @@ EquivCells::findEquivCells(const LibertyLibrary *library, // Map library cells to equiv cells. void EquivCells::mapEquivCells(const LibertyLibrary *library, - LibertyCellHashMap &hash_matches) + LibertyCellHashMap &hash_matches) { LibertyCellIterator cell_iter(library); @@ -173,17 +169,15 @@ EquivCells::mapEquivCells(const LibertyLibrary *library, LibertyCell *cell = cell_iter.next(); if (!cell->dontUse()) { unsigned hash = hashCell(cell); - LibertyCellSeq *matches = hash_matches.findKey(hash); + LibertyCellSeq *matches = findKey(hash_matches, hash); if (matches) { - LibertyCellSeq::Iterator match_iter(matches); - while (match_iter.hasNext()) { - LibertyCell *match = match_iter.next(); - if (equivCells(match, cell)) { - LibertyCellSeq *equivs = equiv_cells_.findKey(match); - equiv_cells_[cell] = equivs; - break; - } - } + for (LibertyCell *match : *matches) { + if (equivCells(match, cell)) { + LibertyCellSeq *equivs = findKey(equiv_cells_, match); + equiv_cells_[cell] = equivs; + break; + } + } } } } @@ -213,16 +207,16 @@ hashCellPorts(const LibertyCell *cell) static unsigned hashPort(const LibertyPort *port) { - return hashString(port->name()) * 3 - + port->direction()->index() * 5; + return hashString(port->name()) * 3U + + port->direction()->index() * 5U; } static unsigned hashCellSequentials(const LibertyCell *cell) { unsigned hash = 0; - for (const Sequential *seq : cell->sequentials()) - hash += hashSequential(seq); + for (const Sequential &seq : cell->sequentials()) + hash += hashSequential(&seq); const Statetable *statetable = cell->statetable(); if (statetable) hash += hashStatetable(statetable); @@ -240,8 +234,8 @@ hashSequential(const Sequential *seq) hash += hashPort(seq->outputInv()) * 11; hash += hashFuncExpr(seq->clear()) * 13; hash += hashFuncExpr(seq->preset()) * 17; - hash += int(seq->clearPresetOutput()) * 19; - hash += int(seq->clearPresetOutputInv()) * 23; + hash += static_cast(seq->clearPresetOutput()) * 19; + hash += static_cast(seq->clearPresetOutputInv()) * 23; return hash; } @@ -286,22 +280,22 @@ hashFuncExpr(const FuncExpr *expr) return 0; else { switch (expr->op()) { - case FuncExpr::op_port: + case FuncExpr::Op::port: return hashPort(expr->port()) * 17; break; - case FuncExpr::op_not: + case FuncExpr::Op::not_: return hashFuncExpr(expr->left()) * 31; break; default: return (hashFuncExpr(expr->left()) + hashFuncExpr(expr->right())) - * ((1 << expr->op()) - 1); + * ((1 << static_cast(expr->op())) - 1); } } } bool equivCells(const LibertyCell *cell1, - const LibertyCell *cell2) + const LibertyCell *cell2) { return equivCellPorts(cell1, cell2) && equivCellFuncs(cell1, cell2) @@ -343,8 +337,7 @@ equivCellFuncs(const LibertyCell *cell1, LibertyCellPortIterator port_iter1(cell1); while (port_iter1.hasNext()) { LibertyPort *port1 = port_iter1.next(); - const char *name = port1->name(); - LibertyPort *port2 = cell2->findLibertyPort(name); + LibertyPort *port2 = cell2->findLibertyPort(port1->name()); if (!(port2 && FuncExpr::equiv(port1->function(), port2->function()) && FuncExpr::equiv(port1->tristateEnable(), @@ -356,7 +349,7 @@ equivCellFuncs(const LibertyCell *cell1, bool equivCellPorts(const LibertyCell *cell1, - const LibertyCell *cell2) + const LibertyCell *cell2) { if (cell1->portCount() != cell2->portCount()) return false; @@ -364,8 +357,7 @@ equivCellPorts(const LibertyCell *cell1, LibertyCellPortIterator port_iter1(cell1); while (port_iter1.hasNext()) { LibertyPort *port1 = port_iter1.next(); - const char* name = port1->name(); - LibertyPort *port2 = cell2->findLibertyPort(name); + LibertyPort *port2 = cell2->findLibertyPort(port1->name()); if (!(port2 && LibertyPort::equiv(port1, port2))) return false; } @@ -375,7 +367,7 @@ equivCellPorts(const LibertyCell *cell1, bool equivCellSequentials(const LibertyCell *cell1, - const LibertyCell *cell2) + const LibertyCell *cell2) { const SequentialSeq &seqs1 = cell1->sequentials(); const SequentialSeq &seqs2 = cell2->sequentials(); @@ -383,14 +375,14 @@ equivCellSequentials(const LibertyCell *cell1, for (; seq_itr1 != seqs1.end() && seq_itr2 != seqs2.end(); seq_itr1++, seq_itr2++) { - const Sequential *seq1 = *seq_itr1; - const Sequential *seq2 = *seq_itr2; - if (!(FuncExpr::equiv(seq1->clock(), seq2->clock()) - && FuncExpr::equiv(seq1->data(), seq2->data()) - && LibertyPort::equiv(seq1->output(), seq2->output()) - && LibertyPort::equiv(seq1->outputInv(), seq2->outputInv()) - && FuncExpr::equiv(seq1->clear(), seq2->clear()) - && FuncExpr::equiv(seq1->preset(), seq2->preset()))) + const Sequential &seq1 = *seq_itr1; + const Sequential &seq2 = *seq_itr2; + if (!(FuncExpr::equiv(seq1.clock(), seq2.clock()) + && FuncExpr::equiv(seq1.data(), seq2.data()) + && LibertyPort::equiv(seq1.output(), seq2.output()) + && LibertyPort::equiv(seq1.outputInv(), seq2.outputInv()) + && FuncExpr::equiv(seq1.clear(), seq2.clear()) + && FuncExpr::equiv(seq1.preset(), seq2.preset()))) return false; } return seq_itr1 == seqs1.end() && seq_itr2 == seqs2.end(); @@ -398,7 +390,7 @@ equivCellSequentials(const LibertyCell *cell1, bool equivCellStatetables(const LibertyCell *cell1, - const LibertyCell *cell2) + const LibertyCell *cell2) { const Statetable *statetable1 = cell1->statetable(); @@ -494,7 +486,7 @@ equivStatetableRow(const StatetableRow &row1, bool equivCellTimingArcSets(const LibertyCell *cell1, - const LibertyCell *cell2) + const LibertyCell *cell2) { if (cell1->timingArcSetCount() != cell2->timingArcSetCount()) return false; @@ -502,10 +494,10 @@ equivCellTimingArcSets(const LibertyCell *cell1, for (TimingArcSet *arc_set1 : cell1->timingArcSets()) { TimingArcSet *arc_set2 = cell2->findTimingArcSet(arc_set1); if (!(arc_set2 && TimingArcSet::equiv(arc_set1, arc_set2))) - return false; + return false; } return true; } } -} // namespace +} // namespace sta diff --git a/liberty/FuncExpr.cc b/liberty/FuncExpr.cc index 67de253c6..fbab84523 100644 --- a/liberty/FuncExpr.cc +++ b/liberty/FuncExpr.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,64 +24,64 @@ #include "FuncExpr.hh" -#include "StringUtil.hh" +#include +#include + #include "Liberty.hh" -#include "Network.hh" +#include "LibertyClass.hh" namespace sta { -using std::string; - FuncExpr * FuncExpr::makePort(LibertyPort *port) { - return new FuncExpr(op_port, nullptr, nullptr, port); + return new FuncExpr(Op::port, nullptr, nullptr, port); } FuncExpr * FuncExpr::makeNot(FuncExpr *expr) { - return new FuncExpr(op_not, expr, nullptr, nullptr); + return new FuncExpr(Op::not_, expr, nullptr, nullptr); } FuncExpr * FuncExpr::makeAnd(FuncExpr *left, - FuncExpr *right) + FuncExpr *right) { - return new FuncExpr(op_and, left, right, nullptr); + return new FuncExpr(Op::and_, left, right, nullptr); } FuncExpr * FuncExpr::makeOr(FuncExpr *left, - FuncExpr *right) + FuncExpr *right) { - return new FuncExpr(op_or, left, right, nullptr); + return new FuncExpr(Op::or_, left, right, nullptr); } FuncExpr * FuncExpr::makeXor(FuncExpr *left, - FuncExpr *right) + FuncExpr *right) { - return new FuncExpr(op_xor, left, right, nullptr); + return new FuncExpr(Op::xor_, left, right, nullptr); } FuncExpr * FuncExpr::makeZero() { - return new FuncExpr(op_zero, nullptr, nullptr, nullptr); + return new FuncExpr(Op::zero, nullptr, nullptr, nullptr); } FuncExpr * FuncExpr::makeOne() { - return new FuncExpr(op_one, nullptr, nullptr, nullptr); + return new FuncExpr(Op::one, nullptr, nullptr, nullptr); } -FuncExpr::FuncExpr(Operator op, - FuncExpr *left, - FuncExpr *right, - LibertyPort *port) : +FuncExpr::FuncExpr(Op op, + FuncExpr *left, + FuncExpr *right, + LibertyPort *port) : op_(op), left_(left), right_(right), @@ -89,13 +89,17 @@ FuncExpr::FuncExpr(Operator op, { } +FuncExpr::~FuncExpr() +{ + delete left_; + delete right_; +} + void -FuncExpr::deleteSubexprs() +FuncExpr::shallowDelete() { - if (left_) - left_->deleteSubexprs(); - if (right_) - right_->deleteSubexprs(); + left_ = nullptr; + right_ = nullptr; delete this; } @@ -110,7 +114,7 @@ FuncExpr::copy() LibertyPort * FuncExpr::port() const { - if (op_ == op_port) + if (op_ == Op::port) return port_; else return nullptr; @@ -123,29 +127,29 @@ FuncExpr::portTimingSense(const LibertyPort *port) const TimingSense left_sense, right_sense; switch (op_) { - case op_port: + case Op::port: if (port == port_) return TimingSense::positive_unate; else return TimingSense::none; - case op_not: + case Op::not_: if (left_) { switch (left_->portTimingSense(port)) { case TimingSense::positive_unate: - return TimingSense::negative_unate; + return TimingSense::negative_unate; case TimingSense::negative_unate: - return TimingSense::positive_unate; + return TimingSense::positive_unate; case TimingSense::non_unate: - return TimingSense::non_unate; + return TimingSense::non_unate; case TimingSense::none: - return TimingSense::none; + return TimingSense::none; case TimingSense::unknown: - return TimingSense::unknown; + return TimingSense::unknown; } } return TimingSense::unknown; - case op_or: - case op_and: + case Op::or_: + case Op::and_: left_sense = TimingSense::unknown; right_sense = TimingSense::unknown; if (left_) @@ -156,21 +160,21 @@ FuncExpr::portTimingSense(const LibertyPort *port) const if (left_sense == right_sense) return left_sense; else if (left_sense == TimingSense::non_unate - || right_sense == TimingSense::non_unate - || (left_sense == TimingSense::positive_unate - && right_sense == TimingSense::negative_unate) - || (left_sense == TimingSense::negative_unate - && right_sense == TimingSense::positive_unate)) + || right_sense == TimingSense::non_unate + || (left_sense == TimingSense::positive_unate + && right_sense == TimingSense::negative_unate) + || (left_sense == TimingSense::negative_unate + && right_sense == TimingSense::positive_unate)) return TimingSense::non_unate; else if (left_sense == TimingSense::none - || left_sense == TimingSense::unknown) + || left_sense == TimingSense::unknown) return right_sense; else if (right_sense == TimingSense::none - || right_sense == TimingSense::unknown) + || right_sense == TimingSense::unknown) return left_sense; else return TimingSense::unknown; - case op_xor: + case Op::xor_: left_sense = TimingSense::unknown; right_sense = TimingSense::unknown; if (left_) @@ -178,66 +182,65 @@ FuncExpr::portTimingSense(const LibertyPort *port) const if (right_) right_sense = right_->portTimingSense(port); if (left_sense == TimingSense::positive_unate - || left_sense == TimingSense::negative_unate - || left_sense == TimingSense::non_unate - || right_sense == TimingSense::positive_unate - || right_sense == TimingSense::negative_unate - || right_sense == TimingSense::non_unate) + || left_sense == TimingSense::negative_unate + || left_sense == TimingSense::non_unate + || right_sense == TimingSense::positive_unate + || right_sense == TimingSense::negative_unate + || right_sense == TimingSense::non_unate) return TimingSense::non_unate; else return TimingSense::unknown; - case op_one: + case Op::one: return TimingSense::none; - case op_zero: + case Op::zero: return TimingSense::none; } // Prevent warnings from lame compilers. return TimingSense::unknown; } -string +std::string FuncExpr::to_string() const { return to_string(false); } -string +std::string FuncExpr::to_string(bool with_parens) const { switch (op_) { - case op_port: + case Op::port: return port_->name(); - case op_not: { - string result = "!"; - result += left_->to_string(true); + case Op::not_: { + std::string result = "!"; + result += left_ ? left_->to_string(true) : "?"; return result; } - case op_or: + case Op::or_: return to_string(with_parens, '+'); - case op_and: + case Op::and_: return to_string(with_parens, '*'); - case op_xor: + case Op::xor_: return to_string(with_parens, '^'); - case op_one: + case Op::one: return "1"; - case op_zero: + case Op::zero: return "0"; default: return "?"; } } -string +std::string FuncExpr::to_string(bool with_parens, char op) const { - string right = right_->to_string(true); - string result; + std::string result; if (with_parens) result += '('; - result += left_->to_string(true); + result += left_ ? left_->to_string(true) : "?"; result += op; - result += right_->to_string(true); + result += right_ ? right_->to_string(true) : "?"; if (with_parens) result += ')'; return result; @@ -247,11 +250,11 @@ FuncExpr * FuncExpr::bitSubExpr(int bit_offset) { switch (op_) { - case op_port: + case Op::port: if (port_->hasMembers()) { if (port_->size() == 1) { - LibertyPort *port = port_->findLibertyMember(0); - return makePort(port); + LibertyPort *port = port_->findLibertyMember(0); + return makePort(port); } else { if (bit_offset < port_->size()) { @@ -265,20 +268,21 @@ FuncExpr::bitSubExpr(int bit_offset) else // Always copy so the subexpr doesn't share memory. return makePort(port_); - case op_not: + case Op::not_: return makeNot(left_->bitSubExpr(bit_offset)); - case op_or: + case Op::or_: return makeOr(left_->bitSubExpr(bit_offset), - right_->bitSubExpr(bit_offset)); - case op_and: + right_->bitSubExpr(bit_offset)); + case Op::and_: return makeAnd(left_->bitSubExpr(bit_offset), - right_->bitSubExpr(bit_offset)); - case op_xor: + right_->bitSubExpr(bit_offset)); + case Op::xor_: return makeXor(left_->bitSubExpr(bit_offset), - right_->bitSubExpr(bit_offset)); - case op_one: - case op_zero: - return this; + right_->bitSubExpr(bit_offset)); + case Op::one: + return makeOne(); + case Op::zero: + return makeZero(); } // Prevent warnings from lame compilers. return nullptr; @@ -288,17 +292,17 @@ bool FuncExpr::hasPort(const LibertyPort *port) const { switch (op_) { - case op_port: + case Op::port: return (port_ == port); - case op_not: + case Op::not_: return left_ && left_->hasPort(port); - case op_or: - case op_and: - case op_xor: + case Op::or_: + case Op::and_: + case Op::xor_: return (left_ && left_->hasPort(port)) || (right_ && right_->hasPort(port)); - case op_one: - case op_zero: + case Op::one: + case Op::zero: return false; } // Prevent warnings from lame compilers. @@ -316,18 +320,18 @@ FuncExpr::checkSize(size_t size) { size_t port_size; switch (op_) { - case op_port: + case Op::port: port_size = port_->size(); return !(port_size == size - || port_size == 1); - case op_not: + || port_size == 1); + case Op::not_: return left_->checkSize(size); - case op_or: - case op_and: - case op_xor: + case Op::or_: + case Op::and_: + case Op::xor_: return left_->checkSize(size) || right_->checkSize(size); - case op_one: - case op_zero: + case Op::one: + case Op::zero: return false; } // Prevent warnings from lame compilers. @@ -335,54 +339,55 @@ FuncExpr::checkSize(size_t size) } FuncExpr * -funcExprNot(FuncExpr *expr) +FuncExpr::invert() { - if (expr->op() == FuncExpr::op_not) { - FuncExpr *not_expr = expr->left(); - delete expr; - return not_expr; + if (op_ == FuncExpr::Op::not_) { + FuncExpr *inv = left_; + shallowDelete() ; + return inv; } else - return FuncExpr::makeNot(expr); + return FuncExpr::makeNot(this); } -//////////////////////////////////////////////////////////////// - -FuncExprPortIterator::FuncExprPortIterator(const FuncExpr *expr) +LibertyPortSet +FuncExpr::ports() const { - findPorts(expr); - iter_.init(ports_); + LibertyPortSet ports; + findPorts(this, ports); + return ports; } void -FuncExprPortIterator::findPorts(const FuncExpr *expr) +FuncExpr::findPorts(const FuncExpr *expr, + LibertyPortSet &ports) const { if (expr) { - if (expr->op() == FuncExpr::op_port) - ports_.insert(expr->port()); + if (expr->op() == FuncExpr::Op::port) + ports.insert(expr->port()); else { - findPorts(expr->left()); - findPorts(expr->right()); + findPorts(expr->left(), ports); + findPorts(expr->right(), ports); } } } bool FuncExpr::equiv(const FuncExpr *expr1, - const FuncExpr *expr2) + const FuncExpr *expr2) { if (expr1 == nullptr && expr2 == nullptr) return true; else if (expr1 != nullptr && expr2 != nullptr - && expr1->op() == expr2->op()) { + && expr1->op() == expr2->op()) { switch (expr1->op()) { - case FuncExpr::op_port: + case FuncExpr::Op::port: return LibertyPort::equiv(expr1->port(), expr2->port()); - case FuncExpr::op_not: + case FuncExpr::Op::not_: return equiv(expr1->left(), expr2->left()); default: return equiv(expr1->left(), expr2->left()) - && equiv(expr1->right(), expr2->right()); + && equiv(expr1->right(), expr2->right()); } } else @@ -391,22 +396,22 @@ FuncExpr::equiv(const FuncExpr *expr1, bool FuncExpr::less(const FuncExpr *expr1, - const FuncExpr *expr2) + const FuncExpr *expr2) { if (expr1 != nullptr && expr2 != nullptr) { - Operator op1 = expr1->op(); - Operator op2 = expr2->op(); + Op op1 = expr1->op(); + Op op2 = expr2->op(); if (op1 == op2) { switch (expr1->op()) { - case FuncExpr::op_port: - return LibertyPort::less(expr1->port(), expr2->port()); - case FuncExpr::op_not: - return less(expr1->left(), expr2->left()); + case FuncExpr::Op::port: + return LibertyPort::less(expr1->port(), expr2->port()); + case FuncExpr::Op::not_: + return less(expr1->left(), expr2->left()); default: - if (equiv(expr1->left(), expr2->left())) - return less(expr1->right(), expr2->right()); - else - return less(expr1->left(), expr2->left()); + if (equiv(expr1->left(), expr2->left())) + return less(expr1->right(), expr2->right()); + else + return less(expr1->left(), expr2->left()); } } else @@ -418,4 +423,4 @@ FuncExpr::less(const FuncExpr *expr1, return (expr1 == nullptr && expr2 != nullptr); } -} // namespace +} // namespace sta diff --git a/liberty/GeneratedClock.cc b/liberty/GeneratedClock.cc index 379c1aa44..30ab4418d 100644 --- a/liberty/GeneratedClock.cc +++ b/liberty/GeneratedClock.cc @@ -14,9 +14,9 @@ GeneratedClock::GeneratedClock(const char *name, bool invert, IntSeq *edges, FloatSeq *edge_shifts) : - name_(name ? stringCopy(name) : nullptr), - clock_pin_(clock_pin ? stringCopy(clock_pin) : nullptr), - master_pin_(master_pin ? stringCopy(master_pin) : nullptr), + name_(name ), + clock_pin_(clock_pin), + master_pin_(master_pin), divided_by_(divided_by), multiplied_by_(multiplied_by), duty_cycle_(duty_cycle), @@ -28,12 +28,6 @@ GeneratedClock::GeneratedClock(const char *name, GeneratedClock::~GeneratedClock() { - if (name_) - stringDelete(name_); - if (clock_pin_) - stringDelete(clock_pin_); - if (master_pin_) - stringDelete(master_pin_); if (edges_) delete edges_; if (edge_shifts_) diff --git a/liberty/InternalPower.cc b/liberty/InternalPower.cc index a68dd1965..97960ff55 100644 --- a/liberty/InternalPower.cc +++ b/liberty/InternalPower.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,86 +24,31 @@ #include "InternalPower.hh" +#include +#include +#include + +#include "Error.hh" #include "FuncExpr.hh" +#include "LibertyClass.hh" +#include "Transition.hh" #include "TableModel.hh" #include "Liberty.hh" #include "Units.hh" namespace sta { -using std::string; - -InternalPowerAttrs::InternalPowerAttrs() : - when_(nullptr), - models_{nullptr, nullptr}, - related_pg_pin_(nullptr) -{ -} - -InternalPowerAttrs::~InternalPowerAttrs() -{ -} - -void -InternalPowerAttrs::deleteContents() -{ - InternalPowerModel *rise_model = models_[RiseFall::riseIndex()]; - InternalPowerModel *fall_model = models_[RiseFall::fallIndex()]; - delete rise_model; - if (fall_model != rise_model) - delete fall_model; - if (when_) - when_->deleteSubexprs(); - stringDelete(related_pg_pin_); -} - -InternalPowerModel * -InternalPowerAttrs::model(const RiseFall *rf) const -{ - return models_[rf->index()]; -} - -void -InternalPowerAttrs::setWhen(FuncExpr *when) -{ - when_ = when; -} - -void -InternalPowerAttrs::setModel(const RiseFall *rf, - InternalPowerModel *model) -{ - models_[rf->index()] = model; -} - -void -InternalPowerAttrs::setRelatedPgPin(const char *related_pg_pin) -{ - stringDelete(related_pg_pin_); - related_pg_pin_ = stringCopy(related_pg_pin); -} - -//////////////////////////////////////////////////////////////// - -InternalPower::InternalPower(LibertyCell *cell, - LibertyPort *port, - LibertyPort *related_port, - InternalPowerAttrs *attrs) : +InternalPower::InternalPower(LibertyPort *port, + LibertyPort *related_port, + LibertyPort *related_pg_pin, + const std::shared_ptr &when, + const InternalPowerModels &models) : port_(port), related_port_(related_port), - when_(attrs->when()), - related_pg_pin_(attrs->relatedPgPin()) + related_pg_pin_(related_pg_pin), + when_(when), + models_(models) { - for (auto rf : RiseFall::range()) { - int rf_index = rf->index(); - models_[rf_index] = attrs->model(rf); - } - cell->addInternalPower(this); -} - -InternalPower::~InternalPower() -{ - // models_, when_ and related_pg_pin_ are owned by InternalPowerAttrs. } LibertyCell * @@ -112,60 +57,63 @@ InternalPower::libertyCell() const return port_->libertyCell(); } +const InternalPowerModel & +InternalPower::model(const RiseFall *rf) const +{ + return models_[rf->index()]; +} + float InternalPower::power(const RiseFall *rf, - const Pvt *pvt, - float in_slew, - float load_cap) + const Pvt *pvt, + float in_slew, + float load_cap) const { - InternalPowerModel *model = models_[rf->index()]; - if (model) - return model->power(libertyCell(), pvt, in_slew, load_cap); - else - return 0.0; + const InternalPowerModel &model = models_[rf->index()]; + return model.power(libertyCell(), pvt, in_slew, load_cap); } //////////////////////////////////////////////////////////////// -InternalPowerModel::InternalPowerModel(TableModel *model) : - model_(model) +InternalPowerModel::InternalPowerModel() : + model_(nullptr) { } -InternalPowerModel::~InternalPowerModel() +InternalPowerModel::InternalPowerModel(std::shared_ptr model) : + model_(std::move(model)) { - delete model_; } float InternalPowerModel::power(const LibertyCell *cell, - const Pvt *pvt, - float in_slew, - float load_cap) const + const Pvt *pvt, + float in_slew, + float load_cap) const { if (model_) { float axis_value1, axis_value2, axis_value3; findAxisValues(in_slew, load_cap, - axis_value1, axis_value2, axis_value3); + axis_value1, axis_value2, axis_value3); return model_->findValue(cell, pvt, axis_value1, axis_value2, axis_value3); } else return 0.0; } -string +std::string InternalPowerModel::reportPower(const LibertyCell *cell, - const Pvt *pvt, - float in_slew, - float load_cap, - int digits) const + const Pvt *pvt, + float in_slew, + float load_cap, + int digits) const { if (model_) { float axis_value1, axis_value2, axis_value3; findAxisValues(in_slew, load_cap, - axis_value1, axis_value2, axis_value3); + axis_value1, axis_value2, axis_value3); const LibertyLibrary *library = cell->libertyLibrary(); - return model_->reportValue("Power", cell, pvt, axis_value1, nullptr, + return model_->reportValue("Power", cell, pvt, axis_value1, {}, axis_value2, axis_value3, library->units()->powerUnit(), digits); } @@ -174,11 +122,11 @@ InternalPowerModel::reportPower(const LibertyCell *cell, void InternalPowerModel::findAxisValues(float in_slew, - float load_cap, - // Return values. - float &axis_value1, - float &axis_value2, - float &axis_value3) const + float load_cap, + // Return values. + float &axis_value1, + float &axis_value2, + float &axis_value3) const { switch (model_->order()) { case 0: @@ -211,8 +159,8 @@ InternalPowerModel::findAxisValues(float in_slew, float InternalPowerModel::axisValue(const TableAxis *axis, - float in_slew, - float load_cap) const + float in_slew, + float load_cap) const { TableAxisVariable var = axis->variable(); if (var == TableAxisVariable::input_transition_time) @@ -249,4 +197,4 @@ InternalPowerModel::checkAxis(const TableAxis *axis) || var == TableAxisVariable::related_out_total_output_net_capacitance; } -} // namespace +} // namespace sta diff --git a/liberty/LeakagePower.cc b/liberty/LeakagePower.cc index 3ff8c5ac8..5a3039250 100644 --- a/liberty/LeakagePower.cc +++ b/liberty/LeakagePower.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,7 +25,6 @@ #include "LeakagePower.hh" #include "FuncExpr.hh" -#include "TableModel.hh" #include "Liberty.hh" namespace sta { @@ -41,10 +40,18 @@ LeakagePower::LeakagePower(LibertyCell *cell, { } +LeakagePower::LeakagePower(LeakagePower &&other) noexcept +{ + cell_ = other.cell_; + related_pg_port_ = other.related_pg_port_; + when_ = other.when_; + other.when_ = nullptr; + power_ = other.power_; +} + LeakagePower::~LeakagePower() { - if (when_) - when_->deleteSubexprs(); + delete when_; } -} // namespace +} // namespace sta diff --git a/liberty/LibExprLex.ll b/liberty/LibExprLex.ll index e9a3f70bf..0398225b2 100644 --- a/liberty/LibExprLex.ll +++ b/liberty/LibExprLex.ll @@ -25,14 +25,14 @@ // Liberty function expression lexical analyzer +#include + #include "util/FlexDisableRegister.hh" #include "Debug.hh" -#include "StringUtil.hh" #include "liberty/LibExprReaderPvt.hh" #include "liberty/LibExprReader.hh" #include "liberty/LibExprScanner.hh" -using sta::stringCopy; using sta::FuncExpr; #include "LibExprParse.hh" @@ -40,7 +40,7 @@ using sta::FuncExpr; #undef YY_DECL #define YY_DECL \ int \ -sta::LibExprScanner::lex(sta::LibExprParse::semantic_type *const yylval) +sta::LibExprScanner::lex(sta::LibExprParse::semantic_type *yylval) typedef sta::LibExprParse::token token; @@ -76,12 +76,12 @@ EOL \r?\n {ESCAPE}{QUOTE} { BEGIN(INITIAL); - yylval->string = stringCopy(token_.c_str()); + yylval->emplace(token_); return token::PORT; } {PORT} { - yylval->string = stringCopy(yytext); + yylval->emplace(yytext, yyleng); return token::PORT; } diff --git a/liberty/LibExprParse.yy b/liberty/LibExprParse.yy index fe8f7e763..443f0ce7d 100644 --- a/liberty/LibExprParse.yy +++ b/liberty/LibExprParse.yy @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,6 +25,8 @@ // Liberty function expression parser. %{ +#include + #include "FuncExpr.hh" #include "liberty/LibExprReader.hh" #include "liberty/LibExprReaderPvt.hh" @@ -39,7 +41,7 @@ void sta::LibExprParse::error(const std::string &msg) { - reader->parseError(msg.c_str()); + reader->parseError(msg); } %} @@ -52,21 +54,15 @@ sta::LibExprParse::error(const std::string &msg) %parse-param{LibExprScanner *scanner} %parse-param{LibExprReader *reader} %define api.parser.class {LibExprParse} +%define api.value.type variant -%union { - int int_val; - const char *string; - sta::FuncExpr *expr; -} - -%token PORT +%token PORT %left '+' '|' %left '*' '&' %left '^' %left '!' '\'' -%type PORT -%type expr terminal terminal_expr implicit_and +%type expr terminal terminal_expr implicit_and %% @@ -76,14 +72,14 @@ result_expr: ; terminal: - PORT { $$ = reader->makeFuncExprPort($1); } + PORT { $$ = reader->makeFuncExprPort(std::move($1)); } | '0' { $$ = sta::FuncExpr::makeZero(); } | '1' { $$ = sta::FuncExpr::makeOne(); } | '(' expr ')' { $$ = $2; } ; terminal_expr: - terminal + terminal { $$ = $1; } | '!' terminal { $$ = reader->makeFuncExprNot($2); } | terminal '\'' { $$ = reader->makeFuncExprNot($1); } ; @@ -96,8 +92,8 @@ implicit_and: ; expr: - terminal_expr -| implicit_and + terminal_expr { $$ = $1; } +| implicit_and { $$ = $1; } | expr '+' expr { $$ = reader->makeFuncExprOr($1, $3); } | expr '|' expr { $$ = reader->makeFuncExprOr($1, $3); } | expr '*' expr { $$ = reader->makeFuncExprAnd($1, $3); } diff --git a/liberty/LibExprReader.cc b/liberty/LibExprReader.cc index f4b873c2f..149be5b8b 100644 --- a/liberty/LibExprReader.cc +++ b/liberty/LibExprReader.cc @@ -1,34 +1,34 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "FuncExpr.hh" -#include #include +#include +#include #include "Report.hh" -#include "StringUtil.hh" #include "Liberty.hh" #include "LibExprReaderPvt.hh" #include "LibExprScanner.hh" @@ -36,14 +36,14 @@ namespace sta { FuncExpr * -parseFuncExpr(const char *func, - LibertyCell *cell, - const char *error_msg, - Report *report) +parseFuncExpr(std::string_view func, + const LibertyCell *cell, + std::string_view error_msg, + Report *report) { - if (func != nullptr && func[0] != '\0') { - std::string func1(func); - std::istringstream stream(func); + if (!func.empty()) { + std::string func_str(func); + std::istringstream stream(func_str); LibExprReader reader(func, cell, error_msg, report); LibExprScanner scanner(stream); LibExprParse parser(&scanner, &reader); @@ -55,34 +55,32 @@ parseFuncExpr(const char *func, return nullptr; } -LibExprReader::LibExprReader(const char *func, - LibertyCell *cell, - const char *error_msg, - Report *report) : +LibExprReader::LibExprReader(std::string_view func, + const LibertyCell *cell, + std::string_view error_msg, + Report *report) : func_(func), cell_(cell), error_msg_(error_msg), - report_(report), - result_(nullptr) + report_(report) { } // defined in LibertyReader.cc LibertyPort * -libertyReaderFindPort(LibertyCell *cell, - const char *port_name); +libertyReaderFindPort(const LibertyCell *cell, + std::string_view port_name); FuncExpr * -LibExprReader::makeFuncExprPort(const char *port_name) +LibExprReader::makeFuncExprPort(std::string &&port_name) { FuncExpr *expr = nullptr; - LibertyPort *port = libertyReaderFindPort(cell_, port_name); + const std::string_view port_view(port_name); + LibertyPort *port = libertyReaderFindPort(cell_, port_view); if (port) expr = FuncExpr::makePort(port); else - report_->warn(1130, "%s references unknown port %s.", - error_msg_, port_name); - stringDelete(port_name); + report_->warn(1130, "{} references unknown port {}.", error_msg_, port_view); return expr; } @@ -97,7 +95,7 @@ LibExprReader::makeFuncExprNot(FuncExpr *arg) FuncExpr * LibExprReader::makeFuncExprXor(FuncExpr *arg1, - FuncExpr *arg2) + FuncExpr *arg2) { if (arg1 && arg2) return FuncExpr::makeXor(arg1, arg2); @@ -107,7 +105,7 @@ LibExprReader::makeFuncExprXor(FuncExpr *arg1, FuncExpr * LibExprReader::makeFuncExprAnd(FuncExpr *arg1, - FuncExpr *arg2) + FuncExpr *arg2) { if (arg1 && arg2) return FuncExpr::makeAnd(arg1, arg2); @@ -117,7 +115,7 @@ LibExprReader::makeFuncExprAnd(FuncExpr *arg1, FuncExpr * LibExprReader::makeFuncExprOr(FuncExpr *arg1, - FuncExpr *arg2) + FuncExpr *arg2) { if (arg1 && arg2) return FuncExpr::makeOr(arg1, arg2); @@ -132,9 +130,9 @@ LibExprReader::setResult(FuncExpr *result) } void -LibExprReader::parseError(const char *msg) +LibExprReader::parseError(std::string_view msg) { - report_->error(1131, "%s %s.", error_msg_, msg); + report_->error(1131, "{} {}.", error_msg_, msg); } //////////////////////////////////////////////////////////////// @@ -144,4 +142,4 @@ LibExprScanner::LibExprScanner(std::istringstream &stream) : { } -} // namespace +} // namespace sta diff --git a/liberty/LibExprReader.hh b/liberty/LibExprReader.hh index 3e15ac351..542992989 100644 --- a/liberty/LibExprReader.hh +++ b/liberty/LibExprReader.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,8 @@ #pragma once +#include + namespace sta { class Report; @@ -31,9 +33,9 @@ class FuncExpr; class LibertyCell; FuncExpr * -parseFuncExpr(const char *func, - LibertyCell *cell, - const char *error_msg, - Report *report); +parseFuncExpr(std::string_view func, + const LibertyCell *cell, + std::string_view error_msg, + Report *report); -} // namespace +} // namespace sta diff --git a/liberty/LibExprReaderPvt.hh b/liberty/LibExprReaderPvt.hh index 78cf21b61..9ef029e26 100644 --- a/liberty/LibExprReaderPvt.hh +++ b/liberty/LibExprReaderPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,9 @@ #pragma once +#include +#include + namespace sta { class Report; @@ -34,31 +37,29 @@ class LibExprScanner; class LibExprReader { public: - LibExprReader(const char *func, - LibertyCell *cell, - const char *error_msg, - Report *report); - FuncExpr *makeFuncExprPort(const char *port_name); + LibExprReader(std::string_view func, + const LibertyCell *cell, + std::string_view error_msg, + Report *report); + FuncExpr *makeFuncExprPort(std::string &&port_name); FuncExpr *makeFuncExprOr(FuncExpr *arg1, - FuncExpr *arg2); + FuncExpr *arg2); FuncExpr *makeFuncExprAnd(FuncExpr *arg1, - FuncExpr *arg2); + FuncExpr *arg2); FuncExpr *makeFuncExprXor(FuncExpr *arg1, - FuncExpr *arg2); + FuncExpr *arg2); FuncExpr *makeFuncExprNot(FuncExpr *arg); void setResult(FuncExpr *result); FuncExpr *result() { return result_; } - void parseError(const char *msg); - size_t copyInput(char *buf, - size_t max_size); + void parseError(std::string_view msg); Report *report() const { return report_; } private: - const char *func_; - LibertyCell *cell_; - const char *error_msg_; + std::string_view func_; + const LibertyCell *cell_; + std::string_view error_msg_; Report *report_; - FuncExpr *result_; + FuncExpr *result_{nullptr}; }; -} // namespace +} // namespace sta diff --git a/liberty/LibExprScanner.hh b/liberty/LibExprScanner.hh index f3ec9cf16..cb0cb0b48 100644 --- a/liberty/LibExprScanner.hh +++ b/liberty/LibExprScanner.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -43,9 +43,7 @@ class LibExprScanner : public LibExprFlexLexer { public: LibExprScanner(std::istringstream &stream); - virtual ~LibExprScanner() {} - - virtual int lex(LibExprParse::semantic_type *const yylval); + virtual int lex(LibExprParse::semantic_type *yylval); // YY_DECL defined in LibertyLex.ll // Method body created by flex in LibertyLex.cc @@ -57,4 +55,4 @@ private: std::string token_; }; -} // namespace +} // namespace sta diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 4be7cf8bb..bf4a8a212 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,37 +24,43 @@ #include "Liberty.hh" -#include "Mutex.hh" -#include "EnumNameMap.hh" -#include "Report.hh" +#include +#include +#include +#include +#include +#include + +#include "ConcreteLibrary.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" +#include "Delay.hh" +#include "EnumNameMap.hh" #include "Error.hh" -#include "StringUtil.hh" -#include "StringSet.hh" -#include "PatternMatch.hh" -#include "Units.hh" -#include "Transition.hh" -#include "TimingRole.hh" +#include "Format.hh" #include "FuncExpr.hh" -#include "TableModel.hh" -#include "TimingArc.hh" -#include "InternalPower.hh" -#include "LeakagePower.hh" -#include "Sequential.hh" #include "GeneratedClock.hh" -#include "Wireload.hh" -#include "EquivCells.hh" +#include "InternalPower.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "Mutex.hh" #include "Network.hh" +#include "NetworkClass.hh" +#include "ObjectId.hh" +#include "PatternMatch.hh" #include "PortDirection.hh" -#include "Corner.hh" -#include "DcalcAnalysisPt.hh" +#include "Report.hh" +#include "Scene.hh" +#include "Sequential.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Transition.hh" +#include "Units.hh" +#include "Wireload.hh" namespace sta { -using std::string; - -typedef Set LatchEnableSet; - void initLiberty() { @@ -67,81 +73,33 @@ deleteLiberty() TimingArcSet::destroy(); } -LibertyLibrary::LibertyLibrary(const char *name, - const char *filename) : +LibertyLibrary::LibertyLibrary(std::string_view name, + std::string_view filename) : ConcreteLibrary(name, filename, true), - units_(new Units()), - delay_model_type_(DelayModelType::table), // default - nominal_process_(0.0), - nominal_voltage_(0.0), - nominal_temperature_(0.0), - scale_factors_(nullptr), - default_input_pin_cap_(0.0), - default_output_pin_cap_(0.0), - default_bidirect_pin_cap_(0.0), - default_fanout_load_(0.0), - default_fanout_load_exists_(false), - default_max_cap_(0.0), - default_max_cap_exists_(false), - default_max_fanout_(0.0), - default_max_fanout_exists_(false), - default_max_slew_(0.0), - default_max_slew_exists_(false), - slew_derate_from_library_(1.0), - default_wire_load_(nullptr), - default_wire_load_mode_(WireloadMode::unknown), - default_wire_load_selection_(nullptr), - default_operating_conditions_(nullptr), - ocv_arc_depth_(0.0), - default_ocv_derate_(nullptr), - buffers_(nullptr), - inverters_(nullptr), - driver_waveform_default_(nullptr) + units_(new Units()) { // Scalar templates are builtin. for (int i = 0; i != table_template_type_count; i++) { TableTemplateType type = static_cast(i); - TableTemplate *scalar_template = new TableTemplate("scalar", nullptr, - nullptr, nullptr); - addTableTemplate(scalar_template, type); - } - - for (auto rf_index : RiseFall::rangeIndex()) { - wire_slew_degradation_tbls_[rf_index] = nullptr; - input_threshold_[rf_index] = input_threshold_default_; - output_threshold_[rf_index] = output_threshold_default_; - slew_lower_threshold_[rf_index] = slew_lower_threshold_default_; - slew_upper_threshold_[rf_index] = slew_upper_threshold_default_; + makeTableTemplate("scalar", type); } } LibertyLibrary::~LibertyLibrary() { - bus_dcls_.deleteContents(); - for (int i = 0; i < table_template_type_count; i++) - template_maps_[i].deleteContents(); - scale_factors_map_.deleteContents(); - delete scale_factors_; - for (auto rf_index : RiseFall::rangeIndex()) { TableModel *model = wire_slew_degradation_tbls_[rf_index]; delete model; } - operating_conditions_.deleteContents(); - wireloads_.deleteContents(); - wire_load_selections_.deleteContents(); delete units_; - // Also deletes default_ocv_derate_ - ocv_derate_map_.deleteContents(); + // default_ocv_derate_ points into ocv_derate_map_; no separate delete delete buffers_; delete inverters_; - driver_waveform_map_.deleteContents(); - delete driver_waveform_default_; } LibertyCell * -LibertyLibrary::findLibertyCell(const char *name) const +LibertyLibrary::findLibertyCell(std::string_view name) const { return static_cast(findCell(name)); } @@ -184,8 +142,8 @@ LibertyLibrary::buffers() while (cell_iter.hasNext()) { LibertyCell *cell = cell_iter.next(); if (!cell->dontUse() - && cell->isBuffer()) - buffers_->push_back(cell); + && cell->isBuffer()) + buffers_->push_back(cell); } } return buffers_; @@ -197,52 +155,69 @@ LibertyLibrary::setDelayModelType(DelayModelType type) delay_model_type_ = type; } -void -LibertyLibrary::addBusDcl(BusDcl *bus_dcl) +BusDcl * +LibertyLibrary::makeBusDcl(std::string_view name, + int from, + int to) { - bus_dcls_[bus_dcl->name()] = bus_dcl; + std::string key(name); + auto [it, inserted] = bus_dcls_.try_emplace(std::move(key), std::string(name), from, to); + return &it->second; } BusDcl * -LibertyLibrary::findBusDcl(const char *name) const +LibertyLibrary::findBusDcl(std::string_view name) { - return bus_dcls_.findKey(name); + return findStringValuePtr(bus_dcls_, name); } BusDclSeq LibertyLibrary::busDcls() const { BusDclSeq dcls; - for (auto [name, dcl] : bus_dcls_) - dcls.push_back(dcl); + for (auto &[key, dcl] : bus_dcls_) + dcls.push_back(const_cast(&dcl)); return dcls; } -void -LibertyLibrary::addTableTemplate(TableTemplate *tbl_template, - TableTemplateType type) +TableTemplate * +LibertyLibrary::makeTableTemplate(std::string_view name, + TableTemplateType type) { - template_maps_[int(type)][tbl_template->name()] = tbl_template; + std::string key(name); + auto [it, inserted] = template_maps_[static_cast(type)].try_emplace(std::move(key), + std::string(name), + type); + return &it->second; } TableTemplate * -LibertyLibrary::findTableTemplate(const char *name, - TableTemplateType type) +LibertyLibrary::findTableTemplate(std::string_view name, + TableTemplateType type) { - return template_maps_[int(type)].findKey(name); + return findStringValuePtr(template_maps_[static_cast(type)], name); } TableTemplateSeq LibertyLibrary::tableTemplates() const { TableTemplateSeq tbl_templates; - for (int type = 0; type < table_template_type_count; type++) { - for (auto [name, tbl_template] : template_maps_[type]) - tbl_templates.push_back(tbl_template); + for (const auto & template_map : template_maps_) { + for (auto &[key, tbl_template] : template_map) + tbl_templates.push_back(const_cast(&tbl_template)); } return tbl_templates; } +TableTemplateSeq +LibertyLibrary::tableTemplates(TableTemplateType type) const +{ + TableTemplateSeq tbl_templates; + for (auto &[key, tbl_template] : template_maps_[static_cast(type)]) + tbl_templates.push_back(const_cast(&tbl_template)); + return tbl_templates; +} + void LibertyLibrary::setNominalProcess(float process) { @@ -267,38 +242,40 @@ LibertyLibrary::setScaleFactors(ScaleFactors *scales) scale_factors_ = scales; } -void -LibertyLibrary::addScaleFactors(ScaleFactors *scales) +ScaleFactors * +LibertyLibrary::makeScaleFactors(std::string_view name) { - scale_factors_map_[scales->name()] = scales; + std::string key(name); + auto [it, inserted] = scale_factors_map_.emplace(std::move(key), std::string(name)); + return &it->second; } ScaleFactors * -LibertyLibrary::findScaleFactors(const char *name) +LibertyLibrary::findScaleFactors(std::string_view name) { - return scale_factors_map_[name]; + return findStringValuePtr(scale_factors_map_,name); } float LibertyLibrary::scaleFactor(ScaleFactorType type, - const Pvt *pvt) const + const Pvt *pvt) const { return scaleFactor(type, 0, nullptr, pvt); } float LibertyLibrary::scaleFactor(ScaleFactorType type, - const LibertyCell *cell, - const Pvt *pvt) const + const LibertyCell *cell, + const Pvt *pvt) const { return scaleFactor(type, 0, cell, pvt); } float LibertyLibrary::scaleFactor(ScaleFactorType type, - int rf_index, - const LibertyCell *cell, - const Pvt *pvt) const + int rf_index, + const LibertyCell *cell, + const Pvt *pvt) const { if (pvt == nullptr) pvt = default_operating_conditions_; @@ -313,11 +290,11 @@ LibertyLibrary::scaleFactor(ScaleFactorType type, scale_factors = scale_factors_; if (scale_factors) { float process_scale = 1.0F + (pvt->process() - nominal_process_) - * scale_factors->scale(type, ScaleFactorPvt::process, rf_index); + * scale_factors->scale(type, ScaleFactorPvt::process, rf_index); float temp_scale = 1.0F + (pvt->temperature() - nominal_temperature_) - * scale_factors->scale(type, ScaleFactorPvt::temp, rf_index); + * scale_factors->scale(type, ScaleFactorPvt::temp, rf_index); float volt_scale = 1.0F + (pvt->voltage() - nominal_voltage_) - * scale_factors->scale(type, ScaleFactorPvt::volt, rf_index); + * scale_factors->scale(type, ScaleFactorPvt::volt, rf_index); float scale = process_scale * temp_scale * volt_scale; return scale; } @@ -327,7 +304,7 @@ LibertyLibrary::scaleFactor(ScaleFactorType type, void LibertyLibrary::setWireSlewDegradationTable(TableModel *model, - const RiseFall *rf) + const RiseFall *rf) { int rf_index = rf->index(); if (wire_slew_degradation_tbls_[rf_index]) @@ -343,8 +320,8 @@ LibertyLibrary::wireSlewDegradationTable(const RiseFall *rf) const float LibertyLibrary::degradeWireSlew(const RiseFall *rf, - float in_slew, - float wire_delay) const + float in_slew, + float wire_delay) const { const TableModel *model = wireSlewDegradationTable(rf); if (model) @@ -355,8 +332,8 @@ LibertyLibrary::degradeWireSlew(const RiseFall *rf, float LibertyLibrary::degradeWireSlew(const TableModel *model, - float in_slew, - float wire_delay) const + float in_slew, + float wire_delay) const { switch (model->order()) { case 0: @@ -379,10 +356,10 @@ LibertyLibrary::degradeWireSlew(const TableModel *model, TableAxisVariable var1 = axis1->variable(); TableAxisVariable var2 = axis2->variable(); if (var1 == TableAxisVariable::output_pin_transition - && var2 == TableAxisVariable::connect_delay) + && var2 == TableAxisVariable::connect_delay) return model->findValue(in_slew, wire_delay, 0.0); else if (var1 == TableAxisVariable::connect_delay - && var2 == TableAxisVariable::output_pin_transition) + && var2 == TableAxisVariable::output_pin_transition) return model->findValue(wire_delay, in_slew, 0.0); else { criticalError(1117, "unsupported slew degradation table axes"); @@ -398,26 +375,26 @@ LibertyLibrary::degradeWireSlew(const TableModel *model, // Check for supported axis variables. // Return true if axes are supported. bool -LibertyLibrary::checkSlewDegradationAxes(const TablePtr &table) +LibertyLibrary::checkSlewDegradationAxes(const TableModel *table_model) { - switch (table->order()) { + switch (table_model->order()) { case 0: return true; case 1: { - const TableAxis *axis1 = table->axis1(); + const TableAxis *axis1 = table_model->axis1(); TableAxisVariable var1 = axis1->variable(); return var1 == TableAxisVariable::output_pin_transition || var1 == TableAxisVariable::connect_delay; } case 2: { - const TableAxis *axis1 = table->axis1(); - const TableAxis *axis2 = table->axis2(); + const TableAxis *axis1 = table_model->axis1(); + const TableAxis *axis2 = table_model->axis2(); TableAxisVariable var1 = axis1->variable(); TableAxisVariable var2 = axis2->variable(); return (var1 == TableAxisVariable::output_pin_transition - && var2 == TableAxisVariable::connect_delay) + && var2 == TableAxisVariable::connect_delay) || (var1 == TableAxisVariable::connect_delay - && var2 == TableAxisVariable::output_pin_transition); + && var2 == TableAxisVariable::output_pin_transition); } default: criticalError(1119, "unsupported slew degradation table axes"); @@ -427,7 +404,7 @@ LibertyLibrary::checkSlewDegradationAxes(const TablePtr &table) void LibertyLibrary::defaultMaxFanout(float &fanout, - bool &exists) const + bool &exists) const { fanout = default_max_fanout_; exists = default_max_fanout_exists_; @@ -442,7 +419,7 @@ LibertyLibrary::setDefaultMaxFanout(float fanout) void LibertyLibrary::defaultMaxSlew(float &slew, - bool &exists) const + bool &exists) const { slew = default_max_slew_; exists = default_max_slew_exists_; @@ -457,7 +434,7 @@ LibertyLibrary::setDefaultMaxSlew(float slew) void LibertyLibrary::defaultMaxCapacitance(float &cap, - bool &exists) const + bool &exists) const { cap = default_max_cap_; exists = default_max_cap_exists_; @@ -472,8 +449,8 @@ LibertyLibrary::setDefaultMaxCapacitance(float cap) void LibertyLibrary::defaultFanoutLoad(// Return values. - float &fanout, - bool &exists) const + float &fanout, + bool &exists) const { fanout = default_fanout_load_; exists = default_fanout_load_exists_; @@ -506,26 +483,26 @@ LibertyLibrary::setDefaultOutputPinCap(float cap) void LibertyLibrary::defaultIntrinsic(const RiseFall *rf, - // Return values. - float &intrinsic, - bool &exists) const + // Return values. + float &intrinsic, + bool &exists) const { default_intrinsic_.value(rf, intrinsic, exists); } void LibertyLibrary::setDefaultIntrinsic(const RiseFall *rf, - float value) + float value) { default_intrinsic_.setValue(rf, value); } void LibertyLibrary::defaultPinResistance(const RiseFall *rf, - const PortDirection *dir, - // Return values. - float &res, - bool &exists) const + const PortDirection *dir, + // Return values. + float &res, + bool &exists) const { if (dir->isAnyTristate()) defaultBidirectPinRes(rf, res, exists); @@ -535,80 +512,85 @@ LibertyLibrary::defaultPinResistance(const RiseFall *rf, void LibertyLibrary::defaultBidirectPinRes(const RiseFall *rf, - // Return values. - float &res, - bool &exists) const + // Return values. + float &res, + bool &exists) const { - return default_inout_pin_res_.value(rf, res, exists); + default_inout_pin_res_.value(rf, res, exists); } void LibertyLibrary::setDefaultBidirectPinRes(const RiseFall *rf, - float value) + float value) { default_inout_pin_res_.setValue(rf, value); } void LibertyLibrary::defaultOutputPinRes(const RiseFall *rf, - // Return values. - float &res, - bool &exists) const + // Return values. + float &res, + bool &exists) const { default_output_pin_res_.value(rf, res, exists); } void LibertyLibrary::setDefaultOutputPinRes(const RiseFall *rf, - float value) + float value) { default_output_pin_res_.setValue(rf, value); } -void -LibertyLibrary::addWireload(Wireload *wireload) +Wireload * +LibertyLibrary::makeWireload(std::string_view name) { - wireloads_[wireload->name()] = wireload; + std::string key(name); + auto [it, inserted] = wireloads_.try_emplace(std::move(key), std::string(name), this); + return &it->second; } -Wireload * -LibertyLibrary::findWireload(const char *name) const +const Wireload * +LibertyLibrary::findWireload(std::string_view name) { - return wireloads_.findKey(name); + return findStringValuePtr(wireloads_, name); } void -LibertyLibrary::setDefaultWireload(Wireload *wireload) +LibertyLibrary::setDefaultWireload(const Wireload *wireload) { default_wire_load_ = wireload; } -Wireload * +const Wireload * LibertyLibrary::defaultWireload() const { return default_wire_load_; } -void -LibertyLibrary::addWireloadSelection(WireloadSelection *selection) +WireloadSelection * +LibertyLibrary::makeWireloadSelection(std::string_view name) { - wire_load_selections_[selection->name()] = selection; + std::string key(name); + auto [it, inserted] = wire_load_selections_.try_emplace(std::move(key), + std::string(name)); + return &it->second; } -WireloadSelection * -LibertyLibrary::findWireloadSelection(const char *name) const +const WireloadSelection * +LibertyLibrary::findWireloadSelection(std::string_view name) const { - return wire_load_selections_.findKey(name); + return findStringValuePtr(wire_load_selections_, name); } -WireloadSelection * +const WireloadSelection * LibertyLibrary::defaultWireloadSelection() const { return default_wire_load_selection_; } void -LibertyLibrary::setDefaultWireloadSelection(WireloadSelection *selection) +LibertyLibrary::setDefaultWireloadSelection(const WireloadSelection *selection) { default_wire_load_selection_ = selection; } @@ -625,16 +607,18 @@ LibertyLibrary::setDefaultWireloadMode(WireloadMode mode) default_wire_load_mode_ = mode; } -void -LibertyLibrary::addOperatingConditions(OperatingConditions *op_cond) +OperatingConditions * +LibertyLibrary::makeOperatingConditions(std::string_view name) { - operating_conditions_[op_cond->name()] = op_cond; + std::string key(name); + auto [it, inserted] = operating_conditions_.try_emplace(std::move(key), std::string(name)); + return &it->second; } OperatingConditions * -LibertyLibrary::findOperatingConditions(const char *name) +LibertyLibrary::findOperatingConditions(std::string_view name) { - return operating_conditions_.findKey(name); + return findStringValuePtr(operating_conditions_, name); } OperatingConditions * @@ -657,7 +641,7 @@ LibertyLibrary::inputThreshold(const RiseFall *rf) const void LibertyLibrary::setInputThreshold(const RiseFall *rf, - float th) + float th) { input_threshold_[rf->index()] = th; } @@ -670,7 +654,7 @@ LibertyLibrary::outputThreshold(const RiseFall *rf) const void LibertyLibrary::setOutputThreshold(const RiseFall *rf, - float th) + float th) { output_threshold_[rf->index()] = th; } @@ -683,7 +667,7 @@ LibertyLibrary::slewLowerThreshold(const RiseFall *rf) const void LibertyLibrary::setSlewLowerThreshold(const RiseFall *rf, - float th) + float th) { slew_lower_threshold_[rf->index()] = th; } @@ -696,7 +680,7 @@ LibertyLibrary::slewUpperThreshold(const RiseFall *rf) const void LibertyLibrary::setSlewUpperThreshold(const RiseFall *rf, - float th) + float th) { slew_upper_threshold_[rf->index()] = th; } @@ -714,112 +698,107 @@ LibertyLibrary::setSlewDerateFromLibrary(float derate) } LibertyCell * -LibertyLibrary::makeScaledCell(const char *name, - const char *filename) +LibertyLibrary::makeScaledCell(std::string_view name, + std::string_view filename) { - LibertyCell *cell = new LibertyCell(this, name, filename); - return cell; + return new LibertyCell(this, name, filename); } //////////////////////////////////////////////////////////////// void -LibertyLibrary::makeCornerMap(LibertyLibrary *lib, - int ap_index, - Network *network, - Report *report) +LibertyLibrary::makeSceneMap(LibertyLibrary *lib, + Scene *scene, + const MinMaxAll *min_max, + Network *network, + Report *report) { LibertyCellIterator cell_iter(lib); while (cell_iter.hasNext()) { LibertyCell *cell = cell_iter.next(); - const char *name = cell->name(); - LibertyCell *link_cell = network->findLibertyCell(name); + LibertyCell *link_cell = network->findLibertyCell(cell->name()); if (link_cell) - makeCornerMap(link_cell, cell, ap_index, report); + makeSceneMap(link_cell, cell, scene, min_max, report); } } // Map a cell linked in the network to the corresponding liberty cell -// to use for delay calculation at a corner. +// to use for delay calculation at a scene. void -LibertyLibrary::makeCornerMap(LibertyCell *link_cell, - LibertyCell *corner_cell, - int ap_index, - Report *report) +LibertyLibrary::makeSceneMap(LibertyCell *link_cell, + LibertyCell *scene_cell, + Scene *scene, + const MinMaxAll *min_max, + Report *report) { - link_cell->setCornerCell(corner_cell, ap_index); - makeCornerMap(link_cell, corner_cell, true, ap_index, report); - // Check for brain damage in the other direction. - makeCornerMap(corner_cell, link_cell, false, ap_index, report); -} + for (const MinMax *mm : min_max->range()) { + size_t lib_ap_index = scene->libertyIndex(mm); + link_cell->setSceneCell(scene_cell, lib_ap_index); + } -void -LibertyLibrary::makeCornerMap(LibertyCell *cell1, - LibertyCell *cell2, - bool link, - int ap_index, - Report *report) -{ - LibertyCellPortBitIterator port_iter1(cell1); + LibertyCellPortBitIterator port_iter1(link_cell); while (port_iter1.hasNext()) { LibertyPort *port1 = port_iter1.next(); - const char *port_name = port1->name(); - LibertyPort *port2 = cell2->findLibertyPort(port_name); + LibertyPort *port2 = scene_cell->findLibertyPort(port1->name()); if (port2) { - if (link) - port1->setCornerPort(port2, ap_index); + for (const MinMax *mm : min_max->range()) { + size_t lib_ap_index = scene->libertyIndex(mm); + port1->setScenePort(port2, lib_ap_index); + } } else - report->warn(1110, "cell %s/%s port %s not found in cell %s/%s.", - cell1->library()->name(), - cell1->name(), - port_name, - cell2->library()->name(), - cell2->name()); + report->warn(1110, "cell {}/{} port {} not found in cell {}/{}.", + link_cell->library()->name(), + link_cell->name(), + port1->name(), + scene_cell->library()->name(), + scene_cell->name()); } - for (TimingArcSet *arc_set1 : cell1->timing_arc_sets_) { - TimingArcSet *arc_set2 = cell2->findTimingArcSet(arc_set1); + for (TimingArcSet *arc_set1 : link_cell->timing_arc_sets_) { + TimingArcSet *arc_set2 = scene_cell->findTimingArcSet(arc_set1); if (arc_set2) { - if (link) { - const TimingArcSeq &arcs1 = arc_set1->arcs(); - const TimingArcSeq &arcs2 = arc_set2->arcs(); - auto arc_itr1 = arcs1.begin(), arc_itr2 = arcs2.begin(); - for (; - arc_itr1 != arcs1.end() && arc_itr2 != arcs2.end(); - arc_itr1++, arc_itr2++) { - TimingArc *arc1 = *arc_itr1; - TimingArc *arc2 = *arc_itr2; - if (TimingArc::equiv(arc1, arc2)) - arc1->setCornerArc(arc2, ap_index); - } + const TimingArcSeq &arcs1 = arc_set1->arcs(); + const TimingArcSeq &arcs2 = arc_set2->arcs(); + auto arc_itr1 = arcs1.begin(), arc_itr2 = arcs2.begin(); + for (; + arc_itr1 != arcs1.end() && arc_itr2 != arcs2.end(); + arc_itr1++, arc_itr2++) { + TimingArc *arc1 = *arc_itr1; + TimingArc *arc2 = *arc_itr2; + if (TimingArc::equiv(arc1, arc2)) { + for (const MinMax *mm : min_max->range()) { + size_t lib_ap_index = scene->libertyIndex(mm); + arc1->setSceneArc(arc2, lib_ap_index); + } + } } } else - report->warn(1111, "cell %s/%s %s -> %s timing group %s not found in cell %s/%s.", - cell1->library()->name(), - cell1->name(), - arc_set1->from() ? arc_set1->from()->name() : "", - arc_set1->to()->name(), - arc_set1->role()->to_string().c_str(), - cell2->library()->name(), - cell2->name()); + report->warn(1111, "cell {}/{} {} -> {} timing group {} not found in cell {}/{}.", + link_cell->library()->name(), + link_cell->name(), + arc_set1->from() ? arc_set1->from()->name() : "", + arc_set1->to()->name(), + arc_set1->role()->to_string(), + scene_cell->library()->name(), + scene_cell->name()); } } void -LibertyLibrary::checkCorners(LibertyCell *cell, - Corners *corners, +LibertyLibrary::checkScenes(LibertyCell *cell, + const SceneSeq &scenes, Report *report) { - for (const Corner *corner : *corners) { + for (const Scene *scene : scenes) { for (auto min_max : MinMax::range()) { - if (!cell->checkCornerCell(corner, min_max)) - report->error(1112, "Liberty cell %s/%s for corner %s/%s not found.", + if (!cell->checkSceneCell(scene, min_max)) + report->error(1112, "Liberty cell {}/{} for corner {}/{} not found.", cell->libertyLibrary()->name(), cell->name(), - corner->name(), - min_max->to_string().c_str()); + scene->name(), + min_max->to_string()); } } } @@ -851,54 +830,64 @@ LibertyLibrary::setDefaultOcvDerate(OcvDerate *derate) } OcvDerate * -LibertyLibrary::findOcvDerate(const char *derate_name) +LibertyLibrary::makeOcvDerate(std::string_view name) { - return ocv_derate_map_.findKey(derate_name); + std::string key(name); + auto [it, inserted] = ocv_derate_map_.try_emplace(std::move(key), std::string(name)); + return &it->second; } -void -LibertyLibrary::addOcvDerate(OcvDerate *derate) +OcvDerate * +LibertyLibrary::findOcvDerate(std::string_view derate_name) { - ocv_derate_map_[derate->name()] = derate; + return findStringValuePtr(ocv_derate_map_, derate_name); } void -LibertyLibrary::addSupplyVoltage(const char *supply_name, - float voltage) +LibertyLibrary::addSupplyVoltage(std::string_view supply_name, + float voltage) { - supply_voltage_map_[supply_name] = voltage; + supply_voltage_map_[std::string(supply_name)] = voltage; } void -LibertyLibrary::supplyVoltage(const char *supply_name, - // Return value. - float &voltage, - bool &exists) const +LibertyLibrary::supplyVoltage(std::string_view supply_name, + // Return value. + float &voltage, + bool &exists) const { - supply_voltage_map_.findKey(supply_name, voltage, exists); + auto itr = supply_voltage_map_.find(supply_name); + if (itr != supply_voltage_map_.end()) { + voltage = itr->second; + exists = true; + } + else { + voltage = 0.0; + exists = false; + } } bool -LibertyLibrary::supplyExists(const char *supply_name) const +LibertyLibrary::supplyExists(std::string_view supply_name) const { - return supply_voltage_map_.hasKey(supply_name); + return supply_voltage_map_.contains(supply_name); } DriverWaveform * -LibertyLibrary::findDriverWaveform(const char *name) +LibertyLibrary::findDriverWaveform(std::string_view name) { - return driver_waveform_map_[name]; + return findStringValuePtr(driver_waveform_map_, name); } -void -LibertyLibrary::addDriverWaveform(DriverWaveform *driver_waveform) +DriverWaveform * +LibertyLibrary::makeDriverWaveform(std::string_view name, + const TablePtr &waveforms) { - if (driver_waveform->name()) - driver_waveform_map_[driver_waveform->name()] = driver_waveform; - else { - delete driver_waveform_default_; - driver_waveform_default_ = driver_waveform; - } + std::string key(name); + auto [it, inserted] = driver_waveform_map_.try_emplace(std::move(key), + std::string(name), + waveforms); + return &it->second; } //////////////////////////////////////////////////////////////// @@ -923,66 +912,26 @@ LibertyCellIterator::next() //////////////////////////////////////////////////////////////// LibertyCell::LibertyCell(LibertyLibrary *library, - const char *name, - const char *filename) : + std::string_view name, + std::string_view filename) : ConcreteCell(name, filename, true, library), - liberty_library_(library), - area_(0.0), - dont_use_(false), - is_macro_(false), - is_memory_(false), - is_pad_(false), - is_clock_cell_(false), - is_level_shifter_(false), - level_shifter_type_(LevelShifterType::HL_LH), - is_isolation_cell_(false), - always_on_(false), - switch_cell_type_(SwitchCellType::fine_grain), - interface_timing_(false), - clock_gate_type_(ClockGateType::none), - has_clk_gate_clk_pin_(false), - has_clk_gate_enable_pin_(false), - has_infered_reg_timing_arcs_(false), - statetable_(nullptr), - scale_factors_(nullptr), - test_cell_(nullptr), - ocv_arc_depth_(0.0), - ocv_derate_(nullptr), - is_disabled_constraint_(false), - leakage_power_(0.0), - leakage_power_exists_(false), - has_internal_ports_(false), - have_voltage_waveforms_(false) + liberty_library_(library) { liberty_cell_ = this; } LibertyCell::~LibertyCell() { - mode_defs_.deleteContents(); - latch_enables_.deleteContents(); + deleteContents(timing_arc_sets_); - timing_arc_sets_.deleteContents(); - port_timing_arc_set_map_.deleteContents(); - timing_arc_set_from_map_.deleteContents(); - timing_arc_set_to_map_.deleteContents(); - - deleteInternalPowerAttrs(); - internal_powers_.deleteContents(); - leakage_powers_.deleteContents(); - - sequentials_.deleteContents(); - generated_clocks_.deleteContents(); delete statetable_; - bus_dcls_.deleteContents(); - scaled_cells_.deleteContents(); + deleteContents(scaled_cells_); delete test_cell_; - ocv_derate_map_.deleteContents(); } LibertyPort * -LibertyCell::findLibertyPort(const char *name) const +LibertyCell::findLibertyPort(std::string_view name) const { return static_cast(findPort(name)); } @@ -999,9 +948,9 @@ LibertyCell::findLibertyPortsMatching(PatternMatch *pattern) const if (port->hasMembers()) { LibertyPortMemberIterator port_iter2(port); while (port_iter2.hasNext()) { - LibertyPort *port2 = port_iter2.next(); - if (pattern->match(port2->name())) - matches.push_back(port2); + LibertyPort *port2 = port_iter2.next(); + if (pattern->match(port2->name())) + matches.push_back(port2); } } } @@ -1024,17 +973,17 @@ LibertyCell::setHasInternalPorts(bool has_internal) } ModeDef * -LibertyCell::makeModeDef(const char *name) +LibertyCell::makeModeDef(std::string_view name) { - ModeDef *mode = new ModeDef(name); - mode_defs_[mode->name()] = mode; - return mode; + std::string key(name); + auto [it, inserted] = mode_defs_.try_emplace(std::move(key), std::string(name)); + return &it->second; } -ModeDef * -LibertyCell::findModeDef(const char *name) +const ModeDef * +LibertyCell::findModeDef(std::string_view name) const { - return mode_defs_.findKey(name); + return findStringValuePtr(mode_defs_, name); } void @@ -1043,16 +992,20 @@ LibertyCell::setScaleFactors(ScaleFactors *scale_factors) scale_factors_ = scale_factors; } -void -LibertyCell::addBusDcl(BusDcl *bus_dcl) +BusDcl * +LibertyCell::makeBusDcl(std::string_view name, + int from, + int to) { - bus_dcls_[bus_dcl->name()] = bus_dcl; + std::string key(name); + auto [it, inserted] = bus_dcls_.try_emplace(std::move(key), std::string(name), from, to); + return &it->second; } BusDcl * -LibertyCell::findBusDcl(const char *name) const +LibertyCell::findBusDcl(std::string_view name) { - return bus_dcls_.findKey(name); + return findStringValuePtr(bus_dcls_, name); } void @@ -1200,11 +1153,11 @@ LibertyCell::isBuffer() const bool LibertyCell::hasBufferFunc(const LibertyPort *input, - const LibertyPort *output) const + const LibertyPort *output) const { FuncExpr *func = output->function(); return func - && func->op() == FuncExpr::op_port + && func->op() == FuncExpr::Op::port && func->port() == input; } @@ -1222,19 +1175,19 @@ LibertyCell::isInverter() const bool LibertyCell::hasInverterFunc(const LibertyPort *input, - const LibertyPort *output) const + const LibertyPort *output) const { FuncExpr *func = output->function(); return func - && func->op() == FuncExpr::op_not - && func->left()->op() == FuncExpr::op_port + && func->op() == FuncExpr::Op::not_ + && func->left()->op() == FuncExpr::Op::port && func->left()->port() == input; } void LibertyCell::bufferPorts(// Return values. - LibertyPort *&input, - LibertyPort *&output) const + LibertyPort *&input, + LibertyPort *&output) const { input = nullptr; output = nullptr; @@ -1243,19 +1196,19 @@ LibertyCell::bufferPorts(// Return values. PortDirection *dir = port->direction(); if (dir->isInput()) { if (input) { - // More than one input. - input = nullptr; - output = nullptr; - break; + // More than one input. + input = nullptr; + output = nullptr; + break; } input = port; } else if (dir->isOutput()) { if (output) { - // More than one output. - input = nullptr; - output = nullptr; - break; + // More than one output. + input = nullptr; + output = nullptr; + break; } output = port; } @@ -1268,57 +1221,47 @@ LibertyCell::bufferPorts(// Return values. } } -unsigned -LibertyCell::addTimingArcSet(TimingArcSet *arc_set) -{ - int set_index = timing_arc_sets_.size(); +TimingArcSet * +LibertyCell::makeTimingArcSet(LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + const TimingRole *role, + TimingArcAttrsPtr attrs) +{ + size_t set_index = timing_arc_sets_.size(); + TimingArcSet *arc_set = new TimingArcSet(this, from, to, related_out, role, + std::move(attrs), set_index); timing_arc_sets_.push_back(arc_set); - - LibertyPort *from = arc_set->from(); - LibertyPort *to = arc_set->to(); - const TimingRole *role = arc_set->role(); - if (role == TimingRole::regClkToQ() - || role == TimingRole::latchEnToQ()) { - from->setIsRegClk(true); - to->setIsRegOutput(true); - } - if (role->isTimingCheck()) - from->setIsCheckClk(true); - return set_index; -} - -void -LibertyCell::addInternalPower(InternalPower *power) -{ - internal_powers_.push_back(power); - port_internal_powers_[power->port()].push_back(power); -} - -const InternalPowerSeq & -LibertyCell::internalPowers(const LibertyPort *port) -{ - return port_internal_powers_[port]; + return arc_set; } void -LibertyCell::addInternalPowerAttrs(InternalPowerAttrs *attrs) +LibertyCell::makeInternalPower(LibertyPort *port, + LibertyPort *related_port, + LibertyPort *related_pg_pin, + const std::shared_ptr &when, + const InternalPowerModels &models) { - internal_power_attrs_.push_back(attrs); + internal_powers_.emplace_back(port, related_port, related_pg_pin, when, models); + port_internal_powers_[port].push_back(internal_powers_.size() - 1); } -void -LibertyCell::deleteInternalPowerAttrs() +InternalPowerPtrSeq +LibertyCell::internalPowers(const LibertyPort *port) const { - for (auto attrs : internal_power_attrs_) { - attrs->deleteContents(); - delete attrs; - } + InternalPowerPtrSeq result; + const InternalPowerIndexSeq &pwrs = findKeyValue(port_internal_powers_,port); + for (size_t idx : pwrs) + result.push_back(&internal_powers_[idx]); + return result; } void -LibertyCell::addLeakagePower(LeakagePower *power) +LibertyCell::makeLeakagePower(LibertyPort *related_pg_port, + FuncExpr *when, + float power) { - leakage_powers_.push_back(power); + leakage_powers_.emplace_back(this, related_pg_port, when, power); } void @@ -1330,8 +1273,8 @@ LibertyCell::setLeakagePower(float leakage) void LibertyCell::leakagePower(// Return values. - float &leakage, - bool &exists) const + float &leakage, + bool &exists) const { leakage = leakage_power_; exists = leakage_power_exists_; @@ -1339,11 +1282,10 @@ LibertyCell::leakagePower(// Return values. void LibertyCell::finish(bool infer_latches, - Report *report, - Debug *debug) + Report *report, + Debug *debug) { translatePresetClrCheckRoles(); - makeTimingArcMap(report); makeTimingArcPortMaps(); findDefaultCondArcs(); makeLatchEnables(report, debug); @@ -1354,18 +1296,18 @@ LibertyCell::finish(bool infer_latches, void LibertyCell::findDefaultCondArcs() { - for (auto [port_pair, sets] : port_timing_arc_set_map_) { + for (auto &[port_pair, sets] : port_timing_arc_set_map_) { bool has_cond_arcs = false; - for (auto set : *sets) { + for (auto set : sets) { if (set->cond()) { - has_cond_arcs = true; - break; + has_cond_arcs = true; + break; } } if (has_cond_arcs) { - for (auto set : *sets) { - if (!set->cond()) - set->setIsCondDefault(true); + for (auto &set : sets) { + if (!set->cond()) + set->setIsCondDefault(true); } } } @@ -1385,113 +1327,78 @@ LibertyCell::translatePresetClrCheckRoles() if (!pre_clr_ports.empty()) { for (auto arc_set : timing_arc_sets_) { - if (pre_clr_ports.findKey(arc_set->to())) { - if (arc_set->role() == TimingRole::setup()) - arc_set->setRole(TimingRole::recovery()); - else if (arc_set->role() == TimingRole::hold()) - arc_set->setRole(TimingRole::removal()); + if (findKey(pre_clr_ports, arc_set->to())) { + if (arc_set->role() == TimingRole::setup()) + arc_set->setRole(TimingRole::recovery()); + else if (arc_set->role() == TimingRole::hold()) + arc_set->setRole(TimingRole::removal()); } } } } -void -LibertyCell::makeTimingArcMap(Report *) -{ - // Filter duplicate timing arcs, keeping the later definition. - for (auto arc_set : timing_arc_sets_) - // The last definition will be left in the set. - timing_arc_set_map_.insert(arc_set); - - // Prune the arc sets not in the map. - int j = 0; - for (size_t i = 0; i < timing_arc_sets_.size(); i++) { - TimingArcSet *arc_set = timing_arc_sets_[i]; - TimingArcSet *match = timing_arc_set_map_.findKey(arc_set); - if (match != arc_set) { - // Unfortunately these errors are common in some brain damaged - // libraries. - // report->warn("cell %s/%s has duplicate %s -> %s %s timing groups.", - // library_->name(), - // name_, - // match->from()->name(), - // match->to()->name(), - // match->role()->asString()); - delete arc_set; - } - else - // Shift arc sets down to fill holes left by removed duplicates. - timing_arc_sets_[j++] = arc_set; - } - timing_arc_sets_.resize(j); - - if (timing_arc_set_map_.size() != timing_arc_sets_.size()) - criticalError(1121, "timing arc count mismatch"); -} - void LibertyCell::makeTimingArcPortMaps() { - for (auto arc_set : timing_arc_sets_) { + for (TimingArcSet *arc_set : timing_arc_sets_) { LibertyPort *from = arc_set->from(); LibertyPort *to = arc_set->to(); - LibertyPortPair port_pair(from, to); - TimingArcSetSeq *sets = port_timing_arc_set_map_.findKey(port_pair); - if (sets == nullptr) { - // First arc set for from/to ports. - sets = new TimingArcSetSeq; - port_timing_arc_set_map_[port_pair] = sets; + if (from && to) { + TimingArcSetSeq &sets = port_timing_arc_set_map_[{from, to}]; + sets.push_back(arc_set); } - sets->push_back(arc_set); - sets = timing_arc_set_from_map_.findKey(from); - if (sets == nullptr) { - sets = new TimingArcSetSeq; - timing_arc_set_from_map_[from] = sets; - } - sets->push_back(arc_set); + TimingArcSetSeq &from_sets = port_timing_arc_set_map_[{from, nullptr}]; + from_sets.push_back(arc_set); - sets = timing_arc_set_to_map_.findKey(to); - if (sets == nullptr) { - sets = new TimingArcSetSeq; - timing_arc_set_to_map_[to] = sets; + TimingArcSetSeq &to_sets = port_timing_arc_set_map_[{nullptr, to}]; + to_sets.push_back(arc_set); + + const TimingRole *role = arc_set->role(); + if (role == TimingRole::regClkToQ() + || role == TimingRole::latchEnToQ()) { + from->setIsRegClk(true); + if (to) + to->setIsRegOutput(true); } - sets->push_back(arc_set); + if (role->isTimingCheck()) + from->setIsCheckClk(true); + + timing_arc_set_set_.insert(arc_set); } } const TimingArcSetSeq & -LibertyCell::timingArcSets(const LibertyPort *from, - const LibertyPort *to) const +LibertyCell::timingArcSetsFrom(const LibertyPort *from) const { - TimingArcSetSeq *arc_sets = nullptr; - if (from && to) { - LibertyPortPair port_pair(from, to); - arc_sets = port_timing_arc_set_map_.findKey(port_pair); - } - else if (from) - arc_sets = timing_arc_set_from_map_.findKey(from); - else if (to) - arc_sets = timing_arc_set_to_map_.findKey(to); + return timingArcSets(from, nullptr); +} - if (arc_sets) - return *arc_sets; - else { - static TimingArcSetSeq null_set; - return null_set; - } +const TimingArcSetSeq & +LibertyCell::timingArcSetsTo(const LibertyPort *to) const +{ + return timingArcSets(nullptr, to); +} + +const TimingArcSetSeq & +LibertyCell::timingArcSets(const LibertyPort *from, + const LibertyPort *to) const +{ + static const TimingArcSetSeq null_set; + auto itr = port_timing_arc_set_map_.find({from, to}); + return (itr == port_timing_arc_set_map_.end()) ? null_set : itr->second; } TimingArcSet * -LibertyCell::findTimingArcSet(TimingArcSet *key) const +LibertyCell::findTimingArcSet(TimingArcSet *arc_set) const { - return timing_arc_set_map_.findKey(key); + return findKey(timing_arc_set_set_, arc_set); } TimingArcSet * -LibertyCell::findTimingArcSet(unsigned arc_set_index) const +LibertyCell::findTimingArcSet(size_t index) const { - return timing_arc_sets_[arc_set_index]; + return timing_arc_sets_[index]; } size_t @@ -1503,15 +1410,14 @@ LibertyCell::timingArcSetCount() const bool LibertyCell::hasTimingArcs() const { - return !timing_arc_set_from_map_.empty() - || !timing_arc_set_to_map_.empty(); + return !timing_arc_set_set_.empty(); } bool LibertyCell::hasTimingArcs(LibertyPort *port) const { - return timing_arc_set_from_map_.findKey(port) - || timing_arc_set_to_map_.findKey(port); + return port_timing_arc_set_map_.contains({port, nullptr}) + || port_timing_arc_set_map_.contains({nullptr, port}); } void @@ -1553,15 +1459,15 @@ LibertyCell::makeGeneratedClock(const char *name, void LibertyCell::makeSequential(int size, - bool is_register, - FuncExpr *clk, - FuncExpr *data, - FuncExpr *clear, - FuncExpr *preset, - LogicValue clr_preset_out, - LogicValue clr_preset_out_inv, - LibertyPort *output, - LibertyPort *output_inv) + bool is_register, + FuncExpr *clk, + FuncExpr *data, + FuncExpr *clear, + FuncExpr *preset, + LogicValue clr_preset_out, + LogicValue clr_preset_out_inv, + LibertyPort *output, + LibertyPort *output_inv) { for (int bit = 0; bit < size; bit++) { FuncExpr *clk_bit = nullptr; @@ -1582,27 +1488,33 @@ LibertyCell::makeSequential(int size, LibertyPort *out_inv_bit = output_inv; if (output_inv && output_inv->hasMembers()) out_inv_bit = output_inv->findLibertyMember(bit); - Sequential *seq = new Sequential(is_register, clk_bit, data_bit, - clear_bit,preset_bit, - clr_preset_out, clr_preset_out_inv, - out_bit, out_inv_bit); - sequentials_.push_back(seq); - port_to_seq_map_[seq->output()] = seq; - port_to_seq_map_[seq->outputInv()] = seq; + sequentials_.emplace_back(is_register, clk_bit, data_bit, + clear_bit, preset_bit, + clr_preset_out, clr_preset_out_inv, + out_bit, out_inv_bit); + size_t idx = sequentials_.size() - 1; + port_to_seq_map_[sequentials_.back().output()] = idx; + port_to_seq_map_[sequentials_.back().outputInv()] = idx; } + delete clk; + delete data; + delete clear; + delete preset; } Sequential * LibertyCell::outputPortSequential(LibertyPort *port) { - return port_to_seq_map_.findKey(port); + auto it = port_to_seq_map_.find(port); + if (it != port_to_seq_map_.end()) + return &sequentials_[it->second]; + return nullptr; } bool LibertyCell::hasSequentials() const { - return !sequentials_.empty() - || statetable_ != nullptr; + return !sequentials_.empty() || statetable_ != nullptr; } void @@ -1615,7 +1527,7 @@ LibertyCell::makeStatetable(LibertyPortSeq &input_ports, void LibertyCell::addScaledCell(OperatingConditions *op_cond, - LibertyCell *scaled_cell) + LibertyCell *scaled_cell) { scaled_cells_[op_cond] = scaled_cell; @@ -1644,9 +1556,9 @@ LibertyCell::addScaledCell(OperatingConditions *op_cond, const TimingArc *scaled_arc = *arc_itr2; if (TimingArc::equiv(arc, scaled_arc)) { - TimingModel *model = scaled_arc->model(); - model->setIsScaled(true); - arc->addScaledModel(op_cond, model); + TimingModel *model = scaled_arc->model(); + model->setIsScaled(true); + arc->addScaledModel(op_cond, model); } } } @@ -1671,53 +1583,41 @@ LibertyCell::setTestCell(TestCell *test) test_cell_ = test; } -void -LibertyCell::setIsDisabledConstraint(bool is_disabled) -{ - is_disabled_constraint_ = is_disabled; -} - -LibertyCell * -LibertyCell::cornerCell(const Corner *corner, - const MinMax *min_max) -{ - return cornerCell(corner->libertyIndex(min_max)); -} - LibertyCell * -LibertyCell::cornerCell(const DcalcAnalysisPt *dcalc_ap) +LibertyCell::sceneCell(const Scene *scene, + const MinMax *min_max) { - return cornerCell(dcalc_ap->libertyIndex()); + return sceneCell(scene->libertyIndex(min_max)); } LibertyCell * -LibertyCell::cornerCell(int ap_index) +LibertyCell::sceneCell(size_t lib_ap_index) { - if (corner_cells_.empty()) + if (scene_cells_.empty()) return this; - else if (ap_index < static_cast(corner_cells_.size())) - return corner_cells_[ap_index]; + else if (lib_ap_index < scene_cells_.size()) + return scene_cells_[lib_ap_index]; else return nullptr; } bool -LibertyCell::checkCornerCell(const Corner *corner, - const MinMax *min_max) const +LibertyCell::checkSceneCell(const Scene *scene, + const MinMax *min_max) const { - unsigned lib_index = corner->libertyIndex(min_max); - return corner_cells_.empty() - || (lib_index < corner_cells_.size() - && corner_cells_[lib_index]); + size_t lib_index = scene->libertyIndex(min_max); + return scene_cells_.empty() + || (lib_index < scene_cells_.size() + && scene_cells_[lib_index]); } void -LibertyCell::setCornerCell(LibertyCell *corner_cell, - int ap_index) +LibertyCell::setSceneCell(LibertyCell *scene_cell, + size_t lib_ap_index) { - if (ap_index >= static_cast(corner_cells_.size())) - corner_cells_.resize(ap_index + 1); - corner_cells_[ap_index] = corner_cell; + if (lib_ap_index >= scene_cells_.size()) + scene_cells_.resize(lib_ap_index + 1); + scene_cells_[lib_ap_index] = scene_cell; } //////////////////////////////////////////////////////////////// @@ -1750,59 +1650,29 @@ LibertyCell::setOcvDerate(OcvDerate *derate) } OcvDerate * -LibertyCell::findOcvDerate(const char *derate_name) +LibertyCell::makeOcvDerate(std::string_view name) { - return ocv_derate_map_.findKey(derate_name); + std::string key(name); + auto [it, inserted] = ocv_derate_map_.try_emplace(std::move(key), std::string(name)); + return &it->second; } -void -LibertyCell::addOcvDerate(OcvDerate *derate) +OcvDerate * +LibertyCell::findOcvDerate(std::string_view derate_name) { - ocv_derate_map_[derate->name()] = derate; + return findStringValuePtr(ocv_derate_map_, derate_name); } //////////////////////////////////////////////////////////////// -// Latch enable port/function for a latch D->Q timing arc set. -class LatchEnable -{ -public: - LatchEnable(LibertyPort *data, - LibertyPort *enable, - const RiseFall *enable_edge, - FuncExpr *enable_func, - LibertyPort *output, - TimingArcSet *d_to_q, - TimingArcSet *en_to_q, - TimingArcSet *setup_check); - LibertyPort *data() const { return data_; } - LibertyPort *output() const { return output_; } - LibertyPort *enable() const { return enable_; } - FuncExpr *enableFunc() const { return enable_func_; } - const RiseFall *enableEdge() const { return enable_edge_; } - TimingArcSet *dToQ() const { return d_to_q_; } - TimingArcSet *enToQ() const { return en_to_q_; } - TimingArcSet *setupCheck() const { return setup_check_; } - -private: - LibertyPort *data_; - LibertyPort *enable_; - const RiseFall *enable_edge_; - FuncExpr *enable_func_; - LibertyPort *output_; - TimingArcSet *d_to_q_; - TimingArcSet *en_to_q_; - TimingArcSet *setup_check_; -}; - LatchEnable::LatchEnable(LibertyPort *data, - LibertyPort *enable, - const RiseFall *enable_edge, - FuncExpr *enable_func, - LibertyPort *output, - TimingArcSet *d_to_q, - TimingArcSet *en_to_q, - TimingArcSet *setup_check) : + LibertyPort *enable, + const RiseFall *enable_edge, + FuncExpr *enable_func, + LibertyPort *output, + TimingArcSet *d_to_q, + TimingArcSet *en_to_q, + TimingArcSet *setup_check) : data_(data), enable_(enable), enable_edge_(enable_edge), @@ -1819,49 +1689,62 @@ LatchEnable::LatchEnable(LibertyPort *data, // Use timing arcs rather than sequentials (because they are optional). void LibertyCell::makeLatchEnables(Report *report, - Debug *debug) + Debug *debug) { if (hasSequentials() || hasInferedRegTimingArcs()) { - for (auto en_to_q : timing_arc_sets_) { - if (en_to_q->role() == TimingRole::latchEnToQ()) { - LibertyPort *en = en_to_q->from(); - LibertyPort *q = en_to_q->to(); - for (TimingArcSet *d_to_q : timingArcSets(nullptr, q)) { - if (d_to_q->role() == TimingRole::latchDtoQ() - && condMatch(en_to_q, d_to_q)) { - LibertyPort *d = d_to_q->from(); - const RiseFall *en_rf = en_to_q->isRisingFallingEdge(); - if (en_rf) { - TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, d_to_q, - report); - LatchEnable *latch_enable = makeLatchEnable(d, en, en_rf, q, d_to_q, - en_to_q, - setup_check, - debug); - FuncExpr *en_func = latch_enable->enableFunc(); - if (en_func) { - TimingSense en_sense = en_func->portTimingSense(en); - if (en_sense == TimingSense::positive_unate - && en_rf != RiseFall::rise()) - report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.", - library_->name(), - name(), - en->name(), - q->name(), - en_rf == RiseFall::rise()?"rising":"falling"); - else if (en_sense == TimingSense::negative_unate - && en_rf != RiseFall::fall()) - report->warn(1115, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense.", - library_->name(), - name(), - en->name(), - q->name(), - en_rf == RiseFall::rise()?"rising":"falling"); - } + for (TimingArcSet *d_to_q : timing_arc_sets_) { + if (d_to_q->role() == TimingRole::latchDtoQ()) { + LibertyPort *d = d_to_q->from(); + LibertyPort *q = d_to_q->to(); + TimingArcSet *en_to_q = nullptr; + TimingArcSet *en_to_q_when = nullptr; + // Prefer en_to_q with matching when. + for (TimingArcSet *arc_to_q : timingArcSetsTo(q)) { + if (arc_to_q->role() == TimingRole::latchEnToQ()) { + if (condMatch(arc_to_q, d_to_q)) + en_to_q_when = arc_to_q; + else + en_to_q = arc_to_q; + } + } + if (en_to_q_when) + en_to_q = en_to_q_when; + if (en_to_q) { + LibertyPort *en = en_to_q->from(); + const RiseFall *en_rf = en_to_q->isRisingFallingEdge(); + if (en_rf) { + TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, d_to_q, report); + LatchEnable *latch_enable = makeLatchEnable(d, en, en_rf, q, d_to_q, + en_to_q, setup_check, debug); + FuncExpr *en_func = latch_enable->enableFunc(); + if (en_func) { + TimingSense en_sense = en_func->portTimingSense(en); + if (en_sense == TimingSense::positive_unate + && en_rf != RiseFall::rise()) + report->warn(1114, "cell {}/{} {} -> {} latch enable {}_edge is inconsistent with latch group enable function positive sense.", + library_->name(), + name(), + en->name(), + q->name(), + en_rf == RiseFall::rise()?"rising":"falling"); + else if (en_sense == TimingSense::negative_unate + && en_rf != RiseFall::fall()) + report->warn(1115, "cell {}/{} {} -> {} latch enable {}_edge is inconsistent with latch group enable function negative sense.", + library_->name(), + name(), + en->name(), + q->name(), + en_rf == RiseFall::rise()?"rising":"falling"); } } - } + } + else + report->warn(1121, "cell {}/{} no latch enable found for {} -> {}.", + library_->name(), + name(), + d->name(), + q->name()); } } } @@ -1903,7 +1786,7 @@ LibertyCell::findLatchSetup(const LibertyPort *d, for (TimingArc *arc : arc_set->arcs()) { const RiseFall *from_rf = arc->fromEdge()->asRiseFall(); if (from_rf == en_rf) { - report->warn(1113, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with %s -> %s setup_%s check.", + report->warn(1113, "cell {}/{} {} -> {} latch enable {}_edge is inconsistent with {} -> {} setup_{} check.", library_->name(), name(), en->name(), @@ -1922,22 +1805,22 @@ LibertyCell::findLatchSetup(const LibertyPort *d, FuncExpr * LibertyCell::findLatchEnableFunc(const LibertyPort *d, - const LibertyPort *en, + const LibertyPort *en, const RiseFall *en_rf) const { - for (auto seq : sequentials_) { - if (seq->isLatch() - && seq->data() - && seq->data()->hasPort(d) - && seq->clock() - && seq->clock()->hasPort(en)) { - FuncExpr *en_func = seq->clock(); + for (const auto &seq : sequentials_) { + if (seq.isLatch() + && seq.data() + && seq.data()->hasPort(d) + && seq.clock() + && seq.clock()->hasPort(en)) { + FuncExpr *en_func = seq.clock(); TimingSense en_sense = en_func->portTimingSense(en); if ((en_sense == TimingSense::positive_unate && en_rf == RiseFall::rise()) || (en_sense == TimingSense::negative_unate && en_rf == RiseFall::fall())) - return seq->clock(); + return seq.clock(); } } return nullptr; @@ -1945,23 +1828,22 @@ LibertyCell::findLatchEnableFunc(const LibertyPort *d, LatchEnable * LibertyCell::makeLatchEnable(LibertyPort *d, - LibertyPort *en, + LibertyPort *en, const RiseFall *en_rf, - LibertyPort *q, - TimingArcSet *d_to_q, - TimingArcSet *en_to_q, - TimingArcSet *setup_check, - Debug *debug) + LibertyPort *q, + TimingArcSet *d_to_q, + TimingArcSet *en_to_q, + TimingArcSet *setup_check, + Debug *debug) { FuncExpr *en_func = findLatchEnableFunc(d, en, en_rf); - LatchEnable *latch_enable = new LatchEnable(d, en, en_rf, en_func, q, - d_to_q, en_to_q, setup_check); - latch_enables_.push_back(latch_enable); - latch_d_to_q_map_[d_to_q] = latch_enable; - latch_check_map_[setup_check] = latch_enable; + latch_enables_.emplace_back(d, en, en_rf, en_func, q, d_to_q, en_to_q, setup_check); + size_t idx = latch_enables_.size() - 1; + latch_d_to_q_map_[d_to_q] = idx; + latch_check_map_[setup_check] = idx; d->setIsLatchData(true); debugPrint(debug, "liberty_latch", 1, - "latch %s -> %s | %s %s -> %s | %s %s -> %s setup", + "latch {} -> {} | {} {} -> {} | {} {} -> {} setup", d->name(), q->name(), en->name(), @@ -1970,7 +1852,7 @@ LibertyCell::makeLatchEnable(LibertyPort *d, en->name(), setup_check->arcs()[0]->fromEdge()->asRiseFall()->shortName(), q->name()); - return latch_enable; + return &latch_enables_.back(); } void @@ -1979,25 +1861,24 @@ LibertyCell::inferLatchRoles(Report *report, { if (hasInferedRegTimingArcs()) { // Hunt down potential latch D/EN/Q triples. - LatchEnableSet latch_enables; for (TimingArcSet *en_to_q : timingArcSets()) { // Locate potential d->q arcs from reg clk->q arcs. if (en_to_q->role() == TimingRole::regClkToQ()) { - LibertyPort *en = en_to_q->from(); - LibertyPort *q = en_to_q->to(); - for (TimingArcSet *d_to_q : timingArcSets(nullptr, q)) { - // Look for combinational d->q arcs. - const TimingRole *d_to_q_role = d_to_q->role(); - if (((d_to_q_role == TimingRole::combinational() + LibertyPort *en = en_to_q->from(); + LibertyPort *q = en_to_q->to(); + for (TimingArcSet *d_to_q : timingArcSetsTo(q)) { + // Look for combinational d->q arcs. + const TimingRole *d_to_q_role = d_to_q->role(); + if (((d_to_q_role == TimingRole::combinational() && d_to_q->arcCount() == 2 && (d_to_q->sense() == TimingSense::positive_unate || d_to_q->sense() == TimingSense::negative_unate)) // Previously identified as D->Q arc. || d_to_q_role == TimingRole::latchDtoQ()) && condMatch(en_to_q, d_to_q)) { - LibertyPort *d = d_to_q->from(); + LibertyPort *d = d_to_q->from(); const RiseFall *en_rf = en_to_q->isRisingFallingEdge(); - if (en_rf) { + if (en_rf) { TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, en_to_q, report); makeLatchEnable(d, en, en_rf, q, d_to_q, en_to_q, setup_check, debug); @@ -2005,7 +1886,7 @@ LibertyCell::inferLatchRoles(Report *report, en_to_q->setRole(TimingRole::latchEnToQ()); } } - } + } } } } @@ -2013,36 +1894,37 @@ LibertyCell::inferLatchRoles(Report *report, void LibertyCell::latchEnable(const TimingArcSet *d_to_q_set, - // Return values. - const LibertyPort *&enable_port, - const FuncExpr *&enable_func, - const RiseFall *&enable_edge) const -{ - LatchEnable *latch_enable = latch_d_to_q_map_.findKey(d_to_q_set); - if (latch_enable) { - enable_port = latch_enable->enable(); - enable_func = latch_enable->enableFunc(); - enable_edge = latch_enable->enableEdge(); + // Return values. + const LibertyPort *&enable_port, + const FuncExpr *&enable_func, + const RiseFall *&enable_rf) const +{ + auto it = latch_d_to_q_map_.find(d_to_q_set); + if (it != latch_d_to_q_map_.end()) { + const LatchEnable &latch_enable = latch_enables_[it->second]; + enable_port = latch_enable.enable(); + enable_func = latch_enable.enableFunc(); + enable_rf = latch_enable.enableEdge(); } else { enable_port = nullptr; enable_func = nullptr; - enable_edge = nullptr; + enable_rf = nullptr; } } const RiseFall * LibertyCell::latchCheckEnableEdge(TimingArcSet *check_set) { - LatchEnable *latch_enable = latch_check_map_.findKey(check_set); - if (latch_enable) - return latch_enable->enableEdge(); + auto it = latch_check_map_.find(check_set); + if (it != latch_check_map_.end()) + return latch_enables_[it->second].enableEdge(); else return nullptr; } void -LibertyCell::ensureVoltageWaveforms(const DcalcAnalysisPtSeq &dcalc_aps) +LibertyCell::ensureVoltageWaveforms(const SceneSeq &scenes) { if (!have_voltage_waveforms_) { LockGuard lock(waveform_lock_); @@ -2055,12 +1937,14 @@ LibertyCell::ensureVoltageWaveforms(const DcalcAnalysisPtSeq &dcalc_aps) criticalError(1120, "library missing vdd"); for (TimingArcSet *arc_set : timingArcSets()) { for (TimingArc *arc : arc_set->arcs()) { - for (const DcalcAnalysisPt *dcalc_ap : dcalc_aps) { - GateTableModel *model = arc->gateTableModel(dcalc_ap); - if (model) { - OutputWaveforms *output_waveforms = model->outputWaveforms(); - if (output_waveforms) - output_waveforms->ensureVoltageWaveforms(vdd); + for (const Scene *scene : scenes) { + for (const MinMax *min_max : MinMax::range()) { + GateTableModel *model = arc->gateTableModel(scene, min_max); + if (model) { + OutputWaveforms *output_waveforms = model->outputWaveforms(); + if (output_waveforms) + output_waveforms->ensureVoltageWaveforms(vdd); + } } } } @@ -2070,33 +1954,14 @@ LibertyCell::ensureVoltageWaveforms(const DcalcAnalysisPtSeq &dcalc_aps) } } -const char * -LibertyCell::footprint() const -{ - if (footprint_.empty()) - return nullptr; - else - return footprint_.c_str(); -} - - void -LibertyCell::setFootprint(const char *footprint) +LibertyCell::setFootprint(std::string_view footprint) { footprint_ = footprint; } -const char * -LibertyCell::userFunctionClass() const -{ - if (user_function_class_.empty()) - return nullptr; - else - return user_function_class_.c_str(); -} - void -LibertyCell::setUserFunctionClass(const char *user_function_class) +LibertyCell::setUserFunctionClass(std::string_view user_function_class) { user_function_class_ = user_function_class; } @@ -2147,74 +2012,28 @@ LibertyCellPortBitIterator::next() //////////////////////////////////////////////////////////////// LibertyPort::LibertyPort(LibertyCell *cell, - const char *name, - bool is_bus, - BusDcl *bus_dcl, + std::string_view name, + bool is_bus, + BusDcl *bus_dcl, int from_index, - int to_index, - bool is_bundle, - ConcretePortSeq *members) : - ConcretePort(name, is_bus, from_index, to_index, is_bundle, members, cell), + int to_index, + bool is_bundle, + ConcretePortSeq *members) : + ConcretePort(name, is_bus, from_index, to_index, + is_bundle, members, cell), liberty_cell_(cell), - bus_dcl_(bus_dcl), - pwr_gnd_type_(PwrGndType::none), - scan_signal_type_(ScanSignalType::none), - function_(nullptr), - tristate_enable_(nullptr), - scaled_ports_(nullptr), - fanout_load_(0.0), - fanout_load_exists_(false), - min_period_(0.0), - pulse_clk_trigger_(nullptr), - pulse_clk_sense_(nullptr), - receiver_model_(nullptr), - driver_waveform_{nullptr, nullptr}, - min_pulse_width_exists_(false), - min_period_exists_(false), - is_clk_(false), - is_reg_clk_(false), - is_reg_output_(false), - is_latch_data_(false), - is_check_clk_(false), - is_clk_gate_clk_(false), - is_clk_gate_enable_(false), - is_clk_gate_out_(false), - is_pll_feedback_(false), - isolation_cell_data_(false), - isolation_cell_enable_(false), - level_shifter_data_(false), - is_switch_(false), - is_disabled_constraint_(false), - is_pad_(false) + bus_dcl_(bus_dcl) { liberty_port_ = this; - min_pulse_width_[RiseFall::riseIndex()] = 0.0; - min_pulse_width_[RiseFall::fallIndex()] = 0.0; - for (auto from_rf_index : RiseFall::rangeIndex()) { - for (auto to_rf_index : RiseFall::rangeIndex()) { - for (auto mm_index : MinMax::rangeIndex()) - clk_tree_delay_[from_rf_index][to_rf_index][mm_index] = nullptr; - } - } } LibertyPort::~LibertyPort() { - if (function_) - function_->deleteSubexprs(); - if (tristate_enable_) - tristate_enable_->deleteSubexprs(); + delete function_; + delete tristate_enable_; delete scaled_ports_; } -void -LibertyPort::setDirection(PortDirection *dir) -{ - ConcretePort::setDirection(dir); - if (dir->isInternal()) - liberty_cell_->setHasInternalPorts(true); -} - void LibertyPort::setScanSignalType(ScanSignalType type) { @@ -2246,7 +2065,7 @@ LibertyPort::setPwrGndType(PwrGndType type) } void -LibertyPort::setVoltageName(const char *voltage_name) +LibertyPort::setVoltageName(std::string_view voltage_name) { voltage_name_ = voltage_name; } @@ -2264,21 +2083,21 @@ static EnumNameMap pwr_gnd_type_map = {PwrGndType::deepnwell, "deepnwell"}, {PwrGndType::deeppwell, "deeppwell"}}; -const char * +const std::string & pwrGndTypeName(PwrGndType pg_type) { return pwr_gnd_type_map.find(pg_type); } PwrGndType -findPwrGndType(const char *pg_name) +findPwrGndType(std::string_view pg_name) { return pwr_gnd_type_map.find(pg_name, PwrGndType::none); } //////////////////////////////////////////////////////////////// -const char * +const std::string & scanSignalTypeName(ScanSignalType scan_type) { return scan_signal_type_map.find(scan_type); @@ -2296,12 +2115,6 @@ LibertyPort::findLibertyBusBit(int index) const return static_cast(findBusBit(index)); } -LibertyPort * -LibertyPort::bundlePort() const -{ - return static_cast(bundle_port_); -} - void LibertyPort::setCapacitance(float cap) { @@ -2313,8 +2126,8 @@ LibertyPort::setCapacitance(float cap) void LibertyPort::setCapacitance(const RiseFall *rf, - const MinMax *min_max, - float cap) + const MinMax *min_max, + float cap) { capacitance_.setValue(rf, min_max, cap); if (hasMembers()) { @@ -2346,7 +2159,7 @@ LibertyPort::capacitance(const MinMax *min_max) const float LibertyPort::capacitance(const RiseFall *rf, - const MinMax *min_max) const + const MinMax *min_max) const { float cap; bool exists; @@ -2359,19 +2172,19 @@ LibertyPort::capacitance(const RiseFall *rf, void LibertyPort::capacitance(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &cap, - bool &exists) const + const MinMax *min_max, + // Return values. + float &cap, + bool &exists) const { capacitance_.value(rf, min_max, cap, exists); } float LibertyPort::capacitance(const RiseFall *rf, - const MinMax *min_max, - const OperatingConditions *op_cond, - const Pvt *pvt) const + const MinMax *min_max, + const OperatingConditions *op_cond, + const Pvt *pvt) const { if (scaled_ports_) { LibertyPort *scaled_port = (*scaled_ports_)[op_cond]; @@ -2402,22 +2215,22 @@ LibertyPort::driveResistance() const // Min/max "drive" for all cell timing arcs. float LibertyPort::driveResistance(const RiseFall *rf, - const MinMax *min_max) const + const MinMax *min_max) const { float max_drive = min_max->initValue(); bool found_drive = false; - for (TimingArcSet *arc_set : liberty_cell_->timingArcSets(nullptr, this)) { + for (TimingArcSet *arc_set : liberty_cell_->timingArcSetsTo(this)) { if (!arc_set->role()->isTimingCheck()) { for (TimingArc *arc : arc_set->arcs()) { - if (rf == nullptr - || arc->toEdge()->asRiseFall() == rf) { + if (rf == nullptr + || arc->toEdge()->asRiseFall() == rf) { float drive = arc->driveResistance(); if (drive > 0.0) { if (min_max->compare(drive, max_drive)) max_drive = drive; - found_drive = true; - } - } + found_drive = true; + } + } } } } @@ -2435,22 +2248,22 @@ LibertyPort::intrinsicDelay(const StaState *sta) const ArcDelay LibertyPort::intrinsicDelay(const RiseFall *rf, - const MinMax *min_max, + const MinMax *min_max, const StaState *sta) const { ArcDelay max_delay = min_max->initValue(); bool found_delay = false; - for (TimingArcSet *arc_set : liberty_cell_->timingArcSets(nullptr, this)) { + for (TimingArcSet *arc_set : liberty_cell_->timingArcSetsTo(this)) { if (!arc_set->role()->isTimingCheck()) { for (TimingArc *arc : arc_set->arcs()) { - if (rf == nullptr - || arc->toEdge()->asRiseFall() == rf) { + if (rf == nullptr + || arc->toEdge()->asRiseFall() == rf) { ArcDelay delay = arc->intrinsicDelay(); if (delayGreater(delay, 0.0, sta)) { - if (delayGreater(delay, max_delay, min_max, sta)) - max_delay = delay; - found_delay = true; - } + if (delayGreater(delay, max_delay, min_max, sta)) + max_delay = delay; + found_delay = true; + } } } } @@ -2472,7 +2285,7 @@ LibertyPort::setFunction(FuncExpr *func) int bit_offset = 0; while (member_iter.hasNext()) { LibertyPort *port_bit = member_iter.next(); - FuncExpr *sub_expr = (func) ? func->bitSubExpr(bit_offset) : nullptr; + FuncExpr *sub_expr = func ? func->bitSubExpr(bit_offset) : nullptr; port_bit->setFunction(sub_expr); bit_offset++; } @@ -2487,8 +2300,9 @@ LibertyPort::setTristateEnable(FuncExpr *enable) LibertyPortMemberIterator member_iter(this); while (member_iter.hasNext()) { LibertyPort *port_bit = member_iter.next(); - FuncExpr *sub_expr = - (enable) ? enable->bitSubExpr(port_bit->busBitIndex()) : nullptr; + FuncExpr *sub_expr = enable + ? enable->bitSubExpr(port_bit->busBitIndex()) + : nullptr; port_bit->setTristateEnable(sub_expr); } } @@ -2496,16 +2310,16 @@ LibertyPort::setTristateEnable(FuncExpr *enable) void LibertyPort::slewLimit(const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const + // Return values. + float &limit, + bool &exists) const { slew_limit_.value(min_max, limit, exists); } void LibertyPort::setSlewLimit(float slew, - const MinMax *min_max) + const MinMax *min_max) { slew_limit_.setValue(min_max, slew); setMemberMinMaxFloat(slew, min_max, &LibertyPort::setSlewLimit); @@ -2513,16 +2327,16 @@ LibertyPort::setSlewLimit(float slew, void LibertyPort::capacitanceLimit(const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const + // Return values. + float &limit, + bool &exists) const { - return cap_limit_.value(min_max, limit, exists); + cap_limit_.value(min_max, limit, exists); } void LibertyPort::setCapacitanceLimit(float cap, - const MinMax *min_max) + const MinMax *min_max) { cap_limit_.setValue(min_max, cap); setMemberMinMaxFloat(cap, min_max, &LibertyPort::setCapacitanceLimit); @@ -2530,8 +2344,8 @@ LibertyPort::setCapacitanceLimit(float cap, void LibertyPort::fanoutLoad(// Return values. - float &fanout_load, - bool &exists) const + float &fanout_load, + bool &exists) const { fanout_load = fanout_load_; exists = fanout_load_exists_; @@ -2547,25 +2361,25 @@ LibertyPort::setFanoutLoad(float fanout_load) void LibertyPort::fanoutLimit(const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const + // Return values. + float &limit, + bool &exists) const { - return fanout_limit_.value(min_max, limit, exists); + fanout_limit_.value(min_max, limit, exists); } void LibertyPort::setFanoutLimit(float fanout, - const MinMax *min_max) + const MinMax *min_max) { fanout_limit_.setValue(min_max, fanout); } void LibertyPort::minPeriod(const OperatingConditions *op_cond, - const Pvt *pvt, - float &min_period, - bool &exists) const + const Pvt *pvt, + float &min_period, + bool &exists) const { if (scaled_ports_) { LibertyPort *scaled_port = (*scaled_ports_)[op_cond]; @@ -2576,13 +2390,13 @@ LibertyPort::minPeriod(const OperatingConditions *op_cond, } LibertyLibrary *lib = liberty_cell_->libertyLibrary(); min_period = min_period_ * lib->scaleFactor(ScaleFactorType::min_period, - liberty_cell_, pvt); + liberty_cell_, pvt); exists = min_period_exists_; } void LibertyPort::minPeriod(float &min_period, - bool &exists) const + bool &exists) const { min_period = min_period_; exists = min_period_exists_; @@ -2604,8 +2418,8 @@ LibertyPort::setMinPeriod(float min_period) void LibertyPort::minPulseWidth(const RiseFall *hi_low, - float &min_width, - bool &exists) const + float &min_width, + bool &exists) const { int hi_low_index = hi_low->index(); min_width = min_pulse_width_[hi_low_index]; @@ -2614,7 +2428,7 @@ LibertyPort::minPulseWidth(const RiseFall *hi_low, void LibertyPort::setMinPulseWidth(const RiseFall *hi_low, - float min_width) + float min_width) { int hi_low_index = hi_low->index(); min_pulse_width_[hi_low_index] = min_width; @@ -2630,36 +2444,29 @@ LibertyPort::setMinPulseWidth(const RiseFall *hi_low, bool LibertyPort::equiv(const LibertyPort *port1, - const LibertyPort *port2) + const LibertyPort *port2) { return (port1 == nullptr && port2 == nullptr) || (port1 != nullptr && port2 != nullptr - && stringEq(port1->name(), port2->name()) - && port1->direction() == port2->direction() - && port1->pwr_gnd_type_ == port2->pwr_gnd_type_); + && port1->name() == port2->name() + && port1->direction() == port2->direction() + && port1->pwr_gnd_type_ == port2->pwr_gnd_type_); } +// Note port1 and port2 may be from different cells (timingArcSetLess). bool LibertyPort::less(const LibertyPort *port1, - const LibertyPort *port2) + const LibertyPort *port2) { - if (port1 == nullptr && port2 != nullptr) - return true; - if (port1 != nullptr && port2 == nullptr) - return false; - const char *name1 = port1->name(); - const char *name2 = port2->name(); - if (stringEq(name1, name2)) { - PortDirection *dir1 = port1->direction(); - PortDirection *dir2 = port2->direction(); - return dir1->index() < dir2->index(); - } - return stringLess(name1, name2); + if (port1 && port2) + return port1->name() < port2->name(); + else + return port1 == nullptr && port2 != nullptr; } void LibertyPort::addScaledPort(OperatingConditions *op_cond, - LibertyPort *scaled_port) + LibertyPort *scaled_port) { if (scaled_ports_ == nullptr) scaled_ports_ = new ScaledPortMap; @@ -2760,18 +2567,12 @@ LibertyPort::setIsSwitch(bool is_switch) void LibertyPort::setPulseClk(const RiseFall *trigger, - const RiseFall *sense) + const RiseFall *sense) { pulse_clk_trigger_ = trigger; pulse_clk_sense_ = sense; } -void -LibertyPort::setIsDisabledConstraint(bool is_disabled) -{ - is_disabled_constraint_ = is_disabled; -} - void LibertyPort::setIsPad(bool is_pad) { @@ -2779,107 +2580,75 @@ LibertyPort::setIsPad(bool is_pad) } LibertyPort * -LibertyPort::cornerPort(const Corner *corner, - const MinMax *min_max) -{ - return cornerPort(corner->libertyIndex(min_max)); -} - -const LibertyPort * -LibertyPort::cornerPort(const Corner *corner, - const MinMax *min_max) const -{ - return cornerPort(corner->libertyIndex(min_max)); -} - -LibertyPort * -LibertyPort::cornerPort(const DcalcAnalysisPt *dcalc_ap) +LibertyPort::scenePort(const Scene *scene, + const MinMax *min_max) { - return cornerPort(dcalc_ap->libertyIndex()); + return scenePort(scene->libertyIndex(min_max)); } const LibertyPort * -LibertyPort::cornerPort(const DcalcAnalysisPt *dcalc_ap) const +LibertyPort::scenePort(const Scene *scene, + const MinMax *min_max) const { - return cornerPort(dcalc_ap->libertyIndex()); + return scenePort(scene->libertyIndex(min_max)); } LibertyPort * -LibertyPort::cornerPort(int ap_index) +LibertyPort::scenePort(size_t lib_ap_index) { - if (corner_ports_.empty()) + if (scene_ports_.empty()) return this; - else if (ap_index < static_cast(corner_ports_.size())) - return corner_ports_[ap_index]; + else if (lib_ap_index < scene_ports_.size()) + return scene_ports_[lib_ap_index]; else return nullptr; } const LibertyPort * -LibertyPort::cornerPort(int ap_index) const +LibertyPort::scenePort(size_t lib_ap_index) const { - if (corner_ports_.empty()) + if (scene_ports_.empty()) return this; - else if (ap_index < static_cast(corner_ports_.size())) - return corner_ports_[ap_index]; + else if (lib_ap_index < scene_ports_.size()) + return scene_ports_[lib_ap_index]; else return nullptr; } void -LibertyPort::setCornerPort(LibertyPort *corner_port, - int ap_index) -{ - if (ap_index >= static_cast(corner_ports_.size())) - corner_ports_.resize(ap_index + 1); - corner_ports_[ap_index] = corner_port; -} - -const char * -LibertyPort::relatedGroundPin() const +LibertyPort::setScenePort(LibertyPort *scene_port, + size_t lib_ap_index) { - if (related_ground_pin_.empty()) - return nullptr; - else - return related_ground_pin_.c_str(); + if (lib_ap_index >= scene_ports_.size()) + scene_ports_.resize(lib_ap_index + 1); + scene_ports_[lib_ap_index] = scene_port; } void -LibertyPort::setRelatedGroundPin(const char *related_ground_pin) +LibertyPort::setRelatedGroundPort(LibertyPort *related_ground_port) { - related_ground_pin_ = related_ground_pin; -} - -const char * -LibertyPort::relatedPowerPin() const -{ - if (related_power_pin_.empty()) - return nullptr; - else - return related_power_pin_.c_str(); + related_ground_port_ = related_ground_port; } void -LibertyPort::setRelatedPowerPin(const char *related_power_pin) +LibertyPort::setRelatedPowerPort(LibertyPort *related_power_port) { - related_power_pin_ = related_power_pin; + related_power_port_ = related_power_port; } void LibertyPort::setReceiverModel(ReceiverModelPtr receiver_model) { - receiver_model_ = receiver_model; + receiver_model_ = std::move(receiver_model); } -string -portLibertyToSta(const char *port_name) +std::string +portLibertyToSta(std::string_view port_name) { constexpr char bus_brkt_left = '['; constexpr char bus_brkt_right = ']'; - size_t name_length = strlen(port_name); - string sta_name; - for (size_t i = 0; i < name_length; i++) { - char ch = port_name[i]; + std::string sta_name; + for (char ch : port_name) { if (ch == bus_brkt_left || ch == bus_brkt_right) sta_name += '\\'; @@ -2888,26 +2657,6 @@ portLibertyToSta(const char *port_name) return sta_name; } -string -portStaToLiberty(const char *sta_name) -{ - string liberty_name; - for (const char *s = sta_name; *s; s++) { - char ch = *s; - if (ch == '\\') { - char next_ch = s[1]; - if (next_ch == '\\') { - liberty_name += ch; - liberty_name += next_ch; - s++; - } - } - else - liberty_name += ch; - } - return liberty_name; -} - DriverWaveform * LibertyPort::driverWaveform(const RiseFall *rf) const { @@ -2921,47 +2670,14 @@ LibertyPort::setDriverWaveform(DriverWaveform *driver_waveform, driver_waveform_[rf->index()] = driver_waveform; } -RiseFallMinMax -LibertyPort::clockTreePathDelays() const -{ - return clkTreeDelays1(); -} - -RiseFallMinMax -LibertyPort::clkTreeDelays() const -{ - return clkTreeDelays1(); -} - -RiseFallMinMax -LibertyPort::clkTreeDelays1() const -{ - RiseFallMinMax delays; - for (const RiseFall *from_rf : RiseFall::range()) { - for (const RiseFall *to_rf : RiseFall::range()) { - for (const MinMax *min_max : MinMax::range()) { - const TableModel *model = - clk_tree_delay_[from_rf->index()][to_rf->index()][min_max->index()]; - if (model) { - float delay = model->findValue(0.0, 0.0, 0.0); - delays.setValue(from_rf, min_max, delay); - } - } - } - } - return delays; -} +//////////////////////////////////////////////////////////////// float LibertyPort::clkTreeDelay(float in_slew, const RiseFall *rf, const MinMax *min_max) const { - const TableModel *model = clk_tree_delay_[rf->index()][rf->index()][min_max->index()]; - if (model) - return model->findValue(in_slew, 0.0, 0.0); - else - return 0.0; + return clkTreeDelay(in_slew, rf, rf, min_max); } float @@ -2970,27 +2686,28 @@ LibertyPort::clkTreeDelay(float in_slew, const RiseFall *to_rf, const MinMax *min_max) const { - const TableModel *model = - clk_tree_delay_[from_rf->index()][to_rf->index()][min_max->index()]; - if (model) - return model->findValue(in_slew, 0.0, 0.0); - else - return 0.0; + for (TimingArcSet *arc_set : liberty_cell_->timingArcSetsTo(this)) { + if ((arc_set->role() == TimingRole::clockTreePathMin() + && min_max == MinMax::min()) + || (arc_set->role() == TimingRole::clockTreePathMax() + && min_max == MinMax::max())) { + const TimingArc *arc = arc_set->arcTo(to_rf); + if (arc->fromEdge()->asRiseFall() == from_rf) { + const GateTableModel *gate_model = dynamic_cast(arc->model()); + if (gate_model) + return gate_model->delayModel()->findValue(in_slew, 0.0, 0.0); + } + } + } + return 0.0; } -void -LibertyPort::setClkTreeDelay(const TableModel *model, - const RiseFall *from_rf, - const RiseFall *to_rf, - const MinMax *min_max) -{ - clk_tree_delay_[from_rf->index()][to_rf->index()][min_max->index()] = model; -} +//////////////////////////////////////////////////////////////// void LibertyPort::setMemberFlag(bool value, - const std::function &setter) + const std::function &setter) { if (hasMembers()) { LibertyPortMemberIterator member_iter(this); @@ -3003,8 +2720,8 @@ LibertyPort::setMemberFlag(bool value, void LibertyPort::setMemberFloat(float value, - const std::function &setter) + const std::function &setter) { if (hasMembers()) { LibertyPortMemberIterator member_iter(this); @@ -3017,10 +2734,10 @@ LibertyPort::setMemberFloat(float value, void LibertyPort::setMemberMinMaxFloat(float value, - const MinMax *min_max, - const std::function &setter) + const MinMax *min_max, + const std::function &setter) { if (hasMembers()) { LibertyPortMemberIterator member_iter(this); @@ -3052,20 +2769,22 @@ LibertyPortLess::operator()(const LibertyPort *port1, bool LibertyPortNameLess::operator()(const LibertyPort *port1, - const LibertyPort *port2) const + const LibertyPort *port2) const { - return stringLess(port1->name(), port2->name()); + return port1->name() < port2->name(); } bool LibertyPortPairLess::operator()(const LibertyPortPair &pair1, - const LibertyPortPair &pair2) const + const LibertyPortPair &pair2) const { - ObjectId id1 = pair1.first ? pair1.first->id() : 0; - ObjectId id2 = pair2.first ? pair2.first->id() : 0; - return id1 < id2 - || (id1 == id2 - && pair1.second->id() < pair2.second->id()); + ObjectId id11 = pair1.first ? pair1.first->id() : 0; + ObjectId id12 = pair1.second ? pair1.second->id() : 0; + ObjectId id21 = pair2.first ? pair2.first->id() : 0; + ObjectId id22 = pair2.second ? pair2.second->id() : 0; + return id11 < id21 + || (id11 == id21 + && id12 < id22); } //////////////////////////////////////////////////////////////// @@ -3094,9 +2813,9 @@ LibertyPortMemberIterator::next() //////////////////////////////////////////////////////////////// -BusDcl::BusDcl(const char *name, - int from, - int to) : +BusDcl::BusDcl(std::string_view name, + int from, + int to) : name_(name), from_(from), to_(to) @@ -3105,47 +2824,43 @@ BusDcl::BusDcl(const char *name, //////////////////////////////////////////////////////////////// -ModeDef::ModeDef(const char *name) : +ModeDef::ModeDef(std::string_view name) : name_(name) { } -ModeDef::~ModeDef() -{ - values_.deleteContents(); -} - ModeValueDef * -ModeDef::defineValue(const char *value, - FuncExpr *cond, - const char *sdf_cond) +ModeDef::defineValue(std::string_view value) { - ModeValueDef *val_def = new ModeValueDef(value, cond, sdf_cond); - values_[val_def->value()] = val_def; - return val_def; + std::string key(value); + auto [it, inserted] = values_.try_emplace(std::move(key), std::string(value)); + return &it->second; } -ModeValueDef * -ModeDef::findValueDef(const char *value) +const ModeValueDef * +ModeDef::findValueDef(std::string_view value) const { - return values_[value]; + return findStringValuePtr(values_, value); } //////////////////////////////////////////////////////////////// -ModeValueDef::ModeValueDef(const char *value, - FuncExpr *cond, - const char *sdf_cond) : - value_(value), - cond_(cond), - sdf_cond_(sdf_cond ? sdf_cond : "") +ModeValueDef::ModeValueDef(std::string_view value) : + value_(value) +{ +} + +ModeValueDef::ModeValueDef(ModeValueDef &&other) noexcept : + value_(std::move(other.value_)), + cond_(other.cond_), + sdf_cond_(std::move(other.sdf_cond_)) { + other.cond_ = nullptr; } ModeValueDef::~ModeValueDef() { - if (cond_) - cond_->deleteSubexprs(); + delete cond_; } void @@ -3155,34 +2870,38 @@ ModeValueDef::setCond(FuncExpr *cond) } void -ModeValueDef::setSdfCond(const char *sdf_cond) +ModeValueDef::setSdfCond(std::string sdf_cond) { - sdf_cond_ = sdf_cond; + sdf_cond_ = std::move(sdf_cond); } //////////////////////////////////////////////////////////////// -TableTemplate::TableTemplate(const char *name) : +TableTemplate::TableTemplate(std::string_view name) : + name_(name) +{ +} + +TableTemplate::TableTemplate(std::string_view name, + TableTemplateType type) : name_(name), - axis1_(nullptr), - axis2_(nullptr), - axis3_(nullptr) + type_(type) { } -TableTemplate::TableTemplate(const char *name, +TableTemplate::TableTemplate(std::string_view name, TableAxisPtr axis1, TableAxisPtr axis2, TableAxisPtr axis3) : name_(name), - axis1_(axis1), - axis2_(axis2), - axis3_(axis3) + axis1_(std::move(axis1)), + axis2_(std::move(axis2)), + axis3_(std::move(axis3)) { } void -TableTemplate::setName(const char *name) +TableTemplate::setName(std::string_view name) { name_ = name; } @@ -3190,26 +2909,26 @@ TableTemplate::setName(const char *name) void TableTemplate::setAxis1(TableAxisPtr axis) { - axis1_ = axis; + axis1_ = std::move(axis); } void TableTemplate::setAxis2(TableAxisPtr axis) { - axis2_ = axis; + axis2_ = std::move(axis); } void TableTemplate::setAxis3(TableAxisPtr axis) { - axis3_ = axis; + axis3_ = std::move(axis); } //////////////////////////////////////////////////////////////// Pvt::Pvt(float process, - float voltage, - float temperature) : + float voltage, + float temperature) : process_(process), voltage_(voltage), temperature_(temperature) @@ -3234,22 +2953,9 @@ Pvt::setTemperature(float temp) temperature_ = temp; } -OperatingConditions::OperatingConditions(const char *name) : +OperatingConditions::OperatingConditions(std::string_view name) : Pvt(0.0, 0.0, 0.0), - name_(name), - // Default wireload tree. - wire_load_tree_(WireloadTree::balanced) -{ -} - -OperatingConditions::OperatingConditions(const char *name, - float process, - float voltage, - float temperature, - WireloadTree wire_load_tree) : - Pvt(process, voltage, temperature), - name_(name), - wire_load_tree_(wire_load_tree) + name_(name) { } @@ -3263,7 +2969,8 @@ OperatingConditions::setWireloadTree(WireloadTree tree) static EnumNameMap scale_factor_type_map = {{ScaleFactorType::pin_cap, "pin_cap"}, - {ScaleFactorType::wire_cap, "wire_res"}, + {ScaleFactorType::wire_cap, "wire_cap"}, + {ScaleFactorType::wire_res, "wire_res"}, {ScaleFactorType::min_period, "min_period"}, {ScaleFactorType::cell, "cell"}, {ScaleFactorType::hold, "hold"}, @@ -3279,14 +2986,14 @@ static EnumNameMap scale_factor_type_map = {ScaleFactorType::unknown, "unknown"} }; -const char * +const std::string & scaleFactorTypeName(ScaleFactorType type) { return scale_factor_type_map.find(type); } ScaleFactorType -findScaleFactorType(const char *name) +findScaleFactorType(std::string_view name) { return scale_factor_type_map.find(name, ScaleFactorType::unknown); } @@ -3300,7 +3007,9 @@ scaleFactorTypeRiseFallSuffix(ScaleFactorType type) || type == ScaleFactorType::recovery || type == ScaleFactorType::removal || type == ScaleFactorType::nochange - || type == ScaleFactorType::skew; + || type == ScaleFactorType::skew + || type == ScaleFactorType::leakage_power + || type == ScaleFactorType::internal_power; } bool @@ -3320,16 +3029,17 @@ scaleFactorTypeLowHighSuffix(ScaleFactorType type) EnumNameMap scale_factor_pvt_names = {{ScaleFactorPvt::process, "process"}, {ScaleFactorPvt::volt, "volt"}, - {ScaleFactorPvt::temp, "temp"} + {ScaleFactorPvt::temp, "temp"}, + {ScaleFactorPvt::unknown, "unknown"} }; ScaleFactorPvt -findScaleFactorPvt(const char *name) +findScaleFactorPvt(std::string_view name) { return scale_factor_pvt_names.find(name, ScaleFactorPvt::unknown); } -const char * +const std::string & scaleFactorPvtName(ScaleFactorPvt pvt) { return scale_factor_pvt_names.find(pvt); @@ -3337,127 +3047,117 @@ scaleFactorPvtName(ScaleFactorPvt pvt) //////////////////////////////////////////////////////////////// -ScaleFactors::ScaleFactors(const char *name) : +ScaleFactors::ScaleFactors(std::string_view name) : name_(name) { - for (int type = 0; type < scale_factor_type_count; type++) { - for (int pvt = 0; pvt < scale_factor_pvt_count; pvt++) { - for (auto rf_index : RiseFall::rangeIndex()) { - scales_[type][pvt][rf_index] = 0.0; - } + for (auto &type_factors : scales_) { + for (auto pvt_factors : type_factors) { + for (size_t rf_index : RiseFall::rangeIndex()) + pvt_factors[rf_index] = 0.0; } } } void ScaleFactors::setScale(ScaleFactorType type, - ScaleFactorPvt pvt, - const RiseFall *rf, - float scale) + ScaleFactorPvt pvt, + const RiseFall *rf, + float scale) { - scales_[int(type)][int(pvt)][rf->index()] = scale; + scales_[static_cast(type)][static_cast(pvt)][rf->index()] = scale; } void ScaleFactors::setScale(ScaleFactorType type, - ScaleFactorPvt pvt, - float scale) + ScaleFactorPvt pvt, + float scale) { - scales_[int(type)][int(pvt)][0] = scale; + scales_[static_cast(type)][static_cast(pvt)][0] = scale; } float ScaleFactors::scale(ScaleFactorType type, - ScaleFactorPvt pvt, - const RiseFall *rf) + ScaleFactorPvt pvt, + const RiseFall *rf) { - return scales_[int(type)][int(pvt)][rf->index()]; + return scales_[static_cast(type)][static_cast(pvt)][rf->index()]; } float ScaleFactors::scale(ScaleFactorType type, - ScaleFactorPvt pvt, - int rf_index) + ScaleFactorPvt pvt, + int rf_index) { - return scales_[int(type)][int(pvt)][rf_index]; + return scales_[static_cast(type)][static_cast(pvt)][rf_index]; } float ScaleFactors::scale(ScaleFactorType type, - ScaleFactorPvt pvt) + ScaleFactorPvt pvt) { - return scales_[int(type)][int(pvt)][0]; + return scales_[static_cast(type)][static_cast(pvt)][0]; } void -ScaleFactors::print() +ScaleFactors::report(Report *report) { - printf("%10s", " "); + std::string line = " "; for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) { - ScaleFactorPvt pvt = (ScaleFactorPvt) pvt_index; - printf("%10s", scaleFactorPvtName(pvt)); + ScaleFactorPvt pvt = static_cast(pvt_index); + line += sta::format("{:>10}", scaleFactorPvtName(pvt)); } - printf("\n"); + report->reportLine(line); + for (int type_index = 0; type_index < scale_factor_type_count; type_index++) { - ScaleFactorType type = (ScaleFactorType) type_index; - printf("%10s ", scaleFactorTypeName(type)); + ScaleFactorType type = static_cast(type_index); + std::string line = sta::format("{:>10}", scaleFactorTypeName(type)); for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) { if (scaleFactorTypeRiseFallSuffix(type) - || scaleFactorTypeRiseFallPrefix(type) - || scaleFactorTypeLowHighSuffix(type)) { - printf(" %.3f,%.3f", - scales_[type_index][pvt_index][RiseFall::riseIndex()], - scales_[type_index][pvt_index][RiseFall::fallIndex()]); + || scaleFactorTypeRiseFallPrefix(type) + || scaleFactorTypeLowHighSuffix(type)) { + line += sta::format(" {:.3f},{:.3f}", + scales_[type_index][pvt_index][RiseFall::riseIndex()], + scales_[type_index][pvt_index][RiseFall::fallIndex()]); } else { - printf(" %.3f", - scales_[type_index][pvt_index][0]); + line += sta::format(" {:.3f}", + scales_[type_index][pvt_index][0]); } } - printf("\n"); + report->reportLine(line); } } TestCell::TestCell(LibertyLibrary *library, - const char *name, - const char *filename) : + std::string_view name, + std::string_view filename) : LibertyCell(library, name, filename) { } //////////////////////////////////////////////////////////////// -OcvDerate::OcvDerate(const char *name) : +OcvDerate::OcvDerate(std::string_view name) : name_(name) { - for (auto el_index : EarlyLate::rangeIndex()) { - for (auto rf_index : RiseFall::rangeIndex()) { - derate_[rf_index][el_index][int(PathType::clk)] = nullptr; - derate_[rf_index][el_index][int(PathType::data)] = nullptr; - } - } -} - -OcvDerate::~OcvDerate() -{ - stringDelete(name_); } const Table * OcvDerate::derateTable(const RiseFall *rf, - const EarlyLate *early_late, - PathType path_type) + const EarlyLate *early_late, + PathType path_type) { - return derate_[rf->index()][early_late->index()][int(path_type)].get(); + return derate_[rf->index()][early_late->index()][static_cast(path_type)].get(); } void OcvDerate::setDerateTable(const RiseFall *rf, - const EarlyLate *early_late, - const PathType path_type, - TablePtr derate) + const EarlyLate *early_late, + const PathType path_type, + TablePtr derate) { - derate_[rf->index()][early_late->index()][int(path_type)] = derate; + derate_[rf->index()][early_late->index()][static_cast(path_type)] + = std::move(derate); } -} // namespace +} // namespace sta diff --git a/liberty/Liberty.i b/liberty/Liberty.i index 12b0273ed..175ae5c78 100644 --- a/liberty/Liberty.i +++ b/liberty/Liberty.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,9 +22,8 @@ // // This notice may not be removed or altered from any source distribution. -%module liberty - %{ +#include "PatternMatch.hh" #include "PortDirection.hh" #include "Liberty.hh" #include "EquivCells.hh" @@ -116,12 +115,12 @@ private: bool read_liberty_cmd(char *filename, - Corner *corner, - const MinMaxAll *min_max, - bool infer_latches) + Scene *scene, + const MinMaxAll *min_max, + bool infer_latches) { Sta *sta = Sta::sta(); - LibertyLibrary *lib = sta->readLiberty(filename, corner, min_max, infer_latches); + LibertyLibrary *lib = sta->readLiberty(filename, scene, min_max, infer_latches); return (lib != nullptr); } @@ -151,21 +150,21 @@ find_equiv_cells(LibertyCell *cell) bool equiv_cells(LibertyCell *cell1, - LibertyCell *cell2) + LibertyCell *cell2) { return sta::equivCells(cell1, cell2); } bool equiv_cell_ports(LibertyCell *cell1, - LibertyCell *cell2) + LibertyCell *cell2) { return equivCellPorts(cell1, cell2); } bool equiv_cell_timing_arcs(LibertyCell *cell1, - LibertyCell *cell2) + LibertyCell *cell2) { return equivCellTimingArcSets(cell1, cell2); } @@ -179,12 +178,12 @@ find_library_buffers(LibertyLibrary *library) return new LibertyCellSeq(); } -const char * +std::string_view liberty_port_direction(const LibertyPort *port) { return port->direction()->name(); } - + bool liberty_supply_exists(const char *supply_name) { @@ -226,7 +225,7 @@ timing_role_is_check(const TimingRole *role) //////////////////////////////////////////////////////////////// %extend LibertyLibrary { -const char *name() { return self->name(); } +const char *name() { return self->name().c_str(); } LibertyCell * find_liberty_cell(const char *name) @@ -236,20 +235,20 @@ find_liberty_cell(const char *name) LibertyCellSeq find_liberty_cells_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); return self->findLibertyCellsMatching(&matcher); } -Wireload * +const Wireload * find_wireload(const char *model_name) { return self->findWireload(model_name); } -WireloadSelection * +const WireloadSelection * find_wireload_selection(const char *selection_name) { return self->findWireloadSelection(selection_name); @@ -270,13 +269,13 @@ default_operating_conditions() } // LibertyLibrary methods %extend LibertyCell { -const char *name() { return self->name(); } +const char *name() { return self->name().c_str(); } bool has_timing_model() { return self->hasTimingArcs(); } +bool is_leaf() { return self->isLeaf(); } bool is_buffer() { return self->isBuffer(); } bool is_clock_gate() { return self->isClockGate(); } bool is_integrated_clock_gating_cell() { return self->isClockGate(); } bool is_inverter() { return self->isInverter(); } -bool is_leaf() { return self->isLeaf(); } bool is_memory() { return self->isMemory(); } bool is_physical_only() { return self->isPhysicalOnly(); } bool is_sequential() { return self->hasSequentials(); } @@ -293,8 +292,8 @@ find_liberty_port(const char *name) LibertyPortSeq find_liberty_ports_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); return self->findLibertyPortsMatching(&matcher); @@ -312,9 +311,8 @@ timing_arc_sets() void ensure_voltage_waveforms() { - Corners *corners = Sta::sta()->corners(); - const DcalcAnalysisPtSeq &dcalc_aps = corners->dcalcAnalysisPts(); - self->ensureVoltageWaveforms(dcalc_aps); + const SceneSeq &scenes = Sta::sta()->scenes(); + self->ensureVoltageWaveforms(scenes); } LibertyCell *test_cell() { return self->testCell(); } @@ -322,7 +320,8 @@ LibertyCell *test_cell() { return self->testCell(); } } // LibertyCell methods %extend LibertyPort { -const char *bus_name() { return self->busName(); } +const char *name() { return self->name().c_str(); } +std::string bus_name() { return self->busName(); } Cell *cell() { return self->cell(); } bool is_bus() { return self->isBus(); } bool is_bus_bit() { return self->isBusBit(); } @@ -331,10 +330,9 @@ bool is_bundle_member() { return self->isBundleMember(); } bool has_members() { return self->hasMembers(); } LibertyPortMemberIterator * member_iterator() { return new LibertyPortMemberIterator(self); } -LibertyPort *bundle_port() { return self->bundlePort(); } bool is_pwr_gnd() { return self->isPwrGnd(); } -string +std::string function() { FuncExpr *func = self->function(); @@ -344,7 +342,7 @@ function() return ""; } -string +std::string tristate_enable() { FuncExpr *enable = self->tristateEnable(); @@ -355,11 +353,11 @@ tristate_enable() } float -capacitance(Corner *corner, - const MinMax *min_max) +capacitance(Scene *scene, + const MinMax *min_max) { Sta *sta = Sta::sta(); - return sta->capacitance(self, corner, min_max); + return sta->capacitance(self, scene, min_max); } void @@ -371,7 +369,7 @@ set_direction(const char *dir) const char * scan_signal_type() { - return scanSignalTypeName(self->scanSignalType()); + return scanSignalTypeName(self->scanSignalType()).c_str(); } } // LibertyPort methods @@ -379,19 +377,27 @@ scan_signal_type() %extend TimingArcSet { LibertyPort *from() { return self->from(); } LibertyPort *to() { return self->to(); } +std::string to_string() { return self->to_string(); } const TimingRole *role() { return self->role(); } -const char *sdf_cond() { return self->sdfCond(); } +const char *sdf_cond() { return self->sdfCond().c_str(); } -const char * +std::string full_name() { - const char *from = self->from()->name(); - const char *to = self->to()->name(); - const char *cell_name = self->libertyCell()->name(); - return stringPrintTmp("%s %s -> %s", - cell_name, - from, - to); + return sta::format("{} {} -> {}", + self->libertyCell()->name(), + self->from()->name(), + self->to()->name()); +} + +const std::string +when() +{ + const FuncExpr *when = self->when(); + if (when) + return when->to_string(); + else + return ""; } TimingArcSeq & @@ -403,9 +409,9 @@ timing_arcs() { return self->arcs(); } LibertyPort *from() { return self->from(); } LibertyPort *to() { return self->to(); } const Transition *from_edge() { return self->fromEdge(); } -const char *from_edge_name() { return self->fromEdge()->asRiseFall()->name(); } +const char *from_edge_name() { return self->fromEdge()->asRiseFall()->name().c_str(); } const Transition *to_edge() { return self->toEdge(); } -const char *to_edge_name() { return self->toEdge()->asRiseFall()->name(); } +const char *to_edge_name() { return self->toEdge()->asRiseFall()->name().c_str(); } const TimingRole *role() { return self->role(); } float @@ -464,7 +470,7 @@ voltage_time(float in_slew, return 0.0; } -Table1 +Table voltage_waveform(float in_slew, float load_cap) { @@ -472,14 +478,14 @@ voltage_waveform(float in_slew, if (gate_model) { OutputWaveforms *waveforms = gate_model->outputWaveforms(); if (waveforms) { - Table1 waveform = waveforms->voltageWaveform(in_slew, load_cap); + Table waveform = waveforms->voltageWaveform(in_slew, load_cap); return waveform; } } - return Table1(); + return Table(); } -const Table1 * +const Table * voltage_waveform_raw(float in_slew, float load_cap) { @@ -487,14 +493,14 @@ voltage_waveform_raw(float in_slew, if (gate_model) { OutputWaveforms *waveforms = gate_model->outputWaveforms(); if (waveforms) { - const Table1 *waveform = waveforms->voltageWaveformRaw(in_slew, load_cap); + const Table *waveform = waveforms->voltageWaveformRaw(in_slew, load_cap); return waveform; } } return nullptr; } -Table1 +Table current_waveform(float in_slew, float load_cap) { @@ -502,14 +508,14 @@ current_waveform(float in_slew, if (gate_model) { OutputWaveforms *waveforms = gate_model->outputWaveforms(); if (waveforms) { - Table1 waveform = waveforms->currentWaveform(in_slew, load_cap); + Table waveform = waveforms->currentWaveform(in_slew, load_cap); return waveform; } } - return Table1(); + return Table(); } -const Table1 * +const Table * current_waveform_raw(float in_slew, float load_cap) { @@ -517,14 +523,14 @@ current_waveform_raw(float in_slew, if (gate_model) { OutputWaveforms *waveforms = gate_model->outputWaveforms(); if (waveforms) { - const Table1 *waveform = waveforms->currentWaveformRaw(in_slew, load_cap); + const Table *waveform = waveforms->currentWaveformRaw(in_slew, load_cap); return waveform; } } return nullptr; } -Table1 +Table voltage_current_waveform(float in_slew, float load_cap) { @@ -532,11 +538,11 @@ voltage_current_waveform(float in_slew, if (gate_model) { OutputWaveforms *waveforms = gate_model->outputWaveforms(); if (waveforms) { - Table1 waveform = waveforms->voltageCurrentWaveform(in_slew, load_cap); + Table waveform = waveforms->voltageCurrentWaveform(in_slew, load_cap); return waveform; } } - return Table1(); + return Table(); } float diff --git a/liberty/Liberty.tcl b/liberty/Liberty.tcl index d4ac84a1d..698289c33 100644 --- a/liberty/Liberty.tcl +++ b/liberty/Liberty.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -35,7 +35,7 @@ proc_redirect read_liberty { check_argc_eq1 "read_liberty" $args set filename [file nativename [lindex $args 0]] - set corner [parse_corner keys] + set corner [parse_scene keys] set min_max [parse_min_max_all_flags flags] set infer_latches [info exists flags(-infer_latches)] read_liberty_cmd $filename $corner $min_max $infer_latches @@ -58,13 +58,13 @@ proc_redirect report_lib_cell { check_argc_eq1 "report_lib_cell" $args set arg [lindex $args 0] set cell [get_lib_cell_warn "lib_cell" $arg] - set corner [cmd_corner] + set scene [cmd_scene] if { $cell != "NULL" } { - report_lib_cell_ $cell $corner + report_lib_cell_ $cell $scene } } -proc report_lib_cell_ { cell corner } { +proc report_lib_cell_ { cell scene } { global sta_report_default_digits set lib [$cell liberty_library] @@ -74,25 +74,30 @@ proc report_lib_cell_ { cell corner } { if { $filename != "" } { report_line "File $filename" } + report_lib_ports $cell $scene + report_timing_arcs $cell +} + +proc report_lib_ports { cell scene } { set iter [$cell liberty_port_iterator] while {[$iter has_next]} { set port [$iter next] if { [$port is_bus] || [$port is_bundle] } { - report_lib_port $port $corner + report_lib_port $port $scene set member_iter [$port member_iterator] while { [$member_iter has_next] } { set port [$member_iter next] - report_lib_port $port $corner + report_lib_port $port $scene } $member_iter finish } elseif { ![$port is_bundle_member] && ![$port is_bus_bit] } { - report_lib_port $port $corner + report_lib_port $port $scene } } $iter finish } -proc report_lib_port { port corner } { +proc report_lib_port { port scene } { global sta_report_default_digits if { [$port is_bus] } { @@ -112,7 +117,26 @@ proc report_lib_port { port corner } { if { $func != "" } { set func " function=$func" } - report_line " ${indent}$port_name [liberty_port_direction $port]$enable$func[port_capacitance_str $port $corner $sta_report_default_digits]" + report_line " ${indent}$port_name [liberty_port_direction $port]$enable$func[port_capacitance_str $port $scene $sta_report_default_digits]" +} + +proc report_timing_arcs { cell } { + set timing_arcs [$cell timing_arc_sets] + if { [llength $timing_arcs] > 0 } { + puts "" + puts "Timing arcs" + foreach timing_arc $timing_arcs { + puts " [$timing_arc to_string]" + puts " [$timing_arc role]" + set when [$timing_arc when] + if { $when != "" } { + puts " when $when" + } + foreach arc [$timing_arc timing_arcs] { + puts " [$arc from_edge] -> [$arc to_edge]" + } + } + } } # sta namespace end diff --git a/liberty/LibertyBuilder.cc b/liberty/LibertyBuilder.cc index f536e54d0..c2571dd5e 100644 --- a/liberty/LibertyBuilder.cc +++ b/liberty/LibertyBuilder.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,44 +24,46 @@ #include "LibertyBuilder.hh" -#include "PortDirection.hh" -#include "TimingRole.hh" +#include +#include +#include + +#include "ConcreteLibrary.hh" #include "FuncExpr.hh" +#include "Liberty.hh" +#include "LibertyClass.hh" +#include "PortDirection.hh" +#include "Transition.hh" +#include "Sequential.hh" +#include "TableModel.hh" #include "TimingArc.hh" #include "TimingModel.hh" -#include "TableModel.hh" -#include "InternalPower.hh" -#include "LeakagePower.hh" -#include "Sequential.hh" -#include "Liberty.hh" +#include "TimingRole.hh" namespace sta { -using std::string; - -void -LibertyBuilder::init(Debug *debug, - Report *report) +LibertyBuilder::LibertyBuilder(Debug *debug, + Report *report) : + debug_(debug), + report_(report) { - debug_ = debug; - report_ = report; } LibertyCell * LibertyBuilder::makeCell(LibertyLibrary *library, - const char *name, - const char *filename) + std::string_view name, + std::string_view filename) { - LibertyCell *cell = new LibertyCell(library, name, filename); + LibertyCell *cell = new LibertyCell(library, std::string(name), std::string(filename)); library->addCell(cell); return cell; } LibertyPort * LibertyBuilder::makePort(LibertyCell *cell, - const char *port_name) + std::string_view port_name) { - LibertyPort *port = new LibertyPort(cell, port_name, false, nullptr, + LibertyPort *port = new LibertyPort(cell, std::string(port_name), false, nullptr, -1, -1, false, nullptr); cell->addPort(port); return port; @@ -69,14 +71,14 @@ LibertyBuilder::makePort(LibertyCell *cell, LibertyPort * LibertyBuilder::makeBusPort(LibertyCell *cell, - const char *bus_name, + std::string_view bus_name, int from_index, - int to_index, - BusDcl *bus_dcl) + int to_index, + BusDcl *bus_dcl) { - LibertyPort *port = new LibertyPort(cell, bus_name, true, bus_dcl, + LibertyPort *port = new LibertyPort(cell, std::string(bus_name), true, bus_dcl, from_index, to_index, - false, new ConcretePortSeq); + false, new ConcretePortSeq); cell->addPort(port); makeBusPortBits(cell->library(), cell, port, bus_name, from_index, to_index); return port; @@ -84,11 +86,11 @@ LibertyBuilder::makeBusPort(LibertyCell *cell, void LibertyBuilder::makeBusPortBits(ConcreteLibrary *library, - LibertyCell *cell, - ConcretePort *bus_port, - const char *bus_name, - int from_index, - int to_index) + LibertyCell *cell, + ConcretePort *bus_port, + std::string_view bus_name, + int from_index, + int to_index) { if (from_index < to_index) { for (int index = from_index; index <= to_index; index++) @@ -102,36 +104,35 @@ LibertyBuilder::makeBusPortBits(ConcreteLibrary *library, void LibertyBuilder::makeBusPortBit(ConcreteLibrary *library, - LibertyCell *cell, - ConcretePort *bus_port, - const char *bus_name, - int bit_index) + LibertyCell *cell, + ConcretePort *bus_port, + std::string_view bus_name, + int bit_index) { - string bit_name; - stringPrint(bit_name, "%s%c%d%c", - bus_name, - library->busBrktLeft(), - bit_index, - library->busBrktRight()); - LibertyPort *port = makePort(cell, bit_name.c_str(), bit_index); + std::string bit_name; + bit_name.append(bus_name); + bit_name += library->busBrktLeft(); + bit_name += std::to_string(bit_index); + bit_name += library->busBrktRight(); + LibertyPort *port = makePort(cell, std::move(bit_name), bit_index); bus_port->addPortBit(port); cell->addPortBit(port); } LibertyPort * LibertyBuilder::makePort(LibertyCell *cell, - const char *bit_name, - int bit_index) + std::string_view bit_name, + int bit_index) { LibertyPort *port = new LibertyPort(cell, bit_name, false, nullptr, - bit_index, bit_index, false, nullptr); + bit_index, bit_index, false, nullptr); return port; } LibertyPort * LibertyBuilder::makeBundlePort(LibertyCell *cell, - const char *name, - ConcretePortSeq *members) + std::string_view name, + ConcretePortSeq *members) { LibertyPort *port = new LibertyPort(cell, name, false, nullptr, -1, -1, true, members); cell->addPort(port); @@ -144,11 +145,10 @@ LibertyBuilder::makeBundlePort(LibertyCell *cell, TimingArcSet * LibertyBuilder::makeTimingArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - LibertyPort *related_out, - TimingArcAttrsPtr attrs, - int /* line */) + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + const TimingArcAttrsPtr &attrs) { FuncExpr *to_func = to_port->function(); Sequential *seq = (to_func && to_func->port()) @@ -191,6 +191,7 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell, case TimingType::combinational: if (seq && seq->isLatch() + && seq->data() && seq->data()->hasPort(from_port)) // Latch D->Q timing arcs. return makeLatchDtoQArcs(cell, from_port, to_port, @@ -203,20 +204,20 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell, return makeCombinationalArcs(cell, from_port, to_port, true, false, attrs); case TimingType::setup_rising: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::rise(), TimingRole::setup(), - attrs); + RiseFall::rise(), TimingRole::setup(), + attrs); case TimingType::setup_falling: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::fall(), TimingRole::setup(), - attrs); + RiseFall::fall(), TimingRole::setup(), + attrs); case TimingType::hold_rising: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::rise(), TimingRole::hold(), - attrs); + RiseFall::rise(), TimingRole::hold(), + attrs); case TimingType::hold_falling: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::fall(), TimingRole::hold(), - attrs); + RiseFall::fall(), TimingRole::hold(), + attrs); case TimingType::rising_edge: return makeRegLatchArcs(cell, from_port, to_port, RiseFall::rise(), attrs); case TimingType::falling_edge: @@ -227,20 +228,20 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell, return makePresetClrArcs(cell, from_port, to_port, RiseFall::fall(), attrs); case TimingType::recovery_rising: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::rise(),TimingRole::recovery(), - attrs); + RiseFall::rise(),TimingRole::recovery(), + attrs); case TimingType::recovery_falling: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::fall(),TimingRole::recovery(), - attrs); + RiseFall::fall(),TimingRole::recovery(), + attrs); case TimingType::removal_rising: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::rise(), TimingRole::removal(), - attrs); + RiseFall::rise(), TimingRole::removal(), + attrs); case TimingType::removal_falling: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::fall(), TimingRole::removal(), - attrs); + RiseFall::fall(), TimingRole::removal(), + attrs); case TimingType::three_state_disable: return makeTristateDisableArcs(cell, from_port, to_port, true, true, attrs); case TimingType::three_state_disable_fall: @@ -255,36 +256,32 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell, return makeTristateEnableArcs(cell, from_port, to_port, true, false, attrs); case TimingType::skew_falling: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::fall(), TimingRole::skew(), - attrs); + RiseFall::fall(), TimingRole::skew(), + attrs); case TimingType::skew_rising: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::rise(), TimingRole::skew(), - attrs); + RiseFall::rise(), TimingRole::skew(), + attrs); case TimingType::non_seq_setup_rising: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::rise(), - TimingRole::nonSeqSetup(), attrs); + RiseFall::rise(), TimingRole::nonSeqSetup(), + attrs); case TimingType::non_seq_setup_falling: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::fall(), - TimingRole::nonSeqSetup(), attrs); + RiseFall::fall(), TimingRole::nonSeqSetup(), + attrs); case TimingType::non_seq_hold_rising: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::rise(), - TimingRole::nonSeqHold(), - attrs); + RiseFall::rise(), TimingRole::nonSeqHold(), + attrs); case TimingType::non_seq_hold_falling: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::fall(), - TimingRole::nonSeqHold(), - attrs); + RiseFall::fall(), TimingRole::nonSeqHold(), + attrs); case TimingType::min_clock_tree_path: - return makeClockTreePathArcs(cell, to_port, TimingRole::clockTreePathMin(), - MinMax::min(), attrs); + return makeClockTreePathArcs(cell, to_port, TimingRole::clockTreePathMin(), attrs); case TimingType::max_clock_tree_path: - return makeClockTreePathArcs(cell, to_port, TimingRole::clockTreePathMax(), - MinMax::max(), attrs); + return makeClockTreePathArcs(cell, to_port, TimingRole::clockTreePathMax(), attrs); case TimingType::min_pulse_width: return makeMinPulseWidthArcs(cell, from_port, to_port, related_out, TimingRole::width(), attrs); @@ -305,16 +302,17 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell, TimingArcSet * LibertyBuilder::makeCombinationalArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - bool to_rise, - bool to_fall, - TimingArcAttrsPtr attrs) + LibertyPort *from_port, + LibertyPort *to_port, + bool to_rise, + bool to_fall, + const TimingArcAttrsPtr &attrs) { FuncExpr *func = to_port->function(); FuncExpr *enable = to_port->tristateEnable(); - TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, - TimingRole::combinational(), attrs); + TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr, + TimingRole::combinational(), + attrs); TimingSense sense = attrs->timingSense(); if (sense == TimingSense::unknown) { // Timing sense not specified - find it from function. @@ -340,13 +338,13 @@ LibertyBuilder::makeCombinationalArcs(LibertyCell *cell, to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, RiseFall::rise(), to_rf, model); + makeTimingArc(arc_set, RiseFall::rise(), to_rf, model); } if (to_fall) { to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, RiseFall::fall(), to_rf, model); + makeTimingArc(arc_set, RiseFall::fall(), to_rf, model); } break; case TimingSense::negative_unate: @@ -354,13 +352,13 @@ LibertyBuilder::makeCombinationalArcs(LibertyCell *cell, to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, RiseFall::rise(), to_rf, model); + makeTimingArc(arc_set, RiseFall::rise(), to_rf, model); } if (to_rise) { to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, RiseFall::fall(), to_rf, model); + makeTimingArc(arc_set, RiseFall::fall(), to_rf, model); } break; case TimingSense::non_unate: @@ -370,16 +368,16 @@ LibertyBuilder::makeCombinationalArcs(LibertyCell *cell, to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) { - makeTimingArc(arc_set, RiseFall::fall(), to_rf, model); - makeTimingArc(arc_set, RiseFall::rise(), to_rf, model); + makeTimingArc(arc_set, RiseFall::fall(), to_rf, model); + makeTimingArc(arc_set, RiseFall::rise(), to_rf, model); } } if (to_rise) { to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) { - makeTimingArc(arc_set, RiseFall::rise(), to_rf, model); - makeTimingArc(arc_set, RiseFall::fall(), to_rf, model); + makeTimingArc(arc_set, RiseFall::rise(), to_rf, model); + makeTimingArc(arc_set, RiseFall::fall(), to_rf, model); } } break; @@ -389,13 +387,14 @@ LibertyBuilder::makeCombinationalArcs(LibertyCell *cell, TimingArcSet * LibertyBuilder::makeLatchDtoQArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, + LibertyPort *from_port, + LibertyPort *to_port, TimingSense sense, - TimingArcAttrsPtr attrs) + const TimingArcAttrsPtr &attrs) { - TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, - TimingRole::latchDtoQ(), attrs); + TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr, + TimingRole::latchDtoQ(), + attrs); TimingModel *model; const RiseFall *to_rf = RiseFall::rise(); model = attrs->model(to_rf); @@ -418,32 +417,33 @@ LibertyBuilder::makeLatchDtoQArcs(LibertyCell *cell, TimingArcSet * LibertyBuilder::makeRegLatchArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - const RiseFall *from_rf, - TimingArcAttrsPtr attrs) + LibertyPort *from_port, + LibertyPort *to_port, + const RiseFall *from_rf, + const TimingArcAttrsPtr &attrs) { FuncExpr *to_func = to_port->function(); - FuncExprPortIterator port_iter(to_func); - while (port_iter.hasNext()) { - LibertyPort *func_port = port_iter.next(); - Sequential *seq = cell->outputPortSequential(func_port); - if (seq) { - if (seq->clock() && seq->clock()->hasPort(from_port)) { - const TimingRole *role = seq->isRegister() ? - TimingRole::regClkToQ() : TimingRole::latchEnToQ(); - return makeFromTransitionArcs(cell, from_port, to_port, nullptr, - from_rf, role, attrs); + if (to_func) { + LibertyPortSet to_ports = to_func->ports(); + for (LibertyPort *func_port : to_ports) { + Sequential *seq = cell->outputPortSequential(func_port); + if (seq) { + if (seq->clock() && seq->clock()->hasPort(from_port)) { + const TimingRole *role = seq->isRegister() ? + TimingRole::regClkToQ() : TimingRole::latchEnToQ(); + return makeFromTransitionArcs(cell, from_port, to_port, nullptr, + from_rf, role, attrs); + } + else if (seq->isLatch() + && seq->data() + && seq->data()->hasPort(from_port)) + return makeFromTransitionArcs(cell, from_port, to_port, nullptr, + from_rf, TimingRole::latchDtoQ(), attrs); + else if ((seq->clear() && seq->clear()->hasPort(from_port)) + || (seq->preset() && seq->preset()->hasPort(from_port))) + return makeFromTransitionArcs(cell, from_port, to_port, nullptr, + from_rf, TimingRole::regSetClr(), attrs); } - else if (seq->isLatch() - && seq->data() - && seq->data()->hasPort(from_port)) - return makeFromTransitionArcs(cell, from_port, to_port, nullptr, - from_rf, TimingRole::latchDtoQ(), attrs); - else if ((seq->clear() && seq->clear()->hasPort(from_port)) - || (seq->preset() && seq->preset()->hasPort(from_port))) - return makeFromTransitionArcs(cell, from_port, to_port, nullptr, - from_rf, TimingRole::regSetClr(), attrs); } } // No associated ff/latch - assume register clk->q. @@ -454,15 +454,15 @@ LibertyBuilder::makeRegLatchArcs(LibertyCell *cell, TimingArcSet * LibertyBuilder::makeFromTransitionArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - LibertyPort *related_out, - const RiseFall *from_rf, - const TimingRole *role, - TimingArcAttrsPtr attrs) + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + const RiseFall *from_rf, + const TimingRole *role, + const TimingArcAttrsPtr &attrs) { - TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, - related_out, role, attrs); + TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, + related_out, role, attrs); for (auto to_rf : RiseFall::range()) { TimingModel *model = attrs->model(to_rf); if (model) @@ -473,16 +473,16 @@ LibertyBuilder::makeFromTransitionArcs(LibertyCell *cell, TimingArcSet * LibertyBuilder::makePresetClrArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - const RiseFall *to_rf, - TimingArcAttrsPtr attrs) + LibertyPort *from_port, + LibertyPort *to_port, + const RiseFall *to_rf, + const TimingArcAttrsPtr &attrs) { TimingArcSet *arc_set = nullptr; TimingModel *model = attrs->model(to_rf); if (model) { - arc_set = makeTimingArcSet(cell, from_port, to_port, - TimingRole::regSetClr(), attrs); + arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr, + TimingRole::regSetClr(), attrs); const RiseFall *opp_rf = to_rf->opposite(); switch (attrs->timingSense()) { case TimingSense::positive_unate: @@ -508,14 +508,15 @@ LibertyBuilder::makePresetClrArcs(LibertyCell *cell, // 1Z, Z0 fall TimingArcSet * LibertyBuilder::makeTristateEnableArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - bool to_rise, - bool to_fall, - TimingArcAttrsPtr attrs) + LibertyPort *from_port, + LibertyPort *to_port, + bool to_rise, + bool to_fall, + const TimingArcAttrsPtr &attrs) { - TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, - TimingRole::tristateEnable(), attrs); + TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr, + TimingRole::tristateEnable(), + attrs); FuncExpr *tristate_enable = to_port->tristateEnable(); TimingSense sense = attrs->timingSense(); if (sense == TimingSense::unknown && tristate_enable) @@ -528,13 +529,13 @@ LibertyBuilder::makeTristateEnableArcs(LibertyCell *cell, to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, Transition::rise(), Transition::trZ1(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::trZ1(), model); } if (to_fall) { to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, Transition::rise(), Transition::trZ0(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::trZ0(), model); } break; case TimingSense::negative_unate: @@ -542,13 +543,13 @@ LibertyBuilder::makeTristateEnableArcs(LibertyCell *cell, to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, Transition::fall(), Transition::trZ1(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::trZ1(), model); } if (to_fall) { to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, Transition::fall(), Transition::trZ0(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::trZ0(), model); } break; case TimingSense::non_unate: @@ -557,16 +558,16 @@ LibertyBuilder::makeTristateEnableArcs(LibertyCell *cell, to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) { - makeTimingArc(arc_set, Transition::rise(), Transition::trZ1(), model); - makeTimingArc(arc_set, Transition::fall(), Transition::trZ1(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::trZ1(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::trZ1(), model); } } if (to_fall) { to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) { - makeTimingArc(arc_set, Transition::rise(), Transition::trZ0(), model); - makeTimingArc(arc_set, Transition::fall(), Transition::trZ0(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::trZ0(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::trZ0(), model); } } break; @@ -578,15 +579,15 @@ LibertyBuilder::makeTristateEnableArcs(LibertyCell *cell, TimingArcSet * LibertyBuilder::makeTristateDisableArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - bool to_rise, - bool to_fall, - TimingArcAttrsPtr attrs) + LibertyPort *from_port, + LibertyPort *to_port, + bool to_rise, + bool to_fall, + const TimingArcAttrsPtr &attrs) { - TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, - TimingRole::tristateDisable(), - attrs); + TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr, + TimingRole::tristateDisable(), + attrs); TimingSense sense = attrs->timingSense(); FuncExpr *tristate_enable = to_port->tristateEnable(); if (sense == TimingSense::unknown && tristate_enable) @@ -599,13 +600,13 @@ LibertyBuilder::makeTristateDisableArcs(LibertyCell *cell, to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, Transition::rise(), Transition::tr0Z(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::tr0Z(), model); } if (to_fall) { to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, Transition::rise(), Transition::tr1Z(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::tr1Z(), model); } break; case TimingSense::negative_unate: @@ -613,13 +614,13 @@ LibertyBuilder::makeTristateDisableArcs(LibertyCell *cell, to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, Transition::fall(), Transition::tr0Z(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::tr0Z(), model); } if (to_fall) { to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, Transition::fall(), Transition::tr1Z(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::tr1Z(), model); } break; case TimingSense::non_unate: @@ -628,16 +629,16 @@ LibertyBuilder::makeTristateDisableArcs(LibertyCell *cell, to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) { - makeTimingArc(arc_set, Transition::fall(), Transition::tr0Z(), model); - makeTimingArc(arc_set, Transition::rise(), Transition::tr0Z(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::tr0Z(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::tr0Z(), model); } } if (to_fall) { to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) { - makeTimingArc(arc_set, Transition::fall(), Transition::tr1Z(), model); - makeTimingArc(arc_set, Transition::rise(), Transition::tr1Z(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::tr1Z(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::tr1Z(), model); } } break; @@ -651,30 +652,25 @@ TimingArcSet * LibertyBuilder::makeClockTreePathArcs(LibertyCell *cell, LibertyPort *to_port, const TimingRole *role, - const MinMax *min_max, - TimingArcAttrsPtr attrs) + const TimingArcAttrsPtr &attrs) { - TimingArcSet *arc_set = makeTimingArcSet(cell, nullptr, to_port, role, attrs); - for (auto to_rf : RiseFall::range()) { + TimingArcSet *arc_set = cell->makeTimingArcSet(nullptr, to_port, nullptr, + role, attrs); + for (const RiseFall *to_rf : RiseFall::range()) { TimingModel *model = attrs->model(to_rf); if (model) { - const GateTableModel *gate_model = dynamic_cast(model); const RiseFall *opp_rf = to_rf->opposite(); switch (attrs->timingSense()) { case TimingSense::positive_unate: makeTimingArc(arc_set, to_rf, to_rf, model); - to_port->setClkTreeDelay(gate_model->delayModel(), to_rf, to_rf, min_max); break; case TimingSense::negative_unate: makeTimingArc(arc_set, opp_rf, to_rf, model); - to_port->setClkTreeDelay(gate_model->delayModel(), opp_rf, to_rf, min_max); break; case TimingSense::non_unate: case TimingSense::unknown: makeTimingArc(arc_set, to_rf, to_rf, model); makeTimingArc(arc_set, opp_rf, to_rf, model); - to_port->setClkTreeDelay(gate_model->delayModel(), to_rf, to_rf, min_max); - to_port->setClkTreeDelay(gate_model->delayModel(), opp_rf, to_rf, min_max); break; case TimingSense::none: break; @@ -690,12 +686,12 @@ LibertyBuilder::makeMinPulseWidthArcs(LibertyCell *cell, LibertyPort *to_port, LibertyPort *related_out, const TimingRole *role, - TimingArcAttrsPtr attrs) + const TimingArcAttrsPtr &attrs) { if (from_port == nullptr) from_port = to_port; - TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, related_out, - role, attrs); + TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, related_out, + role, attrs); for (const RiseFall *from_rf : RiseFall::range()) { TimingModel *model = attrs->model(from_rf); if (model) @@ -706,55 +702,23 @@ LibertyBuilder::makeMinPulseWidthArcs(LibertyCell *cell, //////////////////////////////////////////////////////////////// -TimingArcSet * -LibertyBuilder::makeTimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - const TimingRole *role, - TimingArcAttrsPtr attrs) -{ - return new TimingArcSet(cell, from, to, nullptr, role, attrs); -} - -TimingArcSet * -LibertyBuilder::makeTimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - const TimingRole *role, - TimingArcAttrsPtr attrs) -{ - return new TimingArcSet(cell, from, to, related_out, role, attrs); -} - TimingArc * LibertyBuilder::makeTimingArc(TimingArcSet *set, - const RiseFall *from_rf, - const RiseFall *to_rf, - TimingModel *model) + const RiseFall *from_rf, + const RiseFall *to_rf, + TimingModel *model) { return new TimingArc(set, from_rf->asTransition(), - to_rf->asTransition(), model); + to_rf->asTransition(), model); } TimingArc * LibertyBuilder::makeTimingArc(TimingArcSet *set, - const Transition *from_rf, - const Transition *to_rf, - TimingModel *model) + const Transition *from_rf, + const Transition *to_rf, + TimingModel *model) { return new TimingArc(set, from_rf, to_rf, model); } -//////////////////////////////////////////////////////////////// - -InternalPower * -LibertyBuilder::makeInternalPower(LibertyCell *cell, - LibertyPort *port, - LibertyPort *related_port, - InternalPowerAttrs *attrs) -{ - return new InternalPower(cell, port, related_port, attrs); -} - -} // namespace +} // namespace sta diff --git a/liberty/LibertyBuilder.hh b/liberty/LibertyBuilder.hh index 7aef355ed..ff4d89462 100644 --- a/liberty/LibertyBuilder.hh +++ b/liberty/LibertyBuilder.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,8 +24,10 @@ #pragma once +#include +#include + #include "MinMax.hh" -#include "Vector.hh" #include "Transition.hh" #include "LibertyClass.hh" #include "ConcreteLibrary.hh" @@ -33,139 +35,117 @@ namespace sta { class TimingArcAttrs; -class InternalPowerAttrs; -class LeakagePowerAttrs; class Debug; class Report; class LibertyBuilder { public: - LibertyBuilder() {} - virtual ~LibertyBuilder() {} - void init(Debug *debug, - Report *report); - virtual LibertyCell *makeCell(LibertyLibrary *library, - const char *name, - const char *filename); - virtual LibertyPort *makePort(LibertyCell *cell, - const char *name); - virtual LibertyPort *makeBusPort(LibertyCell *cell, - const char *bus_name, - int from_index, - int to_index, - BusDcl *bus_dcl); - virtual LibertyPort *makeBundlePort(LibertyCell *cell, - const char *name, - ConcretePortSeq *members); + LibertyBuilder(Debug *debug, + Report *report); + LibertyCell *makeCell(LibertyLibrary *library, + std::string_view name, + std::string_view filename); + LibertyPort *makePort(LibertyCell *cell, + std::string_view name); + LibertyPort *makeBusPort(LibertyCell *cell, + std::string_view bus_name, + int from_index, + int to_index, + BusDcl *bus_dcl); + LibertyPort *makeBundlePort(LibertyCell *cell, + std::string_view name, + ConcretePortSeq *members); // Build timing arc sets and their arcs given a type and sense. // Port functions and cell latches are also used by this builder // to get the correct roles. TimingArcSet *makeTimingArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - LibertyPort *related_out, - TimingArcAttrsPtr attrs, - int line); - InternalPower *makeInternalPower(LibertyCell *cell, - LibertyPort *port, - LibertyPort *related_port, - InternalPowerAttrs *attrs); - + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + const TimingArcAttrsPtr &attrs); TimingArcSet *makeFromTransitionArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - LibertyPort *related_out, - const RiseFall *from_rf, - const TimingRole *role, - TimingArcAttrsPtr attrs); + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + const RiseFall *from_rf, + const TimingRole *role, + const TimingArcAttrsPtr &attrs); TimingArcSet *makeCombinationalArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - bool to_rise, - bool to_fall, - TimingArcAttrsPtr attrs); + LibertyPort *from_port, + LibertyPort *to_port, + bool to_rise, + bool to_fall, + const TimingArcAttrsPtr &attrs); TimingArcSet *makeClockTreePathArcs(LibertyCell *cell, LibertyPort *to_port, const TimingRole *role, - const MinMax *min_max, - TimingArcAttrsPtr attrs); + const TimingArcAttrsPtr &attrs); TimingArcSet *makeMinPulseWidthArcs(LibertyCell *cell, LibertyPort *from_port, LibertyPort *to_port, LibertyPort *related_out, const TimingRole *role, - TimingArcAttrsPtr attrs); + const TimingArcAttrsPtr &attrs); protected: - ConcretePort *makeBusPort(const char *name, - int from_index, - int to_index, - ConcretePortSeq *members); + ConcretePort *makeBusPort(std::string_view name, + int from_index, + int to_index, + ConcretePortSeq *members); void makeBusPortBits(ConcreteLibrary *library, - LibertyCell *cell, - ConcretePort *bus_port, - const char *bus_name, - int from_index, - int to_index); + LibertyCell *cell, + ConcretePort *bus_port, + std::string_view bus_name, + int from_index, + int to_index); // Bus port bit (internal to makeBusPortBits). - virtual LibertyPort *makePort(LibertyCell *cell, - const char *bit_name, - int bit_index); + LibertyPort *makePort(LibertyCell *cell, + std::string_view bit_name, + int bit_index); void makeBusPortBit(ConcreteLibrary *library, - LibertyCell *cell, - ConcretePort *bus_port, - const char *bus_name, - int index); - virtual TimingArcSet *makeTimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - const TimingRole *role, - TimingArcAttrsPtr attrs); - virtual TimingArcSet *makeTimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - const TimingRole *role, - TimingArcAttrsPtr attrs); - virtual TimingArc *makeTimingArc(TimingArcSet *set, - const Transition *from_rf, - const Transition *to_rf, - TimingModel *model); + LibertyCell *cell, + ConcretePort *bus_port, + std::string_view bus_name, + int index); + TimingArc *makeTimingArc(TimingArcSet *set, + const Transition *from_rf, + const Transition *to_rf, + TimingModel *model); TimingArc *makeTimingArc(TimingArcSet *set, - const RiseFall *from_rf, - const RiseFall *to_rf, - TimingModel *model); + const RiseFall *from_rf, + const RiseFall *to_rf, + TimingModel *model); TimingArcSet *makeLatchDtoQArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, + LibertyPort *from_port, + LibertyPort *to_port, TimingSense sense, - TimingArcAttrsPtr attrs); + const TimingArcAttrsPtr &attrs); TimingArcSet *makeRegLatchArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - const RiseFall *from_rf, - TimingArcAttrsPtr attrs); + LibertyPort *from_port, + LibertyPort *to_port, + const RiseFall *from_rf, + const TimingArcAttrsPtr &attrs); TimingArcSet *makePresetClrArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - const RiseFall *to_rf, - TimingArcAttrsPtr attrs); + LibertyPort *from_port, + LibertyPort *to_port, + const RiseFall *to_rf, + const TimingArcAttrsPtr &attrs); TimingArcSet *makeTristateEnableArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - bool to_rise, - bool to_fall, - TimingArcAttrsPtr attrs); + LibertyPort *from_port, + LibertyPort *to_port, + bool to_rise, + bool to_fall, + const TimingArcAttrsPtr &attrs); TimingArcSet *makeTristateDisableArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - bool to_rise, - bool to_fall, - TimingArcAttrsPtr attrs); + LibertyPort *from_port, + LibertyPort *to_port, + bool to_rise, + bool to_fall, + const TimingArcAttrsPtr &attrs); Debug *debug_; Report *report_; }; -} // namespace +} // namespace sta diff --git a/liberty/LibertyExt.cc b/liberty/LibertyExt.cc deleted file mode 100644 index aa2850e81..000000000 --- a/liberty/LibertyExt.cc +++ /dev/null @@ -1,276 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -// This file illustratates how to customize the liberty file reader to -// read attributes that are not used by the STA. In this example: -// * code is called at the beginning of a library definition -// * a string attribute named "thingy" is parsed - -#include -#include "Machine.hh" -#include "StringUtil.hh" -#include "LibertyReader.hh" -#include "LibertyReaderPvt.hh" -#include "LibertyBuilder.hh" -#include "Network.hh" -#include "ConcreteNetwork.hh" -#include "Sta.hh" - -// Import symbols from sta package (this example is in the global package). -using sta::Report; -using sta::Debug; -using sta::Network; -using sta::LibertyReader; -using sta::LibertyAttr; -using sta::LibertyGroup; -using sta::TimingGroup; -using sta::LibertyCell; -using sta::LibertyPort; -using sta::LibertyLibrary; -using sta::TimingArcSet; -using sta::LibertyBuilder; -using sta::TimingRole; -using sta::TimingArcAttrs; -using sta::Sta; -using sta::stringCopy; - -// LibertyCell with Bigco thingy variable. -class BigcoCell : public LibertyCell -{ -public: - BigcoCell(LibertyLibrary *library, const char *name, const char *filename); - void setThingy(const char *thingy); - -protected: - const char *thingy_; -}; - -BigcoCell::BigcoCell(LibertyLibrary *library, const char *name, - const char *filename) : - LibertyCell(library, name, filename), - thingy_(0) -{ -} - -void -BigcoCell::setThingy(const char *thingy) -{ - thingy_ = thingy; -} - -//////////////////////////////////////////////////////////////// - -class BigcoTimingGroup : public TimingGroup -{ -public: - BigcoTimingGroup(int line); - const char *frob() const { return frob_; } - void setFrob(const char *frob); - -protected: - const char *frob_; -}; - -BigcoTimingGroup::BigcoTimingGroup(int line) : - TimingGroup(line), - frob_(0) -{ -} - -void -BigcoTimingGroup::setFrob(const char *frob) -{ - frob_ = frob; -} - -//////////////////////////////////////////////////////////////// - -class BigcoTimingArcSet : public TimingArcSet -{ -public: - BigcoTimingArcSet(LibertyCell *cell, LibertyPort *from, LibertyPort *to, - LibertyPort *related_out, TimingRole *role, - TimingArcAttrs *attrs); - -protected: - const char *frob_; -}; - -BigcoTimingArcSet::BigcoTimingArcSet(LibertyCell *cell, LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, TimingRole *role, - TimingArcAttrs *attrs) : - TimingArcSet(cell, from, to, related_out, role, attrs) -{ - const char *frob = static_cast(attrs)->frob(); - if (frob) - frob_ = stringCopy(frob); -} - -//////////////////////////////////////////////////////////////// - -// Make Bigco objects instead of Liberty objects. -class BigcoLibertyBuilder : public LibertyBuilder -{ -public: - virtual LibertyCell *makeCell(LibertyLibrary *library, const char *name, - const char *filename); - -protected: - virtual TimingArcSet *makeTimingArcSet(LibertyCell *cell, LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - TimingRole *role, - TimingArcAttrs *attrs); -}; - -LibertyCell * -BigcoLibertyBuilder::makeCell(LibertyLibrary *library, const char *name, - const char *filename) -{ - LibertyCell *cell = new BigcoCell(library, name, filename); - library->addCell(cell); - return cell; -} - -TimingArcSet * -BigcoLibertyBuilder::makeTimingArcSet(LibertyCell *cell, LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - TimingRole *role, - TimingArcAttrs *attrs) -{ - return new BigcoTimingArcSet(cell, from, to, related_out, role, attrs); -} - -//////////////////////////////////////////////////////////////// - -// Liberty reader to parse Bigco attributes. -class BigcoLibertyReader : public LibertyReader -{ -public: - BigcoLibertyReader(LibertyBuilder *builder); - -protected: - virtual void visitAttr1(LibertyAttr *attr); - virtual void visitAttr2(LibertyAttr *attr); - virtual void beginLibrary(LibertyGroup *group); - virtual TimingGroup *makeTimingGroup(int line); - virtual void beginCell(LibertyGroup *group); -}; - -BigcoLibertyReader::BigcoLibertyReader(LibertyBuilder *builder) : - LibertyReader(builder) -{ - // Define a visitor for the "thingy" attribute. - // Note that the function descriptor passed to defineAttrVisitor - // must be defined by the LibertyVisitor class, so a number of - // extra visitor functions are pre-defined for extensions. - defineAttrVisitor("thingy", &LibertyReader::visitAttr1); - defineAttrVisitor("frob", &LibertyReader::visitAttr2); -} - -bool -libertyCellRequired(const char *) -{ - // Predicate for cell names. - return true; -} - -// Prune cells from liberty file based on libertyCellRequired predicate. -void -BigcoLibertyReader::beginCell(LibertyGroup *group) -{ - const char *name = group->firstName(); - if (name - && libertyCellRequired(name)) - LibertyReader::beginCell(group); -} - -TimingGroup * -BigcoLibertyReader::makeTimingGroup(int line) -{ - return new BigcoTimingGroup(line); -} - -// Called at the beginning of a library group. -void -BigcoLibertyReader::beginLibrary(LibertyGroup *group) -{ - LibertyReader::beginLibrary(group); - // Do Bigco stuff here. - printf("Bigco was here.\n"); -} - -void -BigcoLibertyReader::visitAttr1(LibertyAttr *attr) -{ - const char *thingy = getAttrString(attr); - if (thingy) { - printf("Bigco thingy attribute value is %s.\n", thingy); - if (cell_) - static_cast(cell_)->setThingy(thingy); - } -} - -void -BigcoLibertyReader::visitAttr2(LibertyAttr *attr) -{ - const char *frob = getAttrString(attr); - if (frob) { - if (timing_) - static_cast(timing_)->setFrob(frob); - } -} - -//////////////////////////////////////////////////////////////// - -// Define a BigcoSta class derived from the Sta class to install the -// new liberty reader/visitor in BigcoSta::makeLibertyReader. -class BigcoSta : public Sta -{ -public: - BigcoSta(); - -protected: - virtual LibertyLibrary *readLibertyFile(const char *filename, - bool infer_latches, - Network *network); -}; - -BigcoSta::BigcoSta() : - Sta() -{ -} - -// Replace Sta liberty file reader with Bigco's very own. -LibertyLibrary * -Sta::readLibertyFile(const char *filename, - bool infer_latches, - Network *network) -{ - BigcoLibertyBuilder builder; - BigcoLibertyReader reader(&builder); - return reader.readLibertyFile(filename, infer_latches, network); -} diff --git a/liberty/LibertyLex.ll b/liberty/LibertyLex.ll index f2b18aef4..5add6b3bd 100644 --- a/liberty/LibertyLex.ll +++ b/liberty/LibertyLex.ll @@ -35,7 +35,7 @@ #undef YY_DECL #define YY_DECL \ int \ -sta::LibertyScanner::lex(sta::LibertyParse::semantic_type *const yylval, \ +sta::LibertyScanner::lex(sta::LibertyParse::semantic_type *yylval, \ sta::LibertyParse::location_type *loc) // update location on matching @@ -88,14 +88,14 @@ EOL \r?\n {FLOAT}{TOKEN_END} { /* Push back the TOKEN_END character. */ yyless(yyleng - 1); - yylval->number = strtod(yytext, nullptr); + yylval->emplace(strtof(yytext, nullptr)); return token::FLOAT; } {ALPHA}({ALPHA}|_|{DIGIT})*{TOKEN_END} { /* Push back the TOKEN_END character. */ yyless(yyleng - 1); - yylval->string = sta::stringCopy(yytext); + yylval->emplace(yytext, yyleng); return token::KEYWORD; } @@ -108,7 +108,7 @@ EOL \r?\n {TOKEN}{TOKEN_END} { /* Push back the TOKEN_END character. */ yyless(yyleng - 1); - yylval->string = sta::stringCopy(yytext); + yylval->emplace(yytext, yyleng); return token::STRING; } @@ -140,14 +140,14 @@ EOL \r?\n \" { BEGIN(INITIAL); - yylval->string = stringCopy(token_.c_str()); + yylval->emplace(token_); return token::STRING; } {EOL} { error("unterminated string constant"); BEGIN(INITIAL); - yylval->string = stringCopy(token_.c_str()); + yylval->emplace(token_); return token::STRING; } diff --git a/liberty/LibertyParse.yy b/liberty/LibertyParse.yy index 7cc067e34..f19a7c1fd 100644 --- a/liberty/LibertyParse.yy +++ b/liberty/LibertyParse.yy @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,9 @@ %{ #include +#include +#include +#include #include "Report.hh" #include "liberty/LibertyParser.hh" @@ -41,15 +44,15 @@ void sta::LibertyParse::error(const location_type &loc, const std::string &msg) { - reader->report()->fileError(164, reader->filename().c_str(), - loc.begin.line, "%s", msg.c_str()); + reader->report()->fileError(164, reader->filename(), + loc.begin.line, "{}", msg); } %} %require "3.2" %skeleton "lalr1.cc" -%debug +//%debug %define api.namespace {sta} %locations %define api.location.file "LibertyLocation.hh" @@ -57,32 +60,23 @@ sta::LibertyParse::error(const location_type &loc, %parse-param { LibertyScanner *scanner } %parse-param { LibertyParser *reader } %define api.parser.class {LibertyParse} +%define api.value.type variant %expect 2 -%union { - char *string; - float number; - char ch; - sta::LibertyAttrValue *attr_value; - sta::LibertyAttrValueSeq *attr_values; - sta::LibertyGroup *group; - sta::LibertyStmt *stmt; -} +%token STRING KEYWORD +%token FLOAT %left '+' '-' '|' %left '*' '/' '&' %left '^' %left '!' -%token FLOAT -%token STRING KEYWORD - -%type statement complex_attr simple_attr variable group file -%type attr_values -%type attr_value -%type string expr expr_term expr_term1 volt_expr -%type expr_op volt_op +%type statement complex_attr simple_attr variable group file +%type attr_values +%type attr_value +%type string expr expr_term expr_term1 volt_expr +%type expr_op volt_op %start file @@ -94,19 +88,19 @@ file: group: KEYWORD '(' ')' '{' - { reader->groupBegin($1, nullptr, loc_line(@1)); } + { reader->groupBegin(std::move($1), nullptr, loc_line(@1)); } '}' semi_opt { $$ = reader->groupEnd(); } | KEYWORD '(' ')' '{' - { reader->groupBegin($1, nullptr, loc_line(@1)); } + { reader->groupBegin(std::move($1), nullptr, loc_line(@1)); } statements '}' semi_opt { $$ = reader->groupEnd(); } | KEYWORD '(' attr_values ')' '{' - { reader->groupBegin($1, $3, loc_line(@1)); } + { reader->groupBegin(std::move($1), $3, loc_line(@1)); } '}' semi_opt { $$ = reader->groupEnd(); } | KEYWORD '(' attr_values ')' '{' - { reader->groupBegin($1, $3, loc_line(@1)); } + { reader->groupBegin(std::move($1), $3, loc_line(@1)); } statements '}' semi_opt { $$ = reader->groupEnd(); } ; @@ -125,14 +119,14 @@ statement: simple_attr: KEYWORD ':' attr_value semi_opt - { $$ = reader->makeSimpleAttr($1, $3, loc_line(@1)); } + { $$ = reader->makeSimpleAttr(std::move($1), $3, loc_line(@1)); } ; complex_attr: KEYWORD '(' ')' semi_opt - { $$ = reader->makeComplexAttr($1, nullptr, loc_line(@1)); } + { $$ = reader->makeComplexAttr(std::move($1), nullptr, loc_line(@1)); } | KEYWORD '(' attr_values ')' semi_opt - { $$ = reader->makeComplexAttr($1, $3, loc_line(@1)); } + { $$ = reader->makeComplexAttr(std::move($1), $3, loc_line(@1)); } ; attr_values: @@ -152,7 +146,7 @@ attr_values: variable: string '=' FLOAT semi_opt - { $$ = reader->makeVariable($1, $3, loc_line(@1)); } + { $$ = reader->makeVariable(std::move($1), $3, loc_line(@1)); } ; string: @@ -164,30 +158,24 @@ string: attr_value: FLOAT - { $$ = reader->makeFloatAttrValue($1); } + { $$ = reader->makeAttrValueFloat($1); } | expr - { $$ = reader->makeStringAttrValue($1); } + { $$ = reader->makeAttrValueString(std::move($1)); } | volt_expr - { $$ = reader->makeStringAttrValue($1); } + { $$ = reader->makeAttrValueString(std::move($1)); } ; /* Voltage expressions are ignored. */ /* Crafted to avoid conflicts with expr */ volt_expr: FLOAT volt_op FLOAT - { $$ = sta::stringPrint("%e%c%e", $1, $2, $3); } + { $$ = sta::format("{}{}{}", $1, $2, $3); } | string volt_op FLOAT - { $$ = sta::stringPrint("%s%c%e", $1, $2, $3); - sta::stringDelete($1); - } + { $$ = sta::format("{}{}{}", $1, $2, $3); } | FLOAT volt_op string - { $$ = sta::stringPrint("%e%c%s", $1, $2, $3); - sta::stringDelete($3); - } + { $$ = sta::format("{}{}{}", $1, $2, $3); } | volt_expr volt_op FLOAT - { $$ = sta::stringPrint("%s%c%e", $1, $2, $3); - sta::stringDelete($1); - } + { $$ = sta::format("{}{}{}", $1, $2, $3); } ; volt_op: @@ -204,34 +192,25 @@ volt_op: expr: expr_term1 | expr_term1 expr_op expr - { $$ = sta::stringPrint("%s%c%s", $1, $2, $3); - sta::stringDelete($1); - sta::stringDelete($3); - } + { $$ = sta::format("{}{}{}", $1, $2, $3); } ; expr_term: string | '0' - { $$ = sta::stringPrint("0"); } + { $$ = std::string("0"); } | '1' - { $$ = sta::stringPrint("1"); } + { $$ = std::string("1"); } | '(' expr ')' - { $$ = sta::stringPrint("(%s)", $2); - sta::stringDelete($2); - } + { $$ = "(" + $2 + ")"; } ; expr_term1: expr_term | '!' expr_term - { $$ = sta::stringPrint("!%s", $2); - sta::stringDelete($2); - } + { $$ = "!" + $2; } | expr_term '\'' - { $$ = sta::stringPrint("%s'", $1); - sta::stringDelete($1); - } + { $$ = $1 + "'"; } ; expr_op: diff --git a/liberty/LibertyParser.cc b/liberty/LibertyParser.cc index f54df4bc1..2c8388201 100644 --- a/liberty/LibertyParser.cc +++ b/liberty/LibertyParser.cc @@ -1,49 +1,53 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "LibertyParser.hh" -#include #include +#include #include +#include +#include +#include -#include "Zlib.hh" -#include "Report.hh" +#include "ContainerHelpers.hh" #include "Error.hh" -#include "StringUtil.hh" +#include "LibertyParse.hh" #include "LibertyScanner.hh" +#include "Report.hh" +#include "StringUtil.hh" +#include "util/gzstream.hh" namespace sta { -using std::string; - void -parseLibertyFile(const char *filename, - LibertyGroupVisitor *library_visitor, - Report *report) +parseLibertyFile(std::string_view filename, + LibertyGroupVisitor *library_visitor, + Report *report) { - gzstream::igzstream stream(filename); + std::string fn(filename); + gzstream::igzstream stream(fn.c_str()); if (stream.is_open()) { LibertyParser reader(filename, library_visitor, report); LibertyScanner scanner(&stream, filename, &reader, report); @@ -54,7 +58,7 @@ parseLibertyFile(const char *filename, throw FileNotReadable(filename); } -LibertyParser::LibertyParser(const char *filename, +LibertyParser::LibertyParser(std::string_view filename, LibertyGroupVisitor *library_visitor, Report *report) : filename_(filename), @@ -64,30 +68,32 @@ LibertyParser::LibertyParser(const char *filename, } void -LibertyParser::setFilename(const string &filename) +LibertyParser::setFilename(std::string_view filename) { filename_ = filename; } -LibertyStmt * -LibertyParser::makeDefine(LibertyAttrValueSeq *values, +LibertyDefine * +LibertyParser::makeDefine(const LibertyAttrValueSeq *values, int line) { LibertyDefine *define = nullptr; if (values->size() == 3) { - const char *define_name = (*values)[0]->stringValue(); - const char *group_type_name = (*values)[1]->stringValue(); - const char *value_type_name = (*values)[2]->stringValue(); + std::string &define_name = (*values)[0]->stringValue(); + const std::string &group_type_name = (*values)[1]->stringValue(); + const std::string &value_type_name = (*values)[2]->stringValue(); LibertyAttrType value_type = attrValueType(value_type_name); LibertyGroupType group_type = groupType(group_type_name); - define = new LibertyDefine(define_name, group_type, - value_type, line, group_type_name, value_type_name); + define = new LibertyDefine(std::move(define_name), group_type, value_type, line, group_type_name, value_type_name); LibertyGroup *group = this->group(); group->addDefine(define); group_visitor_->visitDefine(define); + for (auto value : *values) + delete value; + delete values; } else - report_->fileWarn(24, filename_.c_str(), line, + report_->fileWarn(24, filename_, line, "define does not have three arguments."); return define; } @@ -96,43 +102,48 @@ LibertyParser::makeDefine(LibertyAttrValueSeq *values, // used to define valid attribute types. Beyond "string" these are // guesses. LibertyAttrType -LibertyParser::attrValueType(const char *value_type_name) +LibertyParser::attrValueType(const std::string &value_type_name) { - if (stringEq(value_type_name, "string")) + if (value_type_name == "string") return LibertyAttrType::attr_string; - else if (stringEq(value_type_name, "integer")) + else if (value_type_name == "integer") return LibertyAttrType::attr_int; - else if (stringEq(value_type_name, "float")) + else if (value_type_name == "float") return LibertyAttrType::attr_double; - else if (stringEq(value_type_name, "boolean")) + else if (value_type_name == "boolean") return LibertyAttrType::attr_boolean; else return LibertyAttrType::attr_unknown; } LibertyGroupType -LibertyParser::groupType(const char *group_type_name) +LibertyParser::groupType(const std::string &group_type_name) { - if (stringEq(group_type_name, "library")) + if (group_type_name == "library") return LibertyGroupType::library; - else if (stringEq(group_type_name, "cell")) + else if (group_type_name == "cell") return LibertyGroupType::cell; - else if (stringEq(group_type_name, "pin")) + else if (group_type_name == "pin") return LibertyGroupType::pin; - else if (stringEq(group_type_name, "timing")) + else if (group_type_name == "timing") return LibertyGroupType::timing; else return LibertyGroupType::unknown; } void -LibertyParser::groupBegin(const char *type, +LibertyParser::groupBegin(std::string &&type, LibertyAttrValueSeq *params, int line) { - LibertyGroup *group = new LibertyGroup(type, params, line); - stringDelete(type); - group_visitor_->begin(group); + LibertyGroup *group = new LibertyGroup(std::move(type), + params + ? std::move(*params) + : LibertyAttrValueSeq(), + line); + delete params; + LibertyGroup *parent_group = group_stack_.empty() ? nullptr : group_stack_.back(); + group_visitor_->begin(group, parent_group); group_stack_.push_back(group); } @@ -140,18 +151,12 @@ LibertyGroup * LibertyParser::groupEnd() { LibertyGroup *group = this->group(); - group_visitor_->end(group); group_stack_.pop_back(); - LibertyGroup *parent = - group_stack_.empty() ? nullptr : group_stack_.back(); - if (parent && group_visitor_->save(group)) { + LibertyGroup *parent = group_stack_.empty() ? nullptr : group_stack_.back(); + if (parent) parent->addSubgroup(group); - return group; - } - else { - delete group; - return nullptr; - } + group_visitor_->end(group, parent); + return group; } LibertyGroup * @@ -163,397 +168,448 @@ LibertyParser::group() void LibertyParser::deleteGroups() { - group_stack_.deleteContentsClear(); + deleteContents(group_stack_); } -LibertyStmt * -LibertyParser::makeSimpleAttr(const char *name, - LibertyAttrValue *value, +LibertySimpleAttr * +LibertyParser::makeSimpleAttr(std::string &&name, + const LibertyAttrValue *value, int line) { - LibertyAttr *attr = new LibertySimpleAttr(name, value, line); - stringDelete(name); - group_visitor_->visitAttr(attr); + LibertySimpleAttr *attr = new LibertySimpleAttr(std::move(name), *value, line); + delete value; LibertyGroup *group = this->group(); - if (group && group_visitor_->save(attr)) { - group->addAttribute(attr); - return attr; - } - else { - delete attr; - return nullptr; - } + group->addAttr(attr); + group_visitor_->visitAttr(attr); + return attr; } -LibertyStmt * -LibertyParser::makeComplexAttr(const char *name, - LibertyAttrValueSeq *values, +LibertyComplexAttr * +LibertyParser::makeComplexAttr(std::string &&name, + const LibertyAttrValueSeq *values, int line) { // Defines have the same syntax as complex attributes. // Detect and convert them. - if (stringEq(name, "define")) { - LibertyStmt *define = makeDefine(values, line); - stringDelete(name); - LibertyAttrValueSeq::Iterator attr_iter(values); - while (attr_iter.hasNext()) - delete attr_iter.next(); - delete values; - return define; + if (name == "define") { + makeDefine(values, line); + return nullptr; // Define is not a complex attr; already added to group } else { - LibertyAttr *attr = new LibertyComplexAttr(name, values, line); - stringDelete(name); + LibertyComplexAttr *attr = new LibertyComplexAttr(std::move(name), *values, line); + delete values; + LibertyGroup *group = this->group(); + group->addAttr(attr); group_visitor_->visitAttr(attr); - if (group_visitor_->save(attr)) { - LibertyGroup *group = this->group(); - group->addAttribute(attr); - return attr; - } - delete attr; - return nullptr; + return attr; } } -LibertyStmt * -LibertyParser::makeVariable(const char *var, +LibertyVariable * +LibertyParser::makeVariable(std::string &&var, float value, int line) { - LibertyVariable *variable = new LibertyVariable(var, value, line); - stringDelete(var); + LibertyVariable *variable = new LibertyVariable(std::move(var), value, line); + LibertyGroup *group = this->group(); + group->addVariable(variable); group_visitor_->visitVariable(variable); - if (group_visitor_->save(variable)) - return variable; - else { - delete variable; - return nullptr; - } + return variable; } LibertyAttrValue * -LibertyParser::makeStringAttrValue(char *value) +LibertyParser::makeAttrValueString(std::string &&value) { - LibertyAttrValue *attr = new LibertyStringAttrValue(value); - stringDelete(value); - return attr; + return new LibertyAttrValue(std::move(value)); } LibertyAttrValue * -LibertyParser::makeFloatAttrValue(float value) +LibertyParser::makeAttrValueFloat(float value) { - return new LibertyFloatAttrValue(value); + return new LibertyAttrValue(value); } //////////////////////////////////////////////////////////////// -LibertyStmt::LibertyStmt(int line) : - line_(line) +LibertyScanner::LibertyScanner(std::istream *stream, + std::string_view filename, + LibertyParser *reader, + Report *report) : + yyFlexLexer(stream), + stream_(stream), + filename_(filename), + reader_(reader), + report_(report) { } -LibertyGroup::LibertyGroup(const char *type, - LibertyAttrValueSeq *params, - int line) : - LibertyStmt(line), - type_(type), - params_(params), - attrs_(nullptr), - attr_map_(nullptr), - subgroups_(nullptr), - define_map_(nullptr) +bool +LibertyScanner::includeBegin() { + if (stream_prev_ != nullptr) + error("nested include_file's are not supported"); + else { + // include_file(filename); + static const std::regex include_regexp("include_file *\\( *([^)]+) *\\) *;?"); + std::cmatch matches; + if (std::regex_match(yytext, matches, include_regexp)) { + std::string filename = matches[1].str(); + gzstream::igzstream *stream = new gzstream::igzstream(filename.c_str()); + if (stream->is_open()) { + yypush_buffer_state(yy_create_buffer(stream, 16384)); + + filename_prev_ = filename_; + stream_prev_ = stream_; + + filename_ = filename; + reader_->setFilename(filename); + stream_ = stream; + return true; + } + else { + report_->fileWarn(25, filename_, yylineno, + "cannot open include file {}.", filename); + delete stream; + } + } + else + error("include_file syntax error."); + } + return false; } void -LibertyGroup::addSubgroup(LibertyGroup *subgroup) +LibertyScanner::fileEnd() { - if (subgroups_ == nullptr) - subgroups_ = new LibertyGroupSeq; - subgroups_->push_back(subgroup); + if (stream_prev_) + delete stream_; + stream_ = stream_prev_; + filename_ = filename_prev_; + stream_prev_ = nullptr; + + yypop_buffer_state(); } void -LibertyGroup::addDefine(LibertyDefine *define) +LibertyScanner::error(const char *msg) +{ + report_->fileError(1866, filename_, lineno(), "{}", msg); +} + +//////////////////////////////////////////////////////////////// + +LibertyGroup::LibertyGroup(std::string type, + LibertyAttrValueSeq params, + int line) : + type_(std::move(type)), + params_(std::move(params)), + line_(line) { - if (define_map_ == nullptr) - define_map_ = new LibertyDefineMap; - const char *define_name = define->name(); - LibertyDefine *prev_define = define_map_->findKey(define_name); - if (prev_define) { - define_map_->erase(define_name); - delete prev_define; - } - (*define_map_)[define_name] = define; } +LibertyGroup::~LibertyGroup() { clear(); } + void -LibertyGroup::addAttribute(LibertyAttr *attr) +LibertyGroup::clear() { - if (attrs_ == nullptr) - attrs_ = new LibertyAttrSeq; - attrs_->push_back(attr); - if (attr_map_) - (*attr_map_)[attr->name()] = attr; + deleteContents(params_); + deleteContents(simple_attr_map_); + for (auto &attr : complex_attr_map_) + deleteContents(attr.second); + complex_attr_map_.clear(); + deleteContents(subgroups_); + subgroup_map_.clear(); + deleteContents(define_map_); + deleteContents(variables_); } -LibertyGroup::~LibertyGroup() +bool +LibertyGroup::empty() const { - if (params_) { - params_->deleteContents(); - delete params_; - } - if (attrs_) { - LibertyAttrSeq::Iterator iter(attrs_); - attrs_->deleteContents(); - delete attrs_; - delete attr_map_; - } - if (subgroups_) { - subgroups_->deleteContents(); - delete subgroups_; - } - if (define_map_) { - define_map_->deleteContents(); - delete define_map_; - } + return subgroups_.empty() && simple_attr_map_.empty() && complex_attr_map_.empty() + && define_map_.empty(); } -const char * -LibertyGroup::firstName() +bool +LibertyGroup::oneGroupOnly() const { - if (params_ && params_->size() > 0) { - LibertyAttrValue *value = (*params_)[0]; - if (value->isString()) - return value->stringValue(); - } - return nullptr; + return subgroups_.size() == 1 && simple_attr_map_.empty() + && complex_attr_map_.empty() && define_map_.empty(); } -const char * -LibertyGroup::secondName() +void +LibertyGroup::addSubgroup(LibertyGroup *subgroup) { - if (params_ && params_->size() > 1) { - LibertyAttrValue *value = (*params_)[1]; - if (value->isString()) - return value->stringValue(); - } - return nullptr; + subgroups_.push_back(subgroup); + subgroup_map_[subgroup->type()].push_back(subgroup); } -LibertyAttr * -LibertyGroup::findAttr(const char *name) +void +LibertyGroup::deleteSubgroup(const LibertyGroup *subgroup) { - if (attrs_) { - if (attr_map_ == nullptr) { - // Build attribute name map on demand. - LibertyAttrSeq::Iterator attr_iter(attrs_); - while (attr_iter.hasNext()) { - LibertyAttr *attr = attr_iter.next(); - (*attr_map_)[attr->name()] = attr; - } - } - return attr_map_->findKey(name); + if (subgroup == subgroups_.back()) { + subgroups_.pop_back(); + subgroup_map_[subgroup->type()].pop_back(); + delete subgroup; } else - return nullptr; + criticalError(1128, "LibertyAttrValue::floatValue() called on string"); } -LibertySubgroupIterator::LibertySubgroupIterator(LibertyGroup *group) : - LibertyGroupSeq::Iterator(group->subgroups()) +void +LibertyGroup::addDefine(LibertyDefine *define) { + const std::string &define_name = define->name(); + LibertyDefine *prev_define = findKey(define_map_, define_name); + if (prev_define) { + define_map_.erase(define_name); + delete prev_define; + } + define_map_[define_name] = define; } -LibertyAttrIterator::LibertyAttrIterator(LibertyGroup *group) : - LibertyAttrSeq::Iterator(group->attrs()) +void +LibertyGroup::addAttr(LibertySimpleAttr *attr) { + // Only keep the most recent simple attribute value. + const auto &it = simple_attr_map_.find(attr->name()); + if (it != simple_attr_map_.end()) + delete it->second; + simple_attr_map_[attr->name()] = attr; } -//////////////////////////////////////////////////////////////// +void +LibertyGroup::addAttr(LibertyComplexAttr *attr) +{ + complex_attr_map_[attr->name()].push_back(attr); +} -LibertyAttr::LibertyAttr(const char *name, - int line) : - LibertyStmt(line), - name_(name) +void +LibertyGroup::addVariable(LibertyVariable *var) { + variables_.push_back(var); } -LibertySimpleAttr::LibertySimpleAttr(const char *name, - LibertyAttrValue *value, - int line) : - LibertyAttr(name, line), - value_(value) +bool +LibertyGroup::hasFirstParam() const { + return !params_.empty(); } -LibertySimpleAttr::~LibertySimpleAttr() +const std::string & +LibertyGroup::firstParam() const { - delete value_; + LibertyAttrValue *value = params_[0]; + return value->stringValue(); } -LibertyAttrValueSeq * -LibertySimpleAttr::values() const +bool +LibertyGroup::hasSecondParam() const { - criticalError(1125, "valueIterator called for LibertySimpleAttribute"); - return nullptr; + return params_.size() >= 2; } -LibertyComplexAttr::LibertyComplexAttr(const char *name, - LibertyAttrValueSeq *values, - int line) : - LibertyAttr(name, line), - values_(values) +const std::string & +LibertyGroup::secondParam() const { + LibertyAttrValue *value = params_[1]; + return value->stringValue(); } -LibertyComplexAttr::~LibertyComplexAttr() +const LibertyGroupSeq & +LibertyGroup::findSubgroups(std::string_view type) const { - if (values_) { - values_->deleteContents(); - delete values_; - } + auto it = subgroup_map_.find(type); + if (it != subgroup_map_.end()) + return it->second; + static const LibertyGroupSeq empty; + return empty; } -LibertyAttrValue * -LibertyComplexAttr::firstValue() +const LibertyGroup * +LibertyGroup::findSubgroup(std::string_view type) const { - if (values_ && values_->size() > 0) - return (*values_)[0]; + const LibertyGroupSeq &groups = findSubgroups(type); + if (!groups.empty()) + return groups[0]; else return nullptr; } -LibertyStringAttrValue::LibertyStringAttrValue(const char *value) : - LibertyAttrValue(), - value_(value) +const LibertySimpleAttr * +LibertyGroup::findSimpleAttr(std::string_view attr_name) const { + return findStringKey(simple_attr_map_, attr_name); } -float -LibertyStringAttrValue::floatValue() +const LibertyComplexAttrSeq & +LibertyGroup::findComplexAttrs(std::string_view attr_name) const { - criticalError(1126, "LibertyStringAttrValue called for float value"); - return 0.0; + auto it = complex_attr_map_.find(attr_name); + if (it != complex_attr_map_.end()) + return it->second; + static const LibertyComplexAttrSeq empty; + return empty; } -const char * -LibertyStringAttrValue::stringValue() +const LibertyComplexAttr * +LibertyGroup::findComplexAttr(std::string_view attr_name) const { - return value_.c_str(); + const LibertyComplexAttrSeq &attrs = findComplexAttrs(attr_name); + if (!attrs.empty()) + return attrs[0]; + else + return nullptr; } -LibertyFloatAttrValue::LibertyFloatAttrValue(float value) : - value_(value) +const std::string & +LibertyGroup::findAttrString(std::string_view attr_name) const { + const LibertySimpleAttr *attr = findSimpleAttr(attr_name); + if (attr) + return attr->value().stringValue(); + static const std::string null_string; + return null_string; } -float -LibertyFloatAttrValue::floatValue() -{ - return value_; +void +LibertyGroup::findAttrFloat(std::string_view attr_name, + // Return values. + float &value, + bool &exists) const +{ + const LibertySimpleAttr *attr = findSimpleAttr(attr_name); + if (attr) { + const LibertyAttrValue &attr_value = attr->value(); + if (attr_value.isFloat()) { + auto [value1, exists1] = attr_value.floatValue(); + value = value1; + exists = exists1; + return; + } + else { + // Possibly quoted string float. + const std::string &float_str = attr_value.stringValue(); + auto [value1, valid1] = stringFloat(float_str); + value = value1; + exists = valid1; + return; + } + } + exists = false; } -const char * -LibertyFloatAttrValue::stringValue() -{ - criticalError(1127, "LibertyStringAttrValue called for float value"); - return nullptr; +void +LibertyGroup::findAttrInt(std::string_view attr_name, + // Return values. + int &value, + bool &exists) const +{ + const LibertySimpleAttr *attr = findSimpleAttr(attr_name); + if (attr) { + const LibertyAttrValue &attr_value = attr->value(); + if (attr_value.isFloat()) { + auto [value1, exists1] = attr_value.floatValue(); + value = static_cast(value1); + exists = exists1; + return; + } + } + exists = false; } //////////////////////////////////////////////////////////////// -LibertyDefine::LibertyDefine(const char *name, - LibertyGroupType group_type, - LibertyAttrType value_type, - int line, - const char *group_type_raw, - const char *value_type_raw) : - LibertyStmt(line), - name_(name), - group_type_(group_type), - value_type_(value_type), - group_type_raw_(group_type_raw), - value_type_raw_(value_type_raw) +LibertySimpleAttr::LibertySimpleAttr(std::string &&name, + LibertyAttrValue value, + int line) : + name_(std::move(name)), + line_(line), + value_(std::move(value)) { } //////////////////////////////////////////////////////////////// -LibertyVariable::LibertyVariable(const char *var, - float value, - int line) : - LibertyStmt(line), - var_(var), - value_(value) +LibertyComplexAttr::LibertyComplexAttr(std::string &&name, + LibertyAttrValueSeq values, + int line) : + name_(std::move(name)), + values_(std::move(values)), + line_(line) +{ +} + +LibertyComplexAttr::~LibertyComplexAttr() { deleteContents(values_); } + +const LibertyAttrValue * +LibertyComplexAttr::firstValue() const { + if (!values_.empty()) + return values_[0]; + else + return nullptr; } //////////////////////////////////////////////////////////////// -LibertyScanner::LibertyScanner(std::istream *stream, - const char *filename, - LibertyParser *reader, - Report *report) : - yyFlexLexer(stream), - stream_(stream), - filename_(filename), - reader_(reader), - report_(report), - stream_prev_(nullptr) +LibertyAttrValue::LibertyAttrValue(std::string &&value) : + string_value_(std::move(value)) { } -bool -LibertyScanner::includeBegin() +LibertyAttrValue::LibertyAttrValue(float value) : + float_value_(value) { - if (stream_prev_ != nullptr) - error("nested include_file's are not supported"); - else { - // include_file(filename); - std::regex include_regexp("include_file *\\( *([^)]+) *\\) *;?"); - std::cmatch matches; - if (std::regex_match(yytext, matches, include_regexp)) { - string filename = matches[1].str(); - gzstream::igzstream *stream = new gzstream::igzstream(filename.c_str()); - if (stream->is_open()) { - yypush_buffer_state(yy_create_buffer(stream, 256)); +} - filename_prev_ = filename_; - stream_prev_ = stream_; +bool +LibertyAttrValue::isFloat() const +{ + return string_value_.empty(); +} - filename_ = filename; - reader_->setFilename(filename); - stream_ = stream; - return true; - } - else { - report_->fileWarn(25, filename_.c_str(), yylineno, - "cannot open include file %s.", filename.c_str()); - delete stream; - } - } - else - error("include_file syntax error."); - } - return false; +bool +LibertyAttrValue::isString() const +{ + return !string_value_.empty(); } -void -LibertyScanner::fileEnd() +std::pair +LibertyAttrValue::floatValue() const { - if (stream_prev_) - delete stream_; - stream_ = stream_prev_; - filename_ = filename_prev_; - stream_prev_ = nullptr; + if (string_value_.empty()) + return {float_value_, true}; + else + return stringFloat(string_value_); +} - yypop_buffer_state(); +//////////////////////////////////////////////////////////////// + +LibertyDefine::LibertyDefine(std::string &&name, + LibertyGroupType group_type, + LibertyAttrType value_type, + int line, + std::string_view group_type_raw, + std::string_view value_type_raw) : + name_(std::move(name)), + group_type_(group_type), + value_type_(value_type), + line_(line), + group_type_raw_(group_type_raw), + value_type_raw_(value_type_raw) +{ } -void -LibertyScanner::error(const char *msg) +//////////////////////////////////////////////////////////////// + +LibertyVariable::LibertyVariable(std::string var, + float value, + int line) : + var_(std::move(var)), + value_(value), + line_(line) { - report_->fileError(1866, filename_.c_str(), lineno(), "%s", msg); } -} // namespace +} // namespace sta diff --git a/liberty/LibertyParser.hh b/liberty/LibertyParser.hh index c2ac11b4e..7f13397ef 100644 --- a/liberty/LibertyParser.hh +++ b/liberty/LibertyParser.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,73 +24,73 @@ #pragma once +#include +#include +#include +#include +#include + #include "Zlib.hh" -#include "Vector.hh" -#include "Map.hh" -#include "Set.hh" #include "StringUtil.hh" namespace sta { class Report; class LibertyGroupVisitor; -class LibertyAttrVisitor; -class LibertyStmt; class LibertyGroup; -class LibertyAttr; class LibertyDefine; +class LibertySimpleAttr; +class LibertyComplexAttr; class LibertyAttrValue; class LibertyVariable; -class LibertySubgroupIterator; -class LibertyAttrIterator; class LibertyScanner; -typedef Vector LibertyStmtSeq; -typedef Vector LibertyGroupSeq; -typedef Vector LibertyAttrSeq; -typedef Map LibertyAttrMap; -typedef Map LibertyDefineMap; -typedef Vector LibertyAttrValueSeq; -typedef Map LibertyVariableMap; -typedef MapLibertyGroupVisitorMap; -typedef LibertyAttrValueSeq::Iterator LibertyAttrValueIterator; -typedef Vector LibertyGroupSeq; +using LibertyGroupSeq = std::vector; +using LibertySubGroupMap = std::map>; +using LibertySimpleAttrMap = std::map>; +using LibertyComplexAttrSeq = std::vector; +using LibertyComplexAttrMap = std::map>; +using LibertyDefineMap = std::map>; +using LibertyAttrValueSeq = std::vector; +using LibertyVariableSeq = std::vector; +using LibertyVariableMap = std::map>; +using LibertyGroupVisitorMap = std::map>; enum class LibertyAttrType { attr_string, attr_int, attr_double, - attr_boolean, attr_unknown }; + attr_boolean, attr_unknown }; enum class LibertyGroupType { library, cell, pin, timing, unknown }; class LibertyParser { public: - LibertyParser(const char *filename, + LibertyParser(std::string_view filename, LibertyGroupVisitor *library_visitor, Report *report); const std::string &filename() const { return filename_; } - void setFilename(const std::string &filename); + void setFilename(std::string_view filename); Report *report() const { return report_; } - LibertyStmt *makeDefine(LibertyAttrValueSeq *values, - int line); - LibertyAttrType attrValueType(const char *value_type_name); - LibertyGroupType groupType(const char *group_type_name); - void groupBegin(const char *type, + LibertyDefine *makeDefine(const LibertyAttrValueSeq *values, + int line); + LibertyAttrType attrValueType(const std::string &value_type_name); + LibertyGroupType groupType(const std::string &group_type_name); + void groupBegin(std::string &&type, LibertyAttrValueSeq *params, int line); LibertyGroup *groupEnd(); LibertyGroup *group(); void deleteGroups(); - LibertyStmt *makeSimpleAttr(const char *name, - LibertyAttrValue *value, - int line); - LibertyStmt *makeComplexAttr(const char *name, - LibertyAttrValueSeq *values, - int line); - LibertyAttrValue *makeStringAttrValue(char *value); - LibertyAttrValue *makeFloatAttrValue(float value); - LibertyStmt *makeVariable(const char *var, - float value, - int line); + LibertySimpleAttr *makeSimpleAttr(std::string &&name, + const LibertyAttrValue *value, + int line); + LibertyComplexAttr *makeComplexAttr(std::string &&name, + const LibertyAttrValueSeq *values, + int line); + LibertyAttrValue *makeAttrValueString(std::string &&value); + LibertyAttrValue *makeAttrValueFloat(float value); + LibertyVariable *makeVariable(std::string &&var, + float value, + int line); private: std::string filename_; @@ -99,179 +99,146 @@ private: LibertyGroupSeq group_stack_; }; -// Abstract base class for liberty statements. -class LibertyStmt +// Attribute values are a string or float. +class LibertyAttrValue { public: - LibertyStmt(int line); - virtual ~LibertyStmt() {} - int line() const { return line_; } - virtual bool isGroup() const { return false; } - virtual bool isAttribute() const { return false; } - virtual bool isDefine() const { return false; } - virtual bool isVariable() const { return false; } + LibertyAttrValue(float value); + LibertyAttrValue(std::string &&value); + bool isString() const; + bool isFloat() const; + std::pair floatValue() const; + const std::string &stringValue() const { return string_value_; } + std::string &stringValue() { return string_value_; } -protected: - int line_; +private: + float float_value_; + std::string string_value_; }; // Groups are a type keyword with a set of parameters and statements // enclosed in brackets. // type([param1][, param2]...) { stmts.. } -class LibertyGroup : public LibertyStmt +class LibertyGroup { public: - LibertyGroup(const char *type, - LibertyAttrValueSeq *params, - int line); - virtual ~LibertyGroup(); - virtual bool isGroup() const { return true; } - const char *type() const { return type_.c_str(); } - // First param as a string. - const char *firstName(); - // Second param as a string. - const char *secondName(); - LibertyAttr *findAttr(const char *name); + LibertyGroup(std::string type, + LibertyAttrValueSeq params, + int line); + ~LibertyGroup(); + void clear(); + bool empty() const; + bool oneGroupOnly() const; + const std::string &type() const { return type_; } + const LibertyAttrValueSeq ¶ms() const { return params_; } + bool hasFirstParam() const; + const std::string &firstParam() const; + bool hasSecondParam() const; + const std::string &secondParam() const; + int line() const { return line_; } + + const LibertyGroupSeq &findSubgroups(std::string_view type) const; + const LibertyGroup *findSubgroup(std::string_view type) const; + const LibertySimpleAttr *findSimpleAttr(std::string_view attr_name) const; + const LibertyComplexAttrSeq &findComplexAttrs(std::string_view attr_name) const; + const LibertyComplexAttr *findComplexAttr(std::string_view attr_name) const; + const std::string &findAttrString(std::string_view attr_name) const; + void findAttrFloat(std::string_view attr_name, + // Return values. + float &value, + bool &exists) const; + void findAttrInt(std::string_view attr_name, + // Return values. + int &value, + bool &exists) const; + + const LibertyGroupSeq &subgroups() const { return subgroups_; } + const LibertyDefineMap &defineMap() const { return define_map_; } + void addSubgroup(LibertyGroup *subgroup); + void deleteSubgroup(const LibertyGroup *subgroup); + void addAttr(LibertySimpleAttr *attr); + void addAttr(LibertyComplexAttr *attr); void addDefine(LibertyDefine *define); - void addAttribute(LibertyAttr *attr); void addVariable(LibertyVariable *var); - LibertyGroupSeq *subgroups() const { return subgroups_; } - LibertyAttrSeq *attrs() const { return attrs_; } - LibertyAttrValueSeq *params() const { return params_; } protected: - void parseNames(LibertyAttrValueSeq *values); - std::string type_; - LibertyAttrValueSeq *params_; - LibertyAttrSeq *attrs_; - LibertyAttrMap *attr_map_; - LibertyGroupSeq *subgroups_; - LibertyDefineMap *define_map_; -}; - -class LibertySubgroupIterator : public LibertyGroupSeq::Iterator -{ -public: - LibertySubgroupIterator(LibertyGroup *group); -}; + LibertyAttrValueSeq params_; + int line_; -class LibertyAttrIterator : public LibertyAttrSeq::Iterator -{ -public: - LibertyAttrIterator(LibertyGroup *group); + LibertySimpleAttrMap simple_attr_map_; + LibertyComplexAttrMap complex_attr_map_; + LibertyGroupSeq subgroups_; + LibertySubGroupMap subgroup_map_; + LibertyDefineMap define_map_; + LibertyVariableSeq variables_; }; -// Abstract base class for attributes. -class LibertyAttr : public LibertyStmt +class LibertyGroupLineLess { public: - LibertyAttr(const char *name, - int line); - const char *name() const { return name_.c_str(); } - virtual bool isAttribute() const { return true; } - virtual bool isSimple() const = 0; - virtual bool isComplex() const = 0; - virtual LibertyAttrValueSeq *values() const = 0; - virtual LibertyAttrValue *firstValue() = 0; - -protected: - std::string name_; + bool + operator()(const LibertyGroup *group1, + const LibertyGroup *group2) const { + return group1->line() < group2->line(); + } }; -// Abstract base class for simple attributes. -// name : value; -class LibertySimpleAttr : public LibertyAttr +// Simple attributes: name : value; +class LibertySimpleAttr { public: - LibertySimpleAttr(const char *name, - LibertyAttrValue *value, - int line); - virtual ~LibertySimpleAttr(); - virtual bool isSimple() const { return true; } - virtual bool isComplex() const { return false; } - virtual LibertyAttrValue *firstValue() { return value_; } - virtual LibertyAttrValueSeq *values() const; + LibertySimpleAttr(std::string &&name, + LibertyAttrValue value, + int line); + const std::string &name() const { return name_; } + const LibertyAttrValue &value() const { return value_; }; + const std::string &stringValue() const { return value_.stringValue(); } + int line() const { return line_; } private: - LibertyAttrValue *value_; + std::string name_; + int line_; + LibertyAttrValue value_; }; // Complex attributes have multiple values. // name(attr_value1[, attr_value2]...); -class LibertyComplexAttr : public LibertyAttr -{ -public: - LibertyComplexAttr(const char *name, - LibertyAttrValueSeq *values, - int line); - virtual ~LibertyComplexAttr(); - virtual bool isSimple() const { return false; } - virtual bool isComplex() const { return true; } - virtual LibertyAttrValue *firstValue(); - virtual LibertyAttrValueSeq *values() const { return values_; } - -private: - LibertyAttrValueSeq *values_; -}; - -// Attribute values are a string or float. -class LibertyAttrValue -{ -public: - LibertyAttrValue() {} - virtual ~LibertyAttrValue() {} - virtual bool isString() = 0; - virtual bool isFloat() = 0; - virtual float floatValue() = 0; - virtual const char *stringValue() = 0; -}; - -class LibertyStringAttrValue : public LibertyAttrValue -{ -public: - LibertyStringAttrValue(const char *value); - virtual ~LibertyStringAttrValue() {} - virtual bool isFloat() { return false; } - virtual bool isString() { return true; } - virtual float floatValue(); - virtual const char *stringValue(); - -private: - std::string value_; -}; - -class LibertyFloatAttrValue : public LibertyAttrValue +class LibertyComplexAttr { public: - LibertyFloatAttrValue(float value); - virtual ~LibertyFloatAttrValue() {} - virtual bool isString() { return false; } - virtual bool isFloat() { return true; } - virtual float floatValue(); - virtual const char *stringValue(); + LibertyComplexAttr(std::string &&name, + LibertyAttrValueSeq values, + int line); + ~LibertyComplexAttr(); + const std::string &name() const { return name_; } + const LibertyAttrValue *firstValue() const; + const LibertyAttrValueSeq &values() const { return values_; } + int line() const { return line_; } private: - float value_; + std::string name_; + LibertyAttrValueSeq values_; + int line_; }; // Define statements define new simple attributes. // define(attribute_name, group_name, attribute_type); // attribute_type is string|integer|float. -class LibertyDefine : public LibertyStmt +class LibertyDefine { public: - LibertyDefine(const char *name, - LibertyGroupType group_type, - LibertyAttrType value_type, - int line, - const char *group_type_raw, - const char *value_type_raw); - virtual bool isDefine() const { return true; } - const char *name() const { return name_.c_str(); } + LibertyDefine(std::string &&name, + LibertyGroupType group_type, + LibertyAttrType value_type, + int line, + std::string_view group_type_raw, + std::string_view value_type_raw); + const std::string &name() const { return name_; } LibertyGroupType groupType() const { return group_type_; } LibertyAttrType valueType() const { return value_type_; } + int line() const { return line_; } std::string_view groupTypeRaw() const { return group_type_raw_; } std::string_view valueTypeRaw() const { return value_type_raw_; } @@ -279,6 +246,7 @@ private: std::string name_; LibertyGroupType group_type_; LibertyAttrType value_type_; + int line_; std::string group_type_raw_; std::string value_type_raw_; }; @@ -287,39 +255,37 @@ private: // var = value; // The only example I have only uses float values, so I am assuming // that is all that is supported (which is probably wrong). -class LibertyVariable : public LibertyStmt +class LibertyVariable { public: - LibertyVariable(const char *var, - float value, - int line); - virtual bool isVariable() const { return true; } - const char *variable() const { return var_.c_str(); } + LibertyVariable(std::string var, + float value, + int line); + int line() const { return line_; } + const std::string &variable() const { return var_; } float value() const { return value_; } private: std::string var_; float value_; + int line_; }; class LibertyGroupVisitor { public: - LibertyGroupVisitor() {} - virtual ~LibertyGroupVisitor() {} - virtual void begin(LibertyGroup *group) = 0; - virtual void end(LibertyGroup *group) = 0; - virtual void visitAttr(LibertyAttr *attr) = 0; + virtual void begin(const LibertyGroup *group, + LibertyGroup *parent_group) = 0; + virtual void end(const LibertyGroup *group, + LibertyGroup *parent_group) = 0; + virtual void visitAttr(const LibertySimpleAttr *attr) = 0; + virtual void visitAttr(const LibertyComplexAttr *attr) = 0; virtual void visitVariable(LibertyVariable *variable) = 0; - virtual void visitDefine(LibertyDefine *) { }; - // Predicates to save parse structure after visits. - virtual bool save(LibertyGroup *group) = 0; - virtual bool save(LibertyAttr *attr) = 0; - virtual bool save(LibertyVariable *variable) = 0; + virtual void visitDefine(LibertyDefine *) = 0; }; void -parseLibertyFile(const char *filename, - LibertyGroupVisitor *library_visitor, - Report *report); -} // namespace +parseLibertyFile(std::string_view filename, + LibertyGroupVisitor *library_visitor, + Report *report); +} // namespace sta diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index f31ac4264..c1e5b6b84 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,126 +26,74 @@ #include #include +#include +#include +#include #include +#include +#include -#include "EnumNameMap.hh" -#include "Report.hh" +#include "ConcreteLibrary.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" -#include "TokenParser.hh" -#include "Units.hh" -#include "Transition.hh" +#include "EnumNameMap.hh" +#include "EquivCells.hh" +#include "Format.hh" #include "FuncExpr.hh" -#include "TimingArc.hh" -#include "TableModel.hh" -#include "LeakagePower.hh" #include "InternalPower.hh" -#include "LinearModel.hh" -#include "Wireload.hh" -#include "EquivCells.hh" #include "LibExprReader.hh" #include "Liberty.hh" #include "LibertyBuilder.hh" +#include "LibertyClass.hh" +#include "LibertyParser.hh" #include "LibertyReaderPvt.hh" -#include "PortDirection.hh" -#include "ParseBus.hh" +#include "LinearModel.hh" +#include "MinMax.hh" #include "Network.hh" -#include "Clock.hh" +#include "NetworkClass.hh" +#include "ParseBus.hh" +#include "PortDirection.hh" +#include "Sequential.hh" +#include "StringUtil.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "TimingModel.hh" +#include "Transition.hh" +#include "Units.hh" +#include "Wireload.hh" extern int LibertyParse_debug; namespace sta { -using std::make_shared; -using std::string; - static void -scaleFloats(FloatSeq *floats, - float scale); +scaleFloats(FloatSeq &floats, + float scale); LibertyLibrary * -readLibertyFile(const char *filename, - bool infer_latches, - Network *network) +readLibertyFile(std::string_view filename, + bool infer_latches, + Network *network) { LibertyReader reader(filename, infer_latches, network); return reader.readLibertyFile(filename); } -LibertyReader::LibertyReader(const char *filename, +LibertyReader::LibertyReader(std::string_view filename, bool infer_latches, Network *network) : - LibertyGroupVisitor() + filename_(filename), + infer_latches_(infer_latches), + report_(network->report()), + debug_(network->debug()), + network_(network), + builder_(debug_, report_) { - init(filename, infer_latches, network); defineVisitors(); } -void -LibertyReader::init(const char *filename, - bool infer_latches, - Network *network) -{ - filename_ = filename; - infer_latches_ = infer_latches; - report_ = network->report(); - debug_ = network->debug(); - network_ = network; - var_map_ = nullptr; - library_ = nullptr; - wireload_ = nullptr; - wireload_selection_ = nullptr; - default_wireload_ = nullptr; - default_wireload_selection_ = nullptr; - scale_factors_ = nullptr; - save_scale_factors_ = nullptr; - tbl_template_ = nullptr; - cell_ = nullptr; - save_cell_ = nullptr; - scaled_cell_owner_ = nullptr; - test_cell_ = nullptr; - ocv_derate_name_ = nullptr; - op_cond_ = nullptr; - ports_ = nullptr; - port_group_ = nullptr; - saved_ports_ = nullptr; - saved_port_group_ = nullptr; - in_bus_ = false; - in_bundle_ = false; - in_ccsn_ = false; - in_ecsm_waveform_ = false; - sequential_ = nullptr; - statetable_ = nullptr; - timing_ = nullptr; - internal_power_ = nullptr; - leakage_power_ = nullptr; - table_ = nullptr; - rf_ = nullptr; - index_ = 0; - table_model_scale_ = 1.0; - mode_def_ = nullptr; - mode_value_ = nullptr; - ocv_derate_ = nullptr; - pg_port_ = nullptr; - default_operating_condition_ = nullptr; - receiver_model_ = nullptr; - - builder_.init(debug_, report_); - - for (auto rf_index : RiseFall::rangeIndex()) { - have_input_threshold_[rf_index] = false; - have_output_threshold_[rf_index] = false; - have_slew_lower_threshold_[rf_index] = false; - have_slew_upper_threshold_[rf_index] = false; - } -} - -LibertyReader::~LibertyReader() -{ - delete var_map_; -} - LibertyLibrary * -LibertyReader::readLibertyFile(const char *filename) +LibertyReader::readLibertyFile(std::string_view filename) { //::LibertyParse_debug = 1; parseLibertyFile(filename, this, report_); @@ -153,517 +101,186 @@ LibertyReader::readLibertyFile(const char *filename) } void -LibertyReader::defineGroupVisitor(const char *type, - LibraryGroupVisitor begin_visitor, - LibraryGroupVisitor end_visitor) +LibertyReader::defineGroupVisitor(std::string_view type, + LibraryGroupVisitor begin_visitor, + LibraryGroupVisitor end_visitor) { - group_begin_map_[type] = begin_visitor; - group_end_map_[type] = end_visitor; + std::string type_str(type); + if (begin_visitor) + group_begin_map_[type_str] = begin_visitor; + if (end_visitor) + group_end_map_[type_str] = end_visitor; } void -LibertyReader::defineAttrVisitor(const char *attr_name, - LibraryAttrVisitor visitor) +LibertyReader::defineVisitors() { - attr_visitor_map_[attr_name] = visitor; + defineGroupVisitor("library", &LibertyReader::beginLibrary, + &LibertyReader::endLibrary); + defineGroupVisitor("cell", nullptr, &LibertyReader::endCell); + defineGroupVisitor("scaled_cell", nullptr, &LibertyReader::endScaledCell); } void -LibertyReader::defineVisitors() +LibertyReader::visitAttr(const LibertySimpleAttr *) { - // Library - defineGroupVisitor("library", &LibertyReader::beginLibrary, - &LibertyReader::endLibrary); - defineAttrVisitor("time_unit", &LibertyReader::visitTimeUnit); - defineAttrVisitor("pulling_resistance_unit", - &LibertyReader::visitPullingResistanceUnit); - defineAttrVisitor("resistance_unit", &LibertyReader::visitResistanceUnit); - defineAttrVisitor("capacitive_load_unit", - &LibertyReader::visitCapacitiveLoadUnit); - defineAttrVisitor("voltage_unit", &LibertyReader::visitVoltageUnit); - defineAttrVisitor("current_unit", &LibertyReader::visitCurrentUnit); - defineAttrVisitor("leakage_power_unit", &LibertyReader::visitPowerUnit); - defineAttrVisitor("distance_unit", &LibertyReader::visitDistanceUnit); - defineAttrVisitor("delay_model", &LibertyReader::visitDelayModel); - defineAttrVisitor("bus_naming_style", &LibertyReader::visitBusStyle); - defineAttrVisitor("voltage_map", &LibertyReader::visitVoltageMap); - defineAttrVisitor("nom_temperature", &LibertyReader::visitNomTemp); - defineAttrVisitor("nom_voltage", &LibertyReader::visitNomVolt); - defineAttrVisitor("nom_process", &LibertyReader::visitNomProc); - defineAttrVisitor("default_inout_pin_cap", - &LibertyReader::visitDefaultInoutPinCap); - defineAttrVisitor("default_input_pin_cap", - &LibertyReader::visitDefaultInputPinCap); - defineAttrVisitor("default_output_pin_cap", - &LibertyReader::visitDefaultOutputPinCap); - defineAttrVisitor("default_max_transition", - &LibertyReader::visitDefaultMaxTransition); - defineAttrVisitor("default_max_fanout", - &LibertyReader::visitDefaultMaxFanout); - defineAttrVisitor("default_intrinsic_rise", - &LibertyReader::visitDefaultIntrinsicRise); - defineAttrVisitor("default_intrinsic_fall", - &LibertyReader::visitDefaultIntrinsicFall); - defineAttrVisitor("default_inout_pin_rise_res", - &LibertyReader::visitDefaultInoutPinRiseRes); - defineAttrVisitor("default_inout_pin_fall_res", - &LibertyReader::visitDefaultInoutPinFallRes); - defineAttrVisitor("default_output_pin_rise_res", - &LibertyReader::visitDefaultOutputPinRiseRes); - defineAttrVisitor("default_output_pin_fall_res", - &LibertyReader::visitDefaultOutputPinFallRes); - defineAttrVisitor("default_fanout_load", - &LibertyReader::visitDefaultFanoutLoad); - defineAttrVisitor("default_wire_load", - &LibertyReader::visitDefaultWireLoad); - defineAttrVisitor("default_wire_load_mode", - &LibertyReader::visitDefaultWireLoadMode); - defineAttrVisitor("default_wire_load_selection", - &LibertyReader::visitDefaultWireLoadSelection); - defineAttrVisitor("default_operating_conditions", - &LibertyReader::visitDefaultOperatingConditions); - defineAttrVisitor("input_threshold_pct_fall", - &LibertyReader::visitInputThresholdPctFall); - defineAttrVisitor("input_threshold_pct_rise", - &LibertyReader::visitInputThresholdPctRise); - defineAttrVisitor("output_threshold_pct_fall", - &LibertyReader::visitOutputThresholdPctFall); - defineAttrVisitor("output_threshold_pct_rise", - &LibertyReader::visitOutputThresholdPctRise); - defineAttrVisitor("slew_lower_threshold_pct_fall", - &LibertyReader::visitSlewLowerThresholdPctFall); - defineAttrVisitor("slew_lower_threshold_pct_rise", - &LibertyReader::visitSlewLowerThresholdPctRise); - defineAttrVisitor("slew_upper_threshold_pct_fall", - &LibertyReader::visitSlewUpperThresholdPctFall); - defineAttrVisitor("slew_upper_threshold_pct_rise", - &LibertyReader::visitSlewUpperThresholdPctRise); - defineAttrVisitor("slew_derate_from_library", - &LibertyReader::visitSlewDerateFromLibrary); - - defineGroupVisitor("lu_table_template", - &LibertyReader::beginTableTemplateDelay, - &LibertyReader::endTableTemplate); - defineGroupVisitor("output_current_template", - &LibertyReader::beginTableTemplateOutputCurrent, - &LibertyReader::endTableTemplate); - defineAttrVisitor("variable_1", &LibertyReader::visitVariable1); - defineAttrVisitor("variable_2", &LibertyReader::visitVariable2); - defineAttrVisitor("variable_3", &LibertyReader::visitVariable3); - defineAttrVisitor("index_1", &LibertyReader::visitIndex1); - defineAttrVisitor("index_2", &LibertyReader::visitIndex2); - defineAttrVisitor("index_3", &LibertyReader::visitIndex3); - - defineGroupVisitor("technology", - &LibertyReader::beginTechnology, - &LibertyReader::endTechnology); - defineGroupVisitor("rise_transition_degradation", - &LibertyReader::beginRiseTransitionDegredation, - &LibertyReader::endRiseFallTransitionDegredation); - defineGroupVisitor("fall_transition_degradation", - &LibertyReader::beginFallTransitionDegredation, - &LibertyReader::endRiseFallTransitionDegredation); - - defineGroupVisitor("type", &LibertyReader::beginType, - &LibertyReader::endType); - defineAttrVisitor("bit_from", &LibertyReader::visitBitFrom); - defineAttrVisitor("bit_to", &LibertyReader::visitBitTo); - - defineGroupVisitor("scaling_factors", &LibertyReader::beginScalingFactors, - &LibertyReader::endScalingFactors); - defineScalingFactorVisitors(); - - defineGroupVisitor("operating_conditions", &LibertyReader::beginOpCond, - &LibertyReader::endOpCond); - defineAttrVisitor("process", &LibertyReader::visitProc); - defineAttrVisitor("voltage", &LibertyReader::visitVolt); - defineAttrVisitor("temperature", &LibertyReader::visitTemp); - defineAttrVisitor("tree_type", &LibertyReader::visitTreeType); - - defineGroupVisitor("wire_load", &LibertyReader::beginWireload, - &LibertyReader::endWireload); - defineAttrVisitor("resistance", &LibertyReader::visitResistance); - defineAttrVisitor("slope", &LibertyReader::visitSlope); - defineAttrVisitor("fanout_length", &LibertyReader::visitFanoutLength); - - defineGroupVisitor("wire_load_selection", - &LibertyReader::beginWireloadSelection, - &LibertyReader::endWireloadSelection); - defineAttrVisitor("wire_load_from_area", - &LibertyReader::visitWireloadFromArea); - - // Cells - defineGroupVisitor("cell", &LibertyReader::beginCell, - &LibertyReader::endCell); - defineGroupVisitor("scaled_cell", &LibertyReader::beginScaledCell, - &LibertyReader::endScaledCell); - defineAttrVisitor("clock_gating_integrated_cell", - &LibertyReader::visitClockGatingIntegratedCell); - defineAttrVisitor("area", &LibertyReader::visitArea); - defineAttrVisitor("dont_use", &LibertyReader::visitDontUse); - defineAttrVisitor("is_macro_cell", &LibertyReader::visitIsMacro); - defineAttrVisitor("is_memory", &LibertyReader::visitIsMemory); - defineAttrVisitor("pad_cell", &LibertyReader::visitIsPadCell); - defineAttrVisitor("is_pad", &LibertyReader::visitIsPad); - defineAttrVisitor("is_clock_cell", &LibertyReader::visitIsClockCell); - defineAttrVisitor("is_level_shifter", &LibertyReader::visitIsLevelShifter); - defineAttrVisitor("level_shifter_type", &LibertyReader::visitLevelShifterType); - defineAttrVisitor("is_isolation_cell", &LibertyReader::visitIsIsolationCell); - defineAttrVisitor("always_on", &LibertyReader::visitAlwaysOn); - defineAttrVisitor("switch_cell_type", &LibertyReader::visitSwitchCellType); - defineAttrVisitor("interface_timing", &LibertyReader::visitInterfaceTiming); - defineAttrVisitor("scaling_factors", &LibertyReader::visitScalingFactors); - defineAttrVisitor("cell_footprint", &LibertyReader::visitCellFootprint); - defineAttrVisitor("user_function_class", - &LibertyReader::visitCellUserFunctionClass); - - // Generated clock - defineGroupVisitor("generated_clock", &LibertyReader::beginGeneratedClock, - &LibertyReader::endGeneratedClock); - defineAttrVisitor("clock_pin", &LibertyReader::visitClockPin); - defineAttrVisitor("master_pin", &LibertyReader::visitMasterPin); - defineAttrVisitor("divided_by", &LibertyReader::visitDividedBy); - defineAttrVisitor("multiplied_by", &LibertyReader::visitMultipliedBy); - defineAttrVisitor("duty_cycle", &LibertyReader::visitDutyCycle); - defineAttrVisitor("invert", &LibertyReader::visitInvert); - defineAttrVisitor("shifts", &LibertyReader::visitShifts); - defineAttrVisitor("edges", &LibertyReader::visitEdges); - - // Pins - defineGroupVisitor("pin", &LibertyReader::beginPin,&LibertyReader::endPin); - defineGroupVisitor("bus", &LibertyReader::beginBus,&LibertyReader::endBus); - defineGroupVisitor("bundle", &LibertyReader::beginBundle, - &LibertyReader::endBundle); - defineAttrVisitor("direction", &LibertyReader::visitDirection); - defineAttrVisitor("clock", &LibertyReader::visitClock); - defineAttrVisitor("bus_type", &LibertyReader::visitBusType); - defineAttrVisitor("members", &LibertyReader::visitMembers); - defineAttrVisitor("function", &LibertyReader::visitFunction); - defineAttrVisitor("three_state", &LibertyReader::visitThreeState); - defineAttrVisitor("capacitance", &LibertyReader::visitCapacitance); - defineAttrVisitor("rise_capacitance", &LibertyReader::visitRiseCap); - defineAttrVisitor("fall_capacitance", &LibertyReader::visitFallCap); - defineAttrVisitor("rise_capacitance_range", - &LibertyReader::visitRiseCapRange); - defineAttrVisitor("fall_capacitance_range", - &LibertyReader::visitFallCapRange); - defineAttrVisitor("fanout_load", &LibertyReader::visitFanoutLoad); - defineAttrVisitor("max_fanout", &LibertyReader::visitMaxFanout); - defineAttrVisitor("min_fanout", &LibertyReader::visitMinFanout); - defineAttrVisitor("max_transition", &LibertyReader::visitMaxTransition); - defineAttrVisitor("min_transition", &LibertyReader::visitMinTransition); - defineAttrVisitor("max_capacitance", &LibertyReader::visitMaxCapacitance); - defineAttrVisitor("min_capacitance", &LibertyReader::visitMinCapacitance); - defineAttrVisitor("min_period", &LibertyReader::visitMinPeriod); - defineAttrVisitor("min_pulse_width_low", - &LibertyReader::visitMinPulseWidthLow); - defineAttrVisitor("min_pulse_width_high", - &LibertyReader::visitMinPulseWidthHigh); - defineAttrVisitor("pulse_clock", - &LibertyReader::visitPulseClock); - defineAttrVisitor("clock_gate_clock_pin", - &LibertyReader::visitClockGateClockPin); - defineAttrVisitor("clock_gate_enable_pin", - &LibertyReader::visitClockGateEnablePin); - defineAttrVisitor("clock_gate_out_pin", - &LibertyReader::visitClockGateOutPin); - defineAttrVisitor("is_pll_feedback_pin", - &LibertyReader::visitIsPllFeedbackPin); - defineAttrVisitor("signal_type", &LibertyReader::visitSignalType); - - defineAttrVisitor("isolation_cell_data_pin", - &LibertyReader::visitIsolationCellDataPin); - defineAttrVisitor("isolation_cell_enable_pin", - &LibertyReader::visitIsolationCellEnablePin); - defineAttrVisitor("level_shifter_data_pin", - &LibertyReader::visitLevelShifterDataPin); - defineAttrVisitor("switch_pin", &LibertyReader::visitSwitchPin); - - // Memory - defineGroupVisitor("memory", &LibertyReader::beginMemory, - &LibertyReader::endMemory); - - // Register/latch - defineGroupVisitor("ff", &LibertyReader::beginFF, &LibertyReader::endFF); - defineGroupVisitor("ff_bank", &LibertyReader::beginFFBank, - &LibertyReader::endFFBank); - defineGroupVisitor("latch", &LibertyReader::beginLatch, - &LibertyReader::endLatch); - defineGroupVisitor("latch_bank", &LibertyReader::beginLatchBank, - &LibertyReader::endLatchBank); - defineAttrVisitor("clocked_on", &LibertyReader::visitClockedOn); - defineAttrVisitor("enable", &LibertyReader::visitClockedOn); - defineAttrVisitor("data_in", &LibertyReader::visitDataIn); - defineAttrVisitor("next_state", &LibertyReader::visitDataIn); - defineAttrVisitor("clear", &LibertyReader::visitClear); - defineAttrVisitor("preset", &LibertyReader::visitPreset); - defineAttrVisitor("clear_preset_var1", &LibertyReader::visitClrPresetVar1); - defineAttrVisitor("clear_preset_var2", &LibertyReader::visitClrPresetVar2); - - // Statetable - defineGroupVisitor("statetable", &LibertyReader::beginStatetable, - &LibertyReader::endStatetable); - defineAttrVisitor("table", &LibertyReader::visitTable); - - defineGroupVisitor("timing", &LibertyReader::beginTiming, - &LibertyReader::endTiming); - defineAttrVisitor("related_pin", &LibertyReader::visitRelatedPin); - defineAttrVisitor("related_bus_pins", &LibertyReader::visitRelatedBusPins); - defineAttrVisitor("related_output_pin", - &LibertyReader::visitRelatedOutputPin); - defineAttrVisitor("timing_type", &LibertyReader::visitTimingType); - defineAttrVisitor("timing_sense", &LibertyReader::visitTimingSense); - defineAttrVisitor("sdf_cond_start", &LibertyReader::visitSdfCondStart); - defineAttrVisitor("sdf_cond_end", &LibertyReader::visitSdfCondEnd); - defineAttrVisitor("mode", &LibertyReader::visitMode); - defineAttrVisitor("intrinsic_rise", &LibertyReader::visitIntrinsicRise); - defineAttrVisitor("intrinsic_fall", &LibertyReader::visitIntrinsicFall); - defineAttrVisitor("rise_resistance", &LibertyReader::visitRiseResistance); - defineAttrVisitor("fall_resistance", &LibertyReader::visitFallResistance); - defineGroupVisitor("cell_rise", &LibertyReader::beginCellRise, - &LibertyReader::endCellRiseFall); - defineGroupVisitor("cell_fall", &LibertyReader::beginCellFall, - &LibertyReader::endCellRiseFall); - defineGroupVisitor("rise_transition", &LibertyReader::beginRiseTransition, - &LibertyReader::endRiseFallTransition); - defineGroupVisitor("fall_transition", &LibertyReader::beginFallTransition, - &LibertyReader::endRiseFallTransition); - defineGroupVisitor("rise_constraint", &LibertyReader::beginRiseConstraint, - &LibertyReader::endRiseFallConstraint); - defineGroupVisitor("fall_constraint", &LibertyReader::beginFallConstraint, - &LibertyReader::endRiseFallConstraint); - defineAttrVisitor("value", &LibertyReader::visitValue); - defineAttrVisitor("values", &LibertyReader::visitValues); - - defineGroupVisitor("lut", &LibertyReader::beginLut,&LibertyReader::endLut); - - defineGroupVisitor("test_cell", &LibertyReader::beginTestCell, - &LibertyReader::endTestCell); - - defineGroupVisitor("mode_definition", &LibertyReader::beginModeDef, - &LibertyReader::endModeDef); - defineGroupVisitor("mode_value", &LibertyReader::beginModeValue, - &LibertyReader::endModeValue); - defineAttrVisitor("when", &LibertyReader::visitWhen); - defineAttrVisitor("sdf_cond", &LibertyReader::visitSdfCond); - - // Power attributes. - defineGroupVisitor("power_lut_template", - &LibertyReader::beginTableTemplatePower, - &LibertyReader::endTableTemplate); - defineGroupVisitor("leakage_power", &LibertyReader::beginLeakagePower, - &LibertyReader::endLeakagePower); - defineGroupVisitor("internal_power", &LibertyReader::beginInternalPower, - &LibertyReader::endInternalPower); - // power group for both rise/fall - defineGroupVisitor("power", &LibertyReader::beginRisePower, - &LibertyReader::endPower); - defineGroupVisitor("fall_power", &LibertyReader::beginFallPower, - &LibertyReader::endRiseFallPower); - defineGroupVisitor("rise_power", &LibertyReader::beginRisePower, - &LibertyReader::endRiseFallPower); - defineAttrVisitor("related_ground_pin",&LibertyReader::visitRelatedGroundPin); - defineAttrVisitor("related_power_pin", &LibertyReader::visitRelatedPowerPin); - defineAttrVisitor("related_pg_pin", &LibertyReader::visitRelatedPgPin); - - // AOCV attributes. - defineAttrVisitor("ocv_arc_depth", &LibertyReader::visitOcvArcDepth); - defineAttrVisitor("default_ocv_derate_group", - &LibertyReader::visitDefaultOcvDerateGroup); - defineAttrVisitor("ocv_derate_group", &LibertyReader::visitOcvDerateGroup); - defineGroupVisitor("ocv_table_template", - &LibertyReader::beginTableTemplateOcv, - &LibertyReader::endTableTemplate); - defineGroupVisitor("ocv_derate", - &LibertyReader::beginOcvDerate, - &LibertyReader::endOcvDerate); - defineGroupVisitor("ocv_derate_factors", - &LibertyReader::beginOcvDerateFactors, - &LibertyReader::endOcvDerateFactors); - defineAttrVisitor("rf_type", &LibertyReader::visitRfType); - defineAttrVisitor("derate_type", &LibertyReader::visitDerateType); - defineAttrVisitor("path_type", &LibertyReader::visitPathType); - - // POCV attributes. - defineGroupVisitor("ocv_sigma_cell_rise", &LibertyReader::beginOcvSigmaCellRise, - &LibertyReader::endOcvSigmaCell); - defineGroupVisitor("ocv_sigma_cell_fall", &LibertyReader::beginOcvSigmaCellFall, - &LibertyReader::endOcvSigmaCell); - defineGroupVisitor("ocv_sigma_rise_transition", - &LibertyReader::beginOcvSigmaRiseTransition, - &LibertyReader::endOcvSigmaTransition); - defineGroupVisitor("ocv_sigma_fall_transition", - &LibertyReader::beginOcvSigmaFallTransition, - &LibertyReader::endOcvSigmaTransition); - defineGroupVisitor("ocv_sigma_rise_constraint", - &LibertyReader::beginOcvSigmaRiseConstraint, - &LibertyReader::endOcvSigmaConstraint); - defineGroupVisitor("ocv_sigma_fall_constraint", - &LibertyReader::beginOcvSigmaFallConstraint, - &LibertyReader::endOcvSigmaConstraint); - defineAttrVisitor("sigma_type", &LibertyReader::visitSigmaType); - defineAttrVisitor("cell_leakage_power", &LibertyReader::visitCellLeakagePower); - - defineGroupVisitor("pg_pin", &LibertyReader::beginPgPin, - &LibertyReader::endPgPin); - defineAttrVisitor("pg_type", &LibertyReader::visitPgType); - defineAttrVisitor("voltage_name", &LibertyReader::visitVoltageName); - - // ccs receiver capacitance - defineGroupVisitor("receiver_capacitance", - &LibertyReader::beginReceiverCapacitance, - &LibertyReader::endReceiverCapacitance); - - defineGroupVisitor("receiver_capacitance_rise", - &LibertyReader::beginReceiverCapacitance1Rise, - &LibertyReader::endReceiverCapacitanceRiseFall); - defineGroupVisitor("receiver_capacitance_fall", - &LibertyReader::beginReceiverCapacitance1Fall, - &LibertyReader::endReceiverCapacitanceRiseFall); - defineAttrVisitor("segment", &LibertyReader::visitSegement); - - defineGroupVisitor("receiver_capacitance1_rise", - &LibertyReader::beginReceiverCapacitance1Rise, - &LibertyReader::endReceiverCapacitanceRiseFall); - defineGroupVisitor("receiver_capacitance1_fall", - &LibertyReader::beginReceiverCapacitance1Fall, - &LibertyReader::endReceiverCapacitanceRiseFall); - defineGroupVisitor("receiver_capacitance2_rise", - &LibertyReader::beginReceiverCapacitance2Rise, - &LibertyReader::endReceiverCapacitanceRiseFall); - defineGroupVisitor("receiver_capacitance2_fall", - &LibertyReader::beginReceiverCapacitance2Fall, - &LibertyReader::endReceiverCapacitanceRiseFall); - // ccs - defineGroupVisitor("output_current_rise", - &LibertyReader::beginOutputCurrentRise, - &LibertyReader::endOutputCurrentRiseFall); - defineGroupVisitor("output_current_fall", - &LibertyReader::beginOutputCurrentFall, - &LibertyReader::endOutputCurrentRiseFall); - defineGroupVisitor("vector", &LibertyReader::beginVector, &LibertyReader::endVector); - defineAttrVisitor("reference_time", &LibertyReader::visitReferenceTime); - defineGroupVisitor("normalized_driver_waveform", - &LibertyReader::beginNormalizedDriverWaveform, - &LibertyReader::endNormalizedDriverWaveform); - defineAttrVisitor("driver_waveform_name", &LibertyReader::visitDriverWaveformName); - defineAttrVisitor("driver_waveform_rise", &LibertyReader::visitDriverWaveformRise); - defineAttrVisitor("driver_waveform_fall", &LibertyReader::visitDriverWaveformFall); - - // ccsn (not implemented, this is needed to properly ignore ccsn groups) - defineGroupVisitor("ccsn_first_stage", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); - defineGroupVisitor("ccsn_last_stage", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); - defineGroupVisitor("output_voltage_rise", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); - defineGroupVisitor("output_voltage_fall", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); - defineGroupVisitor("propagated_noise_low", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); - defineGroupVisitor("propagated_noise_high", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); - defineGroupVisitor("input_ccb", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); - defineGroupVisitor("output_ccb", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); - - defineGroupVisitor("ecsm_waveform", &LibertyReader::beginEcsmWaveform, - &LibertyReader::endEcsmWaveform); - defineGroupVisitor("ecsm_waveform_set", &LibertyReader::beginEcsmWaveform, - &LibertyReader::endEcsmWaveform); - defineGroupVisitor("ecsm_capacitance", &LibertyReader::beginEcsmWaveform, - &LibertyReader::endEcsmWaveform); } void -LibertyReader::defineScalingFactorVisitors() +LibertyReader::visitAttr(const LibertyComplexAttr *) { - for (int type_index = 0; type_index < scale_factor_type_count; type_index++) { - ScaleFactorType type = static_cast(type_index); - const char *type_name = scaleFactorTypeName(type); - for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) { - ScaleFactorPvt pvt = static_cast(pvt_index); - const char *pvt_name = scaleFactorPvtName(pvt); - if (scaleFactorTypeRiseFallSuffix(type)) { - for (auto tr : RiseFall::range()) { - const char *tr_name = (tr == RiseFall::rise()) ? "rise":"fall"; - string attr_name; - stringPrint(attr_name, "k_%s_%s_%s", - pvt_name, - type_name, - tr_name); - defineAttrVisitor(attr_name.c_str() ,&LibertyReader::visitScaleFactorSuffix); - } - } - else if (scaleFactorTypeRiseFallPrefix(type)) { - for (auto tr : RiseFall::range()) { - const char *tr_name = (tr == RiseFall::rise()) ? "rise":"fall"; - string attr_name; - stringPrint(attr_name, "k_%s_%s_%s", - pvt_name, - tr_name, - type_name); - defineAttrVisitor(attr_name.c_str(),&LibertyReader::visitScaleFactorPrefix); - } - } - else if (scaleFactorTypeLowHighSuffix(type)) { - for (auto tr : RiseFall::range()) { - const char *tr_name = (tr == RiseFall::rise()) ? "high":"low"; - string attr_name; - stringPrint(attr_name, "k_%s_%s_%s", - pvt_name, - type_name, - tr_name); - defineAttrVisitor(attr_name.c_str(),&LibertyReader::visitScaleFactorHiLow); - } - } - else { - string attr_name; - stringPrint(attr_name, "k_%s_%s", - pvt_name, - type_name); - defineAttrVisitor(attr_name.c_str(),&LibertyReader::visitScaleFactor); - } - } - } } void -LibertyReader::visitAttr(LibertyAttr *attr) +LibertyReader::begin(const LibertyGroup *group, + LibertyGroup *parent_group) { - LibraryAttrVisitor visitor = attr_visitor_map_.findKey(attr->name()); + LibraryGroupVisitor *visitor = findKeyValuePtr(group_begin_map_, group->type()); if (visitor) - (this->*visitor)(attr); + (this->**visitor)(group, parent_group); } void -LibertyReader::begin(LibertyGroup *group) +LibertyReader::end(const LibertyGroup *group, + LibertyGroup *parent_group) { - LibraryGroupVisitor visitor = group_begin_map_.findKey(group->type()); + LibraryGroupVisitor *visitor = findKeyValuePtr(group_end_map_, group->type()); if (visitor) - (this->*visitor)(group); + (this->**visitor)(group, parent_group); } void -LibertyReader::end(LibertyGroup *group) +LibertyReader::beginLibrary(const LibertyGroup *library_group, + LibertyGroup *) { - LibraryGroupVisitor visitor = group_end_map_.findKey(group->type()); - if (visitor) - (this->*visitor)(group); + makeLibrary(library_group); +} + +void +LibertyReader::endLibrary(const LibertyGroup *library_group, + LibertyGroup *) +{ + // If a library has no cells endCell is not called. + if (!library_group->empty()) + readLibraryAttributes(library_group); + checkThresholds(library_group); + delete library_group; +} + +//////////////////////////////////////////////////////////////// + +void +LibertyReader::endCell(const LibertyGroup *cell_group, + LibertyGroup *library_group) +{ + // Read library groups defined since the last cell was read. + // Normally they are all defined by the first cell, but there + // are libraries that define table templates and bus tyupes + // between cells. + if (!library_group->oneGroupOnly()) + readLibraryAttributes(library_group); + + if (cell_group->hasFirstParam()) { + const std::string &name = cell_group->firstParam(); + debugPrint(debug_, "liberty", 1, "cell {}", name); + LibertyCell *cell = builder_.makeCell(library_, name, filename_); + readCell(cell, cell_group); + } + else + warn(1193, cell_group, "cell missing name."); + + // Delete the cell group and preceding library attributes + // and groups so they are not revisited and reduce memory peak. + library_group->clear(); } void -LibertyReader::beginLibrary(LibertyGroup *group) +LibertyReader::endScaledCell(const LibertyGroup *scaled_cell_group, + LibertyGroup *library_group) { - const char *name = group->firstName(); - if (name) { - LibertyLibrary *library = network_->findLiberty(name); + readLibraryAttributes(library_group); + readScaledCell(scaled_cell_group); + library_group->deleteSubgroup(scaled_cell_group); +} + +//////////////////////////////////////////////////////////////// + +void +LibertyReader::readLibraryAttributes(const LibertyGroup *library_group) +{ + readTechnology(library_group); + readLibraryUnits(library_group); + readThresholds(library_group); + readDelayModel(library_group); + readBusStyle(library_group); + readBusTypes(nullptr, library_group); + readTableTemplates(library_group); + readVoltateMaps(library_group); + readWireloads(library_group); + readWireloadSelection(library_group); + readDefaultWireLoad(library_group); + readDefaultWireLoadMode(library_group); + readDefaultWireLoadSelection(library_group); + readOperatingConds(library_group); + readScaleFactors(library_group); + readOcvDerateFactors(nullptr, library_group); + readDefaultOcvDerateGroup(library_group); + readGroupAttrFloat("ocv_arc_depth", library_group, + [this](float v) { library_->setOcvArcDepth(v); }); + readNormalizedDriverWaveform(library_group); + readSlewDegradations(library_group); + + readLibAttrFloat(library_group, "nom_temperature", + &LibertyLibrary::setNominalTemperature, 1.0F); + readLibAttrFloat(library_group, "nom_voltage", &LibertyLibrary::setNominalVoltage, + volt_scale_); + readLibAttrFloat(library_group, "nom_process", + &LibertyLibrary::setNominalProcess, 1.0F); + readLibAttrFloat(library_group, "default_inout_pin_cap", + &LibertyLibrary::setDefaultBidirectPinCap, cap_scale_); + readLibAttrFloat(library_group, "default_input_pin_cap", + &LibertyLibrary::setDefaultInputPinCap, cap_scale_); + readLibAttrFloat(library_group, "default_output_pin_cap", + &LibertyLibrary::setDefaultOutputPinCap, cap_scale_); + readLibAttrFloatWarnZero(library_group, "default_max_transition", + &LibertyLibrary::setDefaultMaxSlew, time_scale_); + readLibAttrFloatWarnZero(library_group, "default_max_fanout", + &LibertyLibrary::setDefaultMaxFanout, 1.0F); + readLibAttrFloat(library_group, "default_intrinsic_rise", + &LibertyLibrary::setDefaultIntrinsic, RiseFall::rise(), + time_scale_); + readLibAttrFloat(library_group, "default_intrinsic_fall", + &LibertyLibrary::setDefaultIntrinsic, RiseFall::fall(), + time_scale_); + readLibAttrFloat(library_group, "default_inout_pin_rise_res", + &LibertyLibrary::setDefaultBidirectPinRes, RiseFall::rise(), + res_scale_); + readLibAttrFloat(library_group, "default_inout_pin_fall_res", + &LibertyLibrary::setDefaultBidirectPinRes, RiseFall::fall(), + res_scale_); + readLibAttrFloat(library_group, "default_output_pin_rise_res", + &LibertyLibrary::setDefaultOutputPinRes, RiseFall::rise(), + res_scale_); + readLibAttrFloat(library_group, "default_output_pin_fall_res", + &LibertyLibrary::setDefaultOutputPinRes, RiseFall::fall(), + res_scale_); + readLibAttrFloatWarnZero(library_group, "default_fanout_load", + &LibertyLibrary::setDefaultFanoutLoad, 1.0F); + readLibAttrFloat(library_group, "slew_derate_from_library", + &LibertyLibrary::setSlewDerateFromLibrary, 1.0F); +} + +void +LibertyReader::makeLibrary(const LibertyGroup *library_group) +{ + if (library_group->hasFirstParam()) { + const std::string &lib_name = library_group->firstParam(); + LibertyLibrary *library = network_->findLiberty(lib_name); if (library) - libWarn(1140, group, "library %s already exists.", name); + warn(1140, library_group, "library {} already exists.", lib_name); // Make a new library even if a library with the same name exists. // Both libraries may be accessed by min/max analysis points. - library_ = network_->makeLibertyLibrary(name, filename_); + library_ = network_->makeLibertyLibrary(lib_name, filename_); // 1ns default time_scale_ = 1E-9F; // 1ohm default @@ -676,8 +293,6 @@ LibertyReader::beginLibrary(LibertyGroup *group) current_scale_ = 1E-3F; // Default is 1; power_scale_ = 1; - // Default is fJ. - setEnergyScale(); // Default is 1 micron. distance_scale_ = 1e-6; @@ -689,5810 +304,3432 @@ LibertyReader::beginLibrary(LibertyGroup *group) library_->units()->distanceUnit()->setScale(distance_scale_); library_->setDelayModelType(DelayModelType::cmos_linear); - scale_factors_ = new ScaleFactors(""); - library_->setScaleFactors(scale_factors_); } else - libError(1141, group, "library missing name."); + error(1141, library_group, "library missing name."); } -// Energy scale is derived. -void -LibertyReader::setEnergyScale() +// Energy scale is derived from other units. +float +LibertyReader::energyScale() { - energy_scale_ = volt_scale_ * volt_scale_ * cap_scale_; + return volt_scale_ * volt_scale_ * cap_scale_; } void -LibertyReader::endLibrary(LibertyGroup *group) +LibertyReader::readTechnology(const LibertyGroup *library_group) { - endLibraryAttrs(group); + const LibertyComplexAttr *tech_attr = library_group->findComplexAttr("technology"); + if (tech_attr) { + const LibertyAttrValue *tech_value = tech_attr->firstValue(); + if (tech_value) { + const std::string &tech = tech_value->stringValue(); + if (tech == "fpga") + library_->setDelayModelType(DelayModelType::cmos_linear); + } + } } void -LibertyReader::endLibraryAttrs(LibertyGroup *group) +LibertyReader::readLibraryUnits(const LibertyGroup *library_group) { - // These attributes reference named groups in the library so - // wait until the end of the library to resolve them. - if (default_wireload_) { - Wireload *wireload = library_->findWireload(default_wireload_); - if (wireload) - library_->setDefaultWireload(wireload); - else - libWarn(1142, group, "default_wire_load %s not found.", default_wireload_); - stringDelete(default_wireload_); - default_wireload_ = nullptr; - } + readUnit("time_unit", "s", time_scale_, library_->units()->timeUnit(), library_group); + readUnit("pulling_resistance_unit", "ohm", res_scale_, + library_->units()->resistanceUnit(), library_group); + readUnit("voltage_unit", "V", volt_scale_, library_->units()->voltageUnit(), + library_group); + readUnit("current_unit", "A", current_scale_, library_->units()->currentUnit(), + library_group); + readUnit("leakage_power_unit", "W", power_scale_, library_->units()->powerUnit(), + library_group); + readUnit("distance_unit", "m", distance_scale_, library_->units()->distanceUnit(), + library_group); - if (default_wireload_selection_) { - WireloadSelection *selection = - library_->findWireloadSelection(default_wireload_selection_); - if (selection) - library_->setDefaultWireloadSelection(selection); + const LibertyComplexAttr *cap_attr = + library_group->findComplexAttr("capacitive_load_unit"); + if (cap_attr) { + const LibertyAttrValueSeq &values = cap_attr->values(); + if (values.size() == 2) { + LibertyAttrValue *value = values[0]; + auto [scale, valid] = value->floatValue(); + if (valid) { + value = values[1]; + if (value->isString()) { + const std::string suffix = value->stringValue(); + if (stringEqual(suffix, "ff")) + cap_scale_ = scale * 1E-15F; + else if (stringEqual(suffix, "pf")) + cap_scale_ = scale * 1E-12F; + else + warn(1154, cap_attr, "capacitive_load_units are not ff or pf."); + } + else + warn(1155, cap_attr, "capacitive_load_units are not a string."); + } + else + warn(1157, cap_attr, "capacitive_load_units scale is not a float."); + } + else if (values.size() == 1) + warn(1156, cap_attr, "capacitive_load_units missing suffix."); else - libWarn(1143, group, "default_wire_selection %s not found.", - default_wireload_selection_); - stringDelete(default_wireload_selection_); - default_wireload_selection_ = nullptr; + warn(1158, cap_attr, "capacitive_load_units missing scale and suffix."); + library_->units()->capacitanceUnit()->setScale(cap_scale_); } +} - if (default_operating_condition_) { - OperatingConditions *op_cond = - library_->findOperatingConditions(default_operating_condition_); - if (op_cond) - library_->setDefaultOperatingConditions(op_cond); - else - libWarn(1144, group, "default_operating_condition %s not found.", - default_operating_condition_); - stringDelete(default_operating_condition_); - default_operating_condition_ = nullptr; +void +LibertyReader::readUnit(std::string_view unit_attr_name, + std::string_view unit_suffix, + float &scale_var, + Unit *unit, + const LibertyGroup *library_group) +{ + const LibertySimpleAttr *unit_attr = library_group->findSimpleAttr(unit_attr_name); + if (unit_attr) { + const std::string &units = unit_attr->stringValue(); + if (!units.empty()) { + // Unit format is . + // Find the multiplier digits. + size_t mult_end = units.find_first_not_of("0123456789"); + float mult = 1.0F; + std::string scale_suffix; + if (mult_end != std::string::npos) { + std::string unit_mult = units.substr(0, mult_end); + scale_suffix = units.substr(mult_end); + if (unit_mult == "1") + mult = 1.0F; + else if (unit_mult == "10") + mult = 10.0F; + else if (unit_mult == "100") + mult = 100.0F; + else + warn(1150, unit_attr, "unknown unit multiplier {}.", unit_mult); + } + else + scale_suffix = units; + + float scale_mult = 1.0F; + if (scale_suffix.size() == unit_suffix.size() + 1) { + std::string suffix = scale_suffix.substr(1); + if (stringEqual(suffix, unit_suffix)) { + char scale_char = tolower(scale_suffix[0]); + if (scale_char == 'k') + scale_mult = 1E+3F; + else if (scale_char == 'm') + scale_mult = 1E-3F; + else if (scale_char == 'u') + scale_mult = 1E-6F; + else if (scale_char == 'n') + scale_mult = 1E-9F; + else if (scale_char == 'p') + scale_mult = 1E-12F; + else if (scale_char == 'f') + scale_mult = 1E-15F; + else + warn(1151, unit_attr, "unknown unit scale {}.", scale_char); + } + else + warn(1152, unit_attr, "unknown unit suffix {}.", suffix); + } + else if (!stringEqual(scale_suffix, unit_suffix)) + warn(1153, unit_attr, "unknown unit suffix {}.", scale_suffix); + scale_var = scale_mult * mult; + unit->setScale(scale_var); + } } +} - bool missing_threshold = false; - for (auto rf : RiseFall::range()) { - int rf_index = rf->index(); - if (!have_input_threshold_[rf_index]) { - libWarn(1145, group, "input_threshold_pct_%s not found.", rf->name()); - missing_threshold = true; +void +LibertyReader::readDelayModel(const LibertyGroup *library_group) +{ + const std::string &type_name = library_group->findAttrString("delay_model"); + if (!type_name.empty()) { + if (type_name == "table_lookup") + library_->setDelayModelType(DelayModelType::table); + else if (type_name == "generic_cmos") + library_->setDelayModelType(DelayModelType::cmos_linear); + else if (type_name == "piecewise_cmos") { + library_->setDelayModelType(DelayModelType::cmos_pwl); + warn(1160, library_group, "delay_model {} not supported.", type_name); } - if (!have_output_threshold_[rf_index]) { - libWarn(1146, group, "output_threshold_pct_%s not found.", rf->name()); - missing_threshold = true; + else if (type_name == "cmos2") { + library_->setDelayModelType(DelayModelType::cmos2); + warn(1161, library_group, "delay_model {} not supported.", type_name); } - if (!have_slew_lower_threshold_[rf_index]) { - libWarn(1147, group, "slew_lower_threshold_pct_%s not found.", rf->name()); - missing_threshold = true; + else if (type_name == "polynomial") { + library_->setDelayModelType(DelayModelType::polynomial); + warn(1162, library_group, "delay_model {} not supported.", type_name); } - if (!have_slew_upper_threshold_[rf_index]) { - libWarn(1148, group, "slew_upper_threshold_pct_%s not found.", rf->name()); - missing_threshold = true; + // Evil IBM garbage. + else if (type_name == "dcm") { + library_->setDelayModelType(DelayModelType::dcm); + warn(1163, library_group, "delay_model {} not supported..", type_name); } + else + warn(1164, library_group, "unknown delay_model {}.", type_name); } - if (missing_threshold) - libError(1149, group, "Library %s is missing one or more thresholds.", - library_->name()); -} - -void -LibertyReader::visitTimeUnit(LibertyAttr *attr) -{ - if (library_) - parseUnits(attr, "s", time_scale_, library_->units()->timeUnit()); } void -LibertyReader::visitPullingResistanceUnit(LibertyAttr *attr) +LibertyReader::readBusStyle(const LibertyGroup *library_group) { - if (library_) - parseUnits(attr, "ohm", res_scale_, - library_->units()->resistanceUnit()); + const std::string &bus_style = library_group->findAttrString("bus_naming_style"); + if (!bus_style.empty()) { + // Assume bus style is of the form "%s[%d]". + if (bus_style.size() == 6 + && bus_style[0] == '%' + && bus_style[1] == 's' + && bus_style[3] == '%' + && bus_style[4] == 'd') + library_->setBusBrkts(bus_style[2], bus_style[5]); + else + warn(1165, library_group, "unknown bus_naming_style format."); + } } void -LibertyReader::visitResistanceUnit(LibertyAttr *attr) +LibertyReader::readBusTypes(LibertyCell *cell, + const LibertyGroup *group) { - if (library_) - parseUnits(attr, "ohm", res_scale_, library_->units()->resistanceUnit()); + for (const LibertyGroup *type_group : group->findSubgroups("type")) { + if (type_group->hasFirstParam()) { + const std::string &name = type_group->firstParam(); + int from, to; + bool from_exists, to_exists; + type_group->findAttrInt("bit_from", from, from_exists); + type_group->findAttrInt("bit_to", to, to_exists); + if (from_exists && to_exists) { + if (cell) + cell->makeBusDcl(name, from, to); + else + library_->makeBusDcl(name, from, to); + } + else if (!from_exists) + warn(1179, type_group, "bus type missing bit_from."); + else if (!to_exists) + warn(1180, type_group, "bus type missing bit_to."); + } + } } void -LibertyReader::visitCurrentUnit(LibertyAttr *attr) +LibertyReader::readThresholds(const LibertyGroup *library_group) { - if (library_) - parseUnits(attr, "A", current_scale_, library_->units()->currentUnit()); + for (const RiseFall *rf : RiseFall::range()) { + std::string suffix(rf->to_string()); + readLibAttrFloat(library_group, "input_threshold_pct_" + suffix, + &LibertyLibrary::setInputThreshold, rf, 0.01F); + readLibAttrFloat(library_group, "output_threshold_pct_" + suffix, + &LibertyLibrary::setOutputThreshold, rf, 0.01F); + readLibAttrFloat(library_group, "slew_lower_threshold_pct_" + suffix, + &LibertyLibrary::setSlewLowerThreshold, rf, 0.01F); + readLibAttrFloat(library_group, "slew_upper_threshold_pct_" + suffix, + &LibertyLibrary::setSlewUpperThreshold, rf, 0.01F); + } } void -LibertyReader::visitVoltageUnit(LibertyAttr *attr) +LibertyReader::checkThresholds(const LibertyGroup *library_group) const { - if (library_) - parseUnits(attr, "V", volt_scale_, library_->units()->voltageUnit()); - setEnergyScale(); + for (const RiseFall *rf : RiseFall::range()) { + if (library_->inputThreshold(rf) == 0.0) + warn(1145, library_group, "input_threshold_pct_{} not found.", rf->name()); + if (library_->outputThreshold(rf) == 0.0) + warn(1146, library_group, "output_threshold_pct_{} not found.", rf->name()); + if (library_->slewLowerThreshold(rf) == 0.0) + warn(1147, library_group, "slew_lower_threshold_pct_{} not found.", rf->name()); + if (library_->slewUpperThreshold(rf) == 0.0) + warn(1148, library_group, "slew_upper_threshold_pct_{} not found.", rf->name()); + } } void -LibertyReader::visitPowerUnit(LibertyAttr *attr) +LibertyReader::readTableTemplates(const LibertyGroup *library_group) { - if (library_) - parseUnits(attr, "W", power_scale_, library_->units()->powerUnit()); + readTableTemplates(library_group, "lu_table_template", TableTemplateType::delay); + readTableTemplates(library_group, "output_current_template", + TableTemplateType::output_current); + readTableTemplates(library_group, "power_lut_template", TableTemplateType::power); + readTableTemplates(library_group, "ocv_table_template", TableTemplateType::ocv); } void -LibertyReader::visitDistanceUnit(LibertyAttr *attr) +LibertyReader::readTableTemplates(const LibertyGroup *library_group, + std::string_view group_name, + TableTemplateType type) { - if (library_) - parseUnits(attr, "m", distance_scale_, library_->units()->distanceUnit()); -} - -void -LibertyReader::parseUnits(LibertyAttr *attr, - const char *unit_suffix, - float &scale_var, - Unit *unit) -{ - string units = getAttrString(attr); - if (!units.empty()) { - // Unit format is . - // Find the multiplier digits. - string units = getAttrString(attr); - size_t mult_end = units.find_first_not_of("0123456789"); - float mult = 1.0F; - string scale_suffix; - if (mult_end != units.npos) { - string unit_mult = units.substr(0, mult_end); - scale_suffix = units.substr(mult_end); - if (unit_mult == "1") - mult = 1.0F; - else if (unit_mult == "10") - mult = 10.0F; - else if (unit_mult == "100") - mult = 100.0F; - else - libWarn(1150, attr, "unknown unit multiplier %s.", unit_mult.c_str()); + for (const LibertyGroup *template_group : + library_group->findSubgroups(group_name)) { + if (template_group->hasFirstParam()) { + const std::string &name = template_group->firstParam(); + TableTemplate *tbl_template = library_->makeTableTemplate(name, type); + TableAxisPtr axis1 = makeTableTemplateAxis(template_group, 1); + if (axis1) + tbl_template->setAxis1(axis1); + TableAxisPtr axis2 = makeTableTemplateAxis(template_group, 2); + if (axis2) + tbl_template->setAxis2(axis2); + TableAxisPtr axis3 = makeTableTemplateAxis(template_group, 3); + if (axis3) + tbl_template->setAxis3(axis3); } else - scale_suffix = units; - - float scale_mult = 1.0F; - if (scale_suffix.size() == strlen(unit_suffix) + 1) { - string suffix = scale_suffix.substr(1); - if (stringEqual(suffix.c_str(), unit_suffix)) { - char scale_char = tolower(scale_suffix[0]); - if (scale_char == 'k') - scale_mult = 1E+3F; - else if (scale_char == 'm') - scale_mult = 1E-3F; - else if (scale_char == 'u') - scale_mult = 1E-6F; - else if (scale_char == 'n') - scale_mult = 1E-9F; - else if (scale_char == 'p') - scale_mult = 1E-12F; - else if (scale_char == 'f') - scale_mult = 1E-15F; - else - libWarn(1151, attr, "unknown unit scale %c.", scale_char); - } - else - libWarn(1152, attr, "unknown unit suffix %s.", suffix.c_str()); - } - else if (!stringEqual(scale_suffix.c_str(), unit_suffix)) - libWarn(1153, attr, "unknown unit suffix %s.", scale_suffix.c_str()); - scale_var = scale_mult * mult; - unit->setScale(scale_var); + warn(1175, template_group, "table template missing name."); } } -void -LibertyReader::visitCapacitiveLoadUnit(LibertyAttr *attr) -{ - if (library_) { - if (attr->isComplex()) { - LibertyAttrValueIterator value_iter(attr->values()); - if (value_iter.hasNext()) { - LibertyAttrValue *value = value_iter.next(); - bool valid = false; - float scale; - if (value->isFloat()) { - scale = value->floatValue(); - valid = true; - } - else if (value->isString()) { - try { - scale = std::stof(value->stringValue()); - valid = true; - } - catch (...) { - valid = false; - } - } - - if (valid) { - if (value_iter.hasNext()) { - value = value_iter.next(); - if (value->isString()) { - const char *suffix = value->stringValue(); - if (stringEqual(suffix, "ff")) - cap_scale_ = scale * 1E-15F; - else if (stringEqual(suffix, "pf")) - cap_scale_ = scale * 1E-12F; - else - libWarn(1154, attr, "capacitive_load_units are not ff or pf."); - } - else - libWarn(1155, attr, "capacitive_load_units are not a string."); - } - else - libWarn(1156, attr, "capacitive_load_units missing suffix."); - } - else - libWarn(1157, attr, "capacitive_load_units scale is not a float."); +TableAxisPtr +LibertyReader::makeTableTemplateAxis(const LibertyGroup *template_group, + int axis_index) +{ + std::string var_attr_name = sta::format("variable_{}", axis_index); + const std::string &var_name = template_group->findAttrString(var_attr_name); + if (!var_name.empty()) { + TableAxisVariable axis_var = stringTableAxisVariable(var_name); + if (axis_var == TableAxisVariable::unknown) + warn(1297, template_group, "axis type {} not supported.", var_name); + else { + std::string index_attr_name = sta::format("index_{}", axis_index); + const LibertyComplexAttr *index_attr = + template_group->findComplexAttr(index_attr_name); + FloatSeq axis_values; + if (index_attr) { + axis_values = readFloatSeq(index_attr, 1.0F); + if (!axis_values.empty()) { + float prev = axis_values[0]; + for (size_t i = 1; i < axis_values.size(); i++) { + float value = axis_values[i]; + if (value <= prev) { + warn(1178, template_group, "non-increasing table index values."); + break; + } + prev = value; + } + } } - else - libWarn(1158, attr, "capacitive_load_units missing scale and suffix."); + const Units *units = library_->units(); + float scale = tableVariableUnit(axis_var, units)->scale(); + scaleFloats(axis_values, scale); + return make_shared(axis_var, std::move(axis_values)); } - else - libWarn(1159, attr, "capacitive_load_unit missing values suffix."); - library_->units()->capacitanceUnit()->setScale(cap_scale_); - setEnergyScale(); } + return nullptr; } -void -LibertyReader::visitDelayModel(LibertyAttr *attr) -{ - if (library_) { - const char *type_name = getAttrString(attr); - if (type_name) { - if (stringEq(type_name, "table_lookup")) - library_->setDelayModelType(DelayModelType::table); - else if (stringEq(type_name, "generic_cmos")) - library_->setDelayModelType(DelayModelType::cmos_linear); - else if (stringEq(type_name, "piecewise_cmos")) { - library_->setDelayModelType(DelayModelType::cmos_pwl); - libWarn(1160, attr, "delay_model %s not supported.", type_name); - } - else if (stringEq(type_name, "cmos2")) { - library_->setDelayModelType(DelayModelType::cmos2); - libWarn(1161, attr, "delay_model %s not supported.", type_name); - } - else if (stringEq(type_name, "polynomial")) { - library_->setDelayModelType(DelayModelType::polynomial); - libWarn(1162, attr, "delay_model %s not supported.", type_name); - } - // Evil IBM garbage. - else if (stringEq(type_name, "dcm")) { - library_->setDelayModelType(DelayModelType::dcm); - libWarn(1163, attr, "delay_model %s not supported..", type_name); - } - else - libWarn(1164, attr, "unknown delay_model %s.", type_name); - } - } +static void +scaleFloats(FloatSeq &floats, + float scale) +{ + size_t count = floats.size(); + for (size_t i = 0; i < count; i++) + floats[i] *= scale; } void -LibertyReader::visitBusStyle(LibertyAttr *attr) +LibertyReader::readVoltateMaps(const LibertyGroup *library_group) { - if (library_) { - const char *bus_style = getAttrString(attr); - // Assume bus style is of the form "%s[%d]". - if (bus_style - && strlen(bus_style) == 6 - && bus_style[0] == '%' - && bus_style[1] == 's' - && bus_style[3] == '%' - && bus_style[4] == 'd') - library_->setBusBrkts(bus_style[2], bus_style[5]); - else - libWarn(1165, attr, "unknown bus_naming_style format."); + for (const LibertyComplexAttr *volt_attr : + library_group->findComplexAttrs("voltage_map")) { + const LibertyAttrValueSeq &values = volt_attr->values(); + if (values.size() == 2) { + const std::string &volt_name = values[0]->stringValue(); + auto [volt, valid] = values[1]->floatValue(); + if (valid) + library_->addSupplyVoltage(volt_name, volt); + else + warn(1166, volt_attr, "voltage_map voltage is not a float."); + } } } void -LibertyReader::visitVoltageMap(LibertyAttr *attr) -{ - if (library_) { - if (attr->isComplex()) { - LibertyAttrValueIterator value_iter(attr->values()); - if (value_iter.hasNext()) { - LibertyAttrValue *value = value_iter.next(); - if (value->isString()) { - const char *supply_name = value->stringValue(); - if (value_iter.hasNext()) { - value = value_iter.next(); - bool valid = false; - float voltage; - if (value->isFloat()) { - voltage = value->floatValue(); - valid = true; - } - else if (value->isString()) { - try { - voltage = std::stof(value->stringValue()); - valid = true; - } - catch (...) { - valid = false; - } - } - - if (valid) - library_->addSupplyVoltage(supply_name, voltage); - else - libWarn(1166, attr, "voltage_map voltage is not a float."); - } - else - libWarn(1167, attr, "voltage_map missing voltage."); - } - else - libWarn(1168, attr, "voltage_map supply name is not a string."); +LibertyReader::readOperatingConds(const LibertyGroup *library_group) +{ + for (const LibertyGroup *opcond_group : + library_group->findSubgroups("operating_conditions")) { + if (opcond_group->hasFirstParam()) { + const std::string &name = opcond_group->firstParam(); + OperatingConditions *op_cond = library_->makeOperatingConditions(name); + float value; + bool exists; + opcond_group->findAttrFloat("process", value, exists); + if (exists) + op_cond->setProcess(value); + opcond_group->findAttrFloat("temperature", value, exists); + if (exists) + op_cond->setTemperature(value); + opcond_group->findAttrFloat("voltage", value, exists); + if (exists) + op_cond->setVoltage(value); + const std::string &tree_type = opcond_group->findAttrString("tree_type"); + if (!tree_type.empty()) { + WireloadTree wireload_tree = stringWireloadTree(tree_type); + op_cond->setWireloadTree(wireload_tree); } - else - libWarn(1169, attr, "voltage_map missing supply name and voltage."); } - else - libWarn(1170, attr, "voltage_map missing values suffix."); } -} -void -LibertyReader::visitNomTemp(LibertyAttr *attr) -{ - if (library_) { - float value; - bool valid; - getAttrFloat(attr, value, valid); - if (valid) - library_->setNominalTemperature(value); + const std::string &default_op_cond = + library_group->findAttrString("default_operating_conditions"); + if (!default_op_cond.empty()) { + OperatingConditions *op_cond = + library_->findOperatingConditions(default_op_cond); + if (op_cond) + library_->setDefaultOperatingConditions(op_cond); + else + warn(1144, library_group, "default_operating_condition {} not found.", + default_op_cond); } } void -LibertyReader::visitNomProc(LibertyAttr *attr) -{ - if (library_) { - float value; - bool valid; - getAttrFloat(attr, value, valid); - if (valid) - library_->setNominalProcess(value); +LibertyReader::readScaleFactors(const LibertyGroup *library_group) +{ + // Top level scale factors. + ScaleFactors *scale_factors = library_->makeScaleFactors(""); + library_->setScaleFactors(scale_factors); + readScaleFactors(library_group, scale_factors); + + // Named scale factors. + for (const LibertyGroup *scale_group : library_group->findSubgroups("scaling_factors")){ + if (scale_group->hasFirstParam()) { + const std::string &name = scale_group->firstParam(); + ScaleFactors *scale_factors = library_->makeScaleFactors(name); + readScaleFactors(scale_group, scale_factors); + } } } void -LibertyReader::visitNomVolt(LibertyAttr *attr) -{ - if (library_) { - float value; - bool valid; - getAttrFloat(attr, value, valid); - if (valid) - library_->setNominalVoltage(value); +LibertyReader::readScaleFactors(const LibertyGroup *scale_group, + ScaleFactors *scale_factors) +{ + // Skip unknown type. + for (size_t type_index = 0; type_index < scale_factor_type_count - 1; type_index++) { + ScaleFactorType type = static_cast(type_index); + const std::string &type_name = scaleFactorTypeName(type); + // Skip unknown pvt. + for (size_t pvt_index = 0; pvt_index < scale_factor_pvt_count - 1; pvt_index++) { + ScaleFactorPvt pvt = static_cast(pvt_index); + const std::string pvt_name = scaleFactorPvtName(pvt); + std::string attr_name; + for (const RiseFall *rf : RiseFall::range()) { + if (scaleFactorTypeRiseFallSuffix(type)) { + const std::string rf_name = (rf == RiseFall::rise()) ? "rise" : "fall"; + attr_name = sta::format("k_{}_{}_{}", pvt_name, type_name, rf_name); + } + else if (scaleFactorTypeRiseFallPrefix(type)) { + const std::string rf_name = (rf == RiseFall::rise()) ? "rise" : "fall"; + attr_name = sta::format("k_{}_{}_{}", pvt_name, rf_name, type_name); + } + else if (scaleFactorTypeLowHighSuffix(type)) { + const std::string rf_name = (rf == RiseFall::rise()) ? "high" : "low"; + attr_name = sta::format("k_{}_{}_{}", pvt_name, type_name, rf_name); + } + else + attr_name = sta::format("k_{}_{}", pvt_name, type_name); + float value; + bool exists; + scale_group->findAttrFloat(attr_name, value, exists); + if (exists) + scale_factors->setScale(type, pvt, rf, value); + } + } } } void -LibertyReader::visitDefaultInoutPinCap(LibertyAttr *attr) +LibertyReader::readWireloads(const LibertyGroup *library_group) { - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setDefaultBidirectPinCap(value * cap_scale_); + for (const LibertyGroup *wl_group : library_group->findSubgroups("wire_load")) { + if (wl_group->hasFirstParam()) { + const std::string &name = wl_group->firstParam(); + Wireload *wireload = library_->makeWireload(name); + float value; + bool exists; + wl_group->findAttrFloat("resistance", value, exists); + if (exists) + wireload->setResistance(value * res_scale_); + + wl_group->findAttrFloat("capacitance", value, exists); + if (exists) + wireload->setCapacitance(value * cap_scale_); + + wl_group->findAttrFloat("slope", value, exists); + if (exists) + wireload->setSlope(value); + + for (const LibertyComplexAttr *fanout_attr : + wl_group->findComplexAttrs("fanout_length")) { + float fanout, length; + bool exists; + getAttrFloat2(fanout_attr, fanout, length, exists); + if (exists) + wireload->addFanoutLength(fanout, length); + else + warn(1185, fanout_attr, "fanout_length is missing length and fanout."); + } + } + else + warn(1184, wl_group, "wire_load missing name."); + } +} + +void +LibertyReader::readWireloadSelection(const LibertyGroup *library_group) +{ + const LibertyGroup *sel_group = library_group->findSubgroup("wire_load_selection"); + if (sel_group) { + std::string name; + if (sel_group->hasFirstParam()) + name = sel_group->firstParam(); + WireloadSelection *wireload_selection = library_->makeWireloadSelection(name); + for (const LibertyComplexAttr *area_attr : + sel_group->findComplexAttrs("wire_load_from_area")) { + const LibertyAttrValueSeq &values = area_attr->values(); + if (values.size() == 3) { + auto [min_area, min_valid] = values[0]->floatValue(); + if (min_valid) { + auto [max_area, max_valid] = values[1]->floatValue(); + if (max_valid) { + LibertyAttrValue *value = values[2]; + if (value->isString()) { + const std::string &wireload_name = value->stringValue(); + const Wireload *wireload = + library_->findWireload(wireload_name); + if (wireload) + wireload_selection->addWireloadFromArea(min_area, max_area, + wireload); + else + warn(1187, area_attr, "wireload {} not found.", wireload_name); + } + else + warn(1188, area_attr, + "wire_load_from_area wireload name not a string."); + } + else + warn(1189, area_attr, "wire_load_from_area min not a float."); + } + else + warn(1190, area_attr, "wire_load_from_area max not a float."); + } + else + warn(1191, area_attr, "wire_load_from_area missing parameters."); + } } } void -LibertyReader::visitDefaultInputPinCap(LibertyAttr *attr) +LibertyReader::readDefaultWireLoad(const LibertyGroup *library_group) { - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setDefaultInputPinCap(value * cap_scale_); + const std::string &wireload_name = + library_group->findAttrString("default_wire_load"); + if (!wireload_name.empty()) { + const Wireload *wireload = library_->findWireload(wireload_name); + if (wireload) + library_->setDefaultWireload(wireload); + else + warn(1142, library_group, "default_wire_load {} not found.", + wireload_name); } } void -LibertyReader::visitDefaultOutputPinCap(LibertyAttr *attr) +LibertyReader::readDefaultWireLoadMode(const LibertyGroup *library_group) { - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setDefaultOutputPinCap(value * cap_scale_); + const std::string &wire_load_mode = + library_group->findAttrString("default_wire_load_mode"); + if (!wire_load_mode.empty()) { + WireloadMode mode = stringWireloadMode(wire_load_mode); + if (mode != WireloadMode::unknown) + library_->setDefaultWireloadMode(mode); + else + warn(1174, library_group, "default_wire_load_mode {} not found.", + wire_load_mode); } } void -LibertyReader::visitDefaultMaxTransition(LibertyAttr *attr) +LibertyReader::readDefaultWireLoadSelection(const LibertyGroup *library_group) { - if (library_){ - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) { - if (value == 0.0) - libWarn(1171, attr, "default_max_transition is 0.0."); - library_->setDefaultMaxSlew(value * time_scale_); + const std::string &selection_name = + library_group->findAttrString("default_wire_load_selection"); + if (!selection_name.empty()) { + const WireloadSelection *selection = + library_->findWireloadSelection(selection_name); + if (selection) + library_->setDefaultWireloadSelection(selection); + else + warn(1143, library_group, "default_wire_selection {} not found.", + selection_name); + } +} + +void +LibertyReader::readModeDefs(LibertyCell *cell, + const LibertyGroup *cell_group) +{ + for (const LibertyGroup *mode_group : cell_group->findSubgroups("mode_definition")) { + if (mode_group->hasFirstParam()) { + const std::string &name = mode_group->firstParam(); + ModeDef *mode_def = cell->makeModeDef(name); + for (const LibertyGroup *value_group : mode_group->findSubgroups("mode_value")) { + if (value_group->hasFirstParam()) { + const std::string &value_name = value_group->firstParam(); + ModeValueDef *mode_value = mode_def->defineValue(value_name); + const std::string &sdf_cond = value_group->findAttrString("sdf_cond"); + if (!sdf_cond.empty()) + mode_value->setSdfCond(sdf_cond); + const std::string &when = value_group->findAttrString("when"); + if (!when.empty()) { + // line + FuncExpr *when_expr = parseFunc(when, "when", cell, + value_group->line()); + mode_value->setCond(when_expr); + } + } + else + warn(1264, value_group, "mode value missing name."); + } } + else + warn(1263, mode_group, "mode definition missing name."); } } void -LibertyReader::visitDefaultMaxFanout(LibertyAttr *attr) +LibertyReader::readSlewDegradations(const LibertyGroup *library_group) { - if (library_){ - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) { - if (value == 0.0) - libWarn(1172, attr, "default_max_fanout is 0.0."); - library_->setDefaultMaxFanout(value); + for (const RiseFall *rf : RiseFall::range()) { + const std::string group_name = sta::format("{}_transition_degradation", + rf->to_string()); + const LibertyGroup *degradation_group = + library_group->findSubgroup(group_name); + if (degradation_group) { + TableModel *table_model = readTableModel(degradation_group, rf, + TableTemplateType::delay, + time_scale_, + ScaleFactorType::transition); + if (LibertyLibrary::checkSlewDegradationAxes(table_model)) + library_->setWireSlewDegradationTable(table_model, rf); + else + warn(1254, degradation_group, "unsupported model axis."); } } } void -LibertyReader::visitDefaultIntrinsicRise(LibertyAttr *attr) +LibertyReader::readLibAttrFloat(const LibertyGroup *library_group, + std::string_view attr_name, + void (LibertyLibrary::*set_func)(float value), + float scale) { - visitDefaultIntrinsic(attr, RiseFall::rise()); + float value; + bool exists; + library_group->findAttrFloat(attr_name, value, exists); + if (exists) + (library_->*set_func)(value * scale); } void -LibertyReader::visitDefaultIntrinsicFall(LibertyAttr *attr) +LibertyReader::readLibAttrFloat(const LibertyGroup *library_group, + std::string_view attr_name, + void (LibertyLibrary::*set_func)(const RiseFall *rf, + float value), + const RiseFall *rf, + float scale) { - visitDefaultIntrinsic(attr, RiseFall::fall()); + float value; + bool exists; + library_group->findAttrFloat(attr_name, value, exists); + if (exists) + (library_->*set_func)(rf, value * scale); } void -LibertyReader::visitDefaultIntrinsic(LibertyAttr *attr, - const RiseFall *rf) +LibertyReader::readLibAttrFloatWarnZero(const LibertyGroup *library_group, + std::string_view attr_name, + void (LibertyLibrary::*set_func)(float value), + float scale) { - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setDefaultIntrinsic(rf, value * time_scale_); + float value; + bool exists; + library_group->findAttrFloat(attr_name, value, exists); + if (exists) { + if (value == 0.0F) { + const LibertySimpleAttr *attr = library_group->findSimpleAttr(attr_name); + if (attr) + warn(1171, attr, "{} is 0.0.", attr_name); + else + warn(1172, library_group, "{} is 0.0.", attr_name); + } + (library_->*set_func)(value * scale); } } -void -LibertyReader::visitDefaultInoutPinRiseRes(LibertyAttr *attr) -{ - visitDefaultInoutPinRes(attr, RiseFall::rise()); -} +//////////////////////////////////////////////////////////////// void -LibertyReader::visitDefaultInoutPinFallRes(LibertyAttr *attr) +LibertyReader::readCell(LibertyCell *cell, + const LibertyGroup *cell_group) { - visitDefaultInoutPinRes(attr, RiseFall::fall()); -} + readBusTypes(cell, cell_group); + // Make ports first because they are referenced by functions, timing arcs, etc. + LibertyPortGroupMap port_group_map = makeCellPorts(cell, cell_group); -void -LibertyReader::visitDefaultInoutPinRes(LibertyAttr *attr, - const RiseFall *rf) -{ - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setDefaultBidirectPinRes(rf, value * res_scale_); + // Make ff/latch output ports. + makeSequentials(cell, cell_group); + // Test cell ports may be referenced by a statetable. + readTestCell(cell, cell_group); + + readCellAttributes(cell, cell_group); + + // Set port directions before making timing arcs etc. + for (auto const &[port_group, ports] : port_group_map) + readPortDir(ports, port_group); + + // Pre-pass: build bit_overrides_ so bus-level timing arcs won't overwrite + // per-bit pin() block arcs that cover the same logical arc. + bit_overrides_.clear(); + for (auto const &[port_group, ports] : port_group_map) { + auto timing_groups = port_group->findSubgroups("timing"); + if (timing_groups.empty()) + continue; + for (LibertyPort *port : ports) { + auto &tgs = bit_overrides_[port]; + for (const LibertyGroup *tg : timing_groups) + tgs.push_back(tg); + } } + + for (auto const &[port_group, ports] : port_group_map) { + readPortAttributes(cell, ports, port_group); + makePortFuncs(cell, ports, port_group); + makeTimingArcs(cell, ports, port_group); + readInternalPowerGroups(cell, ports, port_group); + } + + cell->finish(infer_latches_, report_, debug_); } void -LibertyReader::visitDefaultOutputPinRiseRes(LibertyAttr *attr) +LibertyReader::readScaledCell(const LibertyGroup *scaled_cell_group) { - visitDefaultOutputPinRes(attr, RiseFall::rise()); + if (scaled_cell_group->hasFirstParam()) { + const std::string &name = scaled_cell_group->firstParam(); + LibertyCell *owner = library_->findLibertyCell(name); + if (owner) { + if (scaled_cell_group->hasSecondParam()) { + const std::string &op_cond_name = scaled_cell_group->secondParam(); + OperatingConditions *op_cond = library_->findOperatingConditions(op_cond_name); + if (op_cond) { + debugPrint(debug_, "liberty", 1, "scaled cell {} {}", + name, op_cond_name); + LibertyCell *scaled_cell = library_->makeScaledCell(name, filename_); + readCell(scaled_cell, scaled_cell_group); + checkScaledCell(scaled_cell, owner, scaled_cell_group, op_cond_name); + // Add scaled cell AFTER ports and timing arcs are defined. + owner->addScaledCell(op_cond, scaled_cell); + } + else + warn(1202, scaled_cell_group, "operating conditions {} not found.", + op_cond_name); + } + else + warn(1203, scaled_cell_group, "scaled_cell missing operating condition."); + } + else + warn(1204, scaled_cell_group, "scaled_cell cell {} has not been defined.", name); + } + else + warn(1205, scaled_cell_group, "scaled_cell missing name."); } +// Minimal check that is not very specific about where the discrepancies are. void -LibertyReader::visitDefaultOutputPinFallRes(LibertyAttr *attr) -{ - visitDefaultOutputPinRes(attr, RiseFall::fall()); +LibertyReader::checkScaledCell(LibertyCell *scaled_cell, + LibertyCell *owner, + const LibertyGroup *scaled_cell_group, + std::string_view op_cond_name) +{ + if (equivCellPorts(scaled_cell, owner)) { + if (!equivCellPorts(scaled_cell, owner)) + warn(1206, scaled_cell_group, "scaled_cell {}, {} ports do not match cell ports", + scaled_cell->name(), + op_cond_name); + if (!equivCellFuncs(scaled_cell, owner)) + warn(1206, scaled_cell_group, + "scaled_cell {}, {} port functions do not match cell port functions.", + scaled_cell->name(), + op_cond_name); + } + else + warn(1207, scaled_cell_group, "scaled_cell ports do not match cell ports."); + if (!equivCellTimingArcSets(scaled_cell, owner)) + warn(1208, scaled_cell_group, + "scaled_cell {}, {} timing does not match cell timing.", + scaled_cell->name(), + op_cond_name); } -void -LibertyReader::visitDefaultOutputPinRes(LibertyAttr *attr, - const RiseFall *rf) +LibertyPortGroupMap +LibertyReader::makeCellPorts(LibertyCell *cell, + const LibertyGroup *cell_group) { - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setDefaultOutputPinRes(rf, value * res_scale_); + LibertyPortGroupMap port_group_map; + for (const LibertyGroup *subgroup : cell_group->subgroups()) { + const std::string &type = subgroup->type(); + if (type == "pin") + makePinPort(cell, subgroup, port_group_map); + else if (type == "bus") + makeBusPort(cell, subgroup, port_group_map); + else if (type == "bundle") + makeBundlePort(cell, subgroup, port_group_map); + else if (type == "pg_pin") + makePgPinPort(cell, subgroup); } + return port_group_map; } void -LibertyReader::visitDefaultFanoutLoad(LibertyAttr *attr) +LibertyReader::makePinPort(LibertyCell *cell, + const LibertyGroup *pin_group, + LibertyPortGroupMap &port_group_map) { - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) { - if (value == 0.0) - libWarn(1173, attr, "default_fanout_load is 0.0."); - library_->setDefaultFanoutLoad(value); - } + for (const LibertyAttrValue *port_value : pin_group->params()) { + const std::string &port_name = port_value->stringValue(); + LibertyPort *port = makePort(cell, port_name); + port_group_map[pin_group].push_back(port); } } void -LibertyReader::visitDefaultWireLoad(LibertyAttr *attr) -{ - if (library_) { - const char *value = getAttrString(attr); - if (value) { - stringDelete(default_wireload_); - default_wireload_ = stringCopy(value); +LibertyReader::makeBusPort(LibertyCell *cell, + const LibertyGroup *bus_group, + LibertyPortGroupMap &port_group_map) +{ + for (const LibertyAttrValue *port_value : bus_group->params()) { + const std::string &port_name = port_value->stringValue(); + const LibertySimpleAttr *bus_type_attr = bus_group->findSimpleAttr("bus_type"); + if (bus_type_attr) { + const std::string &bus_type = bus_type_attr->stringValue(); + if (!bus_type.empty()) { + // Look for bus dcl local to cell first. + BusDcl *bus_dcl = cell->findBusDcl(bus_type); + if (bus_dcl == nullptr) + bus_dcl = library_->findBusDcl(bus_type); + if (bus_dcl) { + debugPrint(debug_, "liberty", 1, " bus {}", port_name); + LibertyPort *bus_port = makeBusPort(cell, port_name, + bus_dcl->from(), bus_dcl->to(), + bus_dcl); + port_group_map[bus_group].push_back(bus_port); + // Make ports for pin groups inside the bus group. + makeBusPinPorts(cell, bus_group, port_group_map); + } + else + warn(1235, bus_type_attr, "bus_type {} not found.", bus_type); + } } + else + warn(1236, bus_type_attr, "bus_type not found."); } } void -LibertyReader::visitDefaultWireLoadMode(LibertyAttr *attr) -{ - if (library_) { - const char *wire_load_mode = getAttrString(attr); - if (wire_load_mode) { - WireloadMode mode = stringWireloadMode(wire_load_mode); - if (mode != WireloadMode::unknown) - library_->setDefaultWireloadMode(mode); - else - libWarn(1174, attr, "default_wire_load_mode %s not found.", - wire_load_mode); +LibertyReader::makeBusPinPorts(LibertyCell *cell, + const LibertyGroup *bus_group, + LibertyPortGroupMap &port_group_map) +{ + for (const LibertyGroup *pin_group : bus_group->findSubgroups("pin")) { + for (const LibertyAttrValue *param : pin_group->params()) { + if (param->isString()) { + const std::string pin_name = param->stringValue(); + debugPrint(debug_, "liberty", 1, " bus pin port {}", pin_name); + // Expand foo[3:0] port names. + PortNameBitIterator name_iter(cell, pin_name, this, pin_group->line()); + while (name_iter.hasNext()) { + LibertyPort *pin_port = name_iter.next(); + if (pin_port) { + port_group_map[pin_group].push_back(pin_port); + } + else + warn(1232, pin_group, "pin {} not found.", pin_name); + } + } + else + warn(1233, pin_group, "pin name is not a string."); } } } void -LibertyReader::visitDefaultWireLoadSelection(LibertyAttr *attr) +LibertyReader::makeBundlePort(LibertyCell *cell, + const LibertyGroup *bundle_group, + LibertyPortGroupMap &port_group_map) { - if (library_) { - const char *value = getAttrString(attr); - if (value) { - stringDelete(default_wireload_selection_); - default_wireload_selection_ = stringCopy(value); + if (bundle_group->hasFirstParam()) { + const std::string &bundle_name = bundle_group->firstParam(); + debugPrint(debug_, "liberty", 1, " bundle {}", bundle_name); + + const LibertyComplexAttr *member_attr = bundle_group->findComplexAttr("members"); + ConcretePortSeq *members = new ConcretePortSeq; + for (const LibertyAttrValue *member_value : member_attr->values()) { + if (member_value->isString()) { + const std::string &member_name = member_value->stringValue(); + LibertyPort *member = cell->findLibertyPort(member_name); + if (member == nullptr) + member = makePort(cell, member_name); + members->push_back(member); + } } + LibertyPort *bundle_port = builder_.makeBundlePort(cell, bundle_name, members); + port_group_map[bundle_group].push_back(bundle_port); + // Make ports for pin groups inside the bundle group. + makeBundlePinPorts(cell, bundle_group, port_group_map); } + else + warn(1313, bundle_group, "bundle missing name."); } void -LibertyReader::visitDefaultOperatingConditions(LibertyAttr *attr) +LibertyReader::makeBundlePinPorts(LibertyCell *cell, + const LibertyGroup *bundle_group, + LibertyPortGroupMap &port_group_map) { - if (library_) { - const char *value = getAttrString(attr); - if (value) { - stringDelete(default_operating_condition_); - default_operating_condition_ = stringCopy(value); + for (const LibertyGroup *pin_group : bundle_group->findSubgroups("pin")) { + for (LibertyAttrValue *param : pin_group->params()) { + if (param->isString()) { + const std::string pin_name = param->stringValue(); + debugPrint(debug_, "liberty", 1, " bundle pin port {}", pin_name); + LibertyPort *pin_port = cell->findLibertyPort(pin_name); + if (pin_port == nullptr) + pin_port = makePort(cell, pin_name); + port_group_map[pin_group].push_back(pin_port); + } + else + warn(1234, pin_group, "pin name is not a string."); } } } void -LibertyReader::visitInputThresholdPctFall(LibertyAttr *attr) +LibertyReader::makePgPinPort(LibertyCell *cell, + const LibertyGroup *pg_pin_group) { - visitInputThresholdPct(attr, RiseFall::fall()); -} + if (pg_pin_group->hasFirstParam()) { + const std::string &port_name = pg_pin_group->firstParam(); + LibertyPort *pg_port = makePort(cell, port_name); -void -LibertyReader::visitInputThresholdPctRise(LibertyAttr *attr) -{ - visitInputThresholdPct(attr, RiseFall::rise()); -} + const std::string &type_name = pg_pin_group->findAttrString("pg_type"); + if (!type_name.empty()) { + PwrGndType type = findPwrGndType(type_name); + PortDirection *dir = PortDirection::unknown(); + switch (type) { + case PwrGndType::primary_ground: + case PwrGndType::backup_ground: + case PwrGndType::internal_ground: + dir = PortDirection::ground(); + break; + case PwrGndType::primary_power: + case PwrGndType::backup_power: + case PwrGndType::internal_power: + dir = PortDirection::power(); + break; + case PwrGndType::nwell: + case PwrGndType::pwell: + case PwrGndType::deepnwell: + case PwrGndType::deeppwell: + dir = PortDirection::well(); + break; + case PwrGndType::none: + error(1291, pg_pin_group, "unknown pg_type."); + break; + default: + break; + } + pg_port->setPwrGndType(type); + pg_port->setDirection(dir); + } -void -LibertyReader::visitInputThresholdPct(LibertyAttr *attr, - const RiseFall *rf) -{ - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setInputThreshold(rf, value / 100.0F); + const std::string &voltate_name = pg_pin_group->findAttrString("voltage_name"); + if (!voltate_name.empty()) + pg_port->setVoltageName(voltate_name); } - have_input_threshold_[rf->index()] = true; + else + warn(1314, pg_pin_group, "pg_pin missing name."); } -void -LibertyReader::visitOutputThresholdPctFall(LibertyAttr *attr) -{ - visitOutputThresholdPct(attr, RiseFall::fall()); -} +//////////////////////////////////////////////////////////////// void -LibertyReader::visitOutputThresholdPctRise(LibertyAttr *attr) +LibertyReader::readPortAttributes(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group) +{ + readCapacitance(ports, port_group); + readMinPulseWidth(cell, ports, port_group); + readPortAttrFloat("min_period", &LibertyPort::setMinPeriod, ports, + port_group, time_scale_); + readPortAttrBool("clock", &LibertyPort::setIsClock, ports, port_group); + readPortAttrFloat("fanout_load", &LibertyPort::setFanoutLoad, ports, + port_group, 1.0F); + readPortAttrFloatMinMax("max_fanout", &LibertyPort::setFanoutLimit, ports, + port_group, MinMax::max(), 1.0F); + readPortAttrFloatMinMax("min_fanout", &LibertyPort::setFanoutLimit, ports, + port_group, MinMax::min(), 1.0F); + readPulseClock(ports, port_group); + readPortAttrBool("clock_gate_clock_pin", &LibertyPort::setIsClockGateClock, + ports, port_group); + for (LibertyPort *port : ports) + if (port->isClockGateClock()) { cell->setHasClkGateClkPin(); break; } + readPortAttrBool("clock_gate_enable_pin", &LibertyPort::setIsClockGateEnable, + ports, port_group); + for (LibertyPort *port : ports) + if (port->isClockGateEnable()) { cell->setHasClkGateEnablePin(); break; } + readPortAttrBool("clock_gate_out_pin", &LibertyPort::setIsClockGateOut, + ports, port_group); + readPortAttrBool("is_pll_feedback_pin", &LibertyPort::setIsPllFeedback, + ports, port_group); + readSignalType(cell, ports, port_group); + readPortAttrBool("isolation_cell_data_pin", + &LibertyPort::setIsolationCellData, ports, port_group); + readPortAttrBool("isolation_cell_enable_pin", + &LibertyPort::setIsolationCellEnable, ports, port_group); + readPortAttrBool("level_shifter_data_pin", + &LibertyPort::setLevelShifterData, ports, port_group); + readPortAttrBool("switch_pin", &LibertyPort::setIsSwitch, ports, port_group); + readPortAttrLibertyPort("related_ground_pin", + &LibertyPort::setRelatedGroundPort, + cell, ports, port_group); + readPortAttrLibertyPort("related_power_pin", + &LibertyPort::setRelatedPowerPort, + cell, ports, port_group); + readDriverWaveform(ports, port_group); +} + +void +LibertyReader::readDriverWaveform(const LibertyPortSeq &ports, + const LibertyGroup *port_group) +{ + for (const RiseFall *rf : RiseFall::range()) { + const std::string_view attr_name = (rf == RiseFall::rise()) + ? "driver_waveform_rise" + : "driver_waveform_fall"; + const std::string &name = port_group->findAttrString(attr_name); + if (!name.empty()) { + DriverWaveform *waveform = library_->findDriverWaveform(name); + if (waveform) { + for (LibertyPort *port : ports) + port->setDriverWaveform(waveform, rf); + } + } + } +} + +void +LibertyReader::readPortAttrString(std::string_view attr_name, + void (LibertyPort::*set_func)(std::string value), + const LibertyPortSeq &ports, + const LibertyGroup *group) +{ + const std::string &value = group->findAttrString(attr_name); + if (!value.empty()) { + for (LibertyPort *port : ports) + (port->*set_func)(value); + } +} + +void +LibertyReader::readPortAttrLibertyPort(std::string_view attr_name, + void (LibertyPort::*set_func)(LibertyPort *port), + LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *group) +{ + const std::string &value = group->findAttrString(attr_name); + if (!value.empty()) { + LibertyPort *related = cell->findLibertyPort(value); + for (LibertyPort *port : ports) + (port->*set_func)(related); + } +} + +void +LibertyReader::readPortAttrFloat(std::string_view attr_name, + void (LibertyPort::*set_func)(float value), + const LibertyPortSeq &ports, + const LibertyGroup *group, + float scale) { - visitOutputThresholdPct(attr, RiseFall::rise()); + float value; + bool exists; + group->findAttrFloat(attr_name, value, exists); + if (exists) { + for (LibertyPort *port : ports) + (port->*set_func)(value * scale); + } } void -LibertyReader::visitOutputThresholdPct(LibertyAttr *attr, - const RiseFall *rf) +LibertyReader::readPortAttrBool(std::string_view attr_name, + void (LibertyPort::*set_func)(bool value), + const LibertyPortSeq &ports, + const LibertyGroup *group) { - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setOutputThreshold(rf, value / 100.0F); + const LibertySimpleAttr *attr = group->findSimpleAttr(attr_name); + if (attr) { + const LibertyAttrValue &attr_value = attr->value(); + if (attr_value.isString()) { + const std::string &value = attr_value.stringValue(); + if (stringEqual(value, "true")) { + for (LibertyPort *port : ports) + (port->*set_func)(true); + } + else if (stringEqual(value, "false")) { + for (LibertyPort *port : ports) + (port->*set_func)(false); + } + else + warn(1238, attr, "{} attribute is not boolean.", attr_name); + } + else + warn(1239, attr, "{} attribute is not boolean.", attr_name); } - have_output_threshold_[rf->index()] = true; } void -LibertyReader::visitSlewLowerThresholdPctFall(LibertyAttr *attr) +LibertyReader::readPortAttrFloatMinMax(std::string_view attr_name, + void (LibertyPort::*set_func)(float value, + const MinMax *min_max), + const LibertyPortSeq &ports, + const LibertyGroup *group, + const MinMax *min_max, + float scale) { - visitSlewLowerThresholdPct(attr, RiseFall::fall()); + float value; + bool exists; + group->findAttrFloat(attr_name, value, exists); + if (exists) { + for (LibertyPort *port : ports) + (port->*set_func)(value * scale, min_max); + } } void -LibertyReader::visitSlewLowerThresholdPctRise(LibertyAttr *attr) +LibertyReader::readPulseClock(const LibertyPortSeq &ports, + const LibertyGroup *port_group) { - visitSlewLowerThresholdPct(attr, RiseFall::rise()); + const std::string &pulse_clk = port_group->findAttrString("pulse_clock"); + if (!pulse_clk.empty()) { + const RiseFall *trigger = nullptr; + const RiseFall *sense = nullptr; + if (pulse_clk == "rise_triggered_high_pulse") { + trigger = RiseFall::rise(); + sense = RiseFall::rise(); + } + else if (pulse_clk == "rise_triggered_low_pulse") { + trigger = RiseFall::rise(); + sense = RiseFall::fall(); + } + else if (pulse_clk == "fall_triggered_high_pulse") { + trigger = RiseFall::fall(); + sense = RiseFall::rise(); + } + else if (pulse_clk == "fall_triggered_low_pulse") { + trigger = RiseFall::fall(); + sense = RiseFall::fall(); + } + else + warn(1242, port_group, "pulse_latch unknown pulse type."); + if (trigger) { + for (LibertyPort *port : ports) + port->setPulseClk(trigger, sense); + } + } +} + +void +LibertyReader::readSignalType(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group) +{ + if (!dynamic_cast(cell)) + return; + const std::string &type = port_group->findAttrString("signal_type"); + if (type.empty()) + return; + ScanSignalType signal_type = ScanSignalType::none; + if (type == "test_scan_enable") + signal_type = ScanSignalType::enable; + else if (type == "test_scan_enable_inverted") + signal_type = ScanSignalType::enable_inverted; + else if (type == "test_scan_clock") + signal_type = ScanSignalType::clock; + else if (type == "test_scan_clock_a") + signal_type = ScanSignalType::clock_a; + else if (type == "test_scan_clock_b") + signal_type = ScanSignalType::clock_b; + else if (type == "test_scan_in") + signal_type = ScanSignalType::input; + else if (type == "test_scan_in_inverted") + signal_type = ScanSignalType::input_inverted; + else if (type == "test_scan_out") + signal_type = ScanSignalType::output; + else if (type == "test_scan_out_inverted") + signal_type = ScanSignalType::output_inverted; + else { + warn(1299, port_group, "unknown signal_type {}.", type); + return; + } + for (LibertyPort *port : ports) + port->setScanSignalType(signal_type); } void -LibertyReader::visitSlewLowerThresholdPct(LibertyAttr *attr, - const RiseFall *rf) +LibertyReader::readPortDir(const LibertyPortSeq &ports, + const LibertyGroup *port_group) { - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setSlewLowerThreshold(rf, value / 100.0F); + const LibertySimpleAttr *dir_attr = port_group->findSimpleAttr("direction"); + // Note missing direction attribute is not an error because a bus group + // can have pin groups for the bus bits that have direcitons. + if (dir_attr) { + const std::string &dir = dir_attr->stringValue(); + if (!dir.empty()) { + PortDirection *port_dir = PortDirection::unknown(); + if (dir == "input") + port_dir = PortDirection::input(); + else if (dir == "output") + port_dir = PortDirection::output(); + else if (dir == "inout") + port_dir = PortDirection::bidirect(); + else if (dir == "internal") + port_dir = PortDirection::internal(); + else + warn(1240, dir_attr, "unknown port direction."); + for (LibertyPort *port : ports) + port->setDirection(port_dir); + } } - have_slew_lower_threshold_[rf->index()] = true; } void -LibertyReader::visitSlewUpperThresholdPctFall(LibertyAttr *attr) +LibertyReader::readCapacitance(const LibertyPortSeq &ports, + const LibertyGroup *port_group) { - visitSlewUpperThresholdPct(attr, RiseFall::fall()); + // capacitance + readPortAttrFloat("capacitance", &LibertyPort::setCapacitance, ports, + port_group, cap_scale_); + + for (LibertyPort *port : ports) { + // rise/fall_capacitance + for (const RiseFall *rf : RiseFall::range()) { + std::string attr_name = sta::format("{}_capacitance", rf->to_string()); + float cap; + bool exists; + port_group->findAttrFloat(attr_name, cap, exists); + if (exists) { + for (const MinMax *min_max : MinMax::range()) + port->setCapacitance(rf, min_max, cap * cap_scale_); + } + + // rise/fall_capacitance_range(min_cap, max_cap); + attr_name = sta::format("{}_capacitance_range", rf->to_string()); + const LibertyComplexAttrSeq &range_attrs = port_group->findComplexAttrs(attr_name); + if (!range_attrs.empty()) { + const LibertyComplexAttr *attr = range_attrs[0]; + const LibertyAttrValueSeq &values = attr->values(); + if (values.size() == 2) { + auto [cap_min, min_valid] = values[0]->floatValue(); + if (min_valid) + port->setCapacitance(rf, MinMax::min(), cap_min * cap_scale_); + auto [cap_max, max_valid] = values[1]->floatValue(); + if (max_valid) + port->setCapacitance(rf, MinMax::max(), cap_max * cap_scale_); + } + } + } + if (!(port->isBus() || port->isBundle())) + setPortCapDefault(port); + + for (const MinMax *min_max : MinMax::range()) { + // min/max_capacitance + std::string attr_name = sta::format("{}_capacitance", min_max->to_string()); + float limit; + bool exists; + port_group->findAttrFloat(attr_name, limit, exists); + if (exists) + port->setCapacitanceLimit(limit * cap_scale_, min_max); + + // min/max_transition + attr_name = sta::format("{}_transition", min_max->to_string()); + port_group->findAttrFloat(attr_name, limit, exists); + if (exists) { + if (min_max == MinMax::max() && limit == 0.0) + warn(1241, port_group, "max_transition is 0.0."); + port->setSlewLimit(limit * time_scale_, min_max); + } + } + + // Default capacitance. + if (port->isBus() || port->isBundle()) { + // Do not clobber member port capacitances by setting the capacitance + // on a bus or bundle. + LibertyPortMemberIterator member_iter(port); + while (member_iter.hasNext()) { + LibertyPort *member = member_iter.next(); + setPortCapDefault(member); + } + } + else + setPortCapDefault(port); + } } void -LibertyReader::visitSlewUpperThresholdPctRise(LibertyAttr *attr) +LibertyReader::setPortCapDefault(LibertyPort *port) { - visitSlewUpperThresholdPct(attr, RiseFall::rise()); + for (const MinMax *min_max : MinMax::range()) { + for (const RiseFall *rf : RiseFall::range()) { + float cap; + bool exists; + port->capacitance(rf, min_max, cap, exists); + if (!exists) + port->setCapacitance(rf, min_max, defaultCap(port)); + } + } } void -LibertyReader::visitSlewUpperThresholdPct(LibertyAttr *attr, - const RiseFall *rf) +LibertyReader::readMinPulseWidth(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group) { - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setSlewUpperThreshold(rf, value / 100.0F); + for (LibertyPort *port : ports) { + TimingArcAttrsPtr timing_attrs = nullptr; + for (const RiseFall *rf : RiseFall::range()) { + const std::string mpw_attr_name = rf == RiseFall::rise() + ? "min_pulse_width_high" + : "min_pulse_width_low"; + float mpw; + bool exists; + port_group->findAttrFloat(mpw_attr_name, mpw, exists); + if (exists) { + mpw *= time_scale_; + port->setMinPulseWidth(rf, mpw); + + // Make timing arcs for the port min_pulse_width_low/high attributes. + // This is redundant but makes sdf annotation consistent. + if (timing_attrs == nullptr) { + timing_attrs = std::make_shared(); + timing_attrs->setTimingType(TimingType::min_pulse_width); + } + TimingModel *check_model = + makeScalarCheckModel(cell, mpw, ScaleFactorType::min_pulse_width, rf); + timing_attrs->setModel(rf, check_model); + } + } + if (timing_attrs) + builder_.makeTimingArcs(cell, port, port, nullptr, timing_attrs); } - have_slew_upper_threshold_[rf->index()] = true; } void -LibertyReader::visitSlewDerateFromLibrary(LibertyAttr *attr) +LibertyReader::makePortFuncs(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group) { - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setSlewDerateFromLibrary(value); + const LibertySimpleAttr *func_attr = port_group->findSimpleAttr("function"); + if (func_attr) { + const std::string &func = func_attr->stringValue(); + if (!func.empty()) { + FuncExpr *func_expr = parseFunc(func, "function", cell, func_attr->line()); + for (LibertyPort *port : ports) { + port->setFunction(func_expr); + if (func_expr->checkSize(port)) { + warn(1195, func_attr->line(), + "port {} function size does not match port size.", + port->name()); + } + } + } + } + + const LibertySimpleAttr *tri_attr = port_group->findSimpleAttr("three_state"); + if (tri_attr) { + const std::string tri_disable = tri_attr->stringValue(); + if (!tri_disable.empty()) { + FuncExpr *tri_disable_expr = parseFunc(tri_disable, + "three_state", cell, + tri_attr->line()); + FuncExpr *tri_enable_expr = tri_disable_expr->invert(); + for (LibertyPort *port : ports) { + port->setTristateEnable(tri_enable_expr); + if (port->direction() == PortDirection::output()) + port->setDirection(PortDirection::tristate()); + } + } } } //////////////////////////////////////////////////////////////// void -LibertyReader::beginTechnology(LibertyGroup *group) +LibertyReader::makeSequentials(LibertyCell *cell, + const LibertyGroup *cell_group) { - if (library_) { - const char *tech = group->firstName(); - if (stringEq(tech, "fpga")) - library_->setDelayModelType(DelayModelType::cmos_linear); + makeSequentials(cell, cell_group, true, "ff", "clocked_on", "next_state"); + makeSequentials(cell, cell_group, true, "ff_bank", "clocked_on", "next_state"); + makeSequentials(cell, cell_group, false, "latch", "enable", "data_in"); + makeSequentials(cell, cell_group, false, "latch_bank", "enable", "data_in"); + + const LibertyGroup *lut_group = cell_group->findSubgroup("lut");; + if (lut_group) { + LibertyPort *out_port = nullptr; + LibertyPort *out_port_inv = nullptr; + size_t size; + makeSeqPorts(cell, lut_group, out_port, out_port_inv, size); } } void -LibertyReader::endTechnology(LibertyGroup *) +LibertyReader::makeSequentials(LibertyCell *cell, + const LibertyGroup *cell_group, + bool is_register, + std::string_view seq_group_name, + std::string_view clk_attr_name, + std::string_view data_attr_name) { -} + for (const LibertyGroup *seq_group : + cell_group->findSubgroups(seq_group_name)) { + LibertyPort *out_port = nullptr; + LibertyPort *out_port_inv = nullptr; + size_t size; + makeSeqPorts(cell, seq_group, out_port, out_port_inv, size); + FuncExpr *clk_expr = makeSeqFunc(cell, seq_group, clk_attr_name, size); + FuncExpr *data_expr = makeSeqFunc(cell, seq_group, data_attr_name, size); + FuncExpr *clr_expr = makeSeqFunc(cell, seq_group, "clear", size); + FuncExpr *preset_expr = makeSeqFunc(cell, seq_group, "preset", size); -void -LibertyReader::beginTableTemplateDelay(LibertyGroup *group) -{ - beginTableTemplate(group, TableTemplateType::delay); -} + LogicValue clr_preset_var1 = LogicValue::unknown; + const LibertySimpleAttr *var1 = seq_group->findSimpleAttr("clear_preset_var1"); + if (var1) + clr_preset_var1 = getAttrLogicValue(var1); -void -LibertyReader::beginTableTemplateOutputCurrent(LibertyGroup *group) -{ - beginTableTemplate(group, TableTemplateType::output_current); + LogicValue clr_preset_var2 = LogicValue::unknown; + const LibertySimpleAttr *var2 = seq_group->findSimpleAttr("clear_preset_var2"); + if (var2) + clr_preset_var2 = getAttrLogicValue(var2); + + cell->makeSequential(size, is_register, clk_expr, data_expr, clr_expr, + preset_expr, clr_preset_var1, clr_preset_var2, + out_port, out_port_inv); + } } -void -LibertyReader::beginTableTemplate(LibertyGroup *group, - TableTemplateType type) -{ - if (library_) { - const char *name = group->firstName(); - if (name) { - tbl_template_ = new TableTemplate(name); - library_->addTableTemplate(tbl_template_, type); - } +FuncExpr * +LibertyReader::makeSeqFunc(LibertyCell *cell, + const LibertyGroup *seq_group, + std::string_view attr_name, + int size) +{ + FuncExpr *expr = nullptr; + const std::string &attr = seq_group->findAttrString(attr_name); + if (!attr.empty()) { + expr = parseFunc(attr, attr_name, cell, seq_group->line()); + if (expr && expr->checkSize(size)) { + warn(1196, seq_group, "{} {} bus width mismatch.", + seq_group->type(), attr_name); + delete expr; + expr = nullptr; + } + } + return expr; +} + +void +LibertyReader::makeSeqPorts(LibertyCell *cell, + const LibertyGroup *seq_group, + // Return values. + LibertyPort *&out_port, + LibertyPort *&out_port_inv, + size_t &size) +{ + std::string out_name, out_inv_name; + bool has_size; + seqPortNames(seq_group, out_name, out_inv_name, has_size, size); + if (!out_name.empty()) { + if (has_size) + out_port = makeBusPort(cell, out_name, size - 1, 0, nullptr); + else + out_port = makePort(cell, out_name); + out_port->setDirection(PortDirection::internal()); + } + if (!out_inv_name.empty()) { + if (has_size) + out_port_inv = makeBusPort(cell, out_inv_name, size - 1, 0, nullptr); else - libWarn(1175, group, "table template missing name."); - axis_var_[0] = axis_var_[1] = axis_var_[2] = TableAxisVariable::unknown; - clearAxisValues(); + out_port_inv = makePort(cell, out_inv_name); + out_port_inv->setDirection(PortDirection::internal()); } } void -LibertyReader::clearAxisValues() +LibertyReader::seqPortNames(const LibertyGroup *group, + // Return values. + std::string &out_name, + std::string &out_inv_name, + bool &has_size, + size_t &size) { - axis_values_[0] = axis_values_[1] = axis_values_[2] = nullptr; -} - -void -LibertyReader::endTableTemplate(LibertyGroup *group) -{ - if (tbl_template_) { - TableAxisPtr axis1 = makeAxis(0, group); - if (axis1) - tbl_template_->setAxis1(axis1); - TableAxisPtr axis2 = makeAxis(1, group); - if (axis2) - tbl_template_->setAxis2(axis2); - TableAxisPtr axis3 = makeAxis(2, group); - if (axis3) - tbl_template_->setAxis3(axis3); - tbl_template_ = nullptr; - axis_var_[0] = axis_var_[1] = axis_var_[2] = TableAxisVariable::unknown; + has_size = false; + const size_t param_count = group->params().size(); + if (param_count == 1) { + // out_port + out_name = group->firstParam(); + size = 1; } -} - -TableAxisPtr -LibertyReader::makeAxis(int index, - LibertyGroup *group) -{ - TableAxisVariable axis_var = axis_var_[index]; - FloatSeq *axis_values = axis_values_[index]; - if (axis_var != TableAxisVariable::unknown) { - if (axis_values) { - const Units *units = library_->units(); - float scale = tableVariableUnit(axis_var, units)->scale(); - scaleFloats(axis_values, scale); + else if (param_count == 2) { + // out_port, out_port_inv + out_name = group->firstParam(); + out_inv_name = group->secondParam(); + size = 1; + } + else if (param_count == 3) { + LibertyAttrValue *third_value = group->params()[2]; + auto [size_flt, size_valid] = third_value->floatValue(); + if (size_valid) { + // out_port, out_port_inv, bus_size + out_name = group->firstParam(); + out_inv_name = group->secondParam(); + size = static_cast(size_flt); + has_size = true; + } + else { + // in_port (ignored), out_port, out_port_inv + out_name = group->secondParam(); + out_inv_name = third_value->stringValue(); + has_size = true; + size = 1; } - return make_shared(axis_var, axis_values); - } - else if (axis_values) { - libWarn(1176, group, "missing variable_%d attribute.", index + 1); - delete axis_values; - axis_values_[index] = nullptr; } - // No warning for missing index_xx attributes because they are not required. - return nullptr; } -static void -scaleFloats(FloatSeq *floats, float scale) -{ - size_t count = floats->size(); - for (size_t i = 0; i < count; i++) - (*floats)[i] *= scale; +//////////////////////////////////////////////////////////////// + +void +LibertyReader::readCellAttributes(LibertyCell *cell, + const LibertyGroup *cell_group) +{ + readCellAttrFloat("area", &LibertyCell::setArea, cell, cell_group, 1.0); + readCellAttrString("cell_footprint", &LibertyCell::setFootprint, cell, cell_group); + readCellAttrBool("dont_use", &LibertyCell::setDontUse, cell, cell_group); + readCellAttrBool("is_macro_cell", &LibertyCell::setIsMacro, cell, cell_group); + readCellAttrBool("is_pad", &LibertyCell::setIsPad, cell, cell_group); + readCellAttrBool("is_level_shifter", &LibertyCell::setIsLevelShifter, cell, cell_group); + readCellAttrBool("is_clock_cell", &LibertyCell::setIsClockCell, cell, cell_group); + readCellAttrBool("is_isolation_cell", &LibertyCell::setIsIsolationCell,cell,cell_group); + readCellAttrBool("always_on", &LibertyCell::setAlwaysOn,cell,cell_group); + readCellAttrBool("interface_timing", &LibertyCell::setInterfaceTiming,cell,cell_group); + readCellAttrFloat("cell_leakage_power", &LibertyCell::setLeakagePower, cell, + cell_group, power_scale_); + + readCellAttrBool("is_memory", &LibertyCell::setIsMemory, cell, cell_group); + if (cell_group->findSubgroup("memory")) + cell->setIsMemory(true); + + readCellAttrBool("pad_cell", &LibertyCell::setIsPad, cell, cell_group); + readLevelShifterType(cell, cell_group); + readSwitchCellType(cell, cell_group); + readCellAttrString("user_function_class", &LibertyCell::setUserFunctionClass, + cell, cell_group); + + readOcvDerateFactors(cell, cell_group); + readCellOcvDerateGroup(cell, cell_group); + readGroupAttrFloat("ocv_arc_depth", cell_group, + [cell](float v) { cell->setOcvArcDepth(v); }); + + const std::string &clock_gate_type = + cell_group->findAttrString("clock_gating_integrated_cell"); + if (!clock_gate_type.empty()) { + if (stringBeginEqual(clock_gate_type, "latch_posedge")) + cell->setClockGateType(ClockGateType::latch_posedge); + else if (stringBeginEqual(clock_gate_type, "latch_negedge")) + cell->setClockGateType(ClockGateType::latch_negedge); + else + cell->setClockGateType(ClockGateType::other); + } + + readScaleFactors(cell, cell_group); + readLeakageGrouops(cell, cell_group); + readGeneratedClocks(cell, cell_group); + readStatetable(cell, cell_group); + readModeDefs(cell, cell_group); } void -LibertyReader::visitVariable1(LibertyAttr *attr) +LibertyReader::readScaleFactors(LibertyCell *cell, + const LibertyGroup *cell_group) { - visitVariable(0, attr); + const std::string &scale_factors_name = + cell_group->findAttrString("scaling_factors"); + if (!scale_factors_name.empty()) { + ScaleFactors *scale_factors = + library_->findScaleFactors(scale_factors_name); + if (scale_factors) + cell->setScaleFactors(scale_factors); + else + warn(1230, cell_group, "scaling_factors {} not found.", scale_factors_name); + } } void -LibertyReader::visitVariable2(LibertyAttr *attr) +LibertyReader::readCellAttrString(std::string_view attr_name, + void (LibertyCell::*set_func)(std::string_view value), + LibertyCell *cell, + const LibertyGroup *group) { - visitVariable(1, attr); + const std::string &value = group->findAttrString(attr_name); + if (!value.empty()) + (cell->*set_func)(value); } void -LibertyReader::visitVariable3(LibertyAttr *attr) +LibertyReader::readCellAttrFloat(std::string_view attr_name, + void (LibertyCell::*set_func)(float value), + LibertyCell *cell, + const LibertyGroup *group, + float scale) { - visitVariable(2, attr); + float value; + bool exists; + group->findAttrFloat(attr_name, value, exists); + if (exists) + (cell->*set_func)(value * scale); +} + +void +LibertyReader::readCellAttrBool(std::string_view attr_name, + void (LibertyCell::*set_func)(bool value), + LibertyCell *cell, + const LibertyGroup *group) +{ + const LibertySimpleAttr *attr = group->findSimpleAttr(attr_name); + if (attr) { + const LibertyAttrValue &attr_value = attr->value(); + if (attr_value.isString()) { + const std::string &value = attr_value.stringValue(); + if (stringEqual(value, "true")) + (cell->*set_func)(true); + else if (stringEqual(value, "false")) + (cell->*set_func)(false); + else + warn(1279, attr, "{} attribute is not boolean.", attr_name); + } + else + warn(1280, attr, "{} attribute is not boolean.", attr_name); + } } +//////////////////////////////////////////////////////////////// + void -LibertyReader::visitVariable(int index, - LibertyAttr *attr) -{ - if (tbl_template_) { - const char *type = getAttrString(attr); - TableAxisVariable var = stringTableAxisVariable(type); - if (var == TableAxisVariable::unknown) - libWarn(1297, attr, "axis type %s not supported.", type); - else - axis_var_[index] = var; +LibertyReader::makeTimingArcs(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group) +{ + for (const LibertyGroup *timing_group : port_group->findSubgroups("timing")) { + TimingArcAttrsPtr timing_attrs = std::make_shared(); + readTimingArcAttrs(cell, timing_group, *timing_attrs); + makeTimingModels(cell, timing_group, *timing_attrs); + + LibertyPort *related_output_port = findLibertyPort(cell, timing_group, + "related_output_pin"); + StringSeq related_port_names = findAttributStrings(timing_group, "related_pin"); + StringSeq related_bus_names=findAttributStrings(timing_group,"related_bus_pins"); + TimingType timing_type = timing_attrs->timingType(); + + for (LibertyPort *to_port : ports) { + if (timing_type == TimingType::combinational && + to_port->direction()->isInput()) + warn(1209, timing_group, "combinational timing to an input port."); + + if (!related_port_names.empty() || !related_bus_names.empty()) { + for (const std::string &from_port_name : related_port_names) { + debugPrint(debug_, "liberty", 2, " timing {} -> {}", + from_port_name, to_port->name()); + makeTimingArcs(cell, from_port_name, to_port, related_output_port, true, + timing_attrs, timing_group); + } + for (const std::string &from_port_name : related_bus_names) { + debugPrint(debug_, "liberty", 2, " timing {} -> {}", + from_port_name, to_port->name()); + makeTimingArcs(cell, from_port_name, to_port, related_output_port, false, + timing_attrs, timing_group); + } + } + else if (!(timing_type == TimingType::min_pulse_width + || timing_type == TimingType::minimum_period + || timing_type == TimingType::min_clock_tree_path + || timing_type == TimingType::max_clock_tree_path)) + warn(1243, timing_group, "timing group missing related_pin/related_bus_pin."); + else + makeTimingArcs(cell, to_port, related_output_port, timing_attrs); + } } } void -LibertyReader::visitIndex1(LibertyAttr *attr) +LibertyReader::readTimingArcAttrs(LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrs &timing_attrs) { - visitIndex(0, attr); + readTimingSense(timing_group, timing_attrs); + readTimingType(timing_group, timing_attrs); + readTimingWhen(cell, timing_group, timing_attrs); + readTimingMode(timing_group, timing_attrs); + readGroupAttrFloat("ocv_arc_depth", timing_group, + [&timing_attrs](float v) { timing_attrs.setOcvArcDepth(v); }); } void -LibertyReader::visitIndex2(LibertyAttr *attr) +LibertyReader::readGroupAttrFloat(std::string_view attr_name, + const LibertyGroup *group, + const std::function &set_func, + float scale) { - visitIndex(1, attr); + float value; + bool exists; + group->findAttrFloat(attr_name, value, exists); + if (exists) + set_func(value * scale); } void -LibertyReader::visitIndex3(LibertyAttr *attr) +LibertyReader::readTimingSense(const LibertyGroup *timing_group, + TimingArcAttrs &timing_attrs) { - visitIndex(2, attr); + const LibertySimpleAttr *sense_attr = timing_group->findSimpleAttr("timing_sense"); + if (sense_attr) { + const std::string &sense_name = sense_attr->stringValue(); + if (sense_name == "non_unate") + timing_attrs.setTimingSense(TimingSense::non_unate); + else if (sense_name == "positive_unate") + timing_attrs.setTimingSense(TimingSense::positive_unate); + else if (sense_name == "negative_unate") + timing_attrs.setTimingSense(TimingSense::negative_unate); + else + warn(1245, timing_group, "unknown timing_sense {}.", sense_name); + } } void -LibertyReader::visitIndex(int index, - LibertyAttr *attr) -{ - if (tbl_template_ - // Ignore index_xx in ecsm_waveform groups. - && !in_ecsm_waveform_) { - FloatSeq *axis_values = readFloatSeq(attr, 1.0F); - if (axis_values) { - if (axis_values->empty()) - libWarn(1177, attr, "missing table index values."); - else { - float prev = (*axis_values)[0]; - for (size_t i = 1; i < axis_values->size(); i++) { - float value = (*axis_values)[i]; - if (value <= prev) - libWarn(1178, attr, "non-increasing table index values."); - prev = value; - } - } - axis_values_[index] = axis_values; +LibertyReader::readTimingType(const LibertyGroup *timing_group, + TimingArcAttrs &timing_attrs) +{ + TimingType type = TimingType::combinational; + const LibertySimpleAttr *type_attr = timing_group->findSimpleAttr("timing_type"); + if (type_attr) { + const std::string type_name = type_attr->stringValue(); + type = findTimingType(type_name); + if (type == TimingType::unknown) { + warn(1244, type_attr, "unknown timing_type {}.", type_name); + type = TimingType::combinational; } } + timing_attrs.setTimingType(type); } -//////////////////////////////////////////////////////////////// - void -LibertyReader::beginType(LibertyGroup *) +LibertyReader::readTimingWhen(const LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrs &timing_attrs) { - type_bit_from_exists_ = false; - type_bit_to_exists_ = false; + const LibertySimpleAttr *when_attr = timing_group->findSimpleAttr("when"); + if (when_attr) { + const std::string &when = when_attr->stringValue(); + if (!when.empty()) { + FuncExpr *when_expr = parseFunc(when, "when", cell, when_attr->line()); + timing_attrs.setCond(when_expr); + } + } + + const LibertySimpleAttr *cond_attr = timing_group->findSimpleAttr("sdf_cond"); + if (cond_attr) { + const std::string &cond = cond_attr->stringValue(); + timing_attrs.setSdfCond(cond); + } + cond_attr = timing_group->findSimpleAttr("sdf_cond_start"); + if (cond_attr) { + const std::string &cond = cond_attr->stringValue(); + timing_attrs.setSdfCondStart(cond); + } + cond_attr = timing_group->findSimpleAttr("sdf_cond_end"); + if (cond_attr) { + const std::string &cond = cond_attr->stringValue(); + timing_attrs.setSdfCondEnd(cond); + } } void -LibertyReader::endType(LibertyGroup *group) -{ - const char *name = group->firstName(); - if (name) { - if (type_bit_from_exists_ && type_bit_to_exists_) { - BusDcl *bus_dcl = new BusDcl(name, type_bit_from_, type_bit_to_); - if (cell_) - cell_->addBusDcl(bus_dcl); - else if (library_) - library_->addBusDcl(bus_dcl); - } - else { - if (!type_bit_from_exists_) - libWarn(1179, group, "bus type %s missing bit_from.", name); - if (!type_bit_to_exists_) - libWarn(1180, group, "bus type %s missing bit_to.", name); +LibertyReader::readTimingMode(const LibertyGroup *timing_group, + TimingArcAttrs &timing_attrs) +{ + const LibertyComplexAttrSeq &mode_attrs = timing_group->findComplexAttrs("mode"); + if (!mode_attrs.empty()) { + const LibertyComplexAttr *mode_attr = mode_attrs[0]; + const LibertyAttrValueSeq &mode_values = mode_attr->values(); + if (mode_values.size() == 2) { + LibertyAttrValue *value = mode_values[0]; + if (value->isString()) + timing_attrs.setModeName(value->stringValue()); + else + warn(1248, mode_attr, "mode name is not a string."); + + value = mode_values[1]; + if (value->isString()) + timing_attrs.setModeValue(value->stringValue()); + else + warn(1246, mode_attr, "mode value is not a string."); } + else + warn(1249, mode_attr, "mode requirees 2 values."); } - else - libWarn(1181, group, "type missing name."); } void -LibertyReader::visitBitFrom(LibertyAttr *attr) +LibertyReader::makeTimingModels(LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrs &timing_attrs) { - getAttrInt(attr, type_bit_from_, type_bit_from_exists_); + switch (cell->libertyLibrary()->delayModelType()) { + case DelayModelType::cmos_linear: + makeLinearModels(cell, timing_group, timing_attrs); + break; + case DelayModelType::table: + makeTableModels(cell, timing_group, timing_attrs); + break; + case DelayModelType::cmos_pwl: + case DelayModelType::cmos2: + case DelayModelType::polynomial: + case DelayModelType::dcm: + break; + } } void -LibertyReader::visitBitTo(LibertyAttr *attr) +LibertyReader::makeLinearModels(LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrs &timing_attrs) { - getAttrInt(attr, type_bit_to_, type_bit_to_exists_); + LibertyLibrary *library = cell->libertyLibrary(); + for (const RiseFall *rf : RiseFall::range()) { + std::string intr_attr_name = sta::format("intrinsic_{}", rf->to_string()); + float intr = 0.0; + bool intr_exists; + timing_group->findAttrFloat(intr_attr_name, intr, intr_exists); + if (intr_exists) + intr *= time_scale_; + else + library->defaultIntrinsic(rf, intr, intr_exists); + TimingModel *model = nullptr; + if (intr_exists) { + if (timingTypeIsCheck(timing_attrs.timingType())) + model = new CheckLinearModel(cell, intr); + else { + std::string res_attr_name = sta::format("{}_resistance", rf->to_string()); + float res = 0.0; + bool res_exists; + timing_group->findAttrFloat(res_attr_name, res, res_exists); + if (res_exists) + res *= res_scale_; + else + library->defaultPinResistance(rf, PortDirection::output(), + res, res_exists); + model = new GateLinearModel(cell, intr, res); + } + timing_attrs.setModel(rf, model); + } + } +} + +void +LibertyReader::makeTableModels(LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrs &timing_attrs) +{ + bool found_model = false; + for (const RiseFall *rf : RiseFall::range()) { + TableModel *delay_model = readTableModel(timing_group, + sta::format("cell_{}", rf->to_string()), + rf, TableTemplateType::delay, + time_scale_, + ScaleFactorType::cell, + GateTableModel::checkAxes); + TableModel *slew_model = readTableModel(timing_group, + sta::format("{}_transition", + rf->to_string()), + rf, TableTemplateType::delay, + time_scale_, + ScaleFactorType::transition, + GateTableModel::checkAxes); + if (delay_model || slew_model) { + TableModels *delay_models = new TableModels(delay_model); + readLvfModels(timing_group, + sta::format("ocv_sigma_cell_{}", rf->to_string()), + sta::format("ocv_std_dev_cell_{}", rf->to_string()), + sta::format("ocv_mean_shift_cell_{}", rf->to_string()), + sta::format("ocv_skewness_cell_{}", rf->to_string()), + rf, delay_models, GateTableModel::checkAxes); + + TableModels *slew_models = new TableModels(slew_model); + readLvfModels(timing_group, + sta::format("ocv_sigma_{}_transition", rf->to_string()), + sta::format("ocv_std_dev_{}_transition", rf->to_string()), + sta::format("ocv_mean_shift_{}_transition", rf->to_string()), + sta::format("ocv_skewness_{}_transition", rf->to_string()), + rf, slew_models, GateTableModel::checkAxes); + + ReceiverModelPtr receiver_model = readReceiverCapacitance(timing_group, rf); + OutputWaveforms *output_waveforms = readOutputWaveforms(timing_group, rf); + + timing_attrs.setModel(rf, new GateTableModel(cell, delay_models, + slew_models, + std::move(receiver_model), + output_waveforms)); + TimingType timing_type = timing_attrs.timingType(); + if (isGateTimingType(timing_type)) { + if (slew_model == nullptr) + warn(1210, timing_group, "missing {}_transition.", rf->name()); + if (delay_model == nullptr) + warn(1211, timing_group, "missing cell_{}.", rf->name()); + } + found_model = true; + } + + std::string constraint_attr_name = sta::format("{}_constraint", rf->to_string()); + ScaleFactorType scale_factor_type = + timingTypeScaleFactorType(timing_attrs.timingType()); + TableModel *check_model = readTableModel(timing_group, + constraint_attr_name, + rf, TableTemplateType::delay, + time_scale_, scale_factor_type, + CheckTableModel::checkAxes); + if (check_model) { + TableModels *check_models = new TableModels(check_model); + readLvfModels(timing_group, + sta::format("ocv_sigma_{}_constraiint", rf->to_string()), + sta::format("ocv_std_dev_{}_constraiint", rf->to_string()), + sta::format("ocv_mean_shift_{}_constraiint", rf->to_string()), + sta::format("ocv_skewness_{}_constraiint", rf->to_string()), + rf, check_models, CheckTableModel::checkAxes); + timing_attrs.setModel(rf, new CheckTableModel(cell, check_models)); + found_model = true; + } + } + if (!found_model) + warn(1311, timing_group, "no table models found in timing group."); } -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginScalingFactors(LibertyGroup *group) -{ - const char *name = group->firstName(); - if (name) { - save_scale_factors_ = scale_factors_; - scale_factors_ = new ScaleFactors(name); - library_->addScaleFactors(scale_factors_); - } - else - libWarn(1182, group, "scaling_factors do not have a name."); +bool +LibertyReader::isGateTimingType(TimingType timing_type) +{ + return timing_type == TimingType::clear + || timing_type == TimingType::combinational + || timing_type == TimingType::combinational_fall + || timing_type == TimingType::combinational_rise + || timing_type == TimingType::falling_edge + || timing_type == TimingType::preset + || timing_type == TimingType::rising_edge + || timing_type == TimingType::three_state_disable + || timing_type == TimingType::three_state_disable_rise + || timing_type == TimingType::three_state_disable_fall + || timing_type == TimingType::three_state_enable + || timing_type == TimingType::three_state_enable_fall + || timing_type == TimingType::three_state_enable_rise; } -void -LibertyReader::endScalingFactors(LibertyGroup *) -{ - scale_factors_ = save_scale_factors_; +TableModel * +LibertyReader::readTableModel(const LibertyGroup *timing_group, + const std::string &table_group_name, + const RiseFall *rf, + TableTemplateType template_type, + float scale, + ScaleFactorType scale_factor_type, + const std::function &check_axes) +{ + const LibertyGroup *table_group = timing_group->findSubgroup(table_group_name); + if (table_group) + return readTableModel(table_group, rf, template_type, scale, + scale_factor_type, check_axes); + return nullptr; } void -LibertyReader::visitScaleFactorSuffix(LibertyAttr *attr) -{ - if (scale_factors_) { - ScaleFactorPvt pvt = ScaleFactorPvt::unknown; - ScaleFactorType type = ScaleFactorType::unknown; - const RiseFall *rf = nullptr; - // Parse the attribute name. - TokenParser parser(attr->name(), "_"); - if (parser.hasNext()) - parser.next(); - if (parser.hasNext()) { - const char *pvt_name = parser.next(); - pvt = findScaleFactorPvt(pvt_name); - } - if (parser.hasNext()) { - const char *type_name = parser.next(); - type = findScaleFactorType(type_name); - } - if (parser.hasNext()) { - const char *tr_name = parser.next(); - if (stringEq(tr_name, "rise")) - rf = RiseFall::rise(); - else if (stringEq(tr_name, "fall")) - rf = RiseFall::fall(); - } - if (pvt != ScaleFactorPvt::unknown - && type != ScaleFactorType::unknown - && rf) { - float value; +LibertyReader::readLvfModels(const LibertyGroup *timing_group, + const std::string &sigma_group_name, + const std::string &std_dev_group_name, + const std::string &mean_shift_group_name, + const std::string &skewness_group_name, + const RiseFall *rf, + TableModels *table_models, + const std::function &check_axes) +{ + TableModelsEarlyLate sigmas = + readEarlyLateTableModels(timing_group, + sigma_group_name, + rf, TableTemplateType::delay, + time_scale_, + ScaleFactorType::unknown, + check_axes); + for (const EarlyLate *early_late : EarlyLate::range()) + table_models->setSigma(sigmas[early_late->index()], early_late); + + const LibertyGroup *std_dev_group = timing_group->findSubgroup(std_dev_group_name); + if (std_dev_group) { + TableModel *std_dev = readTableModel(std_dev_group, rf, TableTemplateType::delay, + time_scale_, ScaleFactorType::unknown, + check_axes); + table_models->setStdDev(std_dev); + } + + const LibertyGroup *mean_shift_group=timing_group->findSubgroup(mean_shift_group_name); + if (mean_shift_group) { + TableModel *mean_shift = readTableModel(mean_shift_group, rf, + TableTemplateType::delay, + time_scale_, ScaleFactorType::unknown, + check_axes); + table_models->setMeanShift(mean_shift); + } + + const LibertyGroup *skewness_group = timing_group->findSubgroup(skewness_group_name); + if (skewness_group) { + TableModel *skewness = readTableModel(skewness_group, rf, + TableTemplateType::delay, + 1.0, ScaleFactorType::unknown, + check_axes); + table_models->setSkewness(skewness); + } +} + +TableModelsEarlyLate +LibertyReader::readEarlyLateTableModels(const LibertyGroup *timing_group, + std::string_view table_group_name, + const RiseFall *rf, + TableTemplateType template_type, + float scale, + ScaleFactorType scale_factor_type, + const std::function &check_axes) +{ + TableModelsEarlyLate models{}; + for (const LibertyGroup *table_group : timing_group->findSubgroups(table_group_name)){ + TableModel *model = readTableModel(table_group, rf, template_type, scale, + scale_factor_type, check_axes); + const std::string &early_late = table_group->findAttrString("sigma_type"); + if (early_late.empty() + || early_late == "early_and_late") { + models[EarlyLate::early()->index()] = model; + models[EarlyLate::late()->index()] = model; + } + else if (early_late == "early") + models[EarlyLate::early()->index()] = model; + else if (early_late == "late") + models[EarlyLate::late()->index()] = model; + } + return models; +} + +ReceiverModelPtr +LibertyReader::readReceiverCapacitance(const LibertyGroup *timing_group, + const RiseFall *rf) +{ + ReceiverModelPtr receiver_model = nullptr; + readReceiverCapacitance(timing_group, "receiver_capacitance", 0, rf, + receiver_model); + readReceiverCapacitance(timing_group, "receiver_capacitance1", 0, rf, + receiver_model); + readReceiverCapacitance(timing_group, "receiver_capacitance2", 1, rf, + receiver_model); + return receiver_model; +} + +void +LibertyReader::readReceiverCapacitance(const LibertyGroup *timing_group, + std::string_view cap_group_name, + int index, + const RiseFall *rf, + ReceiverModelPtr &receiver_model) +{ + std::string cap_group_name1 = sta::format("{}_{}", cap_group_name, rf->to_string()); + const LibertyGroup *cap_group = timing_group->findSubgroup(cap_group_name1); + if (cap_group) { + const LibertySimpleAttr *segment_attr = cap_group->findSimpleAttr("segment"); + if (segment_attr) { + // For receiver_capacitance groups with mulitiple segments this + // overrides the index passed in beginReceiverCapacitance1Rise/Fall. + int segment; bool exists; - getAttrFloat(attr, value, exists); + getAttrInt(segment_attr, segment, exists); if (exists) - scale_factors_->setScale(type, pvt, rf, value); + index = segment; } + TableModel *model = readTableModel(cap_group, rf, TableTemplateType::delay, + cap_scale_, ScaleFactorType::pin_cap); + if (ReceiverModel::checkAxes(model)) { + if (receiver_model == nullptr) + receiver_model = std::make_shared(); + receiver_model->setCapacitanceModel(std::move(*model), index, rf); + } + else + warn(1219, cap_group, "unsupported model axis."); + delete model; } } -void -LibertyReader::visitScaleFactorPrefix(LibertyAttr *attr) -{ - if (scale_factors_) { - ScaleFactorPvt pvt = ScaleFactorPvt::unknown; - ScaleFactorType type = ScaleFactorType::unknown; - const RiseFall *rf = nullptr; - // Parse the attribute name. - TokenParser parser(attr->name(), "_"); - if (parser.hasNext()) - parser.next(); - if (parser.hasNext()) { - const char *pvt_name = parser.next(); - pvt = findScaleFactorPvt(pvt_name); - } - if (parser.hasNext()) { - const char *tr_name = parser.next(); - if (stringEq(tr_name, "rise")) - rf = RiseFall::rise(); - else if (stringEq(tr_name, "fall")) - rf = RiseFall::fall(); - } - if (parser.hasNext()) { - const char *type_name = parser.next(); - type = findScaleFactorType(type_name); - } - if (pvt != ScaleFactorPvt::unknown - && type != ScaleFactorType::unknown - && rf) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - scale_factors_->setScale(type, pvt, rf, value); +OutputWaveforms * +LibertyReader::readOutputWaveforms(const LibertyGroup *timing_group, + const RiseFall *rf) +{ + const std::string current_group_name=sta::format("output_current_{}",rf->to_string()); + const LibertyGroup *current_group = timing_group->findSubgroup(current_group_name); + if (current_group) { + OutputWaveformSeq output_currents; + for (const LibertyGroup *vector_group : current_group->findSubgroups("vector")) { + float ref_time; + bool ref_time_exists; + vector_group->findAttrFloat("reference_time", ref_time, ref_time_exists); + if (ref_time_exists) { + ref_time *= time_scale_; + TableModel *table = readTableModel(vector_group, rf, + TableTemplateType::output_current, + current_scale_, ScaleFactorType::unknown); + if (table) { + TableTemplate *tbl_template = table->tblTemplate(); + const TableAxis *slew_axis, *cap_axis; + // Canonicalize axis order. + if (tbl_template->axis1()->variable()==TableAxisVariable::input_net_transition){ + slew_axis = table->axis1(); + cap_axis = table->axis2(); + } + else { + slew_axis = table->axis2(); + cap_axis = table->axis1(); + } + + if (slew_axis->size() == 1 && cap_axis->size() == 1) { + // Convert 1x1xN Table (order 3) to 1D Table. + float slew = slew_axis->axisValue(0); + float cap = cap_axis->axisValue(0); + TablePtr table_ptr = table->table(); + FloatTable *values3 = table_ptr->values3(); + FloatSeq row = std::move((*values3)[0]); + values3->erase(values3->begin()); + Table *table1 = new Table(std::move(row), table->table()->axis3ptr()); + output_currents.emplace_back(slew, cap, table1, ref_time); + } + else + warn(1223, vector_group, + "vector index_1 and index_2 must have exactly one value."); + } + delete table; + } + else + warn(1224, vector_group, "vector reference_time not found."); } + if (!output_currents.empty()) + return makeOutputWaveforms(current_group, output_currents, rf); } + return nullptr; } -void -LibertyReader::visitScaleFactorHiLow(LibertyAttr *attr) -{ - if (scale_factors_) { - ScaleFactorPvt pvt = ScaleFactorPvt::unknown; - ScaleFactorType type = ScaleFactorType::unknown; - const RiseFall *rf = nullptr; - const char *pvt_name = nullptr; - const char *type_name = nullptr; - const char *tr_name = nullptr; - // Parse the attribute name. - TokenParser parser(attr->name(), "_"); - if (parser.hasNext()) - parser.next(); - if (parser.hasNext()) { - pvt_name = parser.next(); - pvt = findScaleFactorPvt(pvt_name); - } - if (parser.hasNext()) { - type_name = parser.next(); - type = findScaleFactorType(type_name); - } - if (parser.hasNext()) { - tr_name = parser.next(); - if (stringEq(tr_name, "high")) - rf = RiseFall::rise(); - else if (stringEq(tr_name, "low")) - rf = RiseFall::fall(); - } - if (pvt != ScaleFactorPvt::unknown - && type != ScaleFactorType::unknown - && rf) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - scale_factors_->setScale(type, pvt, rf, value); +OutputWaveforms * +LibertyReader::makeOutputWaveforms(const LibertyGroup *current_group, + OutputWaveformSeq &output_currents, + const RiseFall *rf) +{ + std::set slew_set, cap_set; + FloatSeq slew_values; + FloatSeq cap_values; + for (const OutputWaveform &waveform : output_currents) { + float slew = waveform.slew(); + // Filter duplilcate slews and capacitances. + if (!slew_set.contains(slew)) { + slew_set.insert(slew); + slew_values.push_back(slew); + } + float cap = waveform.cap(); + if (!cap_set.contains(cap)) { + cap_set.insert(cap); + cap_values.push_back(cap); + } + } + sort(slew_values, std::less()); + sort(cap_values, std::less()); + size_t slew_size = slew_values.size(); + size_t cap_size = cap_values.size(); + TableAxisPtr slew_axis = + make_shared(TableAxisVariable::input_net_transition, + std::move(slew_values)); + TableAxisPtr cap_axis = + make_shared(TableAxisVariable::total_output_net_capacitance, + std::move(cap_values)); + FloatSeq ref_times(slew_size); + Table1Seq current_waveforms(slew_size * cap_size); + for (OutputWaveform &waveform : output_currents) { + size_t slew_index, cap_index; + bool slew_exists, cap_exists; + slew_axis->findAxisIndex(waveform.slew(), slew_index, slew_exists); + cap_axis->findAxisIndex(waveform.cap(), cap_index, cap_exists); + if (slew_exists && cap_exists) { + size_t index = slew_index * cap_axis->size() + cap_index; + current_waveforms[index] = waveform.releaseCurrents(); + ref_times[slew_index] = waveform.referenceTime(); } + else + warn(1221, current_group, "output current waveform {:.2e} {:.2e} not found.", + waveform.slew(), + waveform.cap()); } + Table ref_time_tbl(std::move(ref_times), slew_axis); + OutputWaveforms *output_current = new OutputWaveforms(slew_axis, cap_axis, rf, + current_waveforms, + std::move(ref_time_tbl)); + return output_current; } -void -LibertyReader::visitScaleFactor(LibertyAttr *attr) -{ - if (scale_factors_) { - ScaleFactorPvt pvt = ScaleFactorPvt::unknown; - ScaleFactorType type = ScaleFactorType::unknown; - const char *pvt_name = nullptr; - const char *type_name = nullptr; - // Parse the attribute name. - TokenParser parser(attr->name(), " "); - if (parser.hasNext()) - parser.next(); - if (parser.hasNext()) { - pvt_name = parser.next(); - pvt = findScaleFactorPvt(pvt_name); - } - if (parser.hasNext()) { - type_name = parser.next(); - type = findScaleFactorType(type_name); - } - if (pvt != ScaleFactorPvt::unknown - && type != ScaleFactorType::unknown) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - scale_factors_->setScale(type, pvt, value); +TableModel * +LibertyReader::readTableModel(const LibertyGroup *table_group, + const RiseFall *rf, + TableTemplateType template_type, + float scale, + ScaleFactorType scale_factor_type, + const std::function &check_axes) +{ + if (library_ && table_group->hasFirstParam()) { + const std::string &template_name = table_group->firstParam(); + TableTemplate *tbl_template = library_->findTableTemplate(template_name, + template_type); + if (tbl_template) { + TablePtr table = readTableModel(table_group, tbl_template, scale); + if (table) { + TableModel *table_model = new TableModel(table, tbl_template, + scale_factor_type, rf); + if (!check_axes(table_model)) { + warn(1251, table_group, "unsupported model axis."); + } + return table_model; + } } + else + warn(1253, table_group, "table template {} not found.", template_name); } + return nullptr; } -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginOpCond(LibertyGroup *group) +TablePtr +LibertyReader::readTableModel(const LibertyGroup *table_group, + const TableTemplate *tbl_template, + float scale) { - if (library_) { - const char *name = group->firstName(); - if (name) { - op_cond_ = new OperatingConditions(name); - library_->addOperatingConditions(op_cond_); + const LibertyComplexAttr *values_attr = table_group->findComplexAttr("values"); + if (values_attr) { + TableAxisPtr axis1 = makeTableAxis(table_group, "index_1", tbl_template->axis1ptr()); + TableAxisPtr axis2 = makeTableAxis(table_group, "index_2", tbl_template->axis2ptr()); + TableAxisPtr axis3 = makeTableAxis(table_group, "index_3", tbl_template->axis3ptr()); + if (axis1 && axis2 && axis3) { + // 3D table + FloatTable float_table = makeFloatTable(values_attr, table_group, + axis1->size() * axis2->size(), + axis3->size(), scale); + return make_shared

(std::move(float_table), axis1, axis2, axis3); + } + else if (axis1 && axis2) { + FloatTable float_table = makeFloatTable(values_attr, table_group, + axis1->size(), axis2->size(), scale); + return make_shared
(std::move(float_table), axis1, axis2); + } + else if (axis1) { + FloatTable table = makeFloatTable(values_attr, table_group, 1, + axis1->size(), scale); + return make_shared
(std::move(table[0]), axis1); + } + else if (axis1 == nullptr && axis2 == nullptr && axis3 == nullptr) { + FloatTable table = makeFloatTable(values_attr, table_group, 1, 1, scale); + float value = table[0][0]; + return std::make_shared
(value); } - else - libWarn(1183, group, "operating_conditions missing name."); } + else + warn(1257, table_group, "{} is missing values.", table_group->type()); + return nullptr; } -void -LibertyReader::visitProc(LibertyAttr *attr) -{ - if (op_cond_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - op_cond_->setProcess(value); - } -} - -void -LibertyReader::visitVolt(LibertyAttr *attr) -{ - if (op_cond_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - op_cond_->setVoltage(value * volt_scale_); - } -} - -void -LibertyReader::visitTemp(LibertyAttr *attr) -{ - if (op_cond_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - op_cond_->setTemperature(value); - } -} +TableAxisPtr +LibertyReader::makeTableAxis(const LibertyGroup *table_group, + std::string_view index_attr_name, + TableAxisPtr template_axis) +{ + const LibertyComplexAttr *index_attr = table_group->findComplexAttr(index_attr_name); + if (index_attr) { + FloatSeq axis_values = readFloatSeq(index_attr, 1.0F); + if (axis_values.empty()) + warn(1177, index_attr, "missing table index values."); + else { + // Check monotonicity of the values. + float prev = axis_values[0]; + for (size_t i = 1; i < axis_values.size(); i++) { + float value = axis_values[i]; + if (value <= prev) + warn(1173, index_attr, "non-increasing table index values."); + prev = value; + } -void -LibertyReader::visitTreeType(LibertyAttr *attr) -{ - if (op_cond_) { - const char *tree_type = getAttrString(attr); - if (tree_type) { - WireloadTree wire_load_tree = stringWireloadTree(tree_type); - op_cond_->setWireloadTree(wire_load_tree); + TableAxisVariable axis_var = template_axis->variable(); + const Units *units = library_->units(); + float scale = tableVariableUnit(axis_var, units)->scale(); + scaleFloats(axis_values, scale); + return make_shared(axis_var, std::move(axis_values)); } } -} - -void -LibertyReader::endOpCond(LibertyGroup *) -{ - op_cond_ = nullptr; + return template_axis; } //////////////////////////////////////////////////////////////// -void -LibertyReader::beginWireload(LibertyGroup *group) +bool +LibertyReader::relatedPinIncludesPort(LibertyCell *cell, + const LibertyGroup *timing_group, + LibertyPort *from_port, + int line) { - if (library_) { - const char *name = group->firstName(); - if (name) { - wireload_ = new Wireload(name, library_); - library_->addWireload(wireload_); + StringSeq related_names = findAttributStrings(timing_group, "related_pin"); + for (const std::string &related_pin : related_names) { + PortNameBitIterator it(cell, related_pin, this, line); + while (it.hasNext()) { + if (it.next() == from_port) + return true; } } - else - libWarn(1184, group, "wire_load missing name."); -} - -void -LibertyReader::endWireload(LibertyGroup *) -{ - wireload_ = nullptr; + return false; } -void -LibertyReader::visitResistance(LibertyAttr *attr) +bool +LibertyReader::sameArcIdentity(LibertyCell *cell, + const LibertyGroup *pin_timing, + const LibertyGroup *bus_timing, + LibertyPort *from_port) { - if (wireload_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - wireload_->setResistance(value * res_scale_); + static const std::string empty; + const LibertySimpleAttr *pin_type_attr = pin_timing->findSimpleAttr("timing_type"); + const LibertySimpleAttr *bus_type_attr = bus_timing->findSimpleAttr("timing_type"); + const std::string &pin_type = pin_type_attr ? pin_type_attr->stringValue() : empty; + const std::string &bus_type = bus_type_attr ? bus_type_attr->stringValue() : empty; + if (pin_type != bus_type) + return false; + const LibertySimpleAttr *pin_when = pin_timing->findSimpleAttr("when"); + const LibertySimpleAttr *bus_when = bus_timing->findSimpleAttr("when"); + const std::string &pin_when_str = pin_when ? pin_when->stringValue() : empty; + const std::string &bus_when_str = bus_when ? bus_when->stringValue() : empty; + if (pin_when_str != bus_when_str) + return false; + if (from_port == nullptr) { + StringSeq related = findAttributStrings(pin_timing, "related_pin"); + return related.empty(); } + return relatedPinIncludesPort(cell, pin_timing, from_port, bus_timing->line()); } -void -LibertyReader::visitSlope(LibertyAttr *attr) +bool +LibertyReader::hasBitPinTimingOverride(LibertyCell *cell, + LibertyPort *to_port_bit, + LibertyPort *from_port, + const LibertyGroup *bus_timing) { - if (wireload_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - wireload_->setSlope(value); - } + auto it = bit_overrides_.find(to_port_bit); + if (it == bit_overrides_.end()) + return false; + for (const LibertyGroup *pin_tg : it->second) + if (sameArcIdentity(cell, pin_tg, bus_timing, from_port)) + return true; + return false; } void -LibertyReader::visitFanoutLength(LibertyAttr *attr) +LibertyReader::makeTimingArcs(LibertyCell *cell, + const std::string &from_port_name, + LibertyPort *to_port, + LibertyPort *related_out_port, + bool one_to_one, + const TimingArcAttrsPtr &timing_attrs, + const LibertyGroup *timing_group) { - if (wireload_) { - float fanout, length; - bool exists; - getAttrFloat2(attr, fanout, length, exists); - if (exists) - wireload_->addFanoutLength(fanout, length); - else - libWarn(1185, attr, "fanout_length is missing length and fanout."); + int timing_line = timing_group->line(); + PortNameBitIterator from_port_iter(cell, from_port_name, this, timing_line); + if (from_port_iter.size() == 1 && !to_port->hasMembers()) { + // one -> one + if (from_port_iter.PortNameBitIterator::hasNext()) { + LibertyPort *from_port = from_port_iter.PortNameBitIterator::next(); + if (from_port->direction()->isOutput()) + warn(1212, timing_line, "timing group from output port."); + builder_.makeTimingArcs(cell, from_port, to_port, related_out_port, + timing_attrs); + } } -} - -void -LibertyReader::beginWireloadSelection(LibertyGroup *group) -{ - if (library_) { - const char *name = group->firstName(); - if (name) { - wireload_selection_ = new WireloadSelection(name); - library_->addWireloadSelection(wireload_selection_); + else if (from_port_iter.size() > 1 && !to_port->hasMembers()) { + // bus -> one + while (from_port_iter.PortNameBitIterator::hasNext()) { + LibertyPort *from_port = from_port_iter.PortNameBitIterator::next(); + if (from_port->direction()->isOutput()) + warn(1213, timing_line, "timing group from output port."); + builder_.makeTimingArcs(cell, from_port, to_port, related_out_port, + timing_attrs); } } - else - libWarn(1186, group, "wire_load_selection missing name."); -} - -void -LibertyReader::endWireloadSelection(LibertyGroup *) -{ - wireload_selection_ = nullptr; -} - -void -LibertyReader::visitWireloadFromArea(LibertyAttr *attr) -{ - if (wireload_selection_) { - if (attr->isComplex()) { - LibertyAttrValueIterator value_iter(attr->values()); - if (value_iter.hasNext()) { - LibertyAttrValue *value = value_iter.next(); - if (value->isFloat()) { - float min_area = value->floatValue(); - value = value_iter.next(); - if (value->isFloat()) { - float max_area = value->floatValue(); - value = value_iter.next(); - if (value->isString()) { - const char *wireload_name = value->stringValue(); - const Wireload *wireload = - library_->findWireload(wireload_name); - if (wireload) - wireload_selection_->addWireloadFromArea(min_area, max_area, - wireload); - else - libWarn(1187, attr, "wireload %s not found.", wireload_name); - } - else - libWarn(1188, attr, - "wire_load_from_area wireload name not a string."); - } - else - libWarn(1189, attr, "wire_load_from_area min not a float."); - } - else - libWarn(1190, attr, "wire_load_from_area max not a float."); + else if (from_port_iter.size() == 1 && to_port->hasMembers()) { + // one -> bus + if (from_port_iter.PortNameBitIterator::hasNext()) { + LibertyPort *from_port = from_port_iter.PortNameBitIterator::next(); + if (from_port->direction()->isOutput()) + warn(1214, timing_line, "timing group from output port."); + LibertyPortMemberIterator bit_iter(to_port); + while (bit_iter.hasNext()) { + LibertyPort *to_port_bit = bit_iter.next(); + if (!hasBitPinTimingOverride(cell, to_port_bit, from_port, timing_group)) + builder_.makeTimingArcs(cell, from_port, to_port_bit, related_out_port, + timing_attrs); } - else - libWarn(1191, attr, "wire_load_from_area missing parameters."); } - else - libWarn(1192, attr, "wire_load_from_area missing parameters."); } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginCell(LibertyGroup *group) -{ - const char *name = group->firstName(); - if (name) { - debugPrint(debug_, "liberty", 1, "cell %s", name); - if (library_) { - cell_ = builder_.makeCell(library_, name, filename_); - in_bus_ = false; - in_bundle_ = false; + else { + // bus -> bus + if (one_to_one) { + int from_size = from_port_iter.size(); + int to_size = to_port->size(); + LibertyPortMemberIterator to_port_iter(to_port); + // warn about different sizes + if (from_size != to_size) + warn(1216, timing_line, + "timing port {} and related port {} are different sizes.", + from_port_name, + to_port->name()); + // align to/from iterators for one-to-one mapping + while (from_size > to_size) { + from_size--; + from_port_iter.PortNameBitIterator::next(); + } + while (to_size > from_size) { + to_size--; + to_port_iter.next(); + } + // make timing arcs + while (from_port_iter.PortNameBitIterator::hasNext() && to_port_iter.hasNext()) { + LibertyPort *from_port_bit = from_port_iter.PortNameBitIterator::next(); + LibertyPort *to_port_bit = to_port_iter.next(); + if (from_port_bit->direction()->isOutput()) + warn(1215, timing_line, "timing group from output port."); + if (!hasBitPinTimingOverride(cell, to_port_bit, from_port_bit, timing_group)) + builder_.makeTimingArcs(cell, from_port_bit, to_port_bit, + related_out_port, timing_attrs); + } } - } - else - libWarn(1193, group, "cell missing name."); -} - -void -LibertyReader::endCell(LibertyGroup *group) -{ - if (cell_) { - // Sequentials and leakage powers reference expressions outside of port definitions - // so they do not require LibertyFunc's. - makeCellSequentials(); - makeStatetable(); - // Parse functions defined inside of port groups that reference other ports - // and replace the references with the parsed expressions. - parseCellFuncs(); - makeLeakagePowers(); - finishPortGroups(); - // Make generated clocks if they exist - makeGeneratedClocks(); - - if (ocv_derate_name_) { - OcvDerate *derate = cell_->findOcvDerate(ocv_derate_name_); - if (derate == nullptr) - derate = library_->findOcvDerate(ocv_derate_name_); - if (derate) - cell_->setOcvDerate(derate); - else - libWarn(1194, group, "cell %s ocv_derate_group %s not found.", - cell_->name(), ocv_derate_name_); - stringDelete(ocv_derate_name_); - ocv_derate_name_ = nullptr; + else { + // cross product + while (from_port_iter.PortNameBitIterator::hasNext()) { + LibertyPort *from_port_bit = from_port_iter.PortNameBitIterator::next(); + LibertyPortMemberIterator to_port_iter(to_port); + while (to_port_iter.hasNext()) { + LibertyPort *to_port_bit = to_port_iter.next(); + if (!hasBitPinTimingOverride(cell, to_port_bit, from_port_bit, timing_group)) + builder_.makeTimingArcs(cell, from_port_bit, to_port_bit, + related_out_port, timing_attrs); + } + } } - cell_->finish(infer_latches_, report_, debug_); - cell_ = nullptr; } } void -LibertyReader::finishPortGroups() +LibertyReader::makeTimingArcs(LibertyCell *cell, + LibertyPort *to_port, + LibertyPort *related_out_port, + const TimingArcAttrsPtr &timing_attrs) { - // Populate bit_overrides_ with the timing groups of each port. - bit_overrides_.clear(); - for (PortGroup *port_group : cell_port_groups_) { - if (port_group->timingGroups().empty()) - continue; - for (LibertyPort *port : *port_group->ports()) { - // Map port to timing groups in it. - std::vector &timingGroups = bit_overrides_[port]; - for (TimingGroup *timing : port_group->timingGroups()) - timingGroups.push_back(timing); - } - } - - // Make timing arcs for each port group. - for (PortGroup *port_group : cell_port_groups_) { - int line = port_group->line(); - for (LibertyPort *port : *port_group->ports()) { - checkPort(port, line); - makeMinPulseWidthArcs(port, line); + if (to_port->hasMembers()) { + LibertyPortMemberIterator bit_iter(to_port); + while (bit_iter.hasNext()) { + LibertyPort *to_port_bit = bit_iter.next(); + builder_.makeTimingArcs(cell, nullptr, to_port_bit, + related_out_port, timing_attrs); } - makeTimingArcs(port_group); - makeInternalPowers(port_group); } - // Defer deletion until after the loop so overrides can safely walk. - for (PortGroup *port_group : cell_port_groups_) - delete port_group; - cell_port_groups_.clear(); + else + builder_.makeTimingArcs(cell, nullptr, to_port, + related_out_port, timing_attrs); } -void -LibertyReader::checkPort(LibertyPort *port, - int line) -{ - FuncExpr *func_expr = port->function(); - if (func_expr) { - if (func_expr->checkSize(port)) { - libWarn(1195, line, "port %s function size does not match port size.", - port->name()); - } - } - if (port->tristateEnable() - && port->direction() == PortDirection::output()) - port->setDirection(PortDirection::tristate()); -} +//////////////////////////////////////////////////////////////// -// Make timing arcs for the port min_pulse_width_low/high attributes. -// This is redundant but makes sdf annotation consistent. void -LibertyReader::makeMinPulseWidthArcs(LibertyPort *port, - int line) +LibertyReader::readLeakageGrouops(LibertyCell *cell, + const LibertyGroup *cell_group) { - TimingArcAttrsPtr attrs = nullptr; - for (auto hi_low : RiseFall::range()) { - float min_width; + for (const LibertyGroup *leak_group : cell_group->findSubgroups("leakage_power")) { + FuncExpr *when = readFuncExpr(cell, leak_group, "when"); + float power; bool exists; - port->minPulseWidth(hi_low, min_width, exists); + leak_group->findAttrFloat("value", power, exists); if (exists) { - if (attrs == nullptr) { - attrs = make_shared(); - attrs->setTimingType(TimingType::min_pulse_width); - } - // rise/fall_constraint model is on the leading edge of the pulse. - const RiseFall *model_rf = hi_low; - TimingModel *check_model = - makeScalarCheckModel(min_width, ScaleFactorType::min_pulse_width, model_rf); - attrs->setModel(model_rf, check_model); + LibertyPort *related_pg_port = findLibertyPort(cell, leak_group, "related_pg_pin"); + cell->makeLeakagePower(related_pg_port, when, power * power_scale_); } - } - if (attrs) - builder_.makeTimingArcs(cell_, port, port, nullptr, attrs, line); -} - -TimingModel * -LibertyReader::makeScalarCheckModel(float value, - ScaleFactorType scale_factor_type, - const RiseFall *rf) -{ - TablePtr table = make_shared(value); - TableTemplate *tbl_template = - library_->findTableTemplate("scalar", TableTemplateType::delay); - TableModel *table_model = new TableModel(table, tbl_template, - scale_factor_type, rf); - CheckTableModel *check_model = new CheckTableModel(cell_, table_model, nullptr); - return check_model; -} - -void -LibertyReader::makeTimingArcs(PortGroup *port_group) -{ - for (TimingGroup *timing : port_group->timingGroups()) { - timing->makeTimingModels(cell_, this); - - for (LibertyPort *port : *port_group->ports()) - makeTimingArcs(port, timing); - } -} - -void -LibertyReader::makeInternalPowers(PortGroup *port_group) -{ - for (InternalPowerGroup *power_group : port_group->internalPowerGroups()) { - for (LibertyPort *port : *port_group->ports()) - makeInternalPowers(port, power_group); - cell_->addInternalPowerAttrs(power_group); + else + warn(1307, leak_group, "leakage_power missing value."); } } void -LibertyReader::makeCellSequentials() +LibertyReader::readGeneratedClocks(LibertyCell *cell, + const LibertyGroup *cell_group) { - for (SequentialGroup *seq : cell_sequentials_) { - makeCellSequential(seq); - delete seq; - } - cell_sequentials_.clear(); -} - -void -LibertyReader::makeCellSequential(SequentialGroup *seq) -{ - int line = seq->line(); - int size = seq->size(); - bool is_register = seq->isRegister(); - bool is_bank = seq->isBank(); - const char *type = is_register - ? (is_bank ? "ff_bank" : "ff") - : (is_bank ? "latch_bank" : "latch"); - const char *clk = seq->clock(); - FuncExpr *clk_expr = nullptr; - if (clk) { - const char *clk_attr = is_register ? "clocked_on" : "enable"; - clk_expr = parseFunc(clk, clk_attr, line); - if (clk_expr && clk_expr->checkSize(size)) { - libWarn(1196, line, "%s %s bus width mismatch.", type, clk_attr); - clk_expr->deleteSubexprs(); - clk_expr = nullptr; - } - } - const char *data = seq->data(); - FuncExpr *data_expr = nullptr; - if (data) { - const char *data_attr = is_register ? "next_state" : "data_in"; - data_expr = parseFunc(data, data_attr, line); - if (data_expr && data_expr->checkSize(size)) { - libWarn(1197, line, "%s %s bus width mismatch.", type, data_attr); - data_expr->deleteSubexprs(); - data_expr = nullptr; - } - } - const char *clr = seq->clear(); - FuncExpr *clr_expr = nullptr; - if (clr) { - clr_expr = parseFunc(clr, "clear", line); - if (clr_expr && clr_expr->checkSize(size)) { - libWarn(1198, line, "%s %s bus width mismatch.", type, "clear"); - clr_expr->deleteSubexprs(); - clr_expr = nullptr; - } - } - const char *preset = seq->preset(); - FuncExpr *preset_expr = nullptr; - if (preset) { - preset_expr = parseFunc(preset, "preset", line); - if (preset_expr && preset_expr->checkSize(size)) { - libWarn(1199, line, "%s %s bus width mismatch.", type, "preset"); - preset_expr->deleteSubexprs(); - preset_expr = nullptr; + auto &gen_clk_groups = cell_group->findSubgroups("generated_clock"); + for (const LibertyGroup *gen_clk_group : gen_clk_groups) { + if (!gen_clk_group->hasFirstParam()) { + warn(1344, gen_clk_group, "generated_clock missing name."); + continue; } - } - cell_->makeSequential(size, is_register, clk_expr, data_expr, clr_expr, - preset_expr, seq->clrPresetVar1(), - seq->clrPresetVar2(), - seq->outPort(), seq->outInvPort()); - if (!is_register) - checkLatchEnableSense(clk_expr, line); - - // The expressions used in the sequentials are copied by bitSubExpr. - if (clk_expr) - clk_expr->deleteSubexprs(); - if (data_expr) - data_expr->deleteSubexprs(); - if (clr_expr) - clr_expr->deleteSubexprs(); - if (preset_expr) - preset_expr->deleteSubexprs(); -} - -void -LibertyReader::makeGeneratedClocks() -{ - for (GeneratedClockGroup *generated_clock : generated_clocks_) { - makeGeneratedClock(generated_clock); - delete generated_clock; - } - generated_clocks_.clear(); -} - -void -LibertyReader::makeGeneratedClock(GeneratedClockGroup *generated_clock) -{ - if (cell_) { - cell_->makeGeneratedClock(generated_clock->name(), - generated_clock->clockPin(), - generated_clock->masterPin(), - generated_clock->dividedBy(), - generated_clock->multipliedBy(), - generated_clock->dutyCycle(), - generated_clock->invert(), - generated_clock->edges(), - generated_clock->edgeShifts()); - } -} - -void -LibertyReader::checkLatchEnableSense(FuncExpr *enable_func, - int line) -{ - FuncExprPortIterator enable_iter(enable_func); - while (enable_iter.hasNext()) { - LibertyPort *enable_port = enable_iter.next(); - TimingSense enable_sense = enable_func->portTimingSense(enable_port); - switch (enable_sense) { - case TimingSense::positive_unate: - case TimingSense::negative_unate: - break; - case TimingSense::non_unate: - libWarn(1200, line, "latch enable function is non-unate for port %s.", - enable_port->name()); - break; - case TimingSense::none: - case TimingSense::unknown: - libWarn(1201, line, "latch enable function is unknown for port %s.", - enable_port->name()); - break; + const std::string &name = gen_clk_group->firstParam(); + std::string clock_pin = gen_clk_group->findAttrString("clock_pin"); + trim(clock_pin); + if (clock_pin.empty()) { + warn(1345, gen_clk_group, "generated_clock missing clock_pin."); + continue; } - } -} - -//////////////////////////////////////////////////////////////// + std::string master_pin = gen_clk_group->findAttrString("master_pin"); + trim(master_pin); -void -LibertyReader::makeStatetable() -{ - if (statetable_) { - LibertyPortSeq input_ports; - for (const string &input : statetable_->inputPorts()) { - LibertyPort *port = cell_->findLibertyPort(input.c_str()); - if (port) - input_ports.push_back(port); - else - libWarn(1298, statetable_->line(), "statetable input port %s not found.", - input.c_str()); - } - LibertyPortSeq internal_ports; - for (const string &internal : statetable_->internalPorts()) { - LibertyPort *port = cell_->findLibertyPort(internal.c_str()); - if (port == nullptr) - port = makePort(cell_, internal.c_str()); - internal_ports.push_back(port); + int divided_by = 1; + bool exists; + gen_clk_group->findAttrInt("divided_by", divided_by, exists); + + int multiplied_by = 1; + gen_clk_group->findAttrInt("multiplied_by", multiplied_by, exists); + + float duty_cycle = 0.0f; + gen_clk_group->findAttrFloat("duty_cycle", duty_cycle, exists); + + bool invert = false; + const LibertySimpleAttr *invert_attr = gen_clk_group->findSimpleAttr("invert"); + if (invert_attr) { + bool inv_exists; + getAttrBool(invert_attr, invert, inv_exists); + } + + IntSeq *edges = nullptr; + const LibertyComplexAttr *edges_attr = gen_clk_group->findComplexAttr("edges"); + if (edges_attr) { + edges = new IntSeq; + for (const LibertyAttrValue *val : edges_attr->values()) { + auto [fv, is_float] = val->floatValue(); + if (is_float) + edges->push_back(static_cast(fv)); + else { + const std::string &s = val->stringValue(); + if (!s.empty()) + edges->push_back(static_cast(strtof(s.c_str(), nullptr))); + } + } } - cell_->makeStatetable(input_ports, internal_ports, statetable_->table()); - delete statetable_; - statetable_ = nullptr; - } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::makeLeakagePowers() -{ - for (LeakagePowerGroup *power_group : leakage_powers_) { - LibertyPort *related_pg_pin = - cell_->findLibertyPort(power_group->relatedPgPin().c_str()); - LeakagePower *leakage = new LeakagePower(cell_, related_pg_pin, power_group->when(), - power_group->power()); - cell_->addLeakagePower(leakage); - delete power_group; - } - leakage_powers_.clear(); -} - -// Record a reference to a function that will be parsed at the end of -// the cell definition when all of the ports are defined. -void -LibertyReader::makeLibertyFunc(const char *expr, - LibertySetFunc set_func, - bool invert, - const char *attr_name, - LibertyStmt *stmt) -{ - LibertyFunc *func = new LibertyFunc(expr, set_func, - invert, attr_name, stmt->line()); - cell_funcs_.push_back(func); -} -void -LibertyReader::parseCellFuncs() -{ - for (LibertyFunc *func : cell_funcs_) { - FuncExpr *expr = parseFunc(func->expr(), func->attrName(), func->line()); - if (func->invert() && expr) { - if (expr->op() == FuncExpr::op_not) { - FuncExpr *inv = expr; - expr = expr->left(); - delete inv; + FloatSeq *edge_shifts = nullptr; + const LibertyComplexAttr *shifts_attr = gen_clk_group->findComplexAttr("shifts"); + if (shifts_attr) { + edge_shifts = new FloatSeq; + for (const LibertyAttrValue *val : shifts_attr->values()) { + auto [fv, is_float] = val->floatValue(); + if (is_float) + edge_shifts->push_back(fv * time_scale_); + else { + const std::string &s = val->stringValue(); + if (!s.empty()) + edge_shifts->push_back(strtof(s.c_str(), nullptr) * time_scale_); + } } - else - expr = FuncExpr::makeNot(expr); } - if (expr) - func->setFunc()(expr); - delete func; - } - cell_funcs_.clear(); -} -void -LibertyReader::beginScaledCell(LibertyGroup *group) -{ - const char *name = group->firstName(); - if (name) { - scaled_cell_owner_ = library_->findLibertyCell(name); - if (scaled_cell_owner_) { - const char *op_cond_name = group->secondName(); - if (op_cond_name) { - op_cond_ = library_->findOperatingConditions(op_cond_name); - if (op_cond_) { - debugPrint(debug_, "liberty", 1, "scaled cell %s %s", - name, op_cond_name); - cell_ = library_->makeScaledCell(name, filename_); - } - else - libWarn(1202, group, "operating conditions %s not found.", op_cond_name); + cell->makeGeneratedClock(name.c_str(), + clock_pin.c_str(), + master_pin.empty() ? nullptr : master_pin.c_str(), + divided_by, + multiplied_by, + duty_cycle, + invert, + edges, + edge_shifts); + } +} + +void +LibertyReader::readInternalPowerGroups(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group) +{ + for (LibertyPort *port : ports) { + for (const LibertyGroup *ipwr_group : port_group->findSubgroups("internal_power")) { + LibertyPortSeq related_ports = findLibertyPorts(cell, ipwr_group, "related_pin"); + LibertyPort *related_pg_port = findLibertyPort(cell, ipwr_group, "related_pg_pin"); + std::shared_ptr when; + FuncExpr *when1 = readFuncExpr(cell, ipwr_group, "when"); + if (when1) + when = std::shared_ptr(when1); + InternalPowerModels models{}; + // rise/fall_power group + for (const RiseFall *rf : RiseFall::range()) { + std::string pwr_attr_name = sta::format("{}_power", rf->to_string()); + const LibertyGroup *pwr_group = ipwr_group->findSubgroup(pwr_attr_name); + if (pwr_group) { + TableModel *model = readTableModel(pwr_group, rf, TableTemplateType::power, + energyScale(), + ScaleFactorType::internal_power); + std::shared_ptr table_model(model); + InternalPowerModel pwr_model(table_model); + models[rf->index()] = pwr_model; + } + } + // power group (rise/fall power are the same) + const LibertyGroup *pwr_group = ipwr_group->findSubgroup("power"); + if (pwr_group) { + TableModel *model = readTableModel(pwr_group, RiseFall::rise(), + TableTemplateType::power, + energyScale(), + ScaleFactorType::internal_power); + std::shared_ptr table_model(model); + for (const RiseFall *rf : RiseFall::range()) { + InternalPowerModel pwr_model(table_model); + models[rf->index()] = pwr_model; + } + } + if (related_ports.empty()) + cell->makeInternalPower(port, nullptr, related_pg_port, when, models); + else { + for (LibertyPort *related_port : related_ports) + cell->makeInternalPower(port, related_port, related_pg_port, when, models); } - else - libWarn(1203, group, "scaled_cell missing operating condition."); } - else - libWarn(1204, group, "scaled_cell cell %s has not been defined.", name); } - else - libWarn(1205, group, "scaled_cell missing name."); } -void -LibertyReader::endScaledCell(LibertyGroup *group) -{ - if (cell_) { - makeCellSequentials(); - parseCellFuncs(); - finishPortGroups(); - cell_->finish(infer_latches_, report_, debug_); - checkScaledCell(group); - // Add scaled cell AFTER ports and timing arcs are defined. - scaled_cell_owner_->addScaledCell(op_cond_, cell_); - cell_ = nullptr; - scaled_cell_owner_ = nullptr; - op_cond_ = nullptr; - } -} +//////////////////////////////////////////////////////////////// -// Minimal check that is not very specific about where the discrepancies are. -void -LibertyReader::checkScaledCell(LibertyGroup *group) -{ - if (equivCellPorts(cell_, scaled_cell_owner_)) { - if (!equivCellPorts(cell_, scaled_cell_owner_)) - libWarn(1206, group, "scaled_cell %s, %s ports do not match cell ports", - cell_->name(), - op_cond_->name()); - if (!equivCellFuncs(cell_, scaled_cell_owner_)) - libWarn(1206, group, - "scaled_cell %s, %s port functions do not match cell port functions.", - cell_->name(), - op_cond_->name()); - } +FuncExpr * +LibertyReader::readFuncExpr(LibertyCell *cell, + const LibertyGroup *group, + std::string_view attr_name) +{ + const std::string &attr = group->findAttrString(attr_name); + if (!attr.empty()) + return parseFunc(attr, attr_name, cell, group->line()); else - libWarn(1207, group, "scaled_cell ports do not match cell ports."); - if (!equivCellTimingArcSets(cell_, scaled_cell_owner_)) - libWarn(1208, group, "scaled_cell %s, %s timing does not match cell timing.", - cell_->name(), - op_cond_->name()); + return nullptr; } -void -LibertyReader::makeTimingArcs(LibertyPort *to_port, - TimingGroup *timing) -{ - LibertyPort *related_out_port = nullptr; - const char *related_out_port_name = timing->relatedOutputPortName(); - if (related_out_port_name) - related_out_port = findPort(related_out_port_name); - int line = timing->line(); - PortDirection *to_port_dir = to_port->direction(); - // Checks should be more comprehensive (timing checks on inputs, etc). - TimingType type = timing->attrs()->timingType(); - if (type == TimingType::combinational && - to_port_dir->isInput()) - libWarn(1209, line, "combinational timing to an input port."); - if (timing->relatedPortNames()) { - for (const char *from_port_name : *timing->relatedPortNames()) { - PortNameBitIterator from_port_iter(cell_, from_port_name, this, line); - if (from_port_iter.hasNext()) { - debugPrint(debug_, "liberty", 2, " timing %s -> %s", - from_port_name, to_port->name()); - makeTimingArcs(from_port_name, from_port_iter, to_port, - related_out_port, timing); - } - } +LibertyPort * +LibertyReader::findLibertyPort(LibertyCell *cell, + const LibertyGroup *group, + std::string_view port_name_attr) +{ + const LibertySimpleAttr *attr = group->findSimpleAttr(port_name_attr); + if (attr) { + const std::string &port_name = attr->stringValue(); + LibertyPort *port = cell->findLibertyPort(port_name); + if (port) + return port; + else + warn(1290, attr, "port {} not found.", port_name); } - else - makeTimingArcs(to_port, related_out_port, timing); -} - -void -TimingGroup::makeTimingModels(LibertyCell *cell, - LibertyReader *visitor) -{ - switch (cell->libertyLibrary()->delayModelType()) { - case DelayModelType::cmos_linear: - makeLinearModels(cell); - break; - case DelayModelType::table: - makeTableModels(cell, visitor); - break; - case DelayModelType::cmos_pwl: - case DelayModelType::cmos2: - case DelayModelType::polynomial: - case DelayModelType::dcm: - break; - } -} - -void -TimingGroup::makeLinearModels(LibertyCell *cell) -{ - LibertyLibrary *library = cell->libertyLibrary(); - for (auto rf : RiseFall::range()) { - int rf_index = rf->index(); - float intr = intrinsic_[rf_index]; - bool intr_exists = intrinsic_exists_[rf_index]; - if (!intr_exists) - library->defaultIntrinsic(rf, intr, intr_exists); - TimingModel *model = nullptr; - if (timingTypeIsCheck(attrs_->timingType())) { - if (intr_exists) - model = new CheckLinearModel(cell, intr); - } - else { - float res = resistance_[rf_index]; - bool res_exists = resistance_exists_[rf_index]; - if (!res_exists) - library->defaultPinResistance(rf, PortDirection::output(), - res, res_exists); - if (!res_exists) - res = 0.0F; - if (intr_exists) - model = new GateLinearModel(cell, intr, res); - } - attrs_->setModel(rf, model); - } -} - -void -TimingGroup::makeTableModels(LibertyCell *cell, - LibertyReader *reader) -{ - for (auto rf : RiseFall::range()) { - int rf_index = rf->index(); - TableModel *delay = cell_[rf_index]; - TableModel *transition = transition_[rf_index]; - TableModel *constraint = constraint_[rf_index]; - if (delay || transition) { - attrs_->setModel(rf, new GateTableModel(cell, delay, delay_sigma_[rf_index], - transition, - slew_sigma_[rf_index], - receiver_model_, - output_waveforms_[rf_index])); - TimingType timing_type = attrs_->timingType(); - if (timing_type == TimingType::clear - || timing_type == TimingType::combinational - || timing_type == TimingType::combinational_fall - || timing_type == TimingType::combinational_rise - || timing_type == TimingType::falling_edge - || timing_type == TimingType::preset - || timing_type == TimingType::rising_edge - || timing_type == TimingType::three_state_disable - || timing_type == TimingType::three_state_disable_rise - || timing_type == TimingType::three_state_disable_fall - || timing_type == TimingType::three_state_enable - || timing_type == TimingType::three_state_enable_fall - || timing_type == TimingType::three_state_enable_rise) { - if (transition == nullptr) - reader->libWarn(1210, line_, "missing %s_transition.", rf->name()); - if (delay == nullptr) - reader->libWarn(1211, line_, "missing cell_%s.", rf->name()); - } - } - else if (constraint) - attrs_->setModel(rf, new CheckTableModel(cell, constraint, - constraint_sigma_[rf_index])); - } -} - -bool -LibertyReader::relatedPinIncludesPort(TimingGroup *t, - LibertyPort *from_port, - int line) -{ - if (t->relatedPortNames() == nullptr) // no related pin - return false; - // Check every related port name for a matching port. - for (const char *related_pin : *t->relatedPortNames()) { - PortNameBitIterator it(cell_, related_pin, this, line); - while (it.hasNext()) { - if (it.next() == from_port) - return true; - } - } - return false; -} - -bool -LibertyReader::sameArcIdentity(TimingGroup *pin_t, - TimingGroup *bus_timing, - LibertyPort *from_port) -{ - // Check that the timing types are the same. - if (pin_t->attrs()->timingType() == bus_timing->attrs()->timingType()) { - // Check that the conditions are equivalent. - if (FuncExpr::equiv(pin_t->attrs()->cond(), bus_timing->attrs()->cond())) { - // Check that the related pins are the same. - if (from_port == nullptr && pin_t->relatedPortNames() == nullptr) { - return true; - } - return relatedPinIncludesPort(pin_t, from_port, bus_timing->line()); - } - } - return false; -} - -bool -LibertyReader::hasBitPinTimingOverride(LibertyPort *to_port_bit, - LibertyPort *from_port, - TimingGroup *bus_timing) -{ - // Look for destination port bit in overrides map. - auto it = bit_overrides_.find(to_port_bit); - if (it == bit_overrides_.end()) - return false; - // Check every timing group for the port in the overrides map. - for (TimingGroup *pin_t : it->second) - if (sameArcIdentity(pin_t, bus_timing, from_port)) - return true; - return false; -} - -void -LibertyReader::makeTimingArcs(const char *from_port_name, - PortNameBitIterator &from_port_iter, - LibertyPort *to_port, - LibertyPort *related_out_port, - TimingGroup *timing) -{ - if (from_port_iter.size() == 1 && !to_port->hasMembers()) { - // one -> one - if (from_port_iter.hasNext()) { - LibertyPort *from_port = from_port_iter.next(); - if (from_port->direction()->isOutput()) - libWarn(1212, timing->line(), "timing group from output port."); - builder_.makeTimingArcs(cell_, from_port, to_port, related_out_port, - timing->attrs(), timing->line()); - } - } - else if (from_port_iter.size() > 1 && !to_port->hasMembers()) { - // bus -> one - while (from_port_iter.hasNext()) { - LibertyPort *from_port = from_port_iter.next(); - if (from_port->direction()->isOutput()) - libWarn(1213, timing->line(), "timing group from output port."); - builder_.makeTimingArcs(cell_, from_port, to_port, related_out_port, - timing->attrs(), timing->line()); - } - } - else if (from_port_iter.size() == 1 && to_port->hasMembers()) { - // one -> bus - if (from_port_iter.hasNext()) { - LibertyPort *from_port = from_port_iter.next(); - if (from_port->direction()->isOutput()) - libWarn(1214, timing->line(), "timing group from output port."); - LibertyPortMemberIterator bit_iter(to_port); - while (bit_iter.hasNext()) { - LibertyPort *to_port_bit = bit_iter.next(); - // Skip bits whose pin() block declares a matching timing arc - if (!hasBitPinTimingOverride(to_port_bit, from_port, timing)) - builder_.makeTimingArcs(cell_, from_port, to_port_bit, - related_out_port, - timing->attrs(), timing->line()); - } - } - } - else { - // bus -> bus - if (timing->isOneToOne()) { - int from_size = from_port_iter.size(); - int to_size = to_port->size(); - LibertyPortMemberIterator to_port_iter(to_port); - // warn about different sizes - if (from_size != to_size) - libWarn(1216, timing->line(), - "timing port %s and related port %s are different sizes.", - from_port_name, - to_port->name()); - // align to/from iterators for one-to-one mapping - while (from_size > to_size) { - from_size--; - from_port_iter.next(); - } - while (to_size > from_size) { - to_size--; - to_port_iter.next(); - } - // make timing arcs - while (from_port_iter.hasNext() && to_port_iter.hasNext()) { - LibertyPort *from_port_bit = from_port_iter.next(); - LibertyPort *to_port_bit = to_port_iter.next(); - if (from_port_bit->direction()->isOutput()) - libWarn(1215, timing->line(), "timing group from output port."); - // Skip bits whose pin() block declares a matching timing arc. - if (!hasBitPinTimingOverride(to_port_bit, from_port_bit, timing)) - builder_.makeTimingArcs(cell_, from_port_bit, to_port_bit, - related_out_port, timing->attrs(), - timing->line()); - } - } - else { - while (from_port_iter.hasNext()) { - LibertyPort *from_port_bit = from_port_iter.next(); - if (from_port_bit->direction()->isOutput()) - libWarn(1217, timing->line(), "timing group from output port."); - LibertyPortMemberIterator to_iter(to_port); - while (to_iter.hasNext()) { - LibertyPort *to_port_bit = to_iter.next(); - // Skip bits whose pin() block declares a matching timing arc. - if (!hasBitPinTimingOverride(to_port_bit, from_port_bit, timing)) - builder_.makeTimingArcs(cell_, from_port_bit, to_port_bit, - related_out_port, timing->attrs(), - timing->line()); - } - } - } - } -} - -void -LibertyReader::makeTimingArcs(LibertyPort *to_port, - LibertyPort *related_out_port, - TimingGroup *timing) -{ - if (to_port->hasMembers()) { - LibertyPortMemberIterator bit_iter(to_port); - while (bit_iter.hasNext()) { - LibertyPort *to_port_bit = bit_iter.next(); - builder_.makeTimingArcs(cell_, nullptr, to_port_bit, - related_out_port, timing->attrs(), - timing->line()); - } - } - else - builder_.makeTimingArcs(cell_, nullptr, to_port, - related_out_port, timing->attrs(), - timing->line()); -} - -//////////////////////////////////////////////////////////////// - -// Group that encloses receiver_capacitance1/2 etc groups. -void -LibertyReader::beginReceiverCapacitance(LibertyGroup *) -{ - receiver_model_ = make_shared(); -} - -void -LibertyReader::endReceiverCapacitance(LibertyGroup *) -{ - if (ports_) { - for (LibertyPort *port : *ports_) - port->setReceiverModel(receiver_model_); - } - receiver_model_ = nullptr; -} - -// For receiver_capacitance groups with mulitiple segments this -// overrides the index passed in beginReceiverCapacitance1Rise/Fall. -void -LibertyReader::visitSegement(LibertyAttr *attr) -{ - if (receiver_model_) { - int segment; - bool exists; - getAttrInt(attr, segment, exists); - if (exists) - index_ = segment; - } -} - -void -LibertyReader::beginReceiverCapacitance1Rise(LibertyGroup *group) -{ - beginReceiverCapacitance(group, 0, RiseFall::rise()); -} - -void -LibertyReader::beginReceiverCapacitance1Fall(LibertyGroup *group) -{ - beginReceiverCapacitance(group, 0, RiseFall::fall()); -} - -void -LibertyReader::beginReceiverCapacitance2Rise(LibertyGroup *group) -{ - beginReceiverCapacitance(group, 1, RiseFall::rise()); -} - -void -LibertyReader::beginReceiverCapacitance2Fall(LibertyGroup *group) -{ - beginReceiverCapacitance(group, 1, RiseFall::fall()); -} - -void -LibertyReader::beginReceiverCapacitance(LibertyGroup *group, - int index, - const RiseFall *rf) -{ - if (timing_ || ports_) { - beginTableModel(group, TableTemplateType::delay, rf, 1.0, - ScaleFactorType::pin_cap); - index_ = index; - } - else - libWarn(1218, group, "receiver_capacitance group not in timing or pin group."); -} - -void -LibertyReader::endReceiverCapacitanceRiseFall(LibertyGroup *group) -{ - if (table_) { - if (ReceiverModel::checkAxes(table_)) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - if (receiver_model_ == nullptr) { - receiver_model_ = make_shared(); - if (timing_) - timing_->setReceiverModel(receiver_model_); - } - receiver_model_->setCapacitanceModel(table_model, index_, rf_); - } - else - libWarn(1219, group, "unsupported model axis."); - endTableModel(); - } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginOutputCurrentRise(LibertyGroup *group) -{ - beginOutputCurrent(RiseFall::rise(), group); -} - -void -LibertyReader::beginOutputCurrentFall(LibertyGroup *group) -{ - beginOutputCurrent(RiseFall::fall(), group); -} - -void -LibertyReader::beginOutputCurrent(const RiseFall *rf, - LibertyGroup *group) -{ - if (timing_) { - rf_ = rf; - output_currents_.clear(); - } - else - libWarn(1220, group, "output_current_%s group not in timing group.", - rf->name()); -} - -void -LibertyReader::endOutputCurrentRiseFall(LibertyGroup *group) -{ - if (timing_) { - Set slew_set, cap_set; - FloatSeq *slew_values = new FloatSeq; - FloatSeq *cap_values = new FloatSeq; - for (OutputWaveform *waveform : output_currents_) { - float slew = waveform->slew(); - if (!slew_set.hasKey(slew)) { - slew_set.insert(slew); - slew_values->push_back(slew); - } - float cap = waveform->cap(); - if (!cap_set.hasKey(cap)) { - cap_set.insert(cap); - cap_values->push_back(cap); - } - } - sort(slew_values, std::less()); - sort(cap_values, std::less()); - TableAxisPtr slew_axis = make_shared(TableAxisVariable::input_net_transition, - slew_values); - TableAxisPtr cap_axis = make_shared(TableAxisVariable::total_output_net_capacitance, - cap_values); - FloatSeq *ref_times = new FloatSeq(slew_values->size()); - Table1Seq current_waveforms(slew_axis->size() * cap_axis->size()); - for (OutputWaveform *waveform : output_currents_) { - size_t slew_index, cap_index; - bool slew_exists, cap_exists; - slew_axis->findAxisIndex(waveform->slew(), slew_index, slew_exists); - cap_axis->findAxisIndex(waveform->cap(), cap_index, cap_exists); - if (slew_exists && cap_exists) { - size_t index = slew_index * cap_axis->size() + cap_index; - current_waveforms[index] = waveform->stealCurrents(); - (*ref_times)[slew_index] = waveform->referenceTime(); - } - else - libWarn(1221, group, "output current waveform %.2e %.2e not found.", - waveform->slew(), - waveform->cap()); - } - Table1 *ref_time_tbl = new Table1(ref_times, slew_axis); - OutputWaveforms *output_current = new OutputWaveforms(slew_axis, cap_axis, rf_, - current_waveforms, - ref_time_tbl); - timing_->setOutputWaveforms(rf_, output_current); - output_currents_.deleteContentsClear(); - } -} - -void -LibertyReader::beginVector(LibertyGroup *group) -{ - if (timing_ && !in_ccsn_) { - beginTable(group, TableTemplateType::output_current, current_scale_); - scale_factor_type_ = ScaleFactorType::unknown; - reference_time_exists_ = false; - if (tbl_template_ && !OutputWaveforms::checkAxes(tbl_template_)) - libWarn(1222, group, "unsupported model axis."); - } -} - -void -LibertyReader::visitReferenceTime(LibertyAttr *attr) -{ - getAttrFloat(attr, reference_time_, reference_time_exists_); - if (reference_time_exists_) - reference_time_ *= time_scale_; -} - -void -LibertyReader::endVector(LibertyGroup *group) -{ - if (timing_ && tbl_template_) { - FloatSeq *slew_values = axis_values_[0]; - FloatSeq *cap_values = axis_values_[1]; - // Canonicalize axis order. - if (tbl_template_->axis1()->variable() == TableAxisVariable::input_net_transition) { - slew_values = axis_values_[0]; - cap_values = axis_values_[1]; - } - else { - slew_values = axis_values_[1]; - cap_values = axis_values_[0]; - } - - if (slew_values->size() == 1 && cap_values->size() == 1) { - // Convert 1x1xN Table3 to Table1. - float slew = (*slew_values)[0]; - float cap = (*cap_values)[0]; - Table3 *table3 = dynamic_cast(table_.get()); - FloatTable *values3 = table3->values3(); - // Steal the values. - FloatSeq *values = (*values3)[0]; - (*values3)[0] = nullptr; - Table1 *table1 = new Table1(values, axis_[2]); - OutputWaveform *waveform = new OutputWaveform(slew, cap, table1, reference_time_); - output_currents_.push_back(waveform); - } - else - libWarn(1223,group->line(), "vector index_1 and index_2 must have exactly one value."); - if (!reference_time_exists_) - libWarn(1224, group->line(), "vector reference_time not found."); - reference_time_exists_ = false; - tbl_template_ = nullptr; - } -} - -/////////////////////////////////////////////////////////////// - -void -LibertyReader::beginNormalizedDriverWaveform(LibertyGroup *group) -{ - beginTable(group, TableTemplateType::delay, time_scale_); - driver_waveform_name_.clear(); -} - -void -LibertyReader::visitDriverWaveformName(LibertyAttr *attr) -{ - driver_waveform_name_ = getAttrString(attr); -} - -void -LibertyReader::endNormalizedDriverWaveform(LibertyGroup *group) -{ - if (table_) { - if (table_->axis1()->variable() == TableAxisVariable::input_net_transition) { - if (table_->axis2()->variable() == TableAxisVariable::normalized_voltage) { - // Null driver_waveform_name_ means it is the default unnamed waveform. - DriverWaveform *driver_waveform = new DriverWaveform(driver_waveform_name_, - table_); - library_->addDriverWaveform(driver_waveform); - - } - else - libWarn(1225, group, "normalized_driver_waveform variable_2 must be normalized_voltage"); - } - else - libWarn(1226, group, "normalized_driver_waveform variable_1 must be input_net_transition"); - } - endTableModel(); -} - -void -LibertyReader::visitDriverWaveformRise(LibertyAttr *attr) -{ - visitDriverWaveformRiseFall(attr, RiseFall::rise()); -} - -void -LibertyReader::visitDriverWaveformFall(LibertyAttr *attr) -{ - visitDriverWaveformRiseFall(attr, RiseFall::fall()); -} - -void -LibertyReader::visitDriverWaveformRiseFall(LibertyAttr *attr, - const RiseFall *rf) -{ - if (ports_) { - const char *driver_waveform_name = getAttrString(attr); - DriverWaveform *driver_waveform = library_->findDriverWaveform(driver_waveform_name); - if (driver_waveform) { - for (LibertyPort *port : *ports_) - port->setDriverWaveform(driver_waveform, rf); - } - } -} - -/////////////////////////////////////////////////////////////// - -void -LibertyReader::makeInternalPowers(LibertyPort *port, - InternalPowerGroup *power_group) -{ - int line = power_group->line(); - StringSeq *related_port_names = power_group->relatedPortNames(); - if (related_port_names) { - for (const char *related_port_name : *related_port_names) { - PortNameBitIterator related_port_iter(cell_, related_port_name, this, line); - if (related_port_iter.hasNext()) { - debugPrint(debug_, "liberty", 2, " power %s -> %s", - related_port_name, port->name()); - makeInternalPowers(port, related_port_name, related_port_iter, power_group); - } - } - } - else { - if (port->hasMembers()) { - LibertyPortMemberIterator bit_iter(port); - while (bit_iter.hasNext()) { - LibertyPort *port_bit = bit_iter.next(); - builder_.makeInternalPower(cell_, port_bit, nullptr, power_group); - } - } - else - builder_.makeInternalPower(cell_, port, nullptr, power_group); - } -} - -void -LibertyReader::makeInternalPowers(LibertyPort *port, - const char *related_port_name, - PortNameBitIterator &related_port_iter, - InternalPowerGroup *power_group) -{ - if (related_port_iter.size() == 1 && !port->hasMembers()) { - // one -> one - if (related_port_iter.hasNext()) { - LibertyPort *related_port = related_port_iter.next(); - builder_.makeInternalPower(cell_, port, related_port, power_group); - } - } - else if (related_port_iter.size() > 1 && !port->hasMembers()) { - // bus -> one - while (related_port_iter.hasNext()) { - LibertyPort *related_port = related_port_iter.next(); - builder_.makeInternalPower(cell_, port, related_port, power_group); - } - } - else if (related_port_iter.size() == 1 && port->hasMembers()) { - // one -> bus - if (related_port_iter.hasNext()) { - LibertyPort *related_port = related_port_iter.next(); - LibertyPortMemberIterator bit_iter(port); - while (bit_iter.hasNext()) { - LibertyPort *port_bit = bit_iter.next(); - builder_.makeInternalPower(cell_, port_bit, related_port, power_group); - } - } - } - else { - // bus -> bus - if (power_group->isOneToOne()) { - if (static_cast(related_port_iter.size()) == port->size()) { - LibertyPortMemberIterator to_iter(port); - while (related_port_iter.hasNext() && to_iter.hasNext()) { - LibertyPort *related_port_bit = related_port_iter.next(); - LibertyPort *port_bit = to_iter.next(); - builder_.makeInternalPower(cell_, port_bit, related_port_bit, power_group); - } - } - else - libWarn(1227, power_group->line(), - "internal_power port %s and related port %s are different sizes.", - related_port_name, - port->name()); - } - else { - while (related_port_iter.hasNext()) { - LibertyPort *related_port_bit = related_port_iter.next(); - LibertyPortMemberIterator to_iter(port); - while (to_iter.hasNext()) { - LibertyPort *port_bit = to_iter.next(); - builder_.makeInternalPower(cell_, port_bit, related_port_bit, power_group); - } - } - } - } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::visitArea(LibertyAttr *attr) -{ - if (cell_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - cell_->setArea(value); - } - if (wireload_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - wireload_->setArea(value); - } -} - -void -LibertyReader::visitDontUse(LibertyAttr *attr) -{ - if (cell_) { - bool dont_use, exists; - getAttrBool(attr, dont_use, exists); - if (exists) - cell_->setDontUse(dont_use); - } -} - -void -LibertyReader::visitIsMacro(LibertyAttr *attr) -{ - if (cell_) { - bool is_macro, exists; - getAttrBool(attr, is_macro, exists); - if (exists) - cell_->setIsMacro(is_macro); - } -} - -void -LibertyReader::visitIsMemory(LibertyAttr *attr) -{ - if (cell_) { - bool is_memory, exists; - getAttrBool(attr, is_memory, exists); - if (exists) - cell_->setIsMemory(is_memory); - } -} - -void -LibertyReader::visitIsPadCell(LibertyAttr *attr) -{ - if (cell_) { - bool pad_cell, exists; - getAttrBool(attr, pad_cell, exists); - if (exists) - cell_->setIsPad(pad_cell); - } -} - -void -LibertyReader::visitIsClockCell(LibertyAttr *attr) -{ - if (cell_) { - bool is_clock_cell, exists; - getAttrBool(attr, is_clock_cell, exists); - if (exists) - cell_->setIsClockCell(is_clock_cell); - } -} - -void -LibertyReader::visitIsLevelShifter(LibertyAttr *attr) -{ - if (cell_) { - bool is_level_shifter, exists; - getAttrBool(attr, is_level_shifter, exists); - if (exists) - cell_->setIsLevelShifter(is_level_shifter); - } -} - -void -LibertyReader::visitLevelShifterType(LibertyAttr *attr) -{ - if (cell_) { - const char *level_shifter_type = getAttrString(attr); - if (stringEq(level_shifter_type, "HL")) - cell_->setLevelShifterType(LevelShifterType::HL); - else if (stringEq(level_shifter_type, "LH")) - cell_->setLevelShifterType(LevelShifterType::LH); - else if (stringEq(level_shifter_type, "HL_LH")) - cell_->setLevelShifterType(LevelShifterType::HL_LH); - else - libWarn(1228, attr, "level_shifter_type must be HL, LH, or HL_LH"); - } -} - -void -LibertyReader::visitIsIsolationCell(LibertyAttr *attr) -{ - if (cell_) { - bool is_isolation_cell, exists; - getAttrBool(attr, is_isolation_cell, exists); - if (exists) - cell_->setIsIsolationCell(is_isolation_cell); - } -} - -void -LibertyReader::visitAlwaysOn(LibertyAttr *attr) -{ - if (cell_) { - bool always_on, exists; - getAttrBool(attr, always_on, exists); - if (exists) - cell_->setAlwaysOn(always_on); - } -} - -void -LibertyReader::visitSwitchCellType(LibertyAttr *attr) -{ - if (cell_) { - const char *switch_cell_type = getAttrString(attr); - if (stringEq(switch_cell_type, "coarse_grain")) - cell_->setSwitchCellType(SwitchCellType::coarse_grain); - else if (stringEq(switch_cell_type, "fine_grain")) - cell_->setSwitchCellType(SwitchCellType::fine_grain); - else - libWarn(1229, attr, "switch_cell_type must be coarse_grain or fine_grain"); - } -} - -void -LibertyReader::visitInterfaceTiming(LibertyAttr *attr) -{ - if (cell_) { - bool value, exists; - getAttrBool(attr, value, exists); - if (exists) - cell_->setInterfaceTiming(value); - } -} - -void -LibertyReader::visitScalingFactors(LibertyAttr *attr) -{ - if (cell_) { - const char *scale_factors_name = getAttrString(attr); - ScaleFactors *scales = library_->findScaleFactors(scale_factors_name); - if (scales) - cell_->setScaleFactors(scales); - else - libWarn(1230, attr, "scaling_factors %s not found.", scale_factors_name); - } -} - -void -LibertyReader::visitClockGatingIntegratedCell(LibertyAttr *attr) -{ - if (cell_) { - const char *clock_gate_type = getAttrString(attr); - if (clock_gate_type) { - if (stringBeginEqual(clock_gate_type, "latch_posedge")) - cell_->setClockGateType(ClockGateType::latch_posedge); - else if (stringBeginEqual(clock_gate_type, "latch_negedge")) - cell_->setClockGateType(ClockGateType::latch_negedge); - else - cell_->setClockGateType(ClockGateType::other); - } - } -} - -void -LibertyReader::visitCellFootprint(LibertyAttr *attr) -{ - if (cell_) { - const char *footprint = getAttrString(attr); - if (footprint) - cell_->setFootprint(footprint); - } -} - -void -LibertyReader::visitCellUserFunctionClass(LibertyAttr *attr) -{ - if (cell_) { - const char *user_function_class = getAttrString(attr); - if (user_function_class) - cell_->setUserFunctionClass(user_function_class); - } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginGeneratedClock(LibertyGroup *group) -{ - if (cell_) { - const char *name = group->firstName(); - if (name) { - generated_clock_ = new GeneratedClockGroup(); - generated_clock_->setName(name); - generated_clocks_.push_back(generated_clock_); - } - } -} - -void -LibertyReader::endGeneratedClock(LibertyGroup *group) -{ - if (generated_clock_) { - if (!generated_clock_->clockPin()) { - libError(1234, group, "generated_clock missing clock_pin."); - } - } - generated_clock_ = nullptr; -} - -void -LibertyReader::visitClockPin(LibertyAttr *attr) -{ - if (generated_clock_) { - const char *clock_pin = getAttrString(attr); - if (clock_pin) { - string str(clock_pin); - trim(str); - generated_clock_->setClockPin(stringCopy(str.c_str())); - } - } -} - -void -LibertyReader::visitMasterPin(LibertyAttr *attr) -{ - if (generated_clock_) { - const char *master_pin = getAttrString(attr); - if (master_pin) { - string str(master_pin); - trim(str); - generated_clock_->setMasterPin(stringCopy(str.c_str())); - } - } -} - -void -LibertyReader::visitDividedBy(LibertyAttr *attr) -{ - bool exists; - int value; - getAttrInt(attr, value, exists); - if (exists) { - generated_clock_->setDividedBy(value); - } -} - -void -LibertyReader::visitMultipliedBy(LibertyAttr *attr) -{ - bool exists; - int value; - getAttrInt(attr, value, exists); - if (exists) { - generated_clock_->setMultipliedBy(value); - } -} - -void -LibertyReader::visitDutyCycle(LibertyAttr *attr) -{ - bool exists; - float dutyCycle; - getAttrFloat(attr, dutyCycle, exists); - if (exists) { - if (dutyCycle < 0.0 || dutyCycle > 100.0) { - libError( - 1234, - attr, - "duty_cycle must be between 0.0 and 100.0, inclusive. Duty cycle: %f", - dutyCycle); - } - generated_clock_->setDutyCycle(dutyCycle); - } -} - -void -LibertyReader::visitInvert(LibertyAttr *attr) -{ - bool exists, value; - getAttrBool(attr, value, exists); - if (exists) { - generated_clock_->setInvert(value); - } -} - -void -LibertyReader::visitShifts(LibertyAttr *attr) -{ - if (generated_clock_) { - if (!attr->isComplex()) { - libError(1234, attr, "'shifts' attribute must be a complex attribute."); - } - // Initialize edges sequence - FloatSeq *shifts = new FloatSeq; - LibertyAttrValueIterator value_iter(attr->values()); - while (value_iter.hasNext()) { - LibertyAttrValue *value = value_iter.next(); - // Convert string to float if necessary and scale by OpenSTA internal time units. - if (value->isFloat()) { - float float_value = value->floatValue(); - shifts->push_back(float_value * time_scale_); - } - else if (value->isString()) { - const char *string_value = value->stringValue(); - float float_value = strtof(string_value, nullptr); - shifts->push_back(float_value * time_scale_); - } - else { - delete shifts; - libError(1234, attr, "shifts attribute must be a float or string."); - } - } - // Check the number of shifts is odd and greater than or equal to 3. - if (shifts->size() < 3 || shifts->size() % 2 == 0) { - libWarn(1234, attr, "invalid shifts size."); - } - generated_clock_->setEdgeShifts(shifts); - } -} - -void -LibertyReader::visitEdges(LibertyAttr *attr) -{ - if (generated_clock_) { - if (!attr->isComplex()) { - libError(1234, attr, "'edges' attribute must be a complex attribute."); - } - // Initialize edges sequence - IntSeq *edges = new IntSeq; - LibertyAttrValueIterator value_iter(attr->values()); - while (value_iter.hasNext()) { - LibertyAttrValue *value = value_iter.next(); - // Convert string to float if necessary - if (value->isFloat()) { - float float_value = value->floatValue(); - int int_value = static_cast(float_value); - edges->push_back(int_value); - } - else if (value->isString()) { - const char *string_value = value->stringValue(); - float float_value = strtof(string_value, nullptr); - int int_value = static_cast(float_value); - edges->push_back(int_value); - } - else { - delete edges; - libError(1234, attr, "edges attribute must be a float or string."); - } - } - // Check the number of edges is odd and greater than or equal to 3. - if (edges->size() < 3 || edges->size() % 2 == 0) { - libWarn(1234, attr, "invalid edges size."); - } - generated_clock_->setEdges(edges); - } -} - -void -LibertyReader::beginPin(LibertyGroup *group) -{ - if (cell_) { - if (in_bus_) { - saved_ports_ = ports_; - saved_port_group_ = port_group_; - ports_ = new LibertyPortSeq; - for (LibertyAttrValue *param : *group->params()) { - if (param->isString()) { - const char *port_name = param->stringValue(); - debugPrint(debug_, "liberty", 1, " port %s", port_name); - PortNameBitIterator port_iter(cell_, port_name, this, group->line()); - while (port_iter.hasNext()) { - LibertyPort *port = port_iter.next(); - ports_->push_back(port); - } - } - else - libWarn(1231, group, "pin name is not a string."); - } - } - else if (in_bundle_) { - saved_ports_ = ports_; - saved_port_group_ = port_group_; - ports_ = new LibertyPortSeq; - for (LibertyAttrValue *param : *group->params()) { - if (param->isString()) { - const char *name = param->stringValue(); - debugPrint(debug_, "liberty", 1, " port %s", name); - LibertyPort *port = findPort(name); - if (port == nullptr) - port = makePort(cell_, name); - ports_->push_back(port); - } - else - libWarn(1232, group, "pin name is not a string."); - } - } - else { - ports_ = new LibertyPortSeq; - // Multiple port names can share group def. - for (LibertyAttrValue *param : *group->params()) { - if (param->isString()) { - const char *name = param->stringValue(); - debugPrint(debug_, "liberty", 1, " port %s", name); - LibertyPort *port = makePort(cell_, name); - ports_->push_back(port); - } - else - libWarn(1233, group, "pin name is not a string."); - } - } - port_group_ = new PortGroup(ports_, group->line()); - cell_port_groups_.push_back(port_group_); - } -} - -LibertyPort * -LibertyReader::makePort(LibertyCell *cell, - const char *port_name) -{ - string sta_name = portLibertyToSta(port_name); - return builder_.makePort(cell, sta_name.c_str()); -} - -LibertyPort * -LibertyReader::makeBusPort(LibertyCell *cell, - const char *bus_name, - int from_index, - int to_index, - BusDcl *bus_dcl) -{ - string sta_name = portLibertyToSta(bus_name); - return builder_.makeBusPort(cell, bus_name, from_index, to_index, bus_dcl); -} - -void -LibertyReader::endPin(LibertyGroup *) -{ - if (cell_) { - endPorts(); - if (in_bus_ || in_bundle_) { - ports_ = saved_ports_; - port_group_ = saved_port_group_; - } - } -} - -void -LibertyReader::endPorts() -{ - // Capacitances default based on direction so wait until the end - // of the pin group to set them. - if (ports_) { - for (LibertyPort *port : *ports_) { - if (in_bus_ || in_bundle_) { - // Do not clobber member port capacitances by setting the capacitance - // on a bus or bundle. - LibertyPortMemberIterator member_iter(port); - while (member_iter.hasNext()) { - LibertyPort *member = member_iter.next(); - setPortCapDefault(member); - } - } - else - setPortCapDefault(port); - } - ports_ = nullptr; - port_group_ = nullptr; - } -} - -void -LibertyReader::setPortCapDefault(LibertyPort *port) -{ - for (auto min_max : MinMax::range()) { - for (auto tr : RiseFall::range()) { - float cap; - bool exists; - port->capacitance(tr, min_max, cap, exists); - if (!exists) - port->setCapacitance(tr, min_max, defaultCap(port)); - } - } -} - -void -LibertyReader::beginBus(LibertyGroup *group) -{ - if (cell_) { - beginBusOrBundle(group); - in_bus_ = true; - } -} - -void -LibertyReader::endBus(LibertyGroup *group) -{ - if (cell_) { - if (ports_->empty()) - libWarn(1234, group, "bus %s bus_type not found.", group->firstName()); - endBusOrBundle(); - in_bus_ = false; - } -} - -void -LibertyReader::beginBusOrBundle(LibertyGroup *group) -{ - // Multiple port names can share group def. - for (LibertyAttrValue *param : *group->params()) { - if (param->isString()) { - const char *name = param->stringValue(); - if (name) - bus_names_.push_back(stringCopy(name)); - } - } - ports_ = new LibertyPortSeq; - port_group_ = new PortGroup(ports_, group->line()); - cell_port_groups_.push_back(port_group_); -} - -void -LibertyReader::endBusOrBundle() -{ - endPorts(); - deleteContents(&bus_names_); - bus_names_.clear(); - ports_ = nullptr; - port_group_ = nullptr; -} - -// Bus port are not made until the bus_type is specified. -void -LibertyReader::visitBusType(LibertyAttr *attr) -{ - if (cell_) { - const char *bus_type = getAttrString(attr); - if (bus_type) { - // Look for bus dcl local to cell first. - BusDcl *bus_dcl = cell_->findBusDcl(bus_type); - if (bus_dcl == nullptr) - bus_dcl = library_->findBusDcl(bus_type); - if (bus_dcl) { - for (const char *name : bus_names_) { - debugPrint(debug_, "liberty", 1, " bus %s", name); - LibertyPort *port = makeBusPort(cell_, name, bus_dcl->from(), - bus_dcl->to(), bus_dcl); - ports_->push_back(port); - } - } - else - libWarn(1235, attr, "bus_type %s not found.", bus_type); - } - else - libWarn(1236, attr, "bus_type is not a string."); - } -} - -void -LibertyReader::beginBundle(LibertyGroup *group) -{ - if (cell_) { - beginBusOrBundle(group); - in_bundle_ = true; - } -} - -void -LibertyReader::endBundle(LibertyGroup *group) -{ - if (cell_) { - if (ports_ && ports_->empty()) - libWarn(1237, group, "bundle %s member not found.", group->firstName()); - endBusOrBundle(); - in_bundle_ = false; - } -} - -void -LibertyReader::visitMembers(LibertyAttr *attr) -{ - if (cell_) { - if (attr->isComplex()) { - for (const char *name : bus_names_) { - debugPrint(debug_, "liberty", 1, " bundle %s", name); - ConcretePortSeq *members = new ConcretePortSeq; - for (LibertyAttrValue *value : *attr->values()) { - if (value->isString()) { - const char *port_name = value->stringValue(); - LibertyPort *port = findPort(port_name); - if (port == nullptr) - port = makePort(cell_, port_name); - members->push_back(port); - } - else - libWarn(1238, attr, "member is not a string."); - } - LibertyPort *port = builder_.makeBundlePort(cell_, name, members); - ports_->push_back(port); - } - } - else - libWarn(1239, attr,"members attribute is missing values."); - } -} - -LibertyPort * -LibertyReader::findPort(const char *port_name) -{ - return findPort(cell_, port_name); -} - -// Also used by LibExprParser::makeFuncExprPort. -LibertyPort * -libertyReaderFindPort(LibertyCell *cell, - const char *port_name) -{ - LibertyPort *port = cell->findLibertyPort(port_name); - if (port == nullptr) { - const LibertyLibrary *library = cell->libertyLibrary(); - char brkt_left = library->busBrktLeft(); - char brkt_right = library->busBrktRight(); - const char escape = '\\'; - // Pins at top level with bus names have escaped brackets. - string escaped_port_name = escapeChars(port_name, brkt_left, brkt_right, escape); - port = cell->findLibertyPort(escaped_port_name.c_str()); - } - return port; -} - -LibertyPort * -LibertyReader::findPort(LibertyCell *cell, - const char *port_name) -{ - return libertyReaderFindPort(cell, port_name); -} - -void -LibertyReader::visitDirection(LibertyAttr *attr) -{ - if (ports_) { - const char *dir = getAttrString(attr); - if (dir) { - PortDirection *port_dir = PortDirection::unknown(); - if (stringEq(dir, "input")) - port_dir = PortDirection::input(); - else if (stringEq(dir, "output")) - port_dir = PortDirection::output(); - else if (stringEq(dir, "inout")) - port_dir = PortDirection::bidirect(); - else if (stringEq(dir, "internal")) - port_dir = PortDirection::internal(); - else - libWarn(1240, attr, "unknown port direction."); - - for (LibertyPort *port : *ports_) { - // Tristate enable function sets direction to tristate; don't - // clobber it. - if (!port->direction()->isTristate()) - port->setDirection(port_dir); - } - } - } -} - -void -LibertyReader::visitFunction(LibertyAttr *attr) -{ - if (ports_) { - const char *func = getAttrString(attr); - if (func) { - for (LibertyPort *port : *ports_) - makeLibertyFunc(func, - [port] (FuncExpr *expr) { port->setFunction(expr); }, - false, "function", attr); - } - } -} - -void -LibertyReader::visitThreeState(LibertyAttr *attr) -{ - if (ports_) { - const char *three_state = getAttrString(attr); - if (three_state) { - for (LibertyPort *port : *ports_) - makeLibertyFunc(three_state, - [port] (FuncExpr *expr) { port->setTristateEnable(expr); }, - true, "three_state", attr); - } - } -} - -void -LibertyReader::visitPorts(std::function func) -{ - for (LibertyPort *port : *ports_) { - func(port); - LibertyPortMemberIterator member_iter(port); - while (member_iter.hasNext()) { - LibertyPort *member = member_iter.next(); - func(member); - } - } -} - -void -LibertyReader::visitClock(LibertyAttr *attr) -{ - if (ports_) { - bool is_clk, exists; - getAttrBool(attr, is_clk, exists); - if (exists) { - for (LibertyPort *port : *ports_) - port->setIsClock(is_clk); - } - } -} - -void -LibertyReader::visitIsPad(LibertyAttr *attr) -{ - if (ports_) { - bool is_pad, exists; - getAttrBool(attr, is_pad, exists); - if (exists) { - for (LibertyPort *port : *ports_) - port->setIsPad(is_pad); - } - } -} - -void -LibertyReader::visitCapacitance(LibertyAttr *attr) -{ - if (ports_) { - float cap; - bool exists; - getAttrFloat(attr, cap, exists); - if (exists) { - cap *= cap_scale_; - for (LibertyPort *port : *ports_) - port->setCapacitance(cap); - } - } - if (wireload_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - wireload_->setCapacitance(value * cap_scale_); - } -} - -void -LibertyReader::visitRiseCap(LibertyAttr *attr) -{ - if (ports_) { - float cap; - bool exists; - getAttrFloat(attr, cap, exists); - if (exists) { - cap *= cap_scale_; - for (LibertyPort *port : *ports_) { - port->setCapacitance(RiseFall::rise(), MinMax::min(), cap); - port->setCapacitance(RiseFall::rise(), MinMax::max(), cap); - } - } - } -} - -void -LibertyReader::visitFallCap(LibertyAttr *attr) -{ - if (ports_) { - float cap; - bool exists; - getAttrFloat(attr, cap, exists); - if (exists) { - cap *= cap_scale_; - for (LibertyPort *port : *ports_) { - port->setCapacitance(RiseFall::fall(), MinMax::min(), cap); - port->setCapacitance(RiseFall::fall(), MinMax::max(), cap); - } - } - } -} - -void -LibertyReader::visitRiseCapRange(LibertyAttr *attr) -{ - if (ports_) { - bool exists; - float min, max; - getAttrFloat2(attr, min, max, exists); - if (exists) { - min *= cap_scale_; - max *= cap_scale_; - for (LibertyPort *port : *ports_) { - port->setCapacitance(RiseFall::rise(), MinMax::min(), min); - port->setCapacitance(RiseFall::rise(), MinMax::max(), max); - } - } - } -} - -void -LibertyReader::visitFallCapRange(LibertyAttr *attr) -{ - if (ports_) { - bool exists; - float min, max; - getAttrFloat2(attr, min, max, exists); - if (exists) { - min *= cap_scale_; - max *= cap_scale_; - for (LibertyPort *port : *ports_) { - port->setCapacitance(RiseFall::fall(), MinMax::min(), min); - port->setCapacitance(RiseFall::fall(), MinMax::max(), max); - } - } - } -} - -float -LibertyReader::defaultCap(LibertyPort *port) -{ - PortDirection *dir = port->direction(); - float cap = 0.0; - if (dir->isInput()) - cap = library_->defaultInputPinCap(); - else if (dir->isOutput() - || dir->isTristate()) - cap = library_->defaultOutputPinCap(); - else if (dir->isBidirect()) - cap = library_->defaultBidirectPinCap(); - return cap; -} - -void -LibertyReader::visitFanoutLoad(LibertyAttr *attr) -{ - if (ports_) { - float fanout; - bool exists; - getAttrFloat(attr, fanout, exists); - if (exists) { - visitPorts([&] (LibertyPort *port) { - port->setFanoutLoad(fanout); - }); - } - } -} - -void -LibertyReader::visitMaxFanout(LibertyAttr *attr) -{ - visitFanout(attr, MinMax::max()); -} - -void -LibertyReader::visitMinFanout(LibertyAttr *attr) -{ - visitFanout(attr, MinMax::min()); -} - -void -LibertyReader::visitFanout(LibertyAttr *attr, - const MinMax *min_max) -{ - if (ports_) { - float fanout; - bool exists; - getAttrFloat(attr, fanout, exists); - if (exists) { - visitPorts([&] (LibertyPort *port) { - port->setFanoutLimit(fanout, min_max); - }); - } - } -} - -void -LibertyReader::visitMaxTransition(LibertyAttr *attr) -{ - visitMinMaxTransition(attr, MinMax::max()); -} - -void -LibertyReader::visitMinTransition(LibertyAttr *attr) -{ - visitMinMaxTransition(attr, MinMax::min()); -} - -void -LibertyReader::visitMinMaxTransition(LibertyAttr *attr, - const MinMax *min_max) -{ - if (cell_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) { - if (min_max == MinMax::max() && value == 0.0) - libWarn(1241, attr, "max_transition is 0.0."); - value *= time_scale_; - visitPorts([&] (LibertyPort *port) { - port->setSlewLimit(value, min_max); - }); - } - } -} - -void -LibertyReader::visitMaxCapacitance(LibertyAttr *attr) -{ - visitMinMaxCapacitance(attr, MinMax::max()); -} - -void -LibertyReader::visitMinCapacitance(LibertyAttr *attr) -{ - visitMinMaxCapacitance(attr, MinMax::min()); -} - -void -LibertyReader::visitMinMaxCapacitance(LibertyAttr *attr, - const MinMax *min_max) -{ - if (cell_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) { - value *= cap_scale_; - LibertyPortSeq::Iterator port_iter(ports_); - visitPorts([&] (LibertyPort *port) { - port->setCapacitanceLimit(value, min_max); - }); - } - } -} - -void -LibertyReader::visitMinPeriod(LibertyAttr *attr) -{ - if (cell_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) { - for (LibertyPort *port : *ports_) - port->setMinPeriod(value * time_scale_); - } - } -} - -void -LibertyReader::visitMinPulseWidthLow(LibertyAttr *attr) -{ - visitMinPulseWidth(attr, RiseFall::fall()); -} - -void -LibertyReader::visitMinPulseWidthHigh(LibertyAttr *attr) -{ - visitMinPulseWidth(attr, RiseFall::rise()); -} - -void -LibertyReader::visitMinPulseWidth(LibertyAttr *attr, - const RiseFall *rf) -{ - if (cell_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) { - value *= time_scale_; - for (LibertyPort *port : *ports_) - port->setMinPulseWidth(rf, value); - } - } -} - -void -LibertyReader::visitPulseClock(LibertyAttr *attr) -{ - if (cell_) { - const char *pulse_clk = getAttrString(attr); - if (pulse_clk) { - const RiseFall *trigger = nullptr; - const RiseFall *sense = nullptr; - if (stringEq(pulse_clk, "rise_triggered_high_pulse")) { - trigger = RiseFall::rise(); - sense = RiseFall::rise(); - } - else if (stringEq(pulse_clk, "rise_triggered_low_pulse")) { - trigger = RiseFall::rise(); - sense = RiseFall::fall(); - } - else if (stringEq(pulse_clk, "fall_triggered_high_pulse")) { - trigger = RiseFall::fall(); - sense = RiseFall::rise(); - } - else if (stringEq(pulse_clk, "fall_triggered_low_pulse")) { - trigger = RiseFall::fall(); - sense = RiseFall::fall(); - } - else - libWarn(1242,attr, "pulse_latch unknown pulse type."); - if (trigger) { - for (LibertyPort *port : *ports_) - port->setPulseClk(trigger, sense); - } - } - } -} - -void -LibertyReader::visitClockGateClockPin(LibertyAttr *attr) -{ - visitPortBoolAttr(attr, &LibertyPort::setIsClockGateClock); - if (cell_) { - bool value, exists; - getAttrBool(attr, value, exists); - if (exists && value) - cell_->setHasClkGateClkPin(); - } -} - -void -LibertyReader::visitClockGateEnablePin(LibertyAttr *attr) -{ - visitPortBoolAttr(attr, &LibertyPort::setIsClockGateEnable); - if (cell_) { - bool value, exists; - getAttrBool(attr, value, exists); - if (exists && value) - cell_->setHasClkGateEnablePin(); - } -} - -void -LibertyReader::visitClockGateOutPin(LibertyAttr *attr) -{ - visitPortBoolAttr(attr, &LibertyPort::setIsClockGateOut); -} - -void -LibertyReader::visitIsPllFeedbackPin(LibertyAttr *attr) -{ - visitPortBoolAttr(attr, &LibertyPort::setIsPllFeedback); -} - -void -LibertyReader::visitSignalType(LibertyAttr *attr) -{ - if (test_cell_ && ports_) { - const char *type = getAttrString(attr); - if (type) { - ScanSignalType signal_type = ScanSignalType::none; - if (stringEq(type, "test_scan_enable")) - signal_type = ScanSignalType::enable; - else if (stringEq(type, "test_scan_enable_inverted")) - signal_type = ScanSignalType::enable_inverted; - else if (stringEq(type, "test_scan_clock")) - signal_type = ScanSignalType::clock; - else if (stringEq(type, "test_scan_clock_a")) - signal_type = ScanSignalType::clock_a; - else if (stringEq(type, "test_scan_clock_b")) - signal_type = ScanSignalType::clock_b; - else if (stringEq(type, "test_scan_in")) - signal_type = ScanSignalType::input; - else if (stringEq(type, "test_scan_in_inverted")) - signal_type = ScanSignalType::input_inverted; - else if (stringEq(type, "test_scan_out")) - signal_type = ScanSignalType::output; - else if (stringEq(type, "test_scan_out_inverted")) - signal_type = ScanSignalType::output_inverted; - else { - libWarn(1299, attr, "unknown signal_type %s.", type); - return; - } - for (LibertyPort *port : *ports_) - port->setScanSignalType(signal_type); - } - } -} - -void -LibertyReader::visitIsolationCellDataPin(LibertyAttr *attr) -{ - visitPortBoolAttr(attr, &LibertyPort::setIsolationCellData); -} - -void -LibertyReader::visitIsolationCellEnablePin(LibertyAttr *attr) -{ - visitPortBoolAttr(attr, &LibertyPort::setIsolationCellEnable); -} - -void -LibertyReader::visitLevelShifterDataPin(LibertyAttr *attr) -{ - visitPortBoolAttr(attr, &LibertyPort::setLevelShifterData); -} - -void -LibertyReader::visitSwitchPin(LibertyAttr *attr) -{ - visitPortBoolAttr(attr, &LibertyPort::setIsSwitch); -} - -void -LibertyReader::visitPortBoolAttr(LibertyAttr *attr, - LibertyPortBoolSetter setter) -{ - if (cell_) { - bool value, exists; - getAttrBool(attr, value, exists); - if (exists) { - for (LibertyPort *port : *ports_) - (port->*setter)(value); - } - } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginMemory(LibertyGroup *) -{ - if (cell_) { - cell_->setIsMemory(true); - } -} - -void -LibertyReader::endMemory(LibertyGroup *) -{ -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginFF(LibertyGroup *group) -{ - beginSequential(group, true, false); -} - -void -LibertyReader::endFF(LibertyGroup *) -{ - sequential_ = nullptr; -} - -void -LibertyReader::beginFFBank(LibertyGroup *group) -{ - beginSequential(group, true, true); -} - -void -LibertyReader::endFFBank(LibertyGroup *) -{ - sequential_ = nullptr; -} - -void -LibertyReader::beginLatch(LibertyGroup *group) -{ - beginSequential(group, false, false); -} - -void -LibertyReader::endLatch(LibertyGroup *) -{ - sequential_ = nullptr; -} - -void -LibertyReader::beginLatchBank(LibertyGroup *group) -{ - beginSequential(group, false, true); -} - -void -LibertyReader::endLatchBank(LibertyGroup *) -{ - sequential_ = nullptr; -} - -void -LibertyReader::beginSequential(LibertyGroup *group, - bool is_register, - bool is_bank) -{ - if (cell_) { - // Define ff/latch state variables as internal ports. - const char *out_name, *out_inv_name; - int size; - bool has_size; - seqPortNames(group, out_name, out_inv_name, has_size, size); - LibertyPort *out_port = nullptr; - LibertyPort *out_port_inv = nullptr; - if (out_name) { - if (has_size) - out_port = makeBusPort(cell_, out_name, size - 1, 0, nullptr); - else - out_port = makePort(cell_, out_name); - out_port->setDirection(PortDirection::internal()); - } - if (out_inv_name) { - if (has_size) - out_port_inv = makeBusPort(cell_, out_inv_name, size - 1, 0, nullptr); - else - out_port_inv = makePort(cell_, out_inv_name); - out_port_inv->setDirection(PortDirection::internal()); - } - sequential_ = new SequentialGroup(is_register, is_bank, - out_port, out_port_inv, size, - group->line()); - cell_sequentials_.push_back(sequential_); - } -} - -void -LibertyReader::seqPortNames(LibertyGroup *group, - const char *&out_name, - const char *&out_inv_name, - bool &has_size, - int &size) -{ - out_name = nullptr; - out_inv_name = nullptr; - size = 1; - has_size = false; - if (group->params()->size() == 2) { - // out_port, out_port_inv - out_name = group->firstName(); - out_inv_name = group->secondName(); - } - else if (group->params()->size() == 3) { - LibertyAttrValue *third_value = (*group->params())[2]; - if (third_value->isFloat()) { - // out_port, out_port_inv, bus_size - out_name = group->firstName(); - out_inv_name = group->secondName(); - size = static_cast(third_value->floatValue()); - has_size = true; - } - else { - // in_port (ignored), out_port, out_port_inv - out_name = group->secondName(); - out_inv_name = third_value->stringValue(); - } - } -} - -void -LibertyReader::visitClockedOn(LibertyAttr *attr) -{ - if (sequential_) { - const char *func = getAttrString(attr); - if (func) - sequential_->setClock(stringCopy(func)); - } -} - -void -LibertyReader::visitDataIn(LibertyAttr *attr) -{ - if (sequential_) { - const char *func = getAttrString(attr); - if (func) - sequential_->setData(stringCopy(func)); - } -} - -void -LibertyReader::visitClear(LibertyAttr *attr) -{ - if (sequential_) { - const char *func = getAttrString(attr); - if (func) - sequential_->setClear(stringCopy(func)); - } -} - -void -LibertyReader::visitPreset(LibertyAttr *attr) -{ - if (sequential_) { - const char *func = getAttrString(attr); - if (func) - sequential_->setPreset(stringCopy(func)); - } -} - -void -LibertyReader::visitClrPresetVar1(LibertyAttr *attr) -{ - if (sequential_) { - LogicValue var = getAttrLogicValue(attr); - sequential_->setClrPresetVar1(var); - } -} - -void -LibertyReader::visitClrPresetVar2(LibertyAttr *attr) -{ - if (sequential_) { - LogicValue var = getAttrLogicValue(attr); - sequential_->setClrPresetVar2(var); - } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginStatetable(LibertyGroup *group) -{ - if (cell_) { - const char *input_ports_arg = group->firstName(); - StdStringSeq input_ports; - if (input_ports_arg) - input_ports = parseTokenList(input_ports_arg, ' '); - - const char *internal_ports_arg = group->secondName(); - StdStringSeq internal_ports; - if (internal_ports_arg) - internal_ports = parseTokenList(internal_ports_arg, ' '); - statetable_ = new StatetableGroup(input_ports, internal_ports, group->line()); - } -} - -void -LibertyReader::visitTable(LibertyAttr *attr) -{ - if (statetable_) { - const char *table_str = getAttrString(attr); - StdStringSeq table_rows = parseTokenList(table_str, ','); - size_t input_count = statetable_->inputPorts().size(); - size_t internal_count = statetable_->internalPorts().size(); - for (string row : table_rows) { - StdStringSeq row_groups = parseTokenList(row.c_str(), ':'); - if (row_groups.size() != 3) { - libWarn(1300, attr, "table row must have 3 groups separated by ':'."); - break; - } - StdStringSeq inputs = parseTokenList(row_groups[0].c_str(), ' '); - if (inputs.size() != input_count) { - libWarn(1301, attr, "table row has %zu input values but %zu are required.", - inputs.size(), - input_count); - break; - } - StdStringSeq currents = parseTokenList(row_groups[1].c_str(), ' '); - if (currents.size() != internal_count) { - libWarn(1302, attr, "table row has %zu current values but %zu are required.", - currents.size(), - internal_count); - break; - } - StdStringSeq nexts = parseTokenList(row_groups[2].c_str(), ' '); - if (nexts.size() != internal_count) { - libWarn(1303, attr, "table row has %zu next values but %zu are required.", - nexts.size(), - internal_count); - break; - } - - StateInputValues input_values = parseStateInputValues(inputs, attr); - StateInternalValues current_values=parseStateInternalValues(currents,attr); - StateInternalValues next_values = parseStateInternalValues(nexts, attr); - statetable_->addRow(input_values, current_values, next_values); - } - } -} - -static EnumNameMap state_input_value_name_map = - {{StateInputValue::low, "L"}, - {StateInputValue::high, "H"}, - {StateInputValue::dont_care, "-"}, - {StateInputValue::low_high, "L/H"}, - {StateInputValue::high_low, "H/L"}, - {StateInputValue::rise, "R"}, - {StateInputValue::fall, "F"}, - {StateInputValue::not_rise, "~R"}, - {StateInputValue::not_fall, "~F"} - }; - -static EnumNameMap state_internal_value_name_map = - {{StateInternalValue::low, "L"}, - {StateInternalValue::high, "H"}, - {StateInternalValue::unspecified, "-"}, - {StateInternalValue::low_high, "L/H"}, - {StateInternalValue::high_low, "H/L"}, - {StateInternalValue::unknown, "X"}, - {StateInternalValue::hold, "N"} - }; - -StateInputValues -LibertyReader::parseStateInputValues(StdStringSeq &inputs, - LibertyAttr *attr) -{ - StateInputValues input_values; - for (string input : inputs) { - bool exists; - StateInputValue value; - state_input_value_name_map.find(input.c_str(), value, exists); - if (!exists) { - libWarn(1304, attr, "table input value '%s' not recognized.", - input.c_str()); - value = StateInputValue::dont_care; - } - input_values.push_back(value); - } - return input_values; -} - -StateInternalValues -LibertyReader::parseStateInternalValues(StdStringSeq &states, - LibertyAttr *attr) -{ - StateInternalValues state_values; - for (string state : states) { - bool exists; - StateInternalValue value; - state_internal_value_name_map.find(state.c_str(), value, exists); - if (!exists) { - libWarn(1305, attr, "table internal value '%s' not recognized.", - state.c_str()); - value = StateInternalValue::unknown; - } - state_values.push_back(value); - } - return state_values; -} - -void -LibertyReader::endStatetable(LibertyGroup *) -{ -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginTiming(LibertyGroup *group) -{ - if (port_group_) { - timing_ = new TimingGroup(group->line()); - port_group_->addTimingGroup(timing_); - } -} - -void -LibertyReader::endTiming(LibertyGroup *group) -{ - if (timing_) { - // Set scale factor type in constraint tables. - for (auto rf : RiseFall::range()) { - TableModel *model = timing_->constraint(rf); - if (model) { - ScaleFactorType type=timingTypeScaleFactorType(timing_->attrs()->timingType()); - model->setScaleFactorType(type); - } - } - TimingType timing_type = timing_->attrs()->timingType(); - if (timing_->relatedPortNames() == nullptr - && !(timing_type == TimingType::min_pulse_width - || timing_type == TimingType::minimum_period - || timing_type == TimingType::min_clock_tree_path - || timing_type == TimingType::max_clock_tree_path)) - libWarn(1243, group, "timing group missing related_pin/related_bus_pin."); - } - timing_ = nullptr; - receiver_model_ = nullptr; -} - -void -LibertyReader::visitRelatedPin(LibertyAttr *attr) -{ - if (timing_) - visitRelatedPin(attr, timing_); - if (internal_power_) - visitRelatedPin(attr, internal_power_); -} - -void -LibertyReader::visitRelatedPin(LibertyAttr *attr, - RelatedPortGroup *group) -{ - const char *port_names = getAttrString(attr); - if (port_names) { - group->setRelatedPortNames(parseNameList(port_names)); - group->setIsOneToOne(true); - } -} - -StringSeq * -LibertyReader::parseNameList(const char *name_list) -{ - StringSeq *names = new StringSeq; - // Parse space separated list of names. - TokenParser parser(name_list, " "); - while (parser.hasNext()) { - char *token = parser.next(); - // Skip extra spaces. - if (token[0] != '\0') { - const char *name = token; - names->push_back(stringCopy(name)); - } - } - return names; -} - -StdStringSeq -LibertyReader::parseTokenList(const char *token_str, - const char separator) -{ - StdStringSeq tokens; - // Parse space separated list of names. - char separators[2] = {separator, '\0'}; - TokenParser parser(token_str, separators); - while (parser.hasNext()) { - char *token = parser.next(); - // Skip extra spaces. - if (token[0] != '\0') { - tokens.push_back(token); - } - } - return tokens; -} - -void -LibertyReader::visitRelatedBusPins(LibertyAttr *attr) -{ - if (timing_) - visitRelatedBusPins(attr, timing_); - if (internal_power_) - visitRelatedBusPins(attr, internal_power_); -} - -void -LibertyReader::visitRelatedBusPins(LibertyAttr *attr, - RelatedPortGroup *group) -{ - const char *port_names = getAttrString(attr); - if (port_names) { - group->setRelatedPortNames(parseNameList(port_names)); - group->setIsOneToOne(false); - } -} - -void -LibertyReader::visitRelatedOutputPin(LibertyAttr *attr) -{ - if (timing_) { - const char *pin_name = getAttrString(attr); - if (pin_name) - timing_->setRelatedOutputPortName(pin_name); - } -} - -void -LibertyReader::visitTimingType(LibertyAttr *attr) -{ - if (timing_) { - const char *type_name = getAttrString(attr); - if (type_name) { - TimingType type = findTimingType(type_name); - if (type == TimingType::unknown) - libWarn(1244, attr, "unknown timing_type %s.", type_name); - else - timing_->attrs()->setTimingType(type); - } - } -} - -void -LibertyReader::visitTimingSense(LibertyAttr *attr) -{ - if (timing_) { - const char *sense_name = getAttrString(attr); - if (sense_name) { - if (stringEq(sense_name, "non_unate")) - timing_->attrs()->setTimingSense(TimingSense::non_unate); - else if (stringEq(sense_name, "positive_unate")) - timing_->attrs()->setTimingSense(TimingSense::positive_unate); - else if (stringEq(sense_name, "negative_unate")) - timing_->attrs()->setTimingSense(TimingSense::negative_unate); - else - libWarn(1245, attr, "unknown timing_sense %s.", sense_name); - } - } -} - -void -LibertyReader::visitSdfCondStart(LibertyAttr *attr) -{ - if (timing_) { - const char *cond = getAttrString(attr); - if (cond) - timing_->attrs()->setSdfCondStart(cond); - } -} - -void -LibertyReader::visitSdfCondEnd(LibertyAttr *attr) -{ - if (timing_) { - const char *cond = getAttrString(attr); - if (cond) - timing_->attrs()->setSdfCondEnd(cond); - } -} - -void -LibertyReader::visitMode(LibertyAttr *attr) -{ - if (timing_) { - if (attr->isComplex()) { - LibertyAttrValueIterator value_iter(attr->values()); - if (value_iter.hasNext()) { - LibertyAttrValue *value = value_iter.next(); - if (value->isString()) { - timing_->attrs()->setModeName(value->stringValue()); - if (value_iter.hasNext()) { - value = value_iter.next(); - if (value->isString()) - timing_->attrs()->setModeValue(value->stringValue()); - else - libWarn(1246, attr, "mode value is not a string."); - } - else - libWarn(1247, attr, "missing mode value."); - } - else - libWarn(1248, attr, "mode name is not a string."); - } - else - libWarn(1249, attr, "mode missing values."); - } - else - libWarn(1250, attr, "mode missing mode name and value."); - } -} - -void -LibertyReader::visitIntrinsicRise(LibertyAttr *attr) -{ - visitIntrinsic(attr, RiseFall::rise()); -} - -void -LibertyReader::visitIntrinsicFall(LibertyAttr *attr) -{ - visitIntrinsic(attr, RiseFall::fall()); -} - -void -LibertyReader::visitIntrinsic(LibertyAttr *attr, - const RiseFall *rf) -{ - if (timing_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - timing_->setIntrinsic(rf, value * time_scale_); - } -} - -void -LibertyReader::visitRiseResistance(LibertyAttr *attr) -{ - visitRiseFallResistance(attr, RiseFall::rise()); -} - -void -LibertyReader::visitFallResistance(LibertyAttr *attr) -{ - visitRiseFallResistance(attr, RiseFall::fall()); -} - -void -LibertyReader::visitRiseFallResistance(LibertyAttr *attr, - const RiseFall *rf) -{ - if (timing_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - timing_->setResistance(rf, value * res_scale_); - } -} - -void -LibertyReader::beginCellRise(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::cell); -} - -void -LibertyReader::beginCellFall(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::cell); -} - -void -LibertyReader::endCellRiseFall(LibertyGroup *group) -{ - if (table_) { - if (GateTableModel::checkAxes(table_)) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - timing_->setCell(rf_, table_model); - } - else - libWarn(1251, group, "unsupported model axis."); - } - endTableModel(); -} - -void -LibertyReader::beginRiseTransition(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::transition); -} - -void -LibertyReader::beginFallTransition(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::transition); -} - -void -LibertyReader::endRiseFallTransition(LibertyGroup *group) -{ - if (table_) { - if (GateTableModel::checkAxes(table_)) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - timing_->setTransition(rf_, table_model); - } - else - libWarn(1252, group, "unsupported model axis."); - } - endTableModel(); -} - -void -LibertyReader::beginRiseConstraint(LibertyGroup *group) -{ - // Scale factor depends on timing_type, which may follow this stmt. - beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::unknown); -} - -void -LibertyReader::beginFallConstraint(LibertyGroup *group) -{ - // Scale factor depends on timing_type, which may follow this stmt. - beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::unknown); -} - -void -LibertyReader::endRiseFallConstraint(LibertyGroup *group) -{ - if (table_) { - if (CheckTableModel::checkAxes(table_)) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - timing_->setConstraint(rf_, table_model); - } - else - libWarn(1253, group, "unsupported model axis."); - } - endTableModel(); -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginRiseTransitionDegredation(LibertyGroup *group) -{ - if (library_) - beginTableModel(group, TableTemplateType::delay, - RiseFall::rise(), time_scale_, - ScaleFactorType::transition); -} - -void -LibertyReader::beginFallTransitionDegredation(LibertyGroup *group) -{ - if (library_) - beginTableModel(group, TableTemplateType::delay, - RiseFall::fall(), time_scale_, - ScaleFactorType::transition); -} - -void -LibertyReader::endRiseFallTransitionDegredation(LibertyGroup *group) -{ - if (table_) { - if (LibertyLibrary::checkSlewDegradationAxes(table_)) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - library_->setWireSlewDegradationTable(table_model, rf_); - } - else - libWarn(1254, group, "unsupported model axis."); - } - endTableModel(); -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginTimingTableModel(LibertyGroup *group, - const RiseFall *rf, - ScaleFactorType scale_factor_type) -{ - if (timing_) - beginTableModel(group, TableTemplateType::delay, rf, - time_scale_, scale_factor_type); - else - libWarn(1255, group, "%s group not in timing group.", group->firstName()); -} - -void -LibertyReader::beginTableModel(LibertyGroup *group, - TableTemplateType type, - const RiseFall *rf, - float scale, - ScaleFactorType scale_factor_type) -{ - beginTable(group, type, scale); - rf_ = rf; - scale_factor_type_ = scale_factor_type; - sigma_type_ = EarlyLateAll::all(); -} - -void -LibertyReader::endTableModel() -{ - endTable(); - scale_factor_type_ = ScaleFactorType::unknown; - sigma_type_ = nullptr; - index_ = 0; -} - -void -LibertyReader::beginTable(LibertyGroup *group, - TableTemplateType type, - float scale) -{ - const char *template_name = group->firstName(); - if (library_ && template_name) { - tbl_template_ = library_->findTableTemplate(template_name, type); - if (tbl_template_) { - axis_[0] = tbl_template_->axis1ptr(); - axis_[1] = tbl_template_->axis2ptr(); - axis_[2] = tbl_template_->axis3ptr(); - } - else { - libWarn(1256, group, "table template %s not found.", template_name); - axis_[0] = nullptr; - axis_[1] = nullptr; - axis_[2] = nullptr; - } - clearAxisValues(); - table_ = nullptr; - table_model_scale_ = scale; - } -} - -void -LibertyReader::endTable() -{ - table_ = nullptr; - tbl_template_ = nullptr; - axis_[0] = nullptr; - axis_[1] = nullptr; - axis_[2] = nullptr; -} - -void -LibertyReader::visitValue(LibertyAttr *attr) -{ - if (leakage_power_) { - float value; - bool valid; - getAttrFloat(attr, value, valid); - if (valid) - leakage_power_->setPower(value * power_scale_); - } -} - -void -LibertyReader::visitValues(LibertyAttr *attr) -{ - if (tbl_template_ - // Ignore values in ecsm_waveform groups. - && !in_ecsm_waveform_) - makeTable(attr, table_model_scale_); -} - -void -LibertyReader::makeTable(LibertyAttr *attr, - float scale) -{ - if (attr->isComplex()) { - makeTableAxis(0, attr); - makeTableAxis(1, attr); - makeTableAxis(2, attr); - if (axis_[0] && axis_[1] && axis_[2]) { - // 3D table - // Column index1*size(index2) + index2 - // Row index3 - FloatTable *table = makeFloatTable(attr, - axis_[0]->size()*axis_[1]->size(), - axis_[2]->size(), scale); - table_ = make_shared(table, axis_[0], axis_[1], axis_[2]); - } - else if (axis_[0] && axis_[1]) { - // 2D table - // Row variable1/axis[0] - // Column variable2/axis[1] - FloatTable *table = makeFloatTable(attr, axis_[0]->size(), - axis_[1]->size(), scale); - table_ = make_shared(table, axis_[0], axis_[1]); - } - else if (axis_[0]) { - // 1D table - FloatTable *table = makeFloatTable(attr, 1, axis_[0]->size(), scale); - FloatSeq *values = (*table)[0]; - delete table; - table_ = make_shared(values, axis_[0]); - } - else if (axis_[0] == nullptr && axis_[1] == nullptr && axis_[2] == nullptr) { - // scalar - FloatTable *table = makeFloatTable(attr, 1, 1, scale); - float value = (*(*table)[0])[0]; - delete (*table)[0]; - delete table; - table_ = make_shared(value); - } - } - else - libWarn(1257, attr, "%s is missing values.", attr->name()); -} - -FloatTable * -LibertyReader::makeFloatTable(LibertyAttr *attr, - size_t rows, - size_t cols, - float scale) -{ - FloatTable *table = new FloatTable; - table->reserve(rows); - for (LibertyAttrValue *value : *attr->values()) { - FloatSeq *row = new FloatSeq; - row->reserve(cols); - table->push_back(row); - if (value->isString()) { - const char *values_list = value->stringValue(); - parseStringFloatList(values_list, scale, row, attr); - } - else if (value->isFloat()) - // Scalar value. - row->push_back(value->floatValue() * scale); - else - libWarn(1258, attr, "%s is not a list of floats.", attr->name()); - if (row->size() != cols) { - libWarn(1259, attr, "table row has %zu columns but axis has %zu.", - row->size(), - cols); - // Fill out row columns with zeros. - for (size_t c = row->size(); c < cols; c++) - row->push_back(0.0); - } - } - if (table->size() != rows) { - libWarn(1260, attr, "table has %zu rows but axis has %zu.", - table->size(), - rows); - // Fill with zero'd rows. - for (size_t r = table->size(); r < rows; r++) { - FloatSeq *row = new FloatSeq; - table->push_back(row); - // Fill out row with zeros. - for (size_t c = row->size(); c < cols; c++) - row->push_back(0.0); - } - } - return table; -} - -void -LibertyReader::makeTableAxis(int index, - LibertyAttr *attr) -{ - if (axis_values_[index]) { - TableAxisVariable var = axis_[index]->variable(); - FloatSeq *values = axis_values_[index]; - const Units *units = library_->units(); - float scale = tableVariableUnit(var, units)->scale(); - scaleFloats(values, scale); - axis_[index] = make_shared(var, values); - } - else if (axis_[index] && axis_[index]->values() == nullptr) { - libWarn(1344, attr, "Table axis and template missing values."); - axis_[index] = nullptr; - axis_values_[index] = nullptr; - } -} - -//////////////////////////////////////////////////////////////// - -// Define lut output variables as internal ports. -// I can't find any documentation for this group. -void -LibertyReader::beginLut(LibertyGroup *group) -{ - if (cell_) { - for (LibertyAttrValue *param : *group->params()) { - if (param->isString()) { - const char *names = param->stringValue(); - // Parse space separated list of related port names. - TokenParser parser(names, " "); - while (parser.hasNext()) { - char *name = parser.next(); - if (name[0] != '\0') { - LibertyPort *port = makePort(cell_, name); - port->setDirection(PortDirection::internal()); - } - } - } - else - libWarn(1261, group, "lut output is not a string."); - } - } -} - -void -LibertyReader::endLut(LibertyGroup *) -{ -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginTestCell(LibertyGroup *group) -{ - if (cell_ && cell_->testCell()) - libWarn(1262, group, "cell %s test_cell redefinition.", cell_->name()); - else { - string name = cell_->name(); - name += "/test_cell"; - test_cell_ = new TestCell(cell_->libertyLibrary(), name.c_str(), - cell_->filename()); - cell_->setTestCell(test_cell_); - - // Do a recursive parse of cell into the test_cell because it has - // pins, buses, bundles, and sequentials just like a cell. - save_cell_ = cell_; - save_cell_port_groups_ = std::move(cell_port_groups_); - save_statetable_ = statetable_; - statetable_ = nullptr; - save_cell_sequentials_ = std::move(cell_sequentials_); - save_cell_funcs_ = std::move(cell_funcs_); - cell_ = test_cell_; - } -} - -void -LibertyReader::endTestCell(LibertyGroup *) -{ - makeCellSequentials(); - makeStatetable(); - parseCellFuncs(); - finishPortGroups(); - - // Restore reader state to enclosing cell. - cell_port_groups_ = std::move(save_cell_port_groups_); - statetable_ = save_statetable_; - cell_sequentials_ = std::move(save_cell_sequentials_); - cell_funcs_= std::move(save_cell_funcs_); - cell_ = save_cell_; - - test_cell_ = nullptr; - save_statetable_ = nullptr; -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginModeDef(LibertyGroup *group) -{ - const char *name = group->firstName(); - if (name) - mode_def_ = cell_->makeModeDef(name); - else - libWarn(1263, group, "mode definition missing name."); -} - -void -LibertyReader::endModeDef(LibertyGroup *) -{ - mode_def_ = nullptr; -} - -void -LibertyReader::beginModeValue(LibertyGroup *group) -{ - if (mode_def_) { - const char *name = group->firstName(); - if (name) - mode_value_ = mode_def_->defineValue(name, nullptr, nullptr); - else - libWarn(1264, group, "mode value missing name."); - } -} - -void - LibertyReader::endModeValue(LibertyGroup *) -{ - mode_value_ = nullptr; -} - -void -LibertyReader::visitWhen(LibertyAttr *attr) -{ - if (tbl_template_) - libWarn(1265, attr, "when attribute inside table model."); - if (mode_value_) { - const char *func = getAttrString(attr); - if (func) { - ModeValueDef *mode_value = mode_value_; - makeLibertyFunc(func, - [mode_value] (FuncExpr *expr) {mode_value->setCond(expr);}, - false, "when", attr); - } - } - if (timing_ && !in_ccsn_) { - const char *func = getAttrString(attr); - if (func) { - TimingArcAttrs *attrs = timing_->attrs().get(); - makeLibertyFunc(func, - [attrs] (FuncExpr *expr) { attrs->setCond(expr);}, - false, "when", attr); - } - } - if (internal_power_) { - const char *func = getAttrString(attr); - if (func) { - InternalPowerGroup *internal_pwr = internal_power_; - makeLibertyFunc(func, - [internal_pwr] (FuncExpr *expr) { internal_pwr->setWhen(expr);}, - false, "when", attr); - } - } - if (leakage_power_) { - const char *func = getAttrString(attr); - if (func) { - LeakagePowerGroup *leakage_pwr = leakage_power_; - makeLibertyFunc(func, - [leakage_pwr] (FuncExpr *expr) { leakage_pwr->setWhen(expr);}, - false, "when", attr); - } - } -} - -void -LibertyReader::visitSdfCond(LibertyAttr *attr) -{ - if (mode_value_) { - const char *cond = getAttrString(attr); - if (cond) - mode_value_->setSdfCond(cond); - } - else if (timing_) { - const char *cond = getAttrString(attr); - if (cond) - timing_->attrs()->setSdfCond(cond); - } - // sdf_cond can also appear inside minimum_period groups. -} - -//////////////////////////////////////////////////////////////// - -const char * -LibertyReader::getAttrString(LibertyAttr *attr) -{ - if (attr->isSimple()) { - LibertyAttrValue *value = attr->firstValue(); - if (value->isString()) - return value->stringValue(); - else - libWarn(1266, attr, "%s attribute is not a string.", attr->name()); - } - else - libWarn(1267, attr, "%s is not a simple attribute.", attr->name()); - return nullptr; -} - -void -LibertyReader::getAttrInt(LibertyAttr *attr, - // Return values. - int &value, - bool &exists) -{ - value = 0; - exists = false; - if (attr->isSimple()) { - LibertyAttrValue *attr_value = attr->firstValue(); - if (attr_value->isFloat()) { - float float_val = attr_value->floatValue(); - value = static_cast(float_val); - exists = true; - } - else - libWarn(1268, attr, "%s attribute is not an integer.",attr->name()); - } - else - libWarn(1269, attr, "%s is not a simple attribute.", attr->name()); -} - -void -LibertyReader::getAttrFloat(LibertyAttr *attr, - // Return values. - float &value, - bool &valid) -{ - valid = false; - if (attr->isSimple()) - getAttrFloat(attr, attr->firstValue(), value, valid); - else - libWarn(1270, attr, "%s is not a simple attribute.", attr->name()); -} - -void -LibertyReader::getAttrFloat(LibertyAttr *attr, - LibertyAttrValue *attr_value, - // Return values. - float &value, - bool &valid) -{ - if (attr_value->isFloat()) { - valid = true; - value = attr_value->floatValue(); - } - else if (attr_value->isString()) { - const char *string = attr_value->stringValue(); - // See if attribute string is a variable. - variableValue(string, value, valid); - if (!valid) { - // For some reason area attributes for pads are quoted floats. - // Check that the string is a valid double. - char *end; - value = strtof(string, &end); - if ((*end && !isspace(*end)) - // strtof support INF as a valid float. - || stringEqual(string, "inf")) - libWarn(1271, attr, "%s value %s is not a float.", - attr->name(), - string); - valid = true; - } - } -} - -// Get two floats in a complex attribute. -// attr(float1, float2); -void -LibertyReader::getAttrFloat2(LibertyAttr *attr, - // Return values. - float &value1, - float &value2, - bool &exists) -{ - exists = false; - if (attr->isComplex()) { - LibertyAttrValueIterator value_iter(attr->values()); - if (value_iter.hasNext()) { - LibertyAttrValue *value = value_iter.next(); - getAttrFloat(attr, value, value1, exists); - if (exists) { - if (value_iter.hasNext()) { - value = value_iter.next(); - getAttrFloat(attr, value, value2, exists); - } - else - libWarn(1272, attr, "%s missing values.", attr->name()); - } - } - else - libWarn(1273, attr, "%s missing values.", attr->name()); - } - else - libWarn(1274, attr, "%s is not a complex attribute.", attr->name()); -} - -// Parse string of comma separated floats. -// Note that some brain damaged vendors (that used to "Think") are not -// consistent about including the delimiters. -void -LibertyReader::parseStringFloatList(const char *float_list, - float scale, - FloatSeq *values, - LibertyAttr *attr) -{ - const char *delimiters = ", "; - TokenParser parser(float_list, delimiters); - while (parser.hasNext()) { - char *token = parser.next(); - // Some (brain dead) libraries enclose floats in brackets. - if (*token == '{') - token++; - char *end; - float value = strtof(token, &end) * scale; - if (end == token - || (end && !(*end == '\0' - || isspace(*end) - || strchr(delimiters, *end) != nullptr - || *end == '}'))) - libWarn(1275, attr, "%s is not a float.", token); - values->push_back(value); - } -} - -FloatSeq * -LibertyReader::readFloatSeq(LibertyAttr *attr, - float scale) -{ - FloatSeq *values = nullptr; - if (attr->isComplex()) { - LibertyAttrValueIterator value_iter(attr->values()); - if (value_iter.hasNext()) { - LibertyAttrValue *value = value_iter.next(); - if (value->isString()) { - values = new FloatSeq; - parseStringFloatList(value->stringValue(), scale, values, attr); - } - else if (value->isFloat()) { - values = new FloatSeq; - values->push_back(value->floatValue()); - } - else - libWarn(1276, attr, "%s is missing values.", attr->name()); - } - if (value_iter.hasNext()) - libWarn(1277, attr, "%s has more than one string.", attr->name()); - } - else { - LibertyAttrValue *value = attr->firstValue(); - if (value->isString()) { - values = new FloatSeq; - parseStringFloatList(value->stringValue(), scale, values, attr); - } - else - libWarn(1278, attr, "%s is missing values.", attr->name()); - } - return values; -} - -void -LibertyReader::getAttrBool(LibertyAttr *attr, - // Return values. - bool &value, - bool &exists) -{ - exists = false; - if (attr->isSimple()) { - LibertyAttrValue *val = attr->firstValue(); - if (val->isString()) { - const char *str = val->stringValue(); - if (stringEqual(str, "true")) { - value = true; - exists = true; - } - else if (stringEqual(str, "false")) { - value = false; - exists = true; - } - else - libWarn(1279, attr, "%s attribute is not boolean.", attr->name()); - } - else - libWarn(1280, attr, "%s attribute is not boolean.", attr->name()); - } - else - libWarn(1281, attr, "%s is not a simple attribute.", attr->name()); -} - -// Read L/H/X string attribute values as bool. -LogicValue -LibertyReader::getAttrLogicValue(LibertyAttr *attr) -{ - const char *str = getAttrString(attr); - if (str) { - if (stringEq(str, "L")) - return LogicValue::zero; - else if (stringEq(str, "H")) - return LogicValue::one; - else if (stringEq(str, "X")) - return LogicValue::unknown; - else - libWarn(1282, attr, "attribute %s value %s not recognized.", - attr->name(), str); - // fall thru - } - return LogicValue::unknown; -} - -FuncExpr * -LibertyReader::parseFunc(const char *func, - const char *attr_name, - int line) -{ - string error_msg; - stringPrint(error_msg, "%s, line %d %s", - filename_, - line, - attr_name); - return parseFuncExpr(func, cell_, error_msg.c_str(), report_); -} - -const EarlyLateAll * -LibertyReader::getAttrEarlyLate(LibertyAttr *attr) -{ - const char *value = getAttrString(attr); - if (stringEq(value, "early")) - return EarlyLateAll::early(); - else if (stringEq(value, "late")) - return EarlyLateAll::late(); - else if (stringEq(value, "early_and_late")) - return EarlyLateAll::all(); - else { - libWarn(1283, attr, "unknown early/late value."); - return EarlyLateAll::all(); - } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::visitVariable(LibertyVariable *var) -{ - if (var_map_ == nullptr) - var_map_ = new LibertyVariableMap; - const char *var_name = var->variable(); - string key; - float value; - bool exists; - var_map_->findKey(var_name, key, value, exists); - if (exists) { - // Duplicate variable name. - (*var_map_)[key] = var->value(); - } - else - (*var_map_)[var_name] = var->value(); -} - -void -LibertyReader::variableValue(const char *var, - float &value, - bool &exists) -{ - if (var_map_) - var_map_->findKey(var, value, exists); - else - exists = false; -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::libWarn(int id, - LibertyStmt *stmt, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - report_->vfileWarn(id, filename_, stmt->line(), fmt, args); - va_end(args); -} - -void -LibertyReader::libWarn(int id, - int line, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - report_->vfileWarn(id, filename_, line, fmt, args); - va_end(args); -} - -void -LibertyReader::libError(int id, - LibertyStmt *stmt, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - report_->vfileError(id, filename_, stmt->line(), fmt, args); - va_end(args); -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginTableTemplatePower(LibertyGroup *group) -{ - beginTableTemplate(group, TableTemplateType::power); -} - -void -LibertyReader::beginLeakagePower(LibertyGroup *group) -{ - if (cell_) { - leakage_power_ = new LeakagePowerGroup(group->line()); - leakage_powers_.push_back(leakage_power_); - } -} - -void -LibertyReader::endLeakagePower(LibertyGroup *) -{ - leakage_power_ = nullptr; -} - -void -LibertyReader::beginInternalPower(LibertyGroup *group) -{ - if (port_group_) { - internal_power_ = makeInternalPowerGroup(group->line()); - port_group_->addInternalPowerGroup(internal_power_); - } -} - -InternalPowerGroup * -LibertyReader::makeInternalPowerGroup(int line) -{ - return new InternalPowerGroup(line); -} - -void -LibertyReader::endInternalPower(LibertyGroup *) -{ - internal_power_ = nullptr; -} - -void -LibertyReader::beginFallPower(LibertyGroup *group) -{ - if (internal_power_) - beginTableModel(group, TableTemplateType::power, - RiseFall::fall(), energy_scale_, - ScaleFactorType::internal_power); -} - -void -LibertyReader::beginRisePower(LibertyGroup *group) -{ - if (internal_power_) - beginTableModel(group, TableTemplateType::power, - RiseFall::rise(), energy_scale_, - ScaleFactorType::internal_power); -} - -void -LibertyReader::endRiseFallPower(LibertyGroup *) -{ - if (table_) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - internal_power_->setModel(rf_, new InternalPowerModel(table_model)); - } - endTableModel(); -} - -void -LibertyReader::endPower(LibertyGroup *) -{ - if (table_) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - // Share the model for rise/fall. - InternalPowerModel *power_model = new InternalPowerModel(table_model); - internal_power_->setModel(RiseFall::rise(), power_model); - internal_power_->setModel(RiseFall::fall(), power_model); - } - endTableModel(); -} - -void -LibertyReader::visitRelatedGroundPin(LibertyAttr *attr) -{ - if (ports_) { - const char *related_ground_pin = getAttrString(attr); - for (LibertyPort *port : *ports_) - port->setRelatedGroundPin(related_ground_pin); - } -} - -void -LibertyReader::visitRelatedPowerPin(LibertyAttr *attr) -{ - if (ports_) { - const char *related_power_pin = getAttrString(attr); - for (LibertyPort *port : *ports_) - port->setRelatedPowerPin(related_power_pin); - } -} - -void -LibertyReader::visitRelatedPgPin(LibertyAttr *attr) -{ - if (internal_power_) - internal_power_->setRelatedPgPin(getAttrString(attr)); - else if (leakage_power_) - leakage_power_->setRelatedPgPin(getAttrString(attr)); -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginTableTemplateOcv(LibertyGroup *group) -{ - beginTableTemplate(group, TableTemplateType::ocv); -} - -void -LibertyReader::visitOcvArcDepth(LibertyAttr *attr) -{ - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) { - if (timing_) - timing_->attrs()->setOcvArcDepth(value); - else if (cell_) - cell_->setOcvArcDepth(value); - else - library_->setOcvArcDepth(value); - } -} - -void -LibertyReader::visitDefaultOcvDerateGroup(LibertyAttr *attr) -{ - const char *derate_name = getAttrString(attr); - OcvDerate *derate = library_->findOcvDerate(derate_name); - if (derate) - library_->setDefaultOcvDerate(derate); - else - libWarn(1284, attr, "OCV derate group named %s not found.", derate_name); -} - -void -LibertyReader::visitOcvDerateGroup(LibertyAttr *attr) -{ - ocv_derate_name_ = stringCopy(getAttrString(attr)); -} - -void -LibertyReader::beginOcvDerate(LibertyGroup *group) -{ - const char *name = group->firstName(); - if (name) - ocv_derate_ = new OcvDerate(stringCopy(name)); - else - libWarn(1285, group, "ocv_derate missing name."); -} - -void -LibertyReader::endOcvDerate(LibertyGroup *) -{ - if (cell_) - library_->addOcvDerate(ocv_derate_); - else if (library_) - library_->addOcvDerate(ocv_derate_); - ocv_derate_ = nullptr; -} - -void -LibertyReader::beginOcvDerateFactors(LibertyGroup *group) -{ - if (ocv_derate_) { - rf_type_ = RiseFallBoth::riseFall(); - derate_type_ = EarlyLateAll::all(); - path_type_ = PathType::clk_and_data; - beginTable(group, TableTemplateType::ocv, 1.0); - } -} - -void -LibertyReader::endOcvDerateFactors(LibertyGroup *) -{ - if (ocv_derate_) { - for (auto early_late : derate_type_->range()) { - for (auto rf : rf_type_->range()) { - if (path_type_ == PathType::clk_and_data) { - ocv_derate_->setDerateTable(rf, early_late, PathType::clk, table_); - ocv_derate_->setDerateTable(rf, early_late, PathType::data, table_); - } - else - ocv_derate_->setDerateTable(rf, early_late, path_type_, table_); - } - } - } - endTable(); -} - -void -LibertyReader::visitRfType(LibertyAttr *attr) -{ - const char *rf_name = getAttrString(attr); - if (stringEq(rf_name, "rise")) - rf_type_ = RiseFallBoth::rise(); - else if (stringEq(rf_name, "fall")) - rf_type_ = RiseFallBoth::fall(); - else if (stringEq(rf_name, "rise_and_fall")) - rf_type_ = RiseFallBoth::riseFall(); - else - libError(1286, attr, "unknown rise/fall."); -} - -void -LibertyReader::visitDerateType(LibertyAttr *attr) -{ - derate_type_ = getAttrEarlyLate(attr); -} - -void -LibertyReader::visitPathType(LibertyAttr *attr) -{ - const char *path_type = getAttrString(attr); - if (stringEq(path_type, "clock")) - path_type_ = PathType::clk; - else if (stringEq(path_type, "data")) - path_type_ = PathType::data; - else if (stringEq(path_type, "clock_and_data")) - path_type_ = PathType::clk_and_data; - else - libWarn(1287, attr, "unknown derate type."); -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginOcvSigmaCellRise(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::unknown); -} - -void -LibertyReader::beginOcvSigmaCellFall(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::unknown); -} - -void -LibertyReader::endOcvSigmaCell(LibertyGroup *group) -{ - if (table_) { - if (GateTableModel::checkAxes(table_)) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - if (sigma_type_ == EarlyLateAll::all()) { - timing_->setDelaySigma(rf_, EarlyLate::min(), table_model); - timing_->setDelaySigma(rf_, EarlyLate::max(), table_model); - } - else - timing_->setDelaySigma(rf_, sigma_type_->asMinMax(), table_model); - } - else - libWarn(1288, group, "unsupported model axis."); - } - endTableModel(); -} - -void -LibertyReader::beginOcvSigmaRiseTransition(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::unknown); -} - -void -LibertyReader::beginOcvSigmaFallTransition(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::unknown); -} - -void -LibertyReader::endOcvSigmaTransition(LibertyGroup *group) -{ - if (table_) { - if (GateTableModel::checkAxes(table_)) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - if (sigma_type_ == EarlyLateAll::all()) { - timing_->setSlewSigma(rf_, EarlyLate::min(), table_model); - timing_->setSlewSigma(rf_, EarlyLate::max(), table_model); - } - else - timing_->setSlewSigma(rf_, sigma_type_->asMinMax(), table_model); - } - else - libWarn(1289, group, "unsupported model axis."); - } - endTableModel(); -} - -void -LibertyReader::beginOcvSigmaRiseConstraint(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::unknown); -} - -void -LibertyReader::beginOcvSigmaFallConstraint(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::unknown); -} - -void -LibertyReader::endOcvSigmaConstraint(LibertyGroup *group) -{ - if (table_) { - if (CheckTableModel::checkAxes(table_)) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - if (sigma_type_ == EarlyLateAll::all()) { - timing_->setConstraintSigma(rf_, EarlyLate::min(), table_model); - timing_->setConstraintSigma(rf_, EarlyLate::max(), table_model); - } - else - timing_->setConstraintSigma(rf_, sigma_type_->asMinMax(), table_model); - } - else - libWarn(1290, group, "unsupported model axis."); - } - endTableModel(); -} - -void -LibertyReader::visitSigmaType(LibertyAttr *attr) -{ - sigma_type_ = getAttrEarlyLate(attr); -} - -void -LibertyReader::visitCellLeakagePower(LibertyAttr *attr) -{ - if (cell_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - cell_->setLeakagePower(value * power_scale_); - } -} - -void -LibertyReader::beginPgPin(LibertyGroup *group) -{ - if (cell_) { - const char *name = group->firstName(); - pg_port_ = builder_.makePort(cell_, name); - } -} - -void -LibertyReader::endPgPin(LibertyGroup *) -{ - pg_port_ = nullptr; -} - -void -LibertyReader::visitPgType(LibertyAttr *attr) -{ - if (pg_port_) { - const char *type_name = getAttrString(attr); - PwrGndType type = findPwrGndType(type_name); - PortDirection *dir = PortDirection::unknown(); - switch (type) { - case PwrGndType::primary_ground: - case PwrGndType::backup_ground: - case PwrGndType::internal_ground: - dir = PortDirection::ground(); - break; - case PwrGndType::primary_power: - case PwrGndType::backup_power: - case PwrGndType::internal_power: - dir = PortDirection::power(); - break; - case PwrGndType::none: - libError(1291, attr, "unknown pg_type."); - break; - default: - break; - } - pg_port_->setPwrGndType(type); - pg_port_->setDirection(dir); - } -} - -void -LibertyReader::visitVoltageName(LibertyAttr *attr) -{ - if (pg_port_) { - const char *voltage_name = getAttrString(attr); - pg_port_->setVoltageName(voltage_name); - } -} - -// Contents Ignored. -void -LibertyReader::beginCcsn(LibertyGroup *) -{ - in_ccsn_ = true; -} - -void -LibertyReader::endCcsn(LibertyGroup *) -{ - in_ccsn_ = false; -} - -// Contents Ignored. -void -LibertyReader::beginEcsmWaveform(LibertyGroup *) -{ - in_ecsm_waveform_ = true; -} - -void -LibertyReader::endEcsmWaveform(LibertyGroup *) -{ - in_ecsm_waveform_ = false; -} - -//////////////////////////////////////////////////////////////// - -LibertyFunc::LibertyFunc(const char *expr, - LibertySetFunc set_func, - bool invert, - const char *attr_name, - int line) : - expr_(stringCopy(expr)), - set_func_(set_func), - invert_(invert), - attr_name_(stringCopy(attr_name)), - line_(line) -{ -} - -LibertyFunc::~LibertyFunc() -{ - stringDelete(expr_); - stringDelete(attr_name_); -} - -//////////////////////////////////////////////////////////////// - -PortGroup::PortGroup(LibertyPortSeq *ports, - int line) : - ports_(ports), - line_(line) -{ -} - -PortGroup::~PortGroup() -{ - timings_.deleteContents(); - delete ports_; -} - -void -PortGroup::addTimingGroup(TimingGroup *timing) -{ - timings_.push_back(timing); -} - -void -PortGroup::addInternalPowerGroup(InternalPowerGroup *internal_power) -{ - internal_power_groups_.push_back(internal_power); -} - -//////////////////////////////////////////////////////////////// - -SequentialGroup::SequentialGroup(bool is_register, - bool is_bank, - LibertyPort *out_port, - LibertyPort *out_inv_port, - int size, - int line) : - is_register_(is_register), - is_bank_(is_bank), - out_port_(out_port), - out_inv_port_(out_inv_port), - size_(size), - clk_(nullptr), - data_(nullptr), - preset_(nullptr), - clear_(nullptr), - clr_preset_var1_(LogicValue::unknown), - clr_preset_var2_(LogicValue::unknown), - line_(line) -{ -} - -SequentialGroup::~SequentialGroup() -{ - if (clk_) - stringDelete(clk_); - if (data_) - stringDelete(data_); - if (preset_) - stringDelete(preset_); - if (clear_) - stringDelete(clear_); -} - -void -SequentialGroup::setClock(const char *clk) -{ - clk_ = clk; -} - -void -SequentialGroup::setData(const char *data) -{ - data_ = data; -} - -void -SequentialGroup::setClear(const char *clr) -{ - clear_ = clr; -} - -void -SequentialGroup::setPreset(const char *preset) -{ - preset_ = preset; -} - -void -SequentialGroup::setClrPresetVar1(LogicValue var) -{ - clr_preset_var1_ = var; -} - -void -SequentialGroup::setClrPresetVar2(LogicValue var) -{ - clr_preset_var2_ = var; -} - -//////////////////////////////////////////////////////////////// - -GeneratedClockGroup::GeneratedClockGroup() : - name_(nullptr), - clock_pin_(nullptr), - master_pin_(nullptr), - divided_by_(0), - multiplied_by_(0), - duty_cycle_(50.0), - invert_(false), - edges_(nullptr), - edge_shifts_(nullptr) -{ + return nullptr; } -GeneratedClockGroup::~GeneratedClockGroup() +StringSeq +LibertyReader::findAttributStrings(const LibertyGroup *group, + std::string_view name_attr) { - if (name_) - stringDelete(name_); - if (clock_pin_) - stringDelete(clock_pin_); - if (master_pin_) - stringDelete(master_pin_); - if (edges_) - delete edges_; - if (edge_shifts_) - delete edge_shifts_; + const LibertySimpleAttr *attr = group->findSimpleAttr(name_attr); + if (attr) { + const std::string &strings = attr->stringValue(); + return parseTokens(strings); + } + return StringSeq(); } -void -GeneratedClockGroup::setName(const char *name) +LibertyPortSeq +LibertyReader::findLibertyPorts(LibertyCell *cell, + const LibertyGroup *group, + std::string_view port_name_attr) { - if (name_) - stringDelete(name_); - name_ = name ? stringCopy(name) : nullptr; + LibertyPortSeq ports; + StringSeq port_names = findAttributStrings(group, port_name_attr); + for (const std::string &port_name : port_names) { + LibertyPort *port = findPort(cell, port_name); + if (port) + ports.push_back(port); + else + warn(1306, group, "port {} not found.", port_name); + } + return ports; } -void -GeneratedClockGroup::setClockPin(const char *clockPin) +//////////////////////////////////////////////////////////////// + +TimingModel * +LibertyReader::makeScalarCheckModel(LibertyCell *cell, + float value, + ScaleFactorType scale_factor_type, + const RiseFall *rf) { - if (clock_pin_) - stringDelete(clock_pin_); - clock_pin_ = clockPin ? stringCopy(clockPin) : nullptr; + TablePtr table = std::make_shared
(value); + TableTemplate *tbl_template = + library_->findTableTemplate("scalar", TableTemplateType::delay); + TableModel *table_model = new TableModel(table, tbl_template, + scale_factor_type, rf); + + TableModels *check_models = new TableModels(table_model); + TimingModel *check_model = new CheckTableModel(cell, check_models); + return check_model; } void -GeneratedClockGroup::setMasterPin(const char *masterPin) +LibertyReader::checkLatchEnableSense(FuncExpr *enable_func, + int line) { - if (master_pin_) - stringDelete(master_pin_); - master_pin_ = masterPin ? stringCopy(masterPin) : nullptr; + LibertyPortSet enable_ports = enable_func->ports(); + for (LibertyPort *enable_port : enable_ports) { + TimingSense enable_sense = enable_func->portTimingSense(enable_port); + switch (enable_sense) { + case TimingSense::positive_unate: + case TimingSense::negative_unate: + break; + case TimingSense::non_unate: + warn(1200, line, "latch enable function is non-unate for port {}.", + enable_port->name()); + break; + case TimingSense::none: + case TimingSense::unknown: + warn(1201, line, "latch enable function is unknown for port {}.", + enable_port->name()); + break; + } + } } //////////////////////////////////////////////////////////////// -StatetableGroup::StatetableGroup(StdStringSeq &input_ports, - StdStringSeq &internal_ports, - int line) : - input_ports_(input_ports), - internal_ports_(internal_ports), - line_(line) -{ -} - void -StatetableGroup::addRow(StateInputValues &input_values, - StateInternalValues ¤t_values, - StateInternalValues &next_values) -{ - table_.emplace_back(input_values, current_values, next_values); +LibertyReader::readNormalizedDriverWaveform(const LibertyGroup *library_group) +{ + for (const LibertyGroup *waveform_group : + library_group->findSubgroups("normalized_driver_waveform")) { + if (waveform_group->hasFirstParam()) { + const std::string &template_name = waveform_group->firstParam(); + TableTemplate *tbl_template = library_->findTableTemplate(template_name, + TableTemplateType::delay); + if (!tbl_template) { + warn(1256, waveform_group, "table template {} not found.", template_name); + continue; + } + TablePtr table = readTableModel(waveform_group, tbl_template, time_scale_); + if (!table) + continue; + if (table->axis1()->variable() != TableAxisVariable::input_net_transition) { + warn(1265, waveform_group, + "normalized_driver_waveform variable_1 must be input_net_transition"); + continue; + } + if (table->axis2()->variable() != TableAxisVariable::normalized_voltage) { + warn(1225, waveform_group, + "normalized_driver_waveform variable_2 must be normalized_voltage"); + continue; + } + std::string driver_waveform_name; + const std::string &name_attr = + waveform_group->findAttrString("driver_waveform_name"); + if (!name_attr.empty()) + driver_waveform_name = name_attr; + library_->makeDriverWaveform(driver_waveform_name, table); + } + else + warn(1227, waveform_group, "normalized_driver_waveform missing template."); + } } //////////////////////////////////////////////////////////////// -RelatedPortGroup::RelatedPortGroup(int line) : - related_port_names_(nullptr), - line_(line) +void +LibertyReader::readLevelShifterType(LibertyCell *cell, + const LibertyGroup *cell_group) { + const std::string &level_shifter_type = + cell_group->findAttrString("level_shifter_type"); + if (!level_shifter_type.empty()) { + if (level_shifter_type == "HL") + cell->setLevelShifterType(LevelShifterType::HL); + else if (level_shifter_type == "LH") + cell->setLevelShifterType(LevelShifterType::LH); + else if (level_shifter_type == "HL_LH") + cell->setLevelShifterType(LevelShifterType::HL_LH); + else + warn(1228, cell_group, "level_shifter_type must be HL, LH, or HL_LH"); + } } -RelatedPortGroup::~RelatedPortGroup() +void +LibertyReader::readSwitchCellType(LibertyCell *cell, + const LibertyGroup *cell_group) { - if (related_port_names_) { - deleteContents(related_port_names_); - delete related_port_names_; + const std::string &switch_cell_type = + cell_group->findAttrString("switch_cell_type"); + if (!switch_cell_type.empty()) { + if (switch_cell_type == "coarse_grain") + cell->setSwitchCellType(SwitchCellType::coarse_grain); + else if (switch_cell_type == "fine_grain") + cell->setSwitchCellType(SwitchCellType::fine_grain); + else + warn(1229, cell_group, "switch_cell_type must be coarse_grain or fine_grain"); } } void -RelatedPortGroup::setRelatedPortNames(StringSeq *names) +LibertyReader::readCellOcvDerateGroup(LibertyCell *cell, + const LibertyGroup *cell_group) { - related_port_names_ = names; + const std::string &derate_name = cell_group->findAttrString("ocv_derate_group"); + if (!derate_name.empty()) { + OcvDerate *derate = cell->findOcvDerate(derate_name); + if (derate == nullptr) + derate = library_->findOcvDerate(derate_name); + if (derate) + cell->setOcvDerate(derate); + else + warn(1237, cell_group, "OCV derate group named {} not found.", + derate_name); + } } void -RelatedPortGroup::setIsOneToOne(bool one) +LibertyReader::readStatetable(LibertyCell *cell, + const LibertyGroup *cell_group) { - is_one_to_one_ = one; -} + for (const LibertyGroup *statetable_group : cell_group->findSubgroups("statetable")) { + StringSeq input_ports; + if (statetable_group->hasFirstParam()) { + const std::string &input_ports_arg = statetable_group->firstParam(); + input_ports = parseTokens(input_ports_arg); + } -//////////////////////////////////////////////////////////////// + StringSeq internal_ports; + if (statetable_group->hasSecondParam()) { + const std::string &internal_ports_arg = statetable_group->secondParam(); + internal_ports = parseTokens(internal_ports_arg); + } + + const LibertySimpleAttr *table_attr = statetable_group->findSimpleAttr("table"); + if (table_attr) { + const std::string &table_str = table_attr->stringValue(); + StringSeq table_rows = parseTokens(table_str, ","); + size_t input_count = input_ports.size(); + size_t internal_count = internal_ports.size(); + StatetableRows table; + for (const std::string &row : table_rows) { + const StringSeq row_groups = parseTokens(row, ":"); + if (row_groups.size() != 3) { + warn(1300, table_attr, "table row must have 3 groups separated by ':'."); + break; + } + StringSeq inputs = parseTokens(row_groups[0]); + if (inputs.size() != input_count) { + warn(1301, table_attr, "table row has {} input values but {} are required.", + inputs.size(), input_count); + break; + } + StringSeq currents = parseTokens(row_groups[1]); + if (currents.size() != internal_count) { + warn(1302,table_attr, + "table row has {} current values but {} are required.", + currents.size(), internal_count); + break; + } + StringSeq nexts = parseTokens(row_groups[2]); + if (nexts.size() != internal_count) { + warn(1303, table_attr, "table row has {} next values but {} are required.", + nexts.size(), internal_count); + break; + } -TimingGroup::TimingGroup(int line) : - RelatedPortGroup(line), - attrs_(make_shared()), - related_output_port_name_(nullptr), - receiver_model_(nullptr) -{ - for (auto rf_index : RiseFall::rangeIndex()) { - cell_[rf_index] = nullptr; - constraint_[rf_index] = nullptr; - transition_[rf_index] = nullptr; - intrinsic_[rf_index] = 0.0F; - intrinsic_exists_[rf_index] = false; - resistance_[rf_index] = 0.0F; - resistance_exists_[rf_index] = false; - output_waveforms_[rf_index] = nullptr; - - for (auto el_index : EarlyLate::rangeIndex()) { - delay_sigma_[rf_index][el_index] = nullptr; - slew_sigma_[rf_index][el_index] = nullptr; - constraint_sigma_[rf_index][el_index] = nullptr; + StateInputValues input_values = parseStateInputValues(inputs, table_attr); + StateInternalValues current_values=parseStateInternalValues(currents,table_attr); + StateInternalValues next_values = parseStateInternalValues(nexts, table_attr); + table.emplace_back(input_values, current_values, next_values); + } + + LibertyPortSeq input_port_ptrs; + for (const std::string &input : input_ports) { + LibertyPort *port = cell->findLibertyPort(input); + if (port == nullptr + && cell->testCell()) + port = cell->testCell()->findLibertyPort(input); + if (port) + input_port_ptrs.push_back(port); + else + warn(1298, statetable_group, "statetable input port {} not found.", + input); + } + LibertyPortSeq internal_port_ptrs; + for (const std::string &internal : internal_ports) { + LibertyPort *port = cell->findLibertyPort(internal); + if (port == nullptr) + port = makePort(cell, internal); + internal_port_ptrs.push_back(port); + } + cell->makeStatetable(input_port_ptrs, internal_port_ptrs, table); } } } -TimingGroup::~TimingGroup() -{ - if (related_output_port_name_) - stringDelete(related_output_port_name_); -} - void -TimingGroup::setRelatedOutputPortName(const char *name) +LibertyReader::readTestCell(LibertyCell *cell, + const LibertyGroup *cell_group) { - related_output_port_name_ = stringCopy(name); + const LibertyGroup *test_cell_group = cell_group->findSubgroup("test_cell"); + if (test_cell_group) { + if (cell->testCell()) + warn(1262, test_cell_group, "cell {} test_cell redefinition.", cell->name()); + else { + std::string test_cell_name = std::string(cell->name()) + "/test_cell"; + TestCell *test_cell = new TestCell(cell->libertyLibrary(), + std::move(test_cell_name), + cell->filename()); + cell->setTestCell(test_cell); + readCell(test_cell, test_cell_group); + } + } } -void -TimingGroup::setIntrinsic(const RiseFall *rf, - float value) -{ - int rf_index = rf->index(); - intrinsic_[rf_index] = value; - intrinsic_exists_[rf_index] = true; -} +//////////////////////////////////////////////////////////////// -void -TimingGroup::intrinsic(const RiseFall *rf, - // Return values. - float &value, - bool &exists) +LibertyPort * +LibertyReader::makePort(LibertyCell *cell, + std::string_view port_name) { - int rf_index = rf->index(); - value = intrinsic_[rf_index]; - exists = intrinsic_exists_[rf_index]; + std::string sta_name = portLibertyToSta(port_name); + return builder_.makePort(cell, sta_name); } -void -TimingGroup::setResistance(const RiseFall *rf, - float value) +LibertyPort * +LibertyReader::makeBusPort(LibertyCell *cell, + std::string_view bus_name, + int from_index, + int to_index, + BusDcl *bus_dcl) { - int rf_index = rf->index(); - resistance_[rf_index] = value; - resistance_exists_[rf_index] = true; + std::string sta_name = portLibertyToSta(bus_name); + return builder_.makeBusPort(cell, sta_name, from_index, to_index, bus_dcl); } -void -TimingGroup::resistance(const RiseFall *rf, - // Return values. - float &value, - bool &exists) +// Also used by LibExprReader::makeFuncExprPort. +LibertyPort * +libertyReaderFindPort(const LibertyCell *cell, + std::string_view port_name) { - int rf_index = rf->index(); - value = resistance_[rf_index]; - exists = resistance_exists_[rf_index]; + LibertyPort *port = cell->findLibertyPort(port_name); + if (port == nullptr) { + const LibertyLibrary *library = cell->libertyLibrary(); + char brkt_left = library->busBrktLeft(); + 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); + port = cell->findLibertyPort(escaped_port_name); + } + return port; } -TableModel * -TimingGroup::cell(const RiseFall *rf) +LibertyPort * +LibertyReader::findPort(LibertyCell *cell, + std::string_view port_name) { - return cell_[rf->index()]; + return libertyReaderFindPort(cell, port_name); } -void -TimingGroup::setCell(const RiseFall *rf, - TableModel *model) +float +LibertyReader::defaultCap(LibertyPort *port) { - cell_[rf->index()] = model; + PortDirection *dir = port->direction(); + float cap = 0.0; + if (dir->isInput()) + cap = library_->defaultInputPinCap(); + else if (dir->isOutput() + || dir->isTristate()) + cap = library_->defaultOutputPinCap(); + else if (dir->isBidirect()) + cap = library_->defaultBidirectPinCap(); + return cap; } -TableModel * -TimingGroup::constraint(const RiseFall *rf) -{ - return constraint_[rf->index()]; -} +//////////////////////////////////////////////////////////////// -void -TimingGroup::setConstraint(const RiseFall *rf, - TableModel *model) +static EnumNameMap state_input_value_name_map = + {{StateInputValue::low, "L"}, + {StateInputValue::high, "H"}, + {StateInputValue::dont_care, "-"}, + {StateInputValue::low_high, "L/H"}, + {StateInputValue::high_low, "H/L"}, + {StateInputValue::rise, "R"}, + {StateInputValue::fall, "F"}, + {StateInputValue::not_rise, "~R"}, + {StateInputValue::not_fall, "~F"} + }; + +static EnumNameMap state_internal_value_name_map = + {{StateInternalValue::low, "L"}, + {StateInternalValue::high, "H"}, + {StateInternalValue::unspecified, "-"}, + {StateInternalValue::low_high, "L/H"}, + {StateInternalValue::high_low, "H/L"}, + {StateInternalValue::unknown, "X"}, + {StateInternalValue::hold, "N"} + }; + +StateInputValues +LibertyReader::parseStateInputValues(StringSeq &inputs, + const LibertySimpleAttr *attr) { - constraint_[rf->index()] = model; + StateInputValues input_values; + for (std::string input : inputs) { + bool exists; + StateInputValue value; + state_input_value_name_map.find(input, value, exists); + if (!exists) { + warn(1304, attr, "table input value '{}' not recognized.", input); + value = StateInputValue::dont_care; + } + input_values.push_back(value); + } + return input_values; } -TableModel * -TimingGroup::transition(const RiseFall *rf) +StateInternalValues +LibertyReader::parseStateInternalValues(StringSeq &states, + const LibertySimpleAttr *attr) { - return transition_[rf->index()]; + StateInternalValues state_values; + for (std::string state : states) { + bool exists; + StateInternalValue value; + state_internal_value_name_map.find(state, value, exists); + if (!exists) { + warn(1305, attr, "table internal value '{}' not recognized.", state); + value = StateInternalValue::unknown; + } + state_values.push_back(value); + } + return state_values; } -void -TimingGroup::setTransition(const RiseFall *rf, - TableModel *model) -{ - transition_[rf->index()] = model; +//////////////////////////////////////////////////////////////// + +FloatTable +LibertyReader::makeFloatTable(const LibertyComplexAttr *values_attr, + const LibertyGroup *table_group, + size_t rows, + size_t cols, + float scale) +{ + FloatTable table; + table.reserve(rows); + for (const LibertyAttrValue *value : values_attr->values()) { + FloatSeq row; + row.reserve(cols); + if (value->isString()) + row = parseFloatList(value->stringValue(), scale, values_attr->line()); + else if (value->isFloat()) { + auto [entry, valid] = value->floatValue(); + row.push_back(entry * scale); + } + else + warn(1258, values_attr, "{} is not a list of floats.", + values_attr->name()); + if (row.size() != cols) { + warn(1259, values_attr, "{} row has {} columns but axis has {}.", + table_group->type(), + row.size(), + cols); + for (size_t c = row.size(); c < cols; c++) + row.push_back(0.0); + } + table.push_back(std::move(row)); + } + if (table.size() != rows) { + if (rows == 0) + warn(1260, values_attr, "{} missing axis values.", + table_group->type()); + else + warn(1261, values_attr, "{} has {} rows but axis has {}.", + table_group->type(), + table.size(), + rows); + for (size_t r = table.size(); r < rows; r++) { + FloatSeq row(cols, 0.0); + table.push_back(std::move(row)); + } + } + return table; } +//////////////////////////////////////////////////////////////// + void -TimingGroup::setDelaySigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model) +LibertyReader::getAttrInt(const LibertySimpleAttr *attr, + // Return values. + int &value, + bool &exists) { - delay_sigma_[rf->index()][early_late->index()] = model; + value = 0; + exists = false; + const LibertyAttrValue &attr_value = attr->value(); + if (attr_value.isFloat()) { + auto [float_val, valid] = attr_value.floatValue(); + value = static_cast(float_val); + exists = true; + } + else + warn(1268, attr, "{} attribute is not an integer.", attr->name()); } +// Get two floats in a complex attribute. +// attr(float1, float2); void -TimingGroup::setSlewSigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model) +LibertyReader::getAttrFloat2(const LibertyComplexAttr *attr, + // Return values. + float &value1, + float &value2, + bool &exists) { - slew_sigma_[rf->index()][early_late->index()] = model; + exists = false; + const LibertyAttrValueSeq &values = attr->values(); + if (values.size() == 2) { + LibertyAttrValue *value = values[0]; + getAttrFloat(attr, value, value1, exists); + if (!exists) + warn(1272, attr, "{} is not a float.", attr->name()); + + value = values[1]; + getAttrFloat(attr, value, value2, exists); + if (!exists) + warn(1273, attr, "{} is not a float.", attr->name()); + } + else + warn(1274, attr, "{} requires 2 valules.", attr->name()); } void -TimingGroup::setConstraintSigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model) +LibertyReader::getAttrFloat(const LibertyComplexAttr *attr, + const LibertyAttrValue *attr_value, + // Return values. + float &value, + bool &valid) { - constraint_sigma_[rf->index()][early_late->index()] = model; + if (attr_value->isFloat()) { + auto [value1, valid1] = attr_value->floatValue(); + value = value1; + valid = valid1; + } + else if (attr_value->isString()) { + const std::string &str = attr_value->stringValue(); + variableValue(str, value, valid); + if (!valid) { + auto [value1, valid1] = stringFloat(str); + value = value1; + valid = valid1; + if (!valid1) + warn(1183, attr->line(), "{} value {} is not a float.", attr->name(), str); + } + } } -void -TimingGroup::setReceiverModel(ReceiverModelPtr receiver_model) -{ - receiver_model_ = receiver_model; +// Parse string of comma separated floats. +// Note that some brain damaged vendors (that used to "Think") are not +// consistent about including the delimiters. +FloatSeq +LibertyReader::parseFloatList(const std::string &float_list, + float scale, + int line) +{ + StringSeq tokens = parseTokens(float_list, " ,{}"); + FloatSeq values; + values.reserve(tokens.size()); + for (const std::string &token : tokens) { + auto [value, valid] = stringFloat(token); + if (!valid) + warn(1310, line, "{} is not a float.", token); + else + values.push_back(value * scale); + } + return values; } -OutputWaveforms * -TimingGroup::outputWaveforms(const RiseFall *rf) +FloatSeq +LibertyReader::readFloatSeq(const LibertyComplexAttr *attr, + float scale) { - return output_waveforms_[rf->index()]; + FloatSeq values; + const LibertyAttrValueSeq &attr_values = attr->values(); + if (attr_values.size() == 1) { + LibertyAttrValue *value = attr_values[0]; + if (value->isString()) + values = parseFloatList(value->stringValue(), scale, attr->line()); + else { + auto [entry, valid] = value->floatValue(); + if (valid) + values.push_back(entry * scale); + } + } + else if (attr_values.size() > 1) { + for (LibertyAttrValue *value : attr_values) { + if (value->isString()) { + FloatSeq parsed = parseFloatList(value->stringValue(), scale, attr->line()); + values.insert(values.end(), parsed.begin(), parsed.end()); + } + else { + auto [entry, valid] = value->floatValue(); + if (valid) + values.push_back(entry * scale); + } + } + } + else + warn(1277, attr, "{} has no values.", attr->name()); + return values; } +//////////////////////////////////////////////////////////////// + void -TimingGroup::setOutputWaveforms(const RiseFall *rf, - OutputWaveforms *output_waveforms) +LibertyReader::getAttrBool(const LibertySimpleAttr *attr, + // Return values. + bool &value, + bool &exists) { - output_waveforms_[rf->index()] = output_waveforms; + exists = false; + const LibertyAttrValue &val = attr->value(); + if (val.isString()) { + const std::string &str = val.stringValue(); + if (stringEqual(str, "true")) { + value = true; + exists = true; + } + else if (stringEqual(str, "false")) { + value = false; + exists = true; + } + else + warn(1288, attr, "{} attribute is not boolean.", attr->name()); + } + else + warn(1289, attr, "{} attribute is not boolean.", attr->name()); } -//////////////////////////////////////////////////////////////// +// Read L/H/X string attribute values as bool. +LogicValue +LibertyReader::getAttrLogicValue(const LibertySimpleAttr *attr) +{ + const std::string &str = attr->stringValue(); + if (str == "L") + return LogicValue::zero; + else if (str == "H") + return LogicValue::one; + else if (str == "X") + return LogicValue::unknown; + else + warn(1282, attr, "attribute {} value {} not recognized.", attr->name(), str); + // fall thru + return LogicValue::unknown; +} -InternalPowerGroup::InternalPowerGroup(int line) : - InternalPowerAttrs(), - RelatedPortGroup(line) +const EarlyLateAll * +LibertyReader::getAttrEarlyLate(const LibertySimpleAttr *attr) { + const std::string &value = attr->stringValue(); + if (value == "early") + return EarlyLateAll::early(); + else if (value == "late") + return EarlyLateAll::late(); + else if (value == "early_and_late") + return EarlyLateAll::all(); + else { + warn(1283, attr, "unknown early/late value."); + return EarlyLateAll::all(); + } } -InternalPowerGroup::~InternalPowerGroup() +//////////////////////////////////////////////////////////////// + +FuncExpr * +LibertyReader::parseFunc(std::string_view func, + std::string_view attr_name, + const LibertyCell *cell, + int line) { + std::string error_msg = sta::format("{} line {}, {}", + filename_, + line, + attr_name); + return parseFuncExpr(func, cell, error_msg, report_); } //////////////////////////////////////////////////////////////// -LeakagePowerGroup::LeakagePowerGroup(int line) : - when_(nullptr), - power_(0.0), - line_(line) +void +LibertyReader::visitVariable(LibertyVariable *var) { + const std::string &var_name = var->variable(); + float value; + bool exists; + findKeyValue(var_map_, var_name, value, exists); + var_map_[var_name] = var->value(); } void -LeakagePowerGroup::setRelatedPgPin(std::string pin_name) +LibertyReader::visitDefine(LibertyDefine *) { - related_pg_pin_ = std::move(pin_name); + // nop } void -LeakagePowerGroup::setWhen(FuncExpr *when) +LibertyReader::variableValue(std::string_view var, + float &value, + bool &exists) { - when_ = when; + findKeyValue(var_map_, std::string(var), value, exists); } +//////////////////////////////////////////////////////////////// + void -LeakagePowerGroup::setPower(float power) +LibertyReader::readDefaultOcvDerateGroup(const LibertyGroup *library_group) { - power_ = power; + const std::string &derate_name = + library_group->findAttrString("default_ocv_derate_group"); + if (!derate_name.empty()) { + OcvDerate *derate = library_->findOcvDerate(derate_name); + if (derate) + library_->setDefaultOcvDerate(derate); + else + warn(1284, library_group, "OCV derate group named {} not found.", derate_name); + } +} + +// Read cell or library level ocv_derate groups. +void +LibertyReader::readOcvDerateFactors(LibertyCell *cell, + const LibertyGroup *parent_group) +{ + for (const LibertyGroup *ocv_derate_group : + parent_group->findSubgroups("ocv_derate")) { + if (ocv_derate_group->hasFirstParam()) { + const std::string &name = ocv_derate_group->firstParam(); + OcvDerate *ocv_derate = cell + ? cell->makeOcvDerate(name) + : library_->makeOcvDerate(name); + for (const LibertyGroup *factors_group : + ocv_derate_group->findSubgroups("ocv_derate_factors")) { + const RiseFallBoth *rf_type = RiseFallBoth::riseFall(); + const std::string &rf_attr = factors_group->findAttrString("rf_type"); + if (!rf_attr.empty()) { + if (rf_attr == "rise") + rf_type = RiseFallBoth::rise(); + else if (rf_attr == "fall") + rf_type = RiseFallBoth::fall(); + else if (rf_attr == "rise_and_fall") + rf_type = RiseFallBoth::riseFall(); + else + error(1286, factors_group, "unknown rise/fall."); + } + + const EarlyLateAll *derate_type = EarlyLateAll::all(); + const std::string &derate_attr = + factors_group->findAttrString("derate_type"); + if (!derate_attr.empty()) { + if (derate_attr == "early") + derate_type = EarlyLateAll::early(); + else if (derate_attr == "late") + derate_type = EarlyLateAll::late(); + else if (derate_attr == "early_and_late") + derate_type = EarlyLateAll::all(); + else { + warn(1309, factors_group, "unknown early/late value."); + } + } + + PathType path_type = PathType::clk_and_data; + const std::string &path_attr = factors_group->findAttrString("path_type"); + if (!path_attr.empty()) { + if (path_attr == "clock") + path_type = PathType::clk; + else if (path_attr == "data") + path_type = PathType::data; + else if (path_attr == "clock_and_data") + path_type = PathType::clk_and_data; + else + warn(1287, factors_group, "unknown derate type."); + } + + if (factors_group->hasFirstParam()) { + const std::string &template_name = factors_group->firstParam(); + TableTemplate *tbl_template = + library_->findTableTemplate(template_name, TableTemplateType::ocv); + if (tbl_template) { + TablePtr table = readTableModel(factors_group, tbl_template, 1.0F); + if (table) { + for (const EarlyLate *early_late : derate_type->range()) { + for (const RiseFall *rf : rf_type->range()) { + if (path_type == PathType::clk_and_data) { + ocv_derate->setDerateTable(rf, early_late, PathType::clk, table); + ocv_derate->setDerateTable(rf, early_late, PathType::data, table); + } + else + ocv_derate->setDerateTable(rf, early_late, path_type, table); + } + } + } + } + else + warn(1308, factors_group, "table template {} not found.", template_name); + } + else + warn(1312, factors_group, "ocv_derate_factors missing template."); + } + } + else + warn(1285, ocv_derate_group, "ocv_derate missing name."); + } } //////////////////////////////////////////////////////////////// PortNameBitIterator::PortNameBitIterator(LibertyCell *cell, - const char *port_name, - LibertyReader *visitor, - int line) : + std::string_view port_name, + LibertyReader *visitor, + int line) : cell_(cell), visitor_(visitor), - line_(line), - port_(nullptr), - bit_iterator_(nullptr), - range_bus_port_(nullptr), - range_name_next_(nullptr), - size_(0) + line_(line) { init(port_name); } void -PortNameBitIterator::init(const char *port_name) +PortNameBitIterator::init(std::string_view port_name) { - LibertyPort *port = visitor_->findPort(port_name); + LibertyPort *port = visitor_->findPort(cell_, port_name); if (port) { if (port->isBus()) bit_iterator_ = new LibertyPortMemberIterator(port); @@ -6504,42 +3741,41 @@ PortNameBitIterator::init(const char *port_name) // Check for bus range. LibertyLibrary *library = visitor_->library(); bool is_bus, is_range, subscript_wild; - string bus_name; + std::string bus_name; int from, to; parseBusName(port_name, library->busBrktLeft(), library->busBrktRight(), '\\', is_bus, is_range, bus_name, from, to, subscript_wild); if (is_range) { - port = visitor_->findPort(port_name); + port = visitor_->findPort(cell_, port_name); if (port) { - if (port->isBus()) { - if (port->busIndexInRange(from) - && port->busIndexInRange(to)) { - range_bus_port_ = port; - range_from_ = from; - range_to_ = to; - range_bit_ = from; - } - else - visitor_->libWarn(1292, line_, "port %s subscript out of range.", - port_name); - } - else - visitor_->libWarn(1293, line_, "port range %s of non-bus port %s.", - port_name, - bus_name.c_str()); + if (port->isBus()) { + if (port->busIndexInRange(from) + && port->busIndexInRange(to)) { + range_bus_port_ = port; + range_from_ = from; + range_to_ = to; + range_bit_ = from; + } + else + visitor_->warn(1292, line_, "port {} subscript out of range.", port_name); + } + else + visitor_->warn(1293, line_, "port range {} of non-bus port {}.", + port_name, + bus_name); } else { - range_bus_name_ = bus_name; - range_from_ = from; - range_to_ = to; - range_bit_ = from; - findRangeBusNameNext(); + range_bus_name_ = bus_name; + range_from_ = from; + range_to_ = to; + range_bit_ = from; + findRangeBusNameNext(); } - size_ = abs(from - to) + 1; + size_ = std::abs(from - to) + 1; } else - visitor_->libWarn(1294, line_, "port %s not found.", port_name); + visitor_->warn(1294, line_, "port {} not found.", port_name); } } @@ -6554,11 +3790,11 @@ PortNameBitIterator::hasNext() return port_ || (bit_iterator_ && bit_iterator_->hasNext()) || (range_bus_port_ - && ((range_from_ > range_to_) - ? range_bit_ >= range_to_ - : range_bit_ <= range_from_)) + && ((range_from_ > range_to_) + ? range_bit_ >= range_to_ + : range_bit_ <= range_from_)) || (!range_bus_name_.empty() - && range_name_next_); + && range_name_next_); } LibertyPort * @@ -6595,21 +3831,17 @@ PortNameBitIterator::findRangeBusNameNext() ? range_bit_ >= range_to_ : range_bit_ <= range_to_) { LibertyLibrary *library = visitor_->library(); - string bus_bit_name; - stringPrint(bus_bit_name, "%s%c%d%c", - range_bus_name_.c_str(), - library->busBrktLeft(), - range_bit_, - library->busBrktRight()); - range_name_next_ = visitor_->findPort(bus_bit_name.c_str()); + std::string bus_bit_name = range_bus_name_ + library->busBrktLeft() + + std::to_string(range_bit_) + library->busBrktRight(); + range_name_next_ = visitor_->findPort(cell_, bus_bit_name); if (range_name_next_) { if (range_from_ > range_to_) - range_bit_--; + range_bit_--; else - range_bit_++; + range_bit_++; } else - visitor_->libWarn(1295, line_, "port %s not found.", bus_bit_name.c_str()); + visitor_->warn(1295, line_, "port {} not found.", bus_bit_name); } else range_name_next_ = nullptr; @@ -6619,7 +3851,7 @@ PortNameBitIterator::findRangeBusNameNext() OutputWaveform::OutputWaveform(float slew, float cap, - Table1 *currents, + Table *currents, float reference_time) : slew_(slew), cap_(cap), @@ -6628,17 +3860,10 @@ OutputWaveform::OutputWaveform(float slew, { } -OutputWaveform::~OutputWaveform() -{ - delete currents_; -} - -Table1 * -OutputWaveform::stealCurrents() +Table * +OutputWaveform::releaseCurrents() { - Table1 *currents = currents_; - currents_ = nullptr; - return currents; + return currents_.release(); } -} // namespace +} // namespace sta diff --git a/liberty/LibertyReader.hh b/liberty/LibertyReader.hh index bc1a47a78..030c42e82 100644 --- a/liberty/LibertyReader.hh +++ b/liberty/LibertyReader.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,14 +24,16 @@ #pragma once +#include + namespace sta { class Network; class LibertyLibrary; LibertyLibrary * -readLibertyFile(const char *filename, - bool infer_latches, - Network *network); +readLibertyFile(std::string_view filename, + bool infer_latches, + Network *network); -} // namespace +} // namespace sta diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index dc96c7bac..cc4623b44 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,12 +24,16 @@ #pragma once +#include #include +#include +#include +#include +#include #include +#include -#include "Vector.hh" -#include "Map.hh" -#include "StringSeq.hh" +#include "StringUtil.hh" #include "MinMax.hh" #include "NetworkClass.hh" #include "Transition.hh" @@ -42,655 +46,503 @@ #include "LibertyParser.hh" #include "LibertyReader.hh" #include "LibertyBuilder.hh" -#include "SdcClass.hh" +#include "Report.hh" namespace sta { class LibertyBuilder; class LibertyReader; -class LibertyFunc; -class PortGroup; -class SequentialGroup; -class GeneratedClockGroup; -class StatetableGroup; -class RelatedPortGroup; -class TimingGroup; -class InternalPowerGroup; -class LeakagePowerGroup; class PortNameBitIterator; class TimingArcBuilder; -class LibertyAttr; class OutputWaveform; -typedef void (LibertyReader::*LibraryAttrVisitor)(LibertyAttr *attr); -typedef void (LibertyReader::*LibraryGroupVisitor)(LibertyGroup *group); -typedef Map LibraryAttrMap; -typedef Map LibraryGroupMap; -typedef Vector PortGroupSeq; -typedef Vector SequentialGroupSeq; -typedef Vector LibertyFuncSeq; -typedef Vector TimingGroupSeq; -typedef Vector InternalPowerGroupSeq; -typedef Vector LeakagePowerGroupSeq; -typedef Vector GeneratedClockGroupSeq; -typedef void (LibertyPort::*LibertyPortBoolSetter)(bool value); -typedef Vector OutputWaveformSeq; -typedef std::vector StdStringSeq; -typedef std::function LibertySetFunc; +using LibraryGroupVisitor = void (LibertyReader::*)(const LibertyGroup *group, + LibertyGroup *parent_group); +using LibraryGroupVisitorMap = std::unordered_map; +using LibertyPortGroupMap = std::map; +using OutputWaveformSeq = std::vector; class LibertyReader : public LibertyGroupVisitor { public: - LibertyReader(const char *filename, + LibertyReader(std::string_view filename, bool infer_latches, Network *network); - virtual ~LibertyReader(); - virtual LibertyLibrary *readLibertyFile(const char *filename); - virtual void init(const char *filename, - bool infer_latches, - Network *network); - LibertyLibrary *library() const { return library_; } - virtual bool save(LibertyGroup *) { return false; } - virtual bool save(LibertyAttr *) { return false; } - virtual bool save(LibertyVariable *) { return false; } - - virtual void beginLibrary(LibertyGroup *group); - virtual void endLibrary(LibertyGroup *group); - virtual void endLibraryAttrs(LibertyGroup *group); - virtual void visitAttr(LibertyAttr *attr); - virtual void visitTimeUnit(LibertyAttr *attr); - virtual void visitCapacitiveLoadUnit(LibertyAttr *attr); - virtual void visitResistanceUnit(LibertyAttr *attr); - virtual void visitPullingResistanceUnit(LibertyAttr *attr); - virtual void visitVoltageUnit(LibertyAttr *attr); - virtual void visitCurrentUnit(LibertyAttr *attr); - virtual void visitPowerUnit(LibertyAttr *attr); - virtual void visitDistanceUnit(LibertyAttr *attr); - virtual void parseUnits(LibertyAttr *attr, - const char *suffix, - float &scale_var, - Unit *unit_suffix); - virtual void visitDelayModel(LibertyAttr *attr); - virtual void visitVoltageMap(LibertyAttr *attr); - virtual void visitBusStyle(LibertyAttr *attr); - virtual void visitNomTemp(LibertyAttr *attr); - virtual void visitNomVolt(LibertyAttr *attr); - virtual void visitNomProc(LibertyAttr *attr); - virtual void visitDefaultInoutPinCap(LibertyAttr *attr); - virtual void visitDefaultInputPinCap(LibertyAttr *attr); - virtual void visitDefaultOutputPinCap(LibertyAttr *attr); - virtual void visitDefaultMaxTransition(LibertyAttr *attr); - virtual void visitDefaultMaxFanout(LibertyAttr *attr); - virtual void visitDefaultIntrinsicRise(LibertyAttr *attr); - virtual void visitDefaultIntrinsicFall(LibertyAttr *attr); - virtual void visitDefaultIntrinsic(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitDefaultInoutPinRiseRes(LibertyAttr *attr); - virtual void visitDefaultInoutPinFallRes(LibertyAttr *attr); - virtual void visitDefaultInoutPinRes(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitDefaultOutputPinRiseRes(LibertyAttr *attr); - virtual void visitDefaultOutputPinFallRes(LibertyAttr *attr); - virtual void visitDefaultOutputPinRes(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitDefaultFanoutLoad(LibertyAttr *attr); - virtual void visitDefaultWireLoad(LibertyAttr *attr); - virtual void visitDefaultWireLoadMode(LibertyAttr *attr); - virtual void visitDefaultWireLoadSelection(LibertyAttr *attr); - virtual void visitDefaultOperatingConditions(LibertyAttr *attr); - virtual void visitInputThresholdPctFall(LibertyAttr *attr); - virtual void visitInputThresholdPctRise(LibertyAttr *attr); - virtual void visitInputThresholdPct(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitOutputThresholdPctFall(LibertyAttr *attr); - virtual void visitOutputThresholdPctRise(LibertyAttr *attr); - virtual void visitOutputThresholdPct(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitSlewLowerThresholdPctFall(LibertyAttr *attr); - virtual void visitSlewLowerThresholdPctRise(LibertyAttr *attr); - virtual void visitSlewLowerThresholdPct(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitSlewUpperThresholdPctFall(LibertyAttr *attr); - virtual void visitSlewUpperThresholdPctRise(LibertyAttr *attr); - virtual void visitSlewUpperThresholdPct(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitSlewDerateFromLibrary(LibertyAttr *attr); - - virtual void beginTechnology(LibertyGroup *group); - virtual void endTechnology(LibertyGroup *group); - virtual void beginTableTemplateDelay(LibertyGroup *group); - virtual void beginTableTemplateOutputCurrent(LibertyGroup *group); - virtual void beginTableTemplate(LibertyGroup *group, - TableTemplateType type); - virtual void endTableTemplate(LibertyGroup *group); - virtual void visitVariable1(LibertyAttr *attr); - virtual void visitVariable2(LibertyAttr *attr); - virtual void visitVariable3(LibertyAttr *attr); - virtual void visitIndex1(LibertyAttr *attr); - virtual void visitIndex2(LibertyAttr *attr); - virtual void visitIndex3(LibertyAttr *attr); - - virtual void beginType(LibertyGroup *group); - virtual void endType(LibertyGroup *group); - virtual void visitBitFrom(LibertyAttr *attr); - virtual void visitBitTo(LibertyAttr *attr); - - virtual void beginCell(LibertyGroup *group); - virtual void endCell(LibertyGroup *group); - virtual void beginScaledCell(LibertyGroup *group); - virtual void endScaledCell(LibertyGroup *group); - virtual void checkScaledCell(LibertyGroup *group); - virtual void finishPortGroups(); - virtual void checkPort(LibertyPort *port, - int line); - virtual void makeTimingArcs(PortGroup *port_group); - virtual void makeInternalPowers(PortGroup *port_group); - virtual void makeCellSequentials(); - virtual void makeCellSequential(SequentialGroup *seq); - virtual void makeStatetable(); - virtual void makeLeakagePowers(); - virtual void makeGeneratedClocks(); - virtual void makeGeneratedClock(GeneratedClockGroup *generated_clock); - virtual void parseCellFuncs(); - virtual void makeLibertyFunc(const char *expr, - LibertySetFunc set_func, - bool invert, - const char *attr_name, - LibertyStmt *stmt); - virtual void makeTimingArcs(LibertyPort *to_port, - TimingGroup *timing); - virtual void makeTimingArcs(const char *from_port_name, - PortNameBitIterator &from_port_iter, - LibertyPort *to_port, - LibertyPort *related_out_port, - TimingGroup *timing); - virtual void makeTimingArcs(LibertyPort *to_port, - LibertyPort *related_out_port, - TimingGroup *timing); - - // Helper functions for makeTimingArcs() - std::unordered_map> bit_overrides_; - bool relatedPinIncludesPort(TimingGroup *t, - LibertyPort *from_port, - int line); - bool sameArcIdentity(TimingGroup *pin_t, - TimingGroup *bus_timing, - LibertyPort *from_port); - bool hasBitPinTimingOverride(LibertyPort *to_port_bit, - LibertyPort *from_port, - TimingGroup *bus_timing); - - virtual void visitClockGatingIntegratedCell(LibertyAttr *attr); - virtual void visitArea(LibertyAttr *attr); - virtual void visitDontUse(LibertyAttr *attr); - virtual void visitIsMacro(LibertyAttr *attr); - virtual void visitIsMemory(LibertyAttr *attr); - virtual void visitIsPadCell(LibertyAttr *attr); - virtual void visitIsPad(LibertyAttr *attr); - virtual void visitIsClockCell(LibertyAttr *attr); - virtual void visitIsLevelShifter(LibertyAttr *attr); - virtual void visitLevelShifterType(LibertyAttr *attr); - virtual void visitIsIsolationCell(LibertyAttr *attr); - virtual void visitAlwaysOn(LibertyAttr *attr); - virtual void visitSwitchCellType(LibertyAttr *attr); - virtual void visitInterfaceTiming(LibertyAttr *attr); - virtual void visitScalingFactors(LibertyAttr *attr); - virtual void visitCellLeakagePower(LibertyAttr *attr); - virtual void visitCellFootprint(LibertyAttr *attr); - virtual void visitCellUserFunctionClass(LibertyAttr *attr); - - virtual void beginGeneratedClock(LibertyGroup *group); - virtual void endGeneratedClock(LibertyGroup *group); - virtual void visitClockPin(LibertyAttr *attr); - virtual void visitMasterPin(LibertyAttr *attr); - virtual void visitDividedBy(LibertyAttr *attr); - virtual void visitMultipliedBy(LibertyAttr *attr); - virtual void visitDutyCycle(LibertyAttr *attr); - virtual void visitInvert(LibertyAttr *attr); - virtual void visitShifts(LibertyAttr *attr); - virtual void visitEdges(LibertyAttr *attr); - - virtual void beginPin(LibertyGroup *group); - virtual void endPin(LibertyGroup *group); - virtual void beginBus(LibertyGroup *group); - virtual void endBus(LibertyGroup *group); - virtual void beginBundle(LibertyGroup *group); - virtual void endBundle(LibertyGroup *group); - virtual void beginBusOrBundle(LibertyGroup *group); - virtual void endBusOrBundle(); - virtual void endPorts(); - virtual void setPortCapDefault(LibertyPort *port); - virtual void visitMembers(LibertyAttr *attr); - virtual void visitDirection(LibertyAttr *attr); - virtual void visitFunction(LibertyAttr *attr); - virtual void visitThreeState(LibertyAttr *attr); - virtual void visitBusType(LibertyAttr *attr); - virtual void visitCapacitance(LibertyAttr *attr); - virtual void visitRiseCap(LibertyAttr *attr); - virtual void visitFallCap(LibertyAttr *attr); - virtual void visitRiseCapRange(LibertyAttr *attr); - virtual void visitFallCapRange(LibertyAttr *attr); - virtual void visitFanoutLoad(LibertyAttr *attr); - virtual void visitMaxFanout(LibertyAttr *attr); - virtual void visitMinFanout(LibertyAttr *attr); - virtual void visitFanout(LibertyAttr *attr, - const MinMax *min_max); - virtual void visitMaxTransition(LibertyAttr *attr); - virtual void visitMinTransition(LibertyAttr *attr); - virtual void visitMinMaxTransition(LibertyAttr *attr, - const MinMax *min_max); - virtual void visitMaxCapacitance(LibertyAttr *attr); - virtual void visitMinCapacitance(LibertyAttr *attr); - virtual void visitMinMaxCapacitance(LibertyAttr *attr, - const MinMax *min_max); - virtual void visitMinPeriod(LibertyAttr *attr); - virtual void visitMinPulseWidthLow(LibertyAttr *attr); - virtual void visitMinPulseWidthHigh(LibertyAttr *attr); - virtual void visitMinPulseWidth(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitPulseClock(LibertyAttr *attr); - virtual void visitClockGateClockPin(LibertyAttr *attr); - virtual void visitClockGateEnablePin(LibertyAttr *attr); - virtual void visitClockGateOutPin(LibertyAttr *attr); - void visitIsPllFeedbackPin(LibertyAttr *attr); - virtual void visitSignalType(LibertyAttr *attr); - const EarlyLateAll *getAttrEarlyLate(LibertyAttr *attr); - virtual void visitClock(LibertyAttr *attr); - virtual void visitIsolationCellDataPin(LibertyAttr *attr); - virtual void visitIsolationCellEnablePin(LibertyAttr *attr); - virtual void visitLevelShifterDataPin(LibertyAttr *attr); - virtual void visitSwitchPin(LibertyAttr *attr); - void visitPortBoolAttr(LibertyAttr *attr, - LibertyPortBoolSetter setter); - - virtual void beginScalingFactors(LibertyGroup *group); - virtual void endScalingFactors(LibertyGroup *group); - virtual void defineScalingFactorVisitors(); - virtual void visitScaleFactorSuffix(LibertyAttr *attr); - virtual void visitScaleFactorPrefix(LibertyAttr *attr); - virtual void visitScaleFactorHiLow(LibertyAttr *attr); - virtual void visitScaleFactor(LibertyAttr *attr); - - virtual void beginOpCond(LibertyGroup *group); - virtual void endOpCond(LibertyGroup *group); - virtual void visitProc(LibertyAttr *attr); - virtual void visitVolt(LibertyAttr *attr); - virtual void visitTemp(LibertyAttr *attr); - virtual void visitTreeType(LibertyAttr *attr); - - virtual void beginWireload(LibertyGroup *group); - virtual void endWireload(LibertyGroup *group); - virtual void visitResistance(LibertyAttr *attr); - virtual void visitSlope(LibertyAttr *attr); - virtual void visitFanoutLength(LibertyAttr *attr); - - virtual void beginWireloadSelection(LibertyGroup *group); - virtual void endWireloadSelection(LibertyGroup *group); - virtual void visitWireloadFromArea(LibertyAttr *attr); - - virtual void beginMemory(LibertyGroup *group); - virtual void endMemory(LibertyGroup *group); - - virtual void beginFF(LibertyGroup *group); - virtual void endFF(LibertyGroup *group); - virtual void beginFFBank(LibertyGroup *group); - virtual void endFFBank(LibertyGroup *group); - virtual void beginLatch(LibertyGroup *group); - virtual void endLatch(LibertyGroup *group); - virtual void beginLatchBank(LibertyGroup *group); - virtual void endLatchBank(LibertyGroup *group); - virtual void beginSequential(LibertyGroup *group, - bool is_register, - bool is_bank); - virtual void seqPortNames(LibertyGroup *group, - const char *&out_name, - const char *&out_inv_name, - bool &has_size, - int &size); - virtual void checkLatchEnableSense(FuncExpr *enable_func, - int line); - virtual void visitClockedOn(LibertyAttr *attr); - virtual void visitDataIn(LibertyAttr *attr); - virtual void visitClear(LibertyAttr *attr); - virtual void visitPreset(LibertyAttr *attr); - virtual void visitClrPresetVar1(LibertyAttr *attr); - virtual void visitClrPresetVar2(LibertyAttr *attr); - - virtual void beginStatetable(LibertyGroup *group); - virtual void endStatetable(LibertyGroup *group); - virtual void visitTable(LibertyAttr *attr); - - virtual void beginTiming(LibertyGroup *group); - virtual void endTiming(LibertyGroup *group); - virtual void visitRelatedPin(LibertyAttr *attr); - virtual void visitRelatedPin(LibertyAttr *attr, - RelatedPortGroup *group); - virtual void visitRelatedBusPins(LibertyAttr *attr); - virtual void visitRelatedBusPins(LibertyAttr *attr, - RelatedPortGroup *group); - virtual void visitRelatedOutputPin(LibertyAttr *attr); - virtual void visitTimingType(LibertyAttr *attr); - virtual void visitTimingSense(LibertyAttr *attr); - virtual void visitSdfCondStart(LibertyAttr *attr); - virtual void visitSdfCondEnd(LibertyAttr *attr); - virtual void visitMode(LibertyAttr *attr); - virtual void visitIntrinsicRise(LibertyAttr *attr); - virtual void visitIntrinsicFall(LibertyAttr *attr); - virtual void visitIntrinsic(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitRiseResistance(LibertyAttr *attr); - virtual void visitFallResistance(LibertyAttr *attr); - virtual void visitRiseFallResistance(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitValue(LibertyAttr *attr); - virtual void visitValues(LibertyAttr *attr); - virtual void beginCellRise(LibertyGroup *group); - virtual void beginCellFall(LibertyGroup *group); - virtual void endCellRiseFall(LibertyGroup *group); - virtual void beginRiseTransition(LibertyGroup *group); - virtual void endRiseFallTransition(LibertyGroup *group); - virtual void beginFallTransition(LibertyGroup *group); - virtual void beginRiseConstraint(LibertyGroup *group); - virtual void endRiseFallConstraint(LibertyGroup *group); - virtual void beginFallConstraint(LibertyGroup *group); - - virtual void beginRiseTransitionDegredation(LibertyGroup *group); - virtual void beginFallTransitionDegredation(LibertyGroup *group); - virtual void endRiseFallTransitionDegredation(LibertyGroup *group); - - virtual void beginTableModel(LibertyGroup *group, - TableTemplateType type, - const RiseFall *rf, - float scale, - ScaleFactorType scale_factor_type); - virtual void endTableModel(); - virtual void beginTimingTableModel(LibertyGroup *group, - const RiseFall *rf, - ScaleFactorType scale_factor_type); - virtual void beginTable(LibertyGroup *group, - TableTemplateType type, - float scale); - virtual void endTable(); - virtual void makeTable(LibertyAttr *attr, - float scale); - virtual FloatTable *makeFloatTable(LibertyAttr *attr, - size_t rows, - size_t cols, - float scale); - - virtual void beginLut(LibertyGroup *group); - virtual void endLut(LibertyGroup *group); - - virtual void beginTestCell(LibertyGroup *group); - virtual void endTestCell(LibertyGroup *group); - - virtual void beginModeDef(LibertyGroup *group); - virtual void endModeDef(LibertyGroup *group); - virtual void beginModeValue(LibertyGroup *group); - virtual void endModeValue(LibertyGroup *group); - virtual void visitWhen(LibertyAttr *attr); - virtual void visitSdfCond(LibertyAttr *attr); - - // Power attributes. - virtual void beginTableTemplatePower(LibertyGroup *group); - virtual void beginLeakagePower(LibertyGroup *group); - virtual void endLeakagePower(LibertyGroup *group); - virtual void beginInternalPower(LibertyGroup *group); - virtual void endInternalPower(LibertyGroup *group); - virtual InternalPowerGroup *makeInternalPowerGroup(int line); - virtual void beginFallPower(LibertyGroup *group); - virtual void beginRisePower(LibertyGroup *group); - virtual void endRiseFallPower(LibertyGroup *group); - virtual void endPower(LibertyGroup *group); - virtual void visitRelatedGroundPin(LibertyAttr *attr); - virtual void visitRelatedPowerPin(LibertyAttr *attr); - virtual void visitRelatedPgPin(LibertyAttr *attr); - virtual void makeInternalPowers(LibertyPort *port, - InternalPowerGroup *power_group); - virtual void makeInternalPowers(LibertyPort *port, - const char *related_port_name, - PortNameBitIterator &related_port_iter, - InternalPowerGroup *power_group); - - // AOCV attributes. - virtual void beginTableTemplateOcv(LibertyGroup *group); - virtual void visitOcvArcDepth(LibertyAttr *attr); - virtual void visitDefaultOcvDerateGroup(LibertyAttr *attr); - virtual void visitOcvDerateGroup(LibertyAttr *attr); - virtual void beginOcvDerate(LibertyGroup *group); - virtual void endOcvDerate(LibertyGroup *group); - virtual void beginOcvDerateFactors(LibertyGroup *group); - virtual void endOcvDerateFactors(LibertyGroup *group); - virtual void visitRfType(LibertyAttr *attr); - virtual void visitDerateType(LibertyAttr *attr); - virtual void visitPathType(LibertyAttr *attr); - - // POCV attributes. - virtual void beginOcvSigmaCellRise(LibertyGroup *group); - virtual void beginOcvSigmaCellFall(LibertyGroup *group); - virtual void endOcvSigmaCell(LibertyGroup *group); - virtual void beginOcvSigmaRiseTransition(LibertyGroup *group); - virtual void beginOcvSigmaFallTransition(LibertyGroup *group); - virtual void endOcvSigmaTransition(LibertyGroup *group); - virtual void beginOcvSigmaRiseConstraint(LibertyGroup *group); - virtual void beginOcvSigmaFallConstraint(LibertyGroup *group); - virtual void endOcvSigmaConstraint(LibertyGroup *group); - virtual void visitSigmaType(LibertyAttr *attr); - - // PgPin group. - virtual void beginPgPin(LibertyGroup *group); - virtual void endPgPin(LibertyGroup *group); - virtual void visitPgType(LibertyAttr *attr); - virtual void visitVoltageName(LibertyAttr *attr); - - // ccs receiver capacitance - virtual void beginReceiverCapacitance(LibertyGroup *group); - virtual void endReceiverCapacitance(LibertyGroup *group); - - virtual void visitSegement(LibertyAttr *attr); - - virtual void beginReceiverCapacitance1Rise(LibertyGroup *group); - virtual void endReceiverCapacitanceRiseFall(LibertyGroup *group); - virtual void beginReceiverCapacitance1Fall(LibertyGroup *group); - virtual void beginReceiverCapacitance2Rise(LibertyGroup *group); - virtual void beginReceiverCapacitance2Fall(LibertyGroup *group); - void beginReceiverCapacitance(LibertyGroup *group, - int index, - const RiseFall *rf); - void endReceiverCapacitance(LibertyGroup *group, - int index, - const RiseFall *rf); - // ccs - void beginOutputCurrentRise(LibertyGroup *group); - void beginOutputCurrentFall(LibertyGroup *group); - void beginOutputCurrent(const RiseFall *rf, - LibertyGroup *group); - void endOutputCurrentRiseFall(LibertyGroup *group); - void beginVector(LibertyGroup *group); - void endVector(LibertyGroup *group); - void visitReferenceTime(LibertyAttr *attr); - - void beginNormalizedDriverWaveform(LibertyGroup *group); - void endNormalizedDriverWaveform(LibertyGroup *group); - void visitDriverWaveformName(LibertyAttr *attr); + LibertyLibrary *readLibertyFile(std::string_view filename); + LibertyLibrary *library() { return library_; } + const LibertyLibrary *library() const { return library_; } + + void beginLibrary(const LibertyGroup *group, + LibertyGroup *library_group); + void endLibrary(const LibertyGroup *group, + LibertyGroup *null_group); + void visitAttr(const LibertySimpleAttr *attr) override; + void visitAttr(const LibertyComplexAttr *attr) override; + void visitVariable(LibertyVariable *var) override; + void visitDefine(LibertyDefine *define) override; + + void begin(const LibertyGroup *group, + LibertyGroup *parent_group) override; + void end(const LibertyGroup *group, + LibertyGroup *parent_group) override; + + void endCell(const LibertyGroup *group, + LibertyGroup *library_group); + void endScaledCell(const LibertyGroup *group, + LibertyGroup *library_group); + void checkScaledCell(LibertyCell *scaled_cell, + LibertyCell *owner, + const LibertyGroup *scaled_cell_group, + std::string_view op_cond_name); + + void setPortCapDefault(LibertyPort *port); + void checkLatchEnableSense(FuncExpr *enable_func, + int line); + FloatTable makeFloatTable(const LibertyComplexAttr *attr, + const LibertyGroup *table_group, + size_t rows, + size_t cols, + float scale); - void visitDriverWaveformRise(LibertyAttr *attr); - void visitDriverWaveformFall(LibertyAttr *attr); - void visitDriverWaveformRiseFall(LibertyAttr *attr, - const RiseFall *rf); - void beginCcsn(LibertyGroup *group); - void endCcsn(LibertyGroup *group); - void beginEcsmWaveform(LibertyGroup *group); - void endEcsmWaveform(LibertyGroup *group); LibertyPort *findPort(LibertyCell *cell, - const char *port_name); + std::string_view port_name); + StringSeq findAttributStrings(const LibertyGroup *group, + std::string_view name_attr); protected: + // Library gruops. + void makeLibrary(const LibertyGroup *library_group); + void readLibraryAttributes(const LibertyGroup *library_group); + void readLibraryUnits(const LibertyGroup *library_group); + void readDelayModel(const LibertyGroup *library_group); + void readBusStyle(const LibertyGroup *library_group); + void readDefaultWireLoad(const LibertyGroup *library_group); + void readDefaultWireLoadMode(const LibertyGroup *library_group); + void readTechnology(const LibertyGroup *library_group); + void readDefaultWireLoadSelection(const LibertyGroup *library_group); + void readUnit(std::string_view unit_attr_name, + std::string_view unit_suffix, + float &scale_var, + Unit *unit, + const LibertyGroup *library_group); + void readBusTypes(LibertyCell *cell, + const LibertyGroup *type_group); + void readTableTemplates(const LibertyGroup *library_group); + void readTableTemplates(const LibertyGroup *library_group, + std::string_view group_name, + TableTemplateType type); + void readThresholds(const LibertyGroup *library_group); + void checkThresholds(const LibertyGroup *library_group) const; + TableAxisPtr makeTableTemplateAxis(const LibertyGroup *template_group, + int axis_index); + void readVoltateMaps(const LibertyGroup *library_group); + void readWireloads(const LibertyGroup *library_group); + void readWireloadSelection(const LibertyGroup *library_group); + void readOperatingConds(const LibertyGroup *library_group); + void readScaleFactors(const LibertyGroup *library_group); + void readScaleFactors(const LibertyGroup *scale_group, + ScaleFactors *scale_factors); + void readOcvDerateFactors(LibertyCell *cell, + const LibertyGroup *parent_group); + void readDefaultOcvDerateGroup(const LibertyGroup *library_group); + void readNormalizedDriverWaveform(const LibertyGroup *library_group); + void readSlewDegradations(const LibertyGroup *library_group); + void readLibAttrFloat(const LibertyGroup *library_group, + std::string_view attr_name, + void (LibertyLibrary::*set_func)(float value), + float scale); + void readLibAttrFloat(const LibertyGroup *library_group, + std::string_view attr_name, + void (LibertyLibrary::*set_func)(const RiseFall *rf, + float value), + const RiseFall *rf, + float scale); + void readLibAttrFloatWarnZero(const LibertyGroup *library_group, + std::string_view attr_name, + void (LibertyLibrary::*set_func)(float value), + float scale); + + // Cell groups. + void readCell(LibertyCell *cell, + const LibertyGroup *cell_group); + void readScaledCell(const LibertyGroup *scaled_cell_group); + LibertyPortGroupMap makeCellPorts(LibertyCell *cell, + const LibertyGroup *cell_group); + void makePinPort(LibertyCell *cell, + const LibertyGroup *pin_group, + LibertyPortGroupMap &port_group_map); + void makeBusPort(LibertyCell *cell, + const LibertyGroup *bus_group, + LibertyPortGroupMap &port_group_map); + void makeBusPinPorts(LibertyCell *cell, + const LibertyGroup *bus_group, + LibertyPortGroupMap &port_group_map); + void makeBundlePort(LibertyCell *cell, + const LibertyGroup *bundle_group, + LibertyPortGroupMap &port_group_map); + void makeBundlePinPorts(LibertyCell *cell, + const LibertyGroup *bundle_group, + LibertyPortGroupMap &port_group_map); + void makePgPinPort(LibertyCell *cell, + const LibertyGroup *pg_pin_group); LibertyPort *makePort(LibertyCell *cell, - const char *port_name); + std::string_view port_name); LibertyPort *makeBusPort(LibertyCell *cell, - const char *bus_name, + std::string_view bus_name, int from_index, int to_index, BusDcl *bus_dcl); - TimingModel *makeScalarCheckModel(float value, + void readPortAttributes(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group); + void readPortAttrString(std::string_view attr_name, + void (LibertyPort::*set_func)(std::string value), + const LibertyPortSeq &ports, + const LibertyGroup *group); + void readPortAttrLibertyPort(std::string_view attr_name, + void (LibertyPort::*set_func)(LibertyPort *port), + LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *group); + void readPortAttrFloat(std::string_view attr_name, + void (LibertyPort::*set_func)(float value), + const LibertyPortSeq &ports, + const LibertyGroup *group, + float scale); + void readPortAttrBool(std::string_view attr_name, + void (LibertyPort::*set_func)(bool value), + const LibertyPortSeq &ports, + const LibertyGroup *group); + void readDriverWaveform(const LibertyPortSeq &ports, + const LibertyGroup *port_group); + void readPortAttrFloatMinMax(std::string_view attr_name, + void (LibertyPort::*set_func)(float value, + const MinMax *min_max), + const LibertyPortSeq &ports, + const LibertyGroup *group, + const MinMax *min_max, + float scale); + void readPulseClock(const LibertyPortSeq &ports, + const LibertyGroup *port_group); + void readSignalType(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group); + void readMinPulseWidth(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group); + void readModeDefs(LibertyCell *cell, + const LibertyGroup *cell_group); + void makeTimingArcs(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group); + bool isGateTimingType(TimingType timing_type); + TableModel *readTableModel(const LibertyGroup *timing_group, + const std::string &table_group_name, + const RiseFall *rf, + TableTemplateType template_type, + float scale, + ScaleFactorType scale_factor_type, + const std::function &check_axes); + TableModelsEarlyLate + readEarlyLateTableModels(const LibertyGroup *timing_group, + std::string_view table_group_name, + const RiseFall *rf, + TableTemplateType template_type, + float scale, + ScaleFactorType scale_factor_type, + const std::function &check_axes); + ReceiverModelPtr readReceiverCapacitance(const LibertyGroup *timing_group, + const RiseFall *rf); + void readReceiverCapacitance(const LibertyGroup *timing_group, + std::string_view cap_group_name, + int index, + const RiseFall *rf, + ReceiverModelPtr &receiver_model); + OutputWaveforms *readOutputWaveforms(const LibertyGroup *timing_group, + const RiseFall *rf); + OutputWaveforms *makeOutputWaveforms(const LibertyGroup *current_group, + OutputWaveformSeq &output_currents, + const RiseFall *rf); + + TableModel *readTableModel(const LibertyGroup *table_group, + const RiseFall *rf, + TableTemplateType template_type, + float scale, + ScaleFactorType scale_factor_type, + const std::function &check_axes = + [](TableModel *) { return true; }); + TablePtr readTableModel(const LibertyGroup *table_group, + const TableTemplate *tbl_template, + float scale); + void makeTimingModels(LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrs &timing_attrs); + void makeLinearModels(LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrs &timing_attrs); + void makeTableModels(LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrs &timing_attrs); + void readLvfModels(const LibertyGroup *timing_group, + const std::string &sigma_group_name, + const std::string &std_dev_group_name, + const std::string &mean_shift_group_name, + const std::string &skewness_group_name, + const RiseFall *rf, + TableModels *table_models, + const std::function &check_axes); + + TableAxisPtr makeTableAxis(const LibertyGroup *table_group, + std::string_view index_attr_name, + TableAxisPtr template_axis); + void readGroupAttrFloat(std::string_view attr_name, + const LibertyGroup *group, + const std::function &set_func, + float scale = 1.0F); + void readTimingArcAttrs(LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrs &timing_attrs); + void readTimingSense(const LibertyGroup *timing_group, + TimingArcAttrs &timing_attrs); + void readTimingType(const LibertyGroup *timing_group, + TimingArcAttrs &timing_attrs); + void readTimingWhen(const LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrs &timing_attrs); + void readTimingMode(const LibertyGroup *timing_group, + TimingArcAttrs &timing_attrs); + void makePortFuncs(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group); + + void makeSequentials(LibertyCell *cell, + const LibertyGroup *cell_group); + void makeSequentials(LibertyCell *cell, + const LibertyGroup *cell_group, + bool is_register, + std::string_view seq_group_name, + std::string_view clk_attr_name, + std::string_view data_attr_name); + FuncExpr *makeSeqFunc(LibertyCell *cell, + const LibertyGroup *seq_group, + std::string_view attr_name, + int size); + void makeSeqPorts(LibertyCell *cell, + const LibertyGroup *seq_group, + // Return values. + LibertyPort *&out_port, + LibertyPort *&out_port_inv, + size_t &size); + void seqPortNames(const LibertyGroup *group, + // Return values. + std::string &out_name, + std::string &out_inv_name, + bool &has_size, + size_t &size); + TimingModel *makeScalarCheckModel(LibertyCell *cell, + float value, ScaleFactorType scale_factor_type, const RiseFall *rf); - void makeMinPulseWidthArcs(LibertyPort *port, - int line); - void setEnergyScale(); + void readPortDir(const LibertyPortSeq &ports, + const LibertyGroup *port_group); + void readCapacitance(const LibertyPortSeq &ports, + const LibertyGroup *port_group); + void makeTimingArcs(LibertyCell *cell, + const std::string &from_port_name, + LibertyPort *to_port, + LibertyPort *related_out_port, + bool one_to_one, + const TimingArcAttrsPtr &timing_attrs, + const LibertyGroup *timing_group); + void makeTimingArcs(LibertyCell *cell, + LibertyPort *to_port, + LibertyPort *related_out_port, + const TimingArcAttrsPtr &timing_attrs); + + std::unordered_map> bit_overrides_; + bool relatedPinIncludesPort(LibertyCell *cell, + const LibertyGroup *timing_group, + LibertyPort *from_port, + int line); + bool sameArcIdentity(LibertyCell *cell, + const LibertyGroup *pin_timing, + const LibertyGroup *bus_timing, + LibertyPort *from_port); + bool hasBitPinTimingOverride(LibertyCell *cell, + LibertyPort *to_port_bit, + LibertyPort *from_port, + const LibertyGroup *bus_timing); + + void readInternalPowerGroups(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group); + void readLeakageGrouops(LibertyCell *cell, + const LibertyGroup *cell_group); + void readGeneratedClocks(LibertyCell *cell, + const LibertyGroup *cell_group); + + void readCellAttributes(LibertyCell *cell, + const LibertyGroup *cell_group); + void readScaleFactors(LibertyCell *cell, + const LibertyGroup *cell_group); + void readCellAttrString(std::string_view attr_name, + void (LibertyCell::*set_func)(std::string_view value), + LibertyCell *cell, + const LibertyGroup *group); + void readCellAttrFloat(std::string_view attr_name, + void (LibertyCell::*set_func)(float value), + LibertyCell *cell, + const LibertyGroup *group, + float scale); + void readCellAttrBool(std::string_view attr_name, + void (LibertyCell::*set_func)(bool value), + LibertyCell *cell, + const LibertyGroup *group); + void readLevelShifterType(LibertyCell *cell, + const LibertyGroup *cell_group); + void readSwitchCellType(LibertyCell *cell, + const LibertyGroup *cell_group); + void readCellOcvDerateGroup(LibertyCell *cell, + const LibertyGroup *cell_group); + void readStatetable(LibertyCell *cell, + const LibertyGroup *cell_group); + void readTestCell(LibertyCell *cell, + const LibertyGroup *cell_group); + + FuncExpr *readFuncExpr(LibertyCell *cell, + const LibertyGroup *group, + std::string_view attr_name); + LibertyPort *findLibertyPort(LibertyCell *cell, + const LibertyGroup *group, + std::string_view port_name_attr); + LibertyPortSeq findLibertyPorts(LibertyCell *cell, + const LibertyGroup *group, + std::string_view port_name_attr); + + float energyScale(); void defineVisitors(); - virtual void begin(LibertyGroup *group); - virtual void end(LibertyGroup *group); - void defineGroupVisitor(const char *type, - LibraryGroupVisitor begin_visitor, - LibraryGroupVisitor end_visitor); - void defineAttrVisitor(const char *attr_name, - LibraryAttrVisitor visitor); - void parseNames(const char *name_str); - void clearAxisValues(); - void makeTableAxis(int index, - LibertyAttr *attr); - StringSeq *parseNameList(const char *name_list); - StdStringSeq parseTokenList(const char *token_str, - const char separator); - LibertyPort *findPort(const char *port_name); + void defineGroupVisitor(std::string_view type, + LibraryGroupVisitor begin_visitor, + LibraryGroupVisitor end_visitor); + float defaultCap(LibertyPort *port); - virtual void visitVariable(LibertyVariable *var); void visitPorts(std::function func); - StateInputValues parseStateInputValues(StdStringSeq &inputs, - LibertyAttr *attr); - StateInternalValues parseStateInternalValues(StdStringSeq &states, - LibertyAttr *attr); - - const char *getAttrString(LibertyAttr *attr); - void getAttrInt(LibertyAttr *attr, - // Return values. - int &value, - bool &exists); - void getAttrFloat(LibertyAttr *attr, - // Return values. - float &value, - bool &valid); - void getAttrFloat(LibertyAttr *attr, - LibertyAttrValue *attr_value, - // Return values. - float &value, - bool &valid); - void getAttrFloat2(LibertyAttr *attr, - // Return values. - float &value1, - float &value2, - bool &exists); - void parseStringFloatList(const char *float_list, - float scale, - FloatSeq *values, - LibertyAttr *attr); - LogicValue getAttrLogicValue(LibertyAttr *attr); - void getAttrBool(LibertyAttr *attr, - // Return values. - bool &value, - bool &exists); - void visitVariable(int index, - LibertyAttr *attr); - void visitIndex(int index, - LibertyAttr *attr); + StateInputValues parseStateInputValues(StringSeq &inputs, + const LibertySimpleAttr *attr); + StateInternalValues parseStateInternalValues(StringSeq &states, + const LibertySimpleAttr *attr); + + void getAttrInt(const LibertySimpleAttr *attr, + // Return values. + int &value, + bool &exists); + void getAttrFloat2(const LibertyComplexAttr *attr, + // Return values. + float &value1, + float &value2, + bool &exists); + void getAttrFloat(const LibertyComplexAttr *attr, + const LibertyAttrValue *attr_value, + // Return values. + float &value, + bool &valid); + LogicValue getAttrLogicValue(const LibertySimpleAttr *attr); + void getAttrBool(const LibertySimpleAttr *attr, + // Return values. + bool &value, + bool &exists); + const EarlyLateAll *getAttrEarlyLate(const LibertySimpleAttr *attr); + + FloatSeq parseFloatList(const std::string &float_list, + float scale, + int line); TableAxisPtr makeAxis(int index, - LibertyGroup *group); - FloatSeq *readFloatSeq(LibertyAttr *attr, - float scale); - void variableValue(const char *var, - float &value, - bool &exists); - FuncExpr *parseFunc(const char *func, - const char *attr_name, - int line); - void libWarn(int id, - LibertyStmt *stmt, - const char *fmt, - ...) - __attribute__((format (printf, 4, 5))); - void libWarn(int id, - int line, - const char *fmt, - ...) - __attribute__((format (printf, 4, 5))); - void libError(int id, - LibertyStmt *stmt, - const char *fmt, ...) - __attribute__((format (printf, 4, 5))); - - const char *filename_; + const LibertyGroup *group); + FloatSeq readFloatSeq(const LibertyComplexAttr *attr, + float scale); + void variableValue(std::string_view var, + float &value, + bool &exists); + FuncExpr *parseFunc(std::string_view func, + std::string_view attr_name, + const LibertyCell *cell, + int line); + template + void warn(int id, + const LibertyGroup *group, + std::string_view fmt, + Args &&...args) const + { + report_->fileWarn(id, filename_, group->line(), fmt, std::forward(args)...); + } + template + void warn(int id, + const LibertySimpleAttr *attr, + std::string_view fmt, + Args &&...args) const + { + report_->fileWarn(id, filename_, attr->line(), fmt, std::forward(args)...); + } + template + void warn(int id, + const LibertyComplexAttr *attr, + std::string_view fmt, + Args &&...args) const + { + report_->fileWarn(id, filename_, attr->line(), fmt, std::forward(args)...); + } + template + void warn(int id, + int line, + std::string_view fmt, + Args &&...args) const + { + report_->fileWarn(id, filename_, line, fmt, std::forward(args)...); + } + template + void error(int id, + const LibertyGroup *group, + std::string_view fmt, + Args &&...args) const + { + report_->fileError(id, filename_, group->line(), fmt, + std::forward(args)...); + } + template + void error(int id, + const LibertySimpleAttr *attr, + std::string_view fmt, + Args &&...args) const + { + report_->fileError(id, filename_, attr->line(), fmt, + std::forward(args)...); + } + template + void error(int id, + const LibertyComplexAttr *attr, + std::string_view fmt, + Args &&...args) const + { + report_->fileError(id, filename_, attr->line(), fmt, + std::forward(args)...); + } + + std::string_view filename_; bool infer_latches_; Report *report_; Debug *debug_; Network *network_; LibertyBuilder builder_; - LibertyVariableMap *var_map_; - LibertyLibrary *library_; - LibraryGroupMap group_begin_map_; - LibraryGroupMap group_end_map_; - LibraryAttrMap attr_visitor_map_; - Wireload *wireload_; - WireloadSelection *wireload_selection_; - const char *default_wireload_; - const char *default_wireload_selection_; - ScaleFactors *scale_factors_; - ScaleFactors *save_scale_factors_; - bool have_input_threshold_[RiseFall::index_count]; - bool have_output_threshold_[RiseFall::index_count]; - bool have_slew_lower_threshold_[RiseFall::index_count]; - bool have_slew_upper_threshold_[RiseFall::index_count]; - TableTemplate *tbl_template_; - LibertyCell *cell_; - LibertyCell *scaled_cell_owner_; - const char *ocv_derate_name_; - PortGroupSeq cell_port_groups_; - OperatingConditions *op_cond_; - LibertyPortSeq *ports_; - PortGroup *port_group_; - LibertyPortSeq *saved_ports_; - PortGroup *saved_port_group_; - StringSeq bus_names_; - bool in_bus_; - bool in_bundle_; - bool in_ccsn_; - bool in_ecsm_waveform_; - TableAxisVariable axis_var_[3]; - FloatSeq *axis_values_[3]; - int type_bit_from_; - bool type_bit_from_exists_; - int type_bit_to_; - bool type_bit_to_exists_; - SequentialGroup *sequential_; - SequentialGroupSeq cell_sequentials_; + LibertyVariableMap var_map_; + LibertyLibrary *library_{nullptr}; + LibraryGroupVisitorMap group_begin_map_; + LibraryGroupVisitorMap group_end_map_; - GeneratedClockGroup *generated_clock_; - GeneratedClockGroupSeq generated_clocks_; - StatetableGroup *statetable_; - TimingGroup *timing_; - InternalPowerGroup *internal_power_; - LeakagePowerGroup *leakage_power_; - LeakagePowerGroupSeq leakage_powers_; - const RiseFall *rf_; - int index_; - OcvDerate *ocv_derate_; - const RiseFallBoth *rf_type_; - const EarlyLateAll *derate_type_; - const EarlyLateAll *sigma_type_; - PathType path_type_; - LibertyPort *pg_port_; - ScaleFactorType scale_factor_type_; - TableAxisPtr axis_[3]; - TablePtr table_; - float table_model_scale_; - ModeDef *mode_def_; - ModeValueDef *mode_value_; - LibertyFuncSeq cell_funcs_; float time_scale_; float cap_scale_; float res_scale_; @@ -699,288 +551,11 @@ protected: float power_scale_; float energy_scale_; float distance_scale_; - const char *default_operating_condition_; - ReceiverModelPtr receiver_model_; - OutputWaveformSeq output_currents_; - OutputWaveforms *output_waveforms_; - float reference_time_; - bool reference_time_exists_; - std::string driver_waveform_name_; - - TestCell *test_cell_; - // Saved state while parsing test_cell. - LibertyCell *save_cell_; - PortGroupSeq save_cell_port_groups_; - StatetableGroup *save_statetable_; - SequentialGroupSeq save_cell_sequentials_; - LibertyFuncSeq save_cell_funcs_; static constexpr char escape_ = '\\'; private: friend class PortNameBitIterator; - friend class TimingGroup; -}; - -// Reference to a function that will be parsed at the end of the cell -// definition when all of the ports are defined. -class LibertyFunc -{ -public: - LibertyFunc(const char *expr, - LibertySetFunc set_func, - bool invert, - const char *attr_name, - int line); - ~LibertyFunc(); - const char *expr() const { return expr_; } - LibertySetFunc setFunc() const { return set_func_; } - bool invert() const { return invert_; } - const char *attrName() const { return attr_name_; } - int line() const { return line_; } - -protected: - const char *expr_; - LibertySetFunc set_func_; - bool invert_; - const char *attr_name_; - int line_; -}; - -// Port attributes that refer to other ports cannot be parsed -// until all of the ports are defined. This class saves them -// so they can be parsed at the end of the cell. -class PortGroup -{ -public: - PortGroup(LibertyPortSeq *ports, - int line); - ~PortGroup(); - LibertyPortSeq *ports() const { return ports_; } - TimingGroupSeq &timingGroups() { return timings_; } - void addTimingGroup(TimingGroup *timing); - InternalPowerGroupSeq &internalPowerGroups() { return internal_power_groups_; } - void addInternalPowerGroup(InternalPowerGroup *internal_power); - ReceiverModel *receiverModel() const { return receiver_model_; } - void setReceiverModel(ReceiverModelPtr receiver_model); - int line() const { return line_; } - -private: - LibertyPortSeq *ports_; - TimingGroupSeq timings_; - InternalPowerGroupSeq internal_power_groups_; - ReceiverModel *receiver_model_; - int line_; -}; - -// Liberty group with related_pins group attribute. -class RelatedPortGroup -{ -public: - explicit RelatedPortGroup(int line); - virtual ~RelatedPortGroup(); - int line() const { return line_; } - StringSeq *relatedPortNames() const { return related_port_names_; } - void setRelatedPortNames(StringSeq *names); - bool isOneToOne() const { return is_one_to_one_; } - void setIsOneToOne(bool one); - -protected: - StringSeq *related_port_names_; - bool is_one_to_one_; - int line_; -}; - -class SequentialGroup -{ -public: - SequentialGroup(bool is_register, - bool is_bank, - LibertyPort *out_port, - LibertyPort *out_inv_port, - int size, - int line); - ~SequentialGroup(); - LibertyPort *outPort() const { return out_port_; } - LibertyPort *outInvPort() const { return out_inv_port_; } - int size() const { return size_; } - bool isRegister() const { return is_register_; } - bool isBank() const { return is_bank_; } - const char *clock() const { return clk_; } - void setClock(const char *clk); - const char *data() const { return data_; } - void setData(const char *data); - const char *clear() const { return clear_; } - void setClear(const char *clr); - const char *preset() const { return preset_; } - void setPreset(const char *preset); - LogicValue clrPresetVar1() const { return clr_preset_var1_; } - void setClrPresetVar1(LogicValue var); - LogicValue clrPresetVar2() const { return clr_preset_var2_; } - void setClrPresetVar2(LogicValue var); - int line() const { return line_; } - -protected: - bool is_register_; - bool is_bank_; - LibertyPort *out_port_; - LibertyPort *out_inv_port_; - int size_; - const char *clk_; - const char *data_; - const char *preset_; - const char *clear_; - LogicValue clr_preset_var1_; - LogicValue clr_preset_var2_; - int line_; -}; - -class GeneratedClockGroup -{ -public: - GeneratedClockGroup(); - ~GeneratedClockGroup(); - const char *name() const { return name_; } - void setName(const char *name); - const char *clockPin() const { return clock_pin_; } - void setClockPin(const char *clockPin); - const char *masterPin() const { return master_pin_; } - void setMasterPin(const char *masterPin); - int dividedBy() const { return divided_by_; } - void setDividedBy(int dividedBy) { divided_by_ = dividedBy; } - int multipliedBy() const { return multiplied_by_; } - void setMultipliedBy(int multipliedBy) { multiplied_by_ = multipliedBy; } - float dutyCycle() const { return duty_cycle_; } - void setDutyCycle(float dutyCycle) { duty_cycle_ = dutyCycle; } - bool invert() const { return invert_; } - void setInvert(bool invert) { invert_ = invert; } - IntSeq *edges() const { return edges_; } - void setEdges(IntSeq *edges) { edges_ = edges; } - FloatSeq *edgeShifts() const { return edge_shifts_; } - void setEdgeShifts(FloatSeq *edgeShifts) { edge_shifts_ = edgeShifts; } - -protected: - const char *name_; - const char *clock_pin_; - const char *master_pin_; - int divided_by_; - int multiplied_by_; - float duty_cycle_; - bool invert_; - IntSeq *edges_; - FloatSeq *edge_shifts_; -}; - -class StatetableGroup -{ -public: - StatetableGroup(StdStringSeq &input_ports, - StdStringSeq &internal_ports, - int line); - const StdStringSeq &inputPorts() const { return input_ports_; } - const StdStringSeq &internalPorts() const { return internal_ports_; } - void addRow(StateInputValues &input_values, - StateInternalValues ¤t_values, - StateInternalValues &next_values); - StatetableRows &table() { return table_; } - int line() const { return line_; } - -private: - StdStringSeq input_ports_; - StdStringSeq internal_ports_; - StatetableRows table_; - int line_; -}; - -class TimingGroup : public RelatedPortGroup -{ -public: - explicit TimingGroup(int line); - virtual ~TimingGroup(); - TimingArcAttrsPtr attrs() { return attrs_; } - const char *relatedOutputPortName()const {return related_output_port_name_;} - void setRelatedOutputPortName(const char *name); - void intrinsic(const RiseFall *rf, - // Return values. - float &value, - bool &exists); - void setIntrinsic(const RiseFall *rf, - float value); - void resistance(const RiseFall *rf, - // Return values. - float &value, - bool &exists); - void setResistance(const RiseFall *rf, - float value); - TableModel *cell(const RiseFall *rf); - void setCell(const RiseFall *rf, - TableModel *model); - TableModel *constraint(const RiseFall *rf); - void setConstraint(const RiseFall *rf, - TableModel *model); - TableModel *transition(const RiseFall *rf); - void setTransition(const RiseFall *rf, - TableModel *model); - void makeTimingModels(LibertyCell *cell, - LibertyReader *visitor); - void setDelaySigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model); - void setSlewSigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model); - void setConstraintSigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model); - void setReceiverModel(ReceiverModelPtr receiver_model); - OutputWaveforms *outputWaveforms(const RiseFall *rf); - void setOutputWaveforms(const RiseFall *rf, - OutputWaveforms *output_current); - -protected: - void makeLinearModels(LibertyCell *cell); - void makeTableModels(LibertyCell *cell, - LibertyReader *reader); - - TimingArcAttrsPtr attrs_; - const char *related_output_port_name_; - float intrinsic_[RiseFall::index_count]; - bool intrinsic_exists_[RiseFall::index_count]; - float resistance_[RiseFall::index_count]; - bool resistance_exists_[RiseFall::index_count]; - TableModel *cell_[RiseFall::index_count]; - TableModel *constraint_[RiseFall::index_count]; - TableModel *constraint_sigma_[RiseFall::index_count][EarlyLate::index_count]; - TableModel *transition_[RiseFall::index_count]; - TableModel *delay_sigma_[RiseFall::index_count][EarlyLate::index_count]; - TableModel *slew_sigma_[RiseFall::index_count][EarlyLate::index_count]; - OutputWaveforms *output_waveforms_[RiseFall::index_count]; - ReceiverModelPtr receiver_model_; -}; - -class InternalPowerGroup : public InternalPowerAttrs, public RelatedPortGroup -{ -public: - explicit InternalPowerGroup(int line); - virtual ~InternalPowerGroup(); -}; - -class LeakagePowerGroup -{ -public: - LeakagePowerGroup(int line); - const std::string &relatedPgPin() const { return related_pg_pin_; } - void setRelatedPgPin(std::string pin_name); - FuncExpr *when() const { return when_; } - void setWhen(FuncExpr *when); - float power() const { return power_; } - void setPower(float power); - -protected: - std::string related_pg_pin_; - FuncExpr *when_; - float power_; - int line_; }; // Named port iterator. Port name can be: @@ -991,51 +566,50 @@ class PortNameBitIterator : public Iterator { public: PortNameBitIterator(LibertyCell *cell, - const char *port_name, - LibertyReader *visitor, - int line); - ~PortNameBitIterator(); - virtual bool hasNext(); - virtual LibertyPort *next(); + std::string_view port_name, + LibertyReader *visitor, + int line); + ~PortNameBitIterator() override; + bool hasNext() override; + LibertyPort *next() override; unsigned size() const { return size_; } protected: void findRangeBusNameNext(); - void init(const char *port_name); + void init(std::string_view port_name); LibertyCell *cell_; LibertyReader *visitor_; int line_; - LibertyPort *port_; - LibertyPortMemberIterator *bit_iterator_; - LibertyPort *range_bus_port_; + LibertyPort *port_{nullptr}; + LibertyPortMemberIterator *bit_iterator_{nullptr}; + LibertyPort *range_bus_port_{nullptr}; std::string range_bus_name_; - LibertyPort *range_name_next_; - int range_from_; - int range_to_; - int range_bit_; - unsigned size_; + LibertyPort *range_name_next_{nullptr}; + int range_from_{0}; + int range_to_{0}; + int range_bit_{0}; + unsigned size_{0}; }; class OutputWaveform { public: - OutputWaveform(float axis_value1, - float axis_value2, - Table1 *currents, + OutputWaveform(float slew, + float cap, + Table *currents, float reference_time); - ~OutputWaveform(); float slew() const { return slew_; } float cap() const { return cap_; } - Table1 *currents() const { return currents_; } - Table1 *stealCurrents(); + Table *releaseCurrents(); + float referenceTime() { return reference_time_; } private: float slew_; float cap_; - Table1 *currents_; + std::unique_ptr
currents_; float reference_time_; }; -} // namespace +} // namespace sta diff --git a/liberty/LibertyScanner.hh b/liberty/LibertyScanner.hh index c53d3f882..06c9ef5ef 100644 --- a/liberty/LibertyScanner.hh +++ b/liberty/LibertyScanner.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,7 +24,10 @@ #pragma once -#include "LibertyLocation.hh" +#include +#include +#include + #include "LibertyParse.hh" #ifndef __FLEX_LEXER_H @@ -42,12 +45,10 @@ class LibertyScanner : public LibertyFlexLexer { public: LibertyScanner(std::istream *stream, - const char *filename, + std::string_view filename, LibertyParser *reader, Report *report); - virtual ~LibertyScanner() {} - - virtual int lex(LibertyParse::semantic_type *const yylval, + virtual int lex(LibertyParse::semantic_type *yylval, LibertyParse::location_type *yylloc); // YY_DECL defined in LibertyLex.ll // Method body created by flex in LibertyLex.cc @@ -68,7 +69,7 @@ private: // Previous lex state for include files. std::string filename_prev_; - std::istream *stream_prev_; + std::istream *stream_prev_{nullptr}; }; -} // namespace +} // namespace sta diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc index 6252ee22b..bebb36581 100644 --- a/liberty/LibertyWriter.cc +++ b/liberty/LibertyWriter.cc @@ -1,32 +1,39 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "LibertyWriter.hh" +#include #include -#include - +#include +#include + +#include "Error.hh" +#include "Format.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "Transition.hh" #include "Units.hh" #include "FuncExpr.hh" #include "PortDirection.hh" @@ -39,14 +46,12 @@ namespace sta { -using std::abs; - class LibertyWriter { public: LibertyWriter(const LibertyLibrary *lib, const char *filename, - FILE *stream, + std::ofstream &stream, Report *report); void writeLibrary(); @@ -82,22 +87,41 @@ class LibertyWriter const LibertyLibrary *library_; const char *filename_; - FILE *stream_; + std::ofstream &stream_; Report *report_; const Unit *time_unit_; const Unit *cap_unit_; }; +std::string +portStaToLiberty(std::string_view sta_name) +{ + std::string liberty_name; + for (size_t i = 0; i < sta_name.length(); i += 1) { + char ch = sta_name[i]; + if (ch == '\\') { + char next_ch = sta_name[i + 1]; + if (next_ch == '\\') { + liberty_name += ch; + liberty_name += next_ch; + i++; + } + } + else + liberty_name += ch; + } + return liberty_name; +} + void writeLiberty(LibertyLibrary *lib, const char *filename, StaState *sta) { - FILE *stream = fopen(filename, "w"); - if (stream) { + std::ofstream stream(filename); + if (stream.is_open()) { LibertyWriter writer(lib, filename, stream, sta->report()); writer.writeLibrary(); - fclose(stream); } else throw FileNotWritable(filename); @@ -105,7 +129,7 @@ writeLiberty(LibertyLibrary *lib, LibertyWriter::LibertyWriter(const LibertyLibrary *lib, const char *filename, - FILE *stream, + std::ofstream &stream, Report *report) : library_(lib), filename_(filename), @@ -120,87 +144,87 @@ void LibertyWriter::writeLibrary() { writeHeader(); - fprintf(stream_, "\n"); + sta::print(stream_, "\n"); writeTableTemplates(); writeBusDcls(); - fprintf(stream_, "\n"); + sta::print(stream_, "\n"); writeCells(); writeFooter(); } - + void LibertyWriter::writeHeader() { - fprintf(stream_, "library (%s) {\n", library_->name()); - fprintf(stream_, " comment : \"\";\n"); - fprintf(stream_, " delay_model : table_lookup;\n"); - fprintf(stream_, " simulation : false;\n"); + sta::print(stream_, "library ({}) {{\n", library_->name()); + sta::print(stream_, " comment : \"\";\n"); + sta::print(stream_, " delay_model : table_lookup;\n"); + sta::print(stream_, " simulation : false;\n"); const Unit *cap_unit = library_->units()->capacitanceUnit(); - fprintf(stream_, " capacitive_load_unit (1,%s);\n", - cap_unit->scaleAbbrevSuffix().c_str()); - fprintf(stream_, " leakage_power_unit : 1pW;\n"); + sta::print(stream_, " capacitive_load_unit (1,{});\n", + cap_unit->scaleAbbrevSuffix()); + sta::print(stream_, " leakage_power_unit : 1pW;\n"); const Unit *current_unit = library_->units()->currentUnit(); - fprintf(stream_, " current_unit : \"1%s\";\n", - current_unit->scaleAbbrevSuffix().c_str()); + sta::print(stream_, " current_unit : \"1{}\";\n", + current_unit->scaleAbbrevSuffix()); const Unit *res_unit = library_->units()->resistanceUnit(); - fprintf(stream_, " pulling_resistance_unit : \"1%s\";\n", - res_unit->scaleAbbrevSuffix().c_str()); + sta::print(stream_, " pulling_resistance_unit : \"1{}\";\n", + res_unit->scaleAbbrevSuffix()); const Unit *time_unit = library_->units()->timeUnit(); - fprintf(stream_, " time_unit : \"1%s\";\n", - time_unit->scaleAbbrevSuffix().c_str()); + sta::print(stream_, " time_unit : \"1{}\";\n", + time_unit->scaleAbbrevSuffix()); const Unit *volt_unit = library_->units()->voltageUnit(); - fprintf(stream_, " voltage_unit : \"1%s\";\n", - volt_unit->scaleAbbrevSuffix().c_str()); - fprintf(stream_, " library_features(report_delay_calculation);\n"); - fprintf(stream_, "\n"); - - fprintf(stream_, " input_threshold_pct_rise : %.0f;\n", - library_->inputThreshold(RiseFall::rise()) * 100); - fprintf(stream_, " input_threshold_pct_fall : %.0f;\n", - library_->inputThreshold(RiseFall::fall()) * 100); - fprintf(stream_, " output_threshold_pct_rise : %.0f;\n", - library_->inputThreshold(RiseFall::rise()) * 100); - fprintf(stream_, " output_threshold_pct_fall : %.0f;\n", - library_->inputThreshold(RiseFall::fall()) * 100); - fprintf(stream_, " slew_lower_threshold_pct_rise : %.0f;\n", - library_->slewLowerThreshold(RiseFall::rise()) * 100); - fprintf(stream_, " slew_lower_threshold_pct_fall : %.0f;\n", - library_->slewLowerThreshold(RiseFall::fall()) * 100); - fprintf(stream_, " slew_upper_threshold_pct_rise : %.0f;\n", - library_->slewUpperThreshold(RiseFall::rise()) * 100); - fprintf(stream_, " slew_upper_threshold_pct_fall : %.0f;\n", - library_->slewUpperThreshold(RiseFall::rise()) * 100); - fprintf(stream_, " slew_derate_from_library : %.1f;\n", - library_->slewDerateFromLibrary()); - fprintf(stream_, "\n"); + sta::print(stream_, " voltage_unit : \"1{}\";\n", + volt_unit->scaleAbbrevSuffix()); + sta::print(stream_, " library_features(report_delay_calculation);\n"); + sta::print(stream_, "\n"); + + sta::print(stream_, " input_threshold_pct_rise : {:.0f};\n", + library_->inputThreshold(RiseFall::rise()) * 100); + sta::print(stream_, " input_threshold_pct_fall : {:.0f};\n", + library_->inputThreshold(RiseFall::fall()) * 100); + sta::print(stream_, " output_threshold_pct_rise : {:.0f};\n", + library_->inputThreshold(RiseFall::rise()) * 100); + sta::print(stream_, " output_threshold_pct_fall : {:.0f};\n", + library_->inputThreshold(RiseFall::fall()) * 100); + sta::print(stream_, " slew_lower_threshold_pct_rise : {:.0f};\n", + library_->slewLowerThreshold(RiseFall::rise()) * 100); + sta::print(stream_, " slew_lower_threshold_pct_fall : {:.0f};\n", + library_->slewLowerThreshold(RiseFall::fall()) * 100); + sta::print(stream_, " slew_upper_threshold_pct_rise : {:.0f};\n", + library_->slewUpperThreshold(RiseFall::rise()) * 100); + sta::print(stream_, " slew_upper_threshold_pct_fall : {:.0f};\n", + library_->slewUpperThreshold(RiseFall::rise()) * 100); + sta::print(stream_, " slew_derate_from_library : {:.1f};\n", + library_->slewDerateFromLibrary()); + sta::print(stream_, "\n"); bool exists; float max_fanout; library_->defaultFanoutLoad(max_fanout, exists); if (exists) - fprintf(stream_, " default_max_fanout : %.0f;\n", max_fanout); + sta::print(stream_, " default_max_fanout : {:.0f};\n", max_fanout); float max_slew; library_->defaultMaxSlew(max_slew, exists); if (exists) - fprintf(stream_, " default_max_transition : %s;\n", - time_unit_->asString(max_slew, 3)); + sta::print(stream_, " default_max_transition : {};\n", + time_unit_->asString(max_slew, 3)); float max_cap; library_->defaultMaxCapacitance(max_cap, exists); if (exists) - fprintf(stream_, " default_max_capacitance : %s;\n", - cap_unit_->asString(max_cap, 3)); + sta::print(stream_, " default_max_capacitance : {};\n", + cap_unit_->asString(max_cap, 3)); float fanout_load; library_->defaultFanoutLoad(fanout_load, exists); if (exists) - fprintf(stream_, " default_fanout_load : %.2f;\n", fanout_load); - fprintf(stream_, "\n"); - - fprintf(stream_, " nom_process : %.1f;\n", - library_->nominalProcess()); - fprintf(stream_, " nom_temperature : %.1f;\n", - library_->nominalTemperature()); - fprintf(stream_, " nom_voltage : %.2f;\n", - library_->nominalVoltage()); + sta::print(stream_, " default_fanout_load : {:.2f};\n", fanout_load); + sta::print(stream_, "\n"); + + sta::print(stream_, " nom_process : {:.1f};\n", + library_->nominalProcess()); + sta::print(stream_, " nom_temperature : {:.1f};\n", + library_->nominalTemperature()); + sta::print(stream_, " nom_voltage : {:.2f};\n", + library_->nominalVoltage()); } void @@ -218,22 +242,22 @@ LibertyWriter::writeTableTemplate(const TableTemplate *tbl_template) const TableAxis *axis3 = tbl_template->axis3(); // skip scalar templates if (axis1) { - fprintf(stream_, " lu_table_template(%s) {\n", tbl_template->name()); - fprintf(stream_, " variable_1 : %s;\n", - tableVariableString(axis1->variable())); + sta::print(stream_, " lu_table_template({}) {{\n", tbl_template->name()); + sta::print(stream_, " variable_1 : {};\n", + tableVariableString(axis1->variable())); if (axis2) - fprintf(stream_, " variable_2 : %s;\n", - tableVariableString(axis2->variable())); + sta::print(stream_, " variable_2 : {};\n", + tableVariableString(axis2->variable())); if (axis3) - fprintf(stream_, " variable_3 : %s;\n", - tableVariableString(axis3->variable())); - if (axis1 && axis1->values()) + sta::print(stream_, " variable_3 : {};\n", + tableVariableString(axis3->variable())); + if (axis1 && !axis1->values().empty()) writeTableAxis4(axis1, 1); - if (axis2 && axis2->values()) + if (axis2 && !axis2->values().empty()) writeTableAxis4(axis2, 2); - if (axis3 && axis3->values()) + if (axis3 && !axis3->values().empty()) writeTableAxis4(axis3, 3); - fprintf(stream_, " }\n"); + sta::print(stream_, " }}\n"); } } @@ -242,16 +266,16 @@ void LibertyWriter::writeTableAxis4(const TableAxis *axis, int index) { - fprintf(stream_, " index_%d(\"", index); + sta::print(stream_, " index_{}(\"", index); const Unit *unit = tableVariableUnit(axis->variable(), library_->units()); bool first = true; for (size_t i = 0; i < axis->size(); i++) { if (!first) - fprintf(stream_, ", "); - fprintf(stream_, "%s", unit->asString(axis->axisValue(i), 5)); + sta::print(stream_, ", "); + sta::print(stream_, "{}", unit->asString(axis->axisValue(i), 5)); first = false; } - fprintf(stream_, "\");\n"); + sta::print(stream_, "\");\n"); } // indent 10 @@ -259,7 +283,7 @@ void LibertyWriter::writeTableAxis10(const TableAxis *axis, int index) { - fprintf(stream_, " "); + sta::print(stream_, " "); writeTableAxis4(axis, index); } @@ -268,13 +292,13 @@ LibertyWriter::writeBusDcls() { BusDclSeq dcls = library_->busDcls(); for (BusDcl *dcl : dcls) { - fprintf(stream_, " type (\"%s\") {\n", portStaToLiberty(dcl->name()).c_str()); - fprintf(stream_, " base_type : array;\n"); - fprintf(stream_, " data_type : bit;\n"); - fprintf(stream_, " bit_width : %d;\n", abs(dcl->from() - dcl->to() + 1)); - fprintf(stream_, " bit_from : %d;\n", dcl->from()); - fprintf(stream_, " bit_to : %d;\n", dcl->to()); - fprintf(stream_, " }\n"); + sta::print(stream_, " type (\"{}\") {{\n", portStaToLiberty(dcl->name())); + sta::print(stream_, " base_type : array;\n"); + sta::print(stream_, " data_type : bit;\n"); + sta::print(stream_, " bit_width : {};\n", std::abs(dcl->from() - dcl->to() + 1)); + sta::print(stream_, " bit_from : {};\n", dcl->from()); + sta::print(stream_, " bit_to : {};\n", dcl->to()); + sta::print(stream_, " }}\n"); } } @@ -291,49 +315,47 @@ LibertyWriter::writeCells() void LibertyWriter::writeCell(const LibertyCell *cell) { - fprintf(stream_, " cell (\"%s\") {\n", portStaToLiberty(cell->name()).c_str()); + sta::print(stream_, " cell (\"{}\") {{\n", portStaToLiberty(cell->name())); float area = cell->area(); if (area > 0.0) - fprintf(stream_, " area : %.3f \n", area); + sta::print(stream_, " area : {:.3f} \n", area); if (cell->isMacro()) - fprintf(stream_, " is_macro_cell : true;\n"); + sta::print(stream_, " is_macro_cell : true;\n"); if (cell->interfaceTiming()) - fprintf(stream_, " interface_timing : true;\n"); - const char *footprint = cell->footprint(); - if (footprint) - fprintf(stream_, " cell_footprint : \"%s\";\n", footprint); - const char *user_function_class = cell->userFunctionClass(); - if (user_function_class) - fprintf(stream_, " user_function_class : \"%s\";\n", - user_function_class); + sta::print(stream_, " interface_timing : true;\n"); + const std::string &footprint = cell->footprint(); + if (!footprint.empty()) + sta::print(stream_, " cell_footprint : \"{}\";\n", footprint); + const std::string &user_function_class = cell->userFunctionClass(); + if (!user_function_class.empty()) + sta::print(stream_, " user_function_class : \"{}\";\n", user_function_class); LibertyCellPortIterator port_iter(cell); while (port_iter.hasNext()) { const LibertyPort *port = port_iter.next(); if (!port->direction()->isInternal()) { if (port->isPwrGnd()) - writePwrGndPort(port); + writePwrGndPort(port); else if (port->isBus()) writeBusPort(port); else if (port->isBundle()) - report_->error(1340, "%s/%s bundled ports not supported.", - library_->name(), + report_->error(1340, "{}/{} bundled ports not supported.", library_->name(), cell->name()); else writePort(port); } } - fprintf(stream_, " }\n"); - fprintf(stream_, "\n"); + sta::print(stream_, " }}\n"); + sta::print(stream_, "\n"); } void LibertyWriter::writeBusPort(const LibertyPort *port) { - fprintf(stream_, " bus(\"%s\") {\n", portStaToLiberty(port->name()).c_str()); + sta::print(stream_, " bus(\"{}\") {{\n", portStaToLiberty(port->name())); if (port->busDcl()) - fprintf(stream_, " bus_type : %s;\n", portStaToLiberty(port->busDcl()->name()).c_str()); + sta::print(stream_, " bus_type : {};\n", portStaToLiberty(port->busDcl()->name())); writePortAttrs(port); LibertyPortMemberIterator member_iter(port); @@ -341,58 +363,55 @@ LibertyWriter::writeBusPort(const LibertyPort *port) LibertyPort *member = member_iter.next(); writePort(member); } - fprintf(stream_, " }\n"); + sta::print(stream_, " }}\n"); } void LibertyWriter::writePort(const LibertyPort *port) { - fprintf(stream_, " pin(\"%s\") {\n", portStaToLiberty(port->name()).c_str()); + sta::print(stream_, " pin(\"{}\") {{\n", portStaToLiberty(port->name())); writePortAttrs(port); - fprintf(stream_, " }\n"); + sta::print(stream_, " }}\n"); } void LibertyWriter::writePortAttrs(const LibertyPort *port) { - fprintf(stream_, " direction : %s;\n" , asString(port->direction())); + sta::print(stream_, " direction : {};\n", asString(port->direction())); auto func = port->function(); if (func // cannot ref internal ports until sequentials are written - && !(func->port() - && func->port()->direction()->isInternal())) - fprintf(stream_, " function : \"%s\";\n", - portStaToLiberty(func->to_string().c_str()).c_str()); + && !(func->port() && func->port()->direction()->isInternal())) + sta::print(stream_, " function : \"{}\";\n", portStaToLiberty(func->to_string().c_str())); auto tristate_enable = port->tristateEnable(); if (tristate_enable) { - if (tristate_enable->op() == FuncExpr::op_not) { + if (tristate_enable->op() == FuncExpr::Op::not_) { FuncExpr *three_state = tristate_enable->left(); - fprintf(stream_, " three_state : \"%s\";\n", - portStaToLiberty(three_state->to_string().c_str()).c_str()); + sta::print(stream_, " three_state : \"{}\";\n", + portStaToLiberty(three_state->to_string().c_str())); } else { - FuncExpr three_state(FuncExpr::op_not, tristate_enable, nullptr, nullptr); - fprintf(stream_, " three_state : \"%s\";\n", - portStaToLiberty(three_state.to_string().c_str()).c_str()); + FuncExpr *three_state = tristate_enable->copy()->invert(); + sta::print(stream_, " three_state : \"{}\";\n", + portStaToLiberty(three_state->to_string().c_str())); + delete three_state; } } if (port->isClock()) - fprintf(stream_, " clock : true;\n"); - fprintf(stream_, " capacitance : %s;\n", - cap_unit_->asString(port->capacitance(), 4)); - + sta::print(stream_, " clock : true;\n"); + sta::print(stream_, " capacitance : {};\n", + cap_unit_->asString(port->capacitance(), 4)); + float limit; bool exists; port->slewLimit(MinMax::max(), limit, exists); if (exists) - fprintf(stream_, " max_transition : %s;\n", - time_unit_->asString(limit, 3)); + sta::print(stream_, " max_transition : {};\n", time_unit_->asString(limit, 3)); port->capacitanceLimit(MinMax::max(), limit, exists); if (exists) - fprintf(stream_, " max_capacitance : %s;\n", - cap_unit_->asString(limit, 3)); + sta::print(stream_, " max_capacitance : {};\n", cap_unit_->asString(limit, 3)); - for (TimingArcSet *arc_set : port->libertyCell()->timingArcSets(nullptr,port)) { + for (TimingArcSet *arc_set : port->libertyCell()->timingArcSetsTo(port)) { if (!isAutoWidthArc(port, arc_set)) writeTimingArcSet(arc_set); } @@ -401,10 +420,10 @@ LibertyWriter::writePortAttrs(const LibertyPort *port) void LibertyWriter::writePwrGndPort(const LibertyPort *port) { - fprintf(stream_, " pg_pin(\"%s\") {\n", portStaToLiberty(port->name()).c_str()); - fprintf(stream_, " pg_type : \"%s\";\n", pwrGndTypeName(port->pwrGndType())); - fprintf(stream_, " voltage_name : \"%s\";\n", port->voltageName()); - fprintf(stream_, " }\n"); + sta::print(stream_, " pg_pin(\"{}\") {{\n", portStaToLiberty(port->name())); + sta::print(stream_, " pg_type : \"{}\";\n", pwrGndTypeName(port->pwrGndType())); + sta::print(stream_, " voltage_name : \"{}\";\n", port->voltageName()); + sta::print(stream_, " }}\n"); } // Check if arc is added for port min_pulse_width_high/low attribute. @@ -425,31 +444,27 @@ LibertyWriter::isAutoWidthArc(const LibertyPort *port, void LibertyWriter::writeTimingArcSet(const TimingArcSet *arc_set) { - fprintf(stream_, " timing() {\n"); + sta::print(stream_, " timing() {{\n"); if (arc_set->from()) - fprintf(stream_, " related_pin : \"%s\";\n", - portStaToLiberty(arc_set->from()->name()).c_str()); + sta::print(stream_, " related_pin : \"{}\";\n", portStaToLiberty(arc_set->from()->name())); TimingSense sense = arc_set->sense(); - if (sense != TimingSense::unknown - && sense != TimingSense::non_unate) - fprintf(stream_, " timing_sense : %s;\n", - to_string(sense)); + if (sense != TimingSense::unknown && sense != TimingSense::non_unate) + sta::print(stream_, " timing_sense : {};\n", to_string(sense)); const char *timing_type = timingTypeString(arc_set); if (timing_type) - fprintf(stream_, " timing_type : %s;\n", timing_type); + sta::print(stream_, " timing_type : {};\n", timing_type); for (const RiseFall *rf : RiseFall::range()) { TimingArc *arc = arc_set->arcTo(rf); if (arc) { // Min pulse width arcs are wrt to the leading edge of the pulse. - const RiseFall *model_rf = (arc_set->role() == TimingRole::width()) - ? rf->opposite() - : rf; + const RiseFall *model_rf = + (arc_set->role() == TimingRole::width()) ? rf->opposite() : rf; writeTimingModels(arc, model_rf); } } - fprintf(stream_, " }\n"); + sta::print(stream_, " }}\n"); } void @@ -457,53 +472,55 @@ LibertyWriter::writeTimingModels(const TimingArc *arc, const RiseFall *rf) { TimingModel *model = arc->model(); - const GateTableModel *gate_model = dynamic_cast(model); - const CheckTableModel *check_model = dynamic_cast(model); + const GateTableModel *gate_model = dynamic_cast(model); + const CheckTableModel *check_model = dynamic_cast(model); if (gate_model) { const TableModel *delay_model = gate_model->delayModel(); - const char *template_name = delay_model->tblTemplate()->name(); - fprintf(stream_, " cell_%s(%s) {\n", rf->name(), template_name); + const std::string &template_name = delay_model->tblTemplate()->name(); + sta::print(stream_, " cell_{}({}) {{\n", rf->name(), template_name); writeTableModel(delay_model); - fprintf(stream_, " }\n"); + sta::print(stream_, " }}\n"); const TableModel *slew_model = gate_model->slewModel(); if (slew_model) { - template_name = slew_model->tblTemplate()->name(); - fprintf(stream_, " %s_transition(%s) {\n", rf->name(), template_name); + const std::string &slew_template_name = slew_model->tblTemplate()->name(); + sta::print(stream_, " {}_transition({}) {{\n", rf->name(), + slew_template_name); writeTableModel(slew_model); - fprintf(stream_, " }\n"); + sta::print(stream_, " }}\n"); } } else if (check_model) { - const TableModel *model = check_model->model(); - const char *template_name = model->tblTemplate()->name(); - fprintf(stream_, " %s_constraint(%s) {\n", rf->name(), template_name); + const TableModel *model = check_model->checkModel(); + const std::string &template_name = model->tblTemplate()->name(); + sta::print(stream_, " {}_constraint({}) {{\n", rf->name(), + template_name); writeTableModel(model); - fprintf(stream_, " }\n"); + sta::print(stream_, " }}\n"); } else - report_->error(1341, "%s/%s/%s timing model not supported.", - library_->name(), - arc->from()->libertyCell()->name(), - arc->from()->name()); + report_->error(1341, "{}/{}/{} timing model not supported.", library_->name(), + arc->from()->libertyCell()->name(), arc->from()->name()); } void LibertyWriter::writeTableModel(const TableModel *model) { switch (model->order()) { - case 0: - writeTableModel0(model); - break; - case 1: - writeTableModel1(model); - break; - case 2: - writeTableModel2(model); - break; - case 3: - report_->error(1342, "3 axis table models not supported."); - break; + case 0: + writeTableModel0(model); + break; + case 1: + writeTableModel1(model); + break; + case 2: + writeTableModel2(model); + break; + case 3: + report_->error(1342, "3 axis table models not supported."); + break; + default: + break; } } @@ -511,24 +528,23 @@ void LibertyWriter::writeTableModel0(const TableModel *model) { float value = model->value(0, 0, 0); - fprintf(stream_, " values(\"%s\");\n", - time_unit_->asString(value, 5)); + sta::print(stream_, " values(\"{}\");\n", time_unit_->asString(value, 5)); } void LibertyWriter::writeTableModel1(const TableModel *model) { writeTableAxis10(model->axis1(), 1); - fprintf(stream_, " values(\""); + sta::print(stream_, " values(\""); bool first_col = true; for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) { float value = model->value(index1, 0, 0); if (!first_col) - fprintf(stream_, ","); - fprintf(stream_, "%s", time_unit_->asString(value, 5)); + sta::print(stream_, ","); + sta::print(stream_, "{}", time_unit_->asString(value, 5)); first_col = false; } - fprintf(stream_, "\");\n"); + sta::print(stream_, "\");\n"); } void @@ -536,31 +552,31 @@ LibertyWriter::writeTableModel2(const TableModel *model) { writeTableAxis10(model->axis1(), 1); writeTableAxis10(model->axis2(), 2); - fprintf(stream_, " values(\""); + sta::print(stream_, " values(\""); bool first_row = true; for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) { if (!first_row) { - fprintf(stream_, "\\\n"); - fprintf(stream_, " \""); + sta::print(stream_, "\\\n"); + sta::print(stream_, " \""); } bool first_col = true; for (size_t index2 = 0; index2 < model->axis2()->size(); index2++) { float value = model->value(index1, index2, 0); if (!first_col) - fprintf(stream_, ","); - fprintf(stream_, "%s", time_unit_->asString(value, 5)); + sta::print(stream_, ","); + sta::print(stream_, "{}", time_unit_->asString(value, 5)); first_col = false; } - fprintf(stream_, "\""); + sta::print(stream_, "\""); first_row = false; } - fprintf(stream_, ");\n"); + sta::print(stream_, ");\n"); } void LibertyWriter::writeFooter() { - fprintf(stream_, "}\n"); + sta::print(stream_, "}}\n"); } const char * @@ -574,15 +590,15 @@ LibertyWriter::asString(const PortDirection *dir) { if (dir == PortDirection::input()) return "input"; - else if (dir == PortDirection::output() - || (dir == PortDirection::tristate())) + else if (dir == PortDirection::output() || (dir == PortDirection::tristate())) return "output"; else if (dir == PortDirection::internal()) return "internal"; else if (dir == PortDirection::bidirect()) return "inout"; else if (dir == PortDirection::ground() - || dir == PortDirection::power()) + || dir == PortDirection::power() + || dir == PortDirection::well()) return "input"; return "unknown"; } @@ -597,8 +613,7 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set) return "three_state_disable"; else if (role == TimingRole::tristateEnable()) return "three_state_enable"; - else if (role == TimingRole::regClkToQ() - || role == TimingRole::latchEnToQ()) { + else if (role == TimingRole::regClkToQ() || role == TimingRole::latchEnToQ()) { const TimingArc *arc = arc_set->arcs()[0]; if (arc->fromEdge()->asRiseFall() == RiseFall::rise()) return "rising_edge"; @@ -614,16 +629,14 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set) else return "clear"; } - else if (role == TimingRole::setup() - || role == TimingRole::recovery()) { + else if (role == TimingRole::setup() || role == TimingRole::recovery()) { const TimingArc *arc = arc_set->arcs()[0]; if (arc->fromEdge()->asRiseFall() == RiseFall::rise()) return "setup_rising"; else return "setup_falling"; } - else if (role == TimingRole::hold() - || role == TimingRole::removal()) { + else if (role == TimingRole::hold() || role == TimingRole::removal()) { const TimingArc *arc = arc_set->arcs()[0]; if (arc->fromEdge()->asRiseFall() == RiseFall::rise()) return "hold_rising"; @@ -651,13 +664,11 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set) else if (role == TimingRole::width()) return "min_pulse_width"; else { - report_->error(1343, "%s/%s/%s timing arc type %s not supported.", - library_->name(), - arc_set->to()->libertyCell()->name(), - arc_set->to()->name(), - role->to_string().c_str()); + report_->error(1343, "{}/{}/{} timing arc type {} not supported.", + library_->name(), arc_set->to()->libertyCell()->name(), + arc_set->to()->name(), role->to_string()); return nullptr; } } -} // namespace +} // namespace sta diff --git a/liberty/LinearModel.cc b/liberty/LinearModel.cc index fbb47d9eb..d64bfc276 100644 --- a/liberty/LinearModel.cc +++ b/liberty/LinearModel.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,16 +24,22 @@ #include "LinearModel.hh" +#include +#include + +#include "Delay.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "PocvMode.hh" +#include "TimingModel.hh" #include "Units.hh" #include "Liberty.hh" namespace sta { -using std::string; - GateLinearModel::GateLinearModel(LibertyCell *cell, float intrinsic, - float resistance) : + float resistance) : GateTimingModel(cell), intrinsic_(intrinsic), resistance_(resistance) @@ -42,30 +48,42 @@ GateLinearModel::GateLinearModel(LibertyCell *cell, void GateLinearModel::gateDelay(const Pvt *, - float, - float load_cap, - bool, - // return values - ArcDelay &gate_delay, - Slew &drvr_slew) const + float, + float load_cap, + // return values + float &gate_delay, + float &drvr_slew) const { gate_delay = intrinsic_ + resistance_ * load_cap; drvr_slew = 0.0; } -string +void +GateLinearModel::gateDelayPocv(const Pvt *, + float, + float, + const MinMax *, + PocvMode, + // return values + ArcDelay &, + Slew &) const +{ +} + +std::string GateLinearModel::reportGateDelay(const Pvt *, - float, - float load_cap, - bool, - int digits) const + float, + float load_cap, + const MinMax *, + PocvMode, + int digits) const { const LibertyLibrary *library = cell_->libertyLibrary(); const Units *units = library->units(); const Unit *time_unit = units->timeUnit(); const Unit *res_unit = units->resistanceUnit(); const Unit *cap_unit = units->capacitanceUnit(); - string result = "Delay = "; + std::string result = "Delay = "; result += time_unit->asString(intrinsic_, digits); result += " + "; result += res_unit->asString(resistance_, digits); @@ -97,27 +115,29 @@ CheckLinearModel::CheckLinearModel(LibertyCell *cell, ArcDelay CheckLinearModel::checkDelay(const Pvt *, - float, - float, - float, - bool) const + float, + float, + float, + const MinMax *, + PocvMode) const { return intrinsic_; } -string +std::string CheckLinearModel::reportCheckDelay(const Pvt *, - float, - const char *, - float, - float, - bool, - int digits) const + float, + std::string_view, + float, + float, + const MinMax *, + PocvMode, + int digits) const { const LibertyLibrary *library = cell_->libertyLibrary(); const Units *units = library->units(); const Unit *time_unit = units->timeUnit(); - string result = "Check = "; + std::string result = "Check = "; result += time_unit->asString(intrinsic_, digits); return result; } @@ -127,4 +147,4 @@ CheckLinearModel::setIsScaled(bool) { } -} // namespace +} // namespace sta diff --git a/liberty/Sequential.cc b/liberty/Sequential.cc index 559c61587..0fffdbe2a 100644 --- a/liberty/Sequential.cc +++ b/liberty/Sequential.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,18 +25,20 @@ #include "Sequential.hh" #include "FuncExpr.hh" +#include "LibertyClass.hh" +#include "NetworkClass.hh" namespace sta { Sequential::Sequential(bool is_register, - FuncExpr *clock, - FuncExpr *data, - FuncExpr *clear, - FuncExpr *preset, - LogicValue clr_preset_out, - LogicValue clr_preset_out_inv, - LibertyPort *output, - LibertyPort *output_inv) : + FuncExpr *clock, + FuncExpr *data, + FuncExpr *clear, + FuncExpr *preset, + LogicValue clr_preset_out, + LogicValue clr_preset_out_inv, + LibertyPort *output, + LibertyPort *output_inv) : is_register_(is_register), clock_(clock), data_(data), @@ -49,16 +51,54 @@ Sequential::Sequential(bool is_register, { } +Sequential::Sequential(Sequential &&other) noexcept : + is_register_(other.is_register_), + clock_(other.clock_), + data_(other.data_), + clear_(other.clear_), + preset_(other.preset_), + clr_preset_out_(other.clr_preset_out_), + clr_preset_out_inv_(other.clr_preset_out_inv_), + output_(other.output_), + output_inv_(other.output_inv_) +{ + other.clock_ = nullptr; + other.data_ = nullptr; + other.clear_ = nullptr; + other.preset_ = nullptr; +} + +Sequential & +Sequential::operator=(Sequential &&other) noexcept +{ + if (this != &other) { + delete clock_; + delete data_; + delete clear_; + delete preset_; + is_register_ = other.is_register_; + clock_ = other.clock_; + data_ = other.data_; + clear_ = other.clear_; + preset_ = other.preset_; + clr_preset_out_ = other.clr_preset_out_; + clr_preset_out_inv_ = other.clr_preset_out_inv_; + output_ = other.output_; + output_inv_ = other.output_inv_; + other.clock_ = nullptr; + other.data_ = nullptr; + other.clear_ = nullptr; + other.preset_ = nullptr; + } + return *this; +} + Sequential::~Sequential() { - if (clock_) - clock_->deleteSubexprs(); - if (data_) - data_->deleteSubexprs(); - if (clear_) - clear_->deleteSubexprs(); - if (preset_) - preset_->deleteSubexprs(); + delete clock_; + delete data_; + delete clear_; + delete preset_; } //////////////////////////////////////////////////////////////// @@ -81,4 +121,4 @@ StatetableRow::StatetableRow(StateInputValues &input_values, { } -} // namespace +} // namespace sta diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index b4a0b3258..5cdd92f2e 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -1,229 +1,285 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "TableModel.hh" +#include #include +#include +#include #include +#include +#include "Delay.hh" #include "Error.hh" +#include "ContainerHelpers.hh" #include "EnumNameMap.hh" -#include "Units.hh" +#include "Format.hh" +#include "LibertyClass.hh" #include "Liberty.hh" +#include "MinMax.hh" +#include "PocvMode.hh" +#include "TimingModel.hh" +#include "Transition.hh" +#include "Units.hh" namespace sta { -using std::string; -using std::min; -using std::max; -using std::abs; -using std::make_shared; - size_t findValueIndex(float value, const FloatSeq *values); -static void -deleteSigmaModels(TableModel *models[EarlyLate::index_count]); -static string +static std::string reportPvt(const LibertyCell *cell, const Pvt *pvt, - int digits); + int digits); static void -appendSpaces(string &result, - int count); +appendSpaces(std::string &result, + int count); TimingModel::TimingModel(LibertyCell *cell) : cell_(cell) { } +//////////////////////////////////////////////////////////////// + GateTableModel::GateTableModel(LibertyCell *cell, - TableModel *delay_model, - TableModel *delay_sigma_models[EarlyLate::index_count], - TableModel *slew_model, - TableModel *slew_sigma_models[EarlyLate::index_count], + TableModels *delay_models, + TableModels *slew_models, ReceiverModelPtr receiver_model, OutputWaveforms *output_waveforms) : GateTimingModel(cell), - delay_model_(delay_model), - slew_model_(slew_model), - receiver_model_(receiver_model), + delay_models_(delay_models), + slew_models_(slew_models), + receiver_model_(std::move(receiver_model)), output_waveforms_(output_waveforms) { - for (auto el_index : EarlyLate::rangeIndex()) { - slew_sigma_models_[el_index] = slew_sigma_models - ? slew_sigma_models[el_index] - : nullptr; - delay_sigma_models_[el_index] = delay_sigma_models - ? delay_sigma_models[el_index] - : nullptr; - } } -GateTableModel::~GateTableModel() +GateTableModel::GateTableModel(LibertyCell *cell, + TableModels *delay_models, + TableModels *slew_models) : + GateTimingModel(cell), + delay_models_(delay_models), + slew_models_(slew_models), + receiver_model_(nullptr), + output_waveforms_(nullptr) { - delete delay_model_; - delete slew_model_; - delete output_waveforms_; - deleteSigmaModels(slew_sigma_models_); - deleteSigmaModels(delay_sigma_models_); } -static void -deleteSigmaModels(TableModel *models[EarlyLate::index_count]) +const TableModel * +GateTableModel::delayModel() const { - TableModel *early_model = models[EarlyLate::earlyIndex()]; - TableModel *late_model = models[EarlyLate::lateIndex()]; - if (early_model == late_model) - delete early_model; - else { - delete early_model; - delete late_model; - } + return delay_models_ ? delay_models_->model() : nullptr; } -void -GateTableModel::setIsScaled(bool is_scaled) +const TableModel * +GateTableModel::slewModel() const { - if (delay_model_) - delay_model_->setIsScaled(is_scaled); - if (slew_model_) - slew_model_->setIsScaled(is_scaled); + return slew_models_ ? slew_models_->model() : nullptr;; } void -GateTableModel::gateDelay(const Pvt *pvt, - float in_slew, - float load_cap, - bool pocv_enabled, - // return values - ArcDelay &gate_delay, - Slew &drvr_slew) const -{ - float delay = findValue(pvt, delay_model_, in_slew, load_cap, 0.0); - float sigma_early = 0.0; - float sigma_late = 0.0; - if (pocv_enabled && delay_sigma_models_[EarlyLate::earlyIndex()]) - sigma_early = findValue(pvt, delay_sigma_models_[EarlyLate::earlyIndex()], - in_slew, load_cap, 0.0); - if (pocv_enabled && delay_sigma_models_[EarlyLate::lateIndex()]) - sigma_late = findValue(pvt, delay_sigma_models_[EarlyLate::lateIndex()], - in_slew, load_cap, 0.0); - gate_delay = makeDelay(delay, sigma_early, sigma_late); - - float slew = findValue(pvt, slew_model_, in_slew, load_cap, 0.0); - if (pocv_enabled && slew_sigma_models_[EarlyLate::earlyIndex()]) - sigma_early = findValue(pvt, slew_sigma_models_[EarlyLate::earlyIndex()], - in_slew, load_cap, 0.0); - if (pocv_enabled && slew_sigma_models_[EarlyLate::lateIndex()]) - sigma_late = findValue(pvt, slew_sigma_models_[EarlyLate::lateIndex()], - in_slew, load_cap, 0.0); - // Clip negative slews to zero. - if (slew < 0.0) - slew = 0.0; - drvr_slew = makeDelay(slew, sigma_early, sigma_late); +GateTableModel::setIsScaled(bool is_scaled) +{ + if (delay_models_) + delay_models_->model()->setIsScaled(is_scaled); + if (slew_models_) + slew_models_->model()->setIsScaled(is_scaled); } void GateTableModel::gateDelay(const Pvt *pvt, float in_slew, float load_cap, - float, - bool pocv_enabled, - ArcDelay &gate_delay, - Slew &drvr_slew) const + // return values + float &gate_delay, + float &drvr_slew) const { - gateDelay(pvt, in_slew, load_cap, pocv_enabled, gate_delay, drvr_slew); + if (delay_models_ && delay_models_->model()) + gate_delay = findValue(pvt, delay_models_->model(), in_slew, load_cap, 0.0); + else + gate_delay = 0.0; + if (slew_models_ && slew_models_->model()) { + drvr_slew = findValue(pvt, slew_models_->model(), in_slew, load_cap, 0.0); + // Clip negative slews to zero. + drvr_slew = std::max(drvr_slew, 0.0F); + } + else + drvr_slew = 0.0; +} + +void +GateTableModel::gateDelayPocv(const Pvt *pvt, + float in_slew, + float load_cap, + const MinMax *min_max, + PocvMode pocv_mode, + // return values + ArcDelay &gate_delay, + Slew &drvr_slew) const +{ + switch (pocv_mode) { + case PocvMode::normal: { + // Delay + TableModel *std_dev_model = delay_models_->stdDev(); + if (std_dev_model == nullptr) + std_dev_model = delay_models_->sigma(min_max); + if (std_dev_model) { + float std_dev = findValue(pvt, std_dev_model, in_slew, load_cap, 0.0); + gate_delay.setStdDev(std_dev); + } + + // Slew + std_dev_model = slew_models_->stdDev(); + if (std_dev_model == nullptr) + std_dev_model = slew_models_->sigma(min_max); + if (std_dev_model) { + float std_dev = findValue(pvt, std_dev_model, in_slew, load_cap, 0.0); + drvr_slew.setStdDev(std_dev); + } + break; + } + case PocvMode::skew_normal: { + // Delay + if (delay_models_->meanShift()) { + float mean_shift = findValue(pvt, delay_models_->meanShift(), + in_slew, load_cap, 0.0); + gate_delay.setMeanShift(mean_shift); + } + + if (delay_models_->stdDev()) { + float std_dev = findValue(pvt, delay_models_->stdDev(), in_slew, load_cap, 0.0); + gate_delay.setStdDev(std_dev); + } + + if (delay_models_->skewness()) { + float skewness = findValue(pvt, delay_models_->skewness(), in_slew, load_cap, 0.0); + gate_delay.setSkewness(skewness); + } + + // Slew + if (slew_models_->meanShift()) { + float mean_shift = findValue(pvt, slew_models_->meanShift(), + in_slew, load_cap, 0.0); + drvr_slew.setMeanShift(mean_shift); + } + + if (slew_models_->stdDev()) { + float std_dev = findValue(pvt, slew_models_->stdDev(), in_slew, load_cap, 0.0); + drvr_slew.setStdDev(std_dev); + } + + if (slew_models_->skewness()) { + float skewness = findValue(pvt, slew_models_->skewness(), in_slew, load_cap, 0.0); + drvr_slew.setSkewness(skewness); + } + break; + } + default: + break; + } } -string +std::string GateTableModel::reportGateDelay(const Pvt *pvt, - float in_slew, - float load_cap, - bool pocv_enabled, - int digits) const -{ - string result = reportPvt(cell_, pvt, digits); - result += reportTableLookup("Delay", pvt, delay_model_, in_slew, + float in_slew, + float load_cap, + const MinMax *min_max, + PocvMode pocv_mode, + int digits) const +{ + std::string result = reportPvt(cell_, pvt, digits); + result += reportTableLookup("Delay", pvt, delay_models_->model(), in_slew, load_cap, 0.0, digits); - if (pocv_enabled && delay_sigma_models_[EarlyLate::earlyIndex()]) - result += reportTableLookup("Delay sigma(early)", pvt, - delay_sigma_models_[EarlyLate::earlyIndex()], - in_slew, load_cap, 0.0, digits); - if (pocv_enabled && delay_sigma_models_[EarlyLate::lateIndex()]) - result += reportTableLookup("Delay sigma(late)", pvt, - delay_sigma_models_[EarlyLate::lateIndex()], - in_slew, load_cap, 0.0, digits); - result += '\n'; - result += reportTableLookup("Slew", pvt, slew_model_, in_slew, - load_cap, 9.0, digits); - if (pocv_enabled && slew_sigma_models_[EarlyLate::earlyIndex()]) - result += reportTableLookup("Slew sigma(early)", pvt, - slew_sigma_models_[EarlyLate::earlyIndex()], - in_slew, load_cap, 0.0, digits); - if (pocv_enabled && slew_sigma_models_[EarlyLate::lateIndex()]) - result += reportTableLookup("Slew sigma(late)", pvt, - slew_sigma_models_[EarlyLate::lateIndex()], - in_slew, load_cap, 0.0, digits); - float drvr_slew = findValue(pvt, slew_model_, in_slew, load_cap, 0.0); + if (pocv_mode != PocvMode::scalar) { + if (delay_models_->sigma(min_max)) + result += reportTableLookup("Delay sigma(early)", pvt, + delay_models_->sigma(min_max), + in_slew, load_cap, 0.0, digits); + if (delay_models_->sigma(EarlyLate::late())) + result += reportTableLookup("Delay sigma(late)", pvt, + delay_models_->sigma(min_max), + in_slew, load_cap, 0.0, digits); + result += '\n'; + + result += reportTableLookup("Slew", pvt, slew_models_->model(), in_slew, + load_cap, 9.0, digits); + + if (slew_models_->sigma(EarlyLate::early())) + result += reportTableLookup("Slew sigma(early)", pvt, + slew_models_->sigma(min_max), + in_slew, load_cap, 0.0, digits); + if (slew_models_->sigma(EarlyLate::late())) + result += reportTableLookup("Slew sigma(late)", pvt, + slew_models_->sigma(min_max), + in_slew, load_cap, 0.0, digits); + } + else { + result += '\n'; + result += reportTableLookup("Slew", pvt, slew_models_->model(), in_slew, + load_cap, 9.0, digits); + } + + float drvr_slew = findValue(pvt, slew_models_->model(), in_slew, load_cap, 0.0); if (drvr_slew < 0.0) result += "Negative slew clipped to 0.0\n"; return result; } -string -GateTableModel::reportTableLookup(const char *result_name, - const Pvt *pvt, - const TableModel *model, - float in_slew, - float load_cap, - float related_out_cap, - int digits) const +std::string +GateTableModel::reportTableLookup(std::string_view result_name, + const Pvt *pvt, + const TableModel *model, + float in_slew, + float load_cap, + float related_out_cap, + int digits) const { if (model) { float axis_value1, axis_value2, axis_value3; - findAxisValues(model, in_slew, load_cap, related_out_cap, - axis_value1, axis_value2, axis_value3); + findAxisValues(model, in_slew, load_cap, related_out_cap, axis_value1, + axis_value2, axis_value3); const LibertyLibrary *library = cell_->libertyLibrary(); - return model->reportValue(result_name, cell_, pvt, axis_value1, nullptr, - axis_value2, axis_value3, - library->units()->timeUnit(), digits); + return model->reportValue(result_name, cell_, pvt, axis_value1, {}, + axis_value2, axis_value3, library->units()->timeUnit(), + digits); } return ""; } float GateTableModel::findValue(const Pvt *pvt, - const TableModel *model, - float in_slew, - float load_cap, - float related_out_cap) const + const TableModel *model, + float in_slew, + float load_cap, + float related_out_cap) const { if (model) { float axis_value1, axis_value2, axis_value3; - findAxisValues(model, in_slew, load_cap, related_out_cap, - axis_value1, axis_value2, axis_value3); + findAxisValues(model, in_slew, load_cap, related_out_cap, axis_value1, + axis_value2, axis_value3); return model->findValue(cell_, pvt, axis_value1, axis_value2, axis_value3); } else @@ -232,46 +288,40 @@ GateTableModel::findValue(const Pvt *pvt, void GateTableModel::findAxisValues(const TableModel *model, - float in_slew, - float load_cap, - float related_out_cap, - // Return values. - float &axis_value1, - float &axis_value2, - float &axis_value3) const + float in_slew, + float load_cap, + float related_out_cap, + // Return values. + float &axis_value1, + float &axis_value2, + float &axis_value3) const { switch (model->order()) { - case 0: - axis_value1 = 0.0; - axis_value2 = 0.0; - axis_value3 = 0.0; - break; - case 1: - axis_value1 = axisValue(model->axis1(), in_slew, load_cap, - related_out_cap); - axis_value2 = 0.0; - axis_value3 = 0.0; - break; - case 2: - axis_value1 = axisValue(model->axis1(), in_slew, load_cap, - related_out_cap); - axis_value2 = axisValue(model->axis2(), in_slew, load_cap, - related_out_cap); - axis_value3 = 0.0; - break; - case 3: - axis_value1 = axisValue(model->axis1(), in_slew, load_cap, - related_out_cap); - axis_value2 = axisValue(model->axis2(), in_slew, load_cap, - related_out_cap); - axis_value3 = axisValue(model->axis3(), in_slew, load_cap, - related_out_cap); - break; - default: - axis_value1 = 0.0; - axis_value2 = 0.0; - axis_value3 = 0.0; - criticalError(239, "unsupported table order"); + case 0: + axis_value1 = 0.0; + axis_value2 = 0.0; + axis_value3 = 0.0; + break; + case 1: + axis_value1 = axisValue(model->axis1(), in_slew, load_cap, related_out_cap); + axis_value2 = 0.0; + axis_value3 = 0.0; + break; + case 2: + axis_value1 = axisValue(model->axis1(), in_slew, load_cap, related_out_cap); + axis_value2 = axisValue(model->axis2(), in_slew, load_cap, related_out_cap); + axis_value3 = 0.0; + break; + case 3: + axis_value1 = axisValue(model->axis1(), in_slew, load_cap, related_out_cap); + axis_value2 = axisValue(model->axis2(), in_slew, load_cap, related_out_cap); + axis_value3 = axisValue(model->axis3(), in_slew, load_cap, related_out_cap); + break; + default: + axis_value1 = 0.0; + axis_value2 = 0.0; + axis_value3 = 0.0; + criticalError(239, "unsupported table order"); } } @@ -287,27 +337,28 @@ GateTableModel::driveResistance(const Pvt *pvt) const void GateTableModel::maxCapSlew(float in_slew, - const Pvt *pvt, - float &slew, - float &cap) const -{ - const TableAxis *axis1 = slew_model_->axis1(); - const TableAxis *axis2 = slew_model_->axis2(); - const TableAxis *axis3 = slew_model_->axis3(); + const Pvt *pvt, + float &slew, + float &cap) const +{ + TableModel *model = slew_models_->model(); + const TableAxis *axis1 = model->axis1(); + const TableAxis *axis2 = model->axis2(); + const TableAxis *axis3 = model->axis3(); if (axis1 && axis1->variable() == TableAxisVariable::total_output_net_capacitance) { cap = axis1->axisValue(axis1->size() - 1); - slew = findValue(pvt, slew_model_, in_slew, cap, 0.0); + slew = findValue(pvt, model, in_slew, cap, 0.0); } else if (axis2 - && axis2->variable()==TableAxisVariable::total_output_net_capacitance) { + && axis2->variable() == TableAxisVariable::total_output_net_capacitance) { cap = axis2->axisValue(axis2->size() - 1); - slew = findValue(pvt, slew_model_, in_slew, cap, 0.0); + slew = findValue(pvt, model, in_slew, cap, 0.0); } else if (axis3 - && axis3->variable()==TableAxisVariable::total_output_net_capacitance) { + && axis3->variable() == TableAxisVariable::total_output_net_capacitance) { cap = axis3->axisValue(axis3->size() - 1); - slew = findValue(pvt, slew_model_, in_slew, cap, 0.0); + slew = findValue(pvt, model, in_slew, cap, 0.0); } else { // Table not dependent on capacitance. @@ -315,15 +366,14 @@ GateTableModel::maxCapSlew(float in_slew, slew = 0.0; } // Clip negative slews to zero. - if (slew < 0.0) - slew = 0.0; + slew = std::max(slew, 0.0F); } float GateTableModel::axisValue(const TableAxis *axis, - float in_slew, - float load_cap, - float related_out_cap) const + float in_slew, + float load_cap, + float related_out_cap) const { TableAxisVariable var = axis->variable(); if (var == TableAxisVariable::input_transition_time @@ -340,7 +390,7 @@ GateTableModel::axisValue(const TableAxis *axis, } bool -GateTableModel::checkAxes(const TablePtr &table) +GateTableModel::checkAxes(const TableModel *table) { const TableAxis *axis1 = table->axis1(); const TableAxis *axis2 = table->axis2(); @@ -360,43 +410,38 @@ GateTableModel::checkAxis(const TableAxis *axis) { TableAxisVariable var = axis->variable(); return var == TableAxisVariable::total_output_net_capacitance - || var == TableAxisVariable::input_transition_time - || var == TableAxisVariable::input_net_transition - || var == TableAxisVariable::related_out_total_output_net_capacitance; + || var == TableAxisVariable::input_transition_time + || var == TableAxisVariable::input_net_transition + || var == TableAxisVariable::related_out_total_output_net_capacitance; } //////////////////////////////////////////////////////////////// -ReceiverModel::~ReceiverModel() -{ - for (TableModel *model : capacitance_models_) - delete model; -} - void -ReceiverModel::setCapacitanceModel(TableModel *table_model, +ReceiverModel::setCapacitanceModel(TableModel table_model, size_t segment, const RiseFall *rf) { if ((segment + 1) * RiseFall::index_count > capacitance_models_.size()) capacitance_models_.resize((segment + 1) * RiseFall::index_count); size_t idx = segment * RiseFall::index_count + rf->index(); - capacitance_models_[idx] = table_model; + capacitance_models_[idx] = std::move(table_model); } bool -ReceiverModel::checkAxes(TablePtr table) +ReceiverModel::checkAxes(const TableModel *table) { const TableAxis *axis1 = table->axis1(); const TableAxis *axis2 = table->axis2(); const TableAxis *axis3 = table->axis3(); return (axis1 && axis1->variable() == TableAxisVariable::input_net_transition - && axis2 == nullptr - && axis3 == nullptr) - || (axis1 && axis1->variable() == TableAxisVariable::input_net_transition - && axis2 && axis2->variable() == TableAxisVariable::total_output_net_capacitance + && axis2 == nullptr && axis3 == nullptr) + || (axis1 && axis1->variable() == TableAxisVariable::input_net_transition + && axis2 + && axis2->variable() == TableAxisVariable::total_output_net_capacitance && axis3 == nullptr) - || (axis1 && axis1->variable() == TableAxisVariable::total_output_net_capacitance + || (axis1 + && axis1->variable() == TableAxisVariable::total_output_net_capacitance && axis2 && axis2->variable() == TableAxisVariable::input_net_transition && axis3 == nullptr); } @@ -404,111 +449,145 @@ ReceiverModel::checkAxes(TablePtr table) //////////////////////////////////////////////////////////////// CheckTableModel::CheckTableModel(LibertyCell *cell, - TableModel *model, - TableModel *sigma_models[EarlyLate::index_count]) : + TableModels *check_models) : CheckTimingModel(cell), - model_(model) + check_models_(check_models) { - for (auto el_index : EarlyLate::rangeIndex()) - sigma_models_[el_index] = sigma_models ? sigma_models[el_index] : nullptr; } -CheckTableModel::~CheckTableModel() +const TableModel * +CheckTableModel::checkModel() const { - delete model_; - deleteSigmaModels(sigma_models_); + return check_models_ ? check_models_->model() : nullptr; } void CheckTableModel::setIsScaled(bool is_scaled) { - model_->setIsScaled(is_scaled); + check_models_->model()->setIsScaled(is_scaled); } ArcDelay CheckTableModel::checkDelay(const Pvt *pvt, - float from_slew, - float to_slew, - float related_out_cap, - bool pocv_enabled) const -{ - if (model_) { - float mean = findValue(pvt, model_, from_slew, to_slew, related_out_cap); - float sigma_early = 0.0; - float sigma_late = 0.0; - if (pocv_enabled && sigma_models_[EarlyLate::earlyIndex()]) - sigma_early = findValue(pvt, sigma_models_[EarlyLate::earlyIndex()], - from_slew, to_slew, related_out_cap); - if (pocv_enabled && sigma_models_[EarlyLate::lateIndex()]) - sigma_late = findValue(pvt, sigma_models_[EarlyLate::lateIndex()], - from_slew, to_slew, related_out_cap); - return makeDelay(mean, sigma_early, sigma_late); + float from_slew, + float to_slew, + float related_out_cap, + const MinMax *min_max, + PocvMode pocv_mode) const +{ + ArcDelay check_delay; + if (check_models_) { + float margin = findValue(pvt, check_models_->model(), from_slew, + to_slew, related_out_cap); + check_delay.setMean(margin); + + switch (pocv_mode) { + case PocvMode::normal: { + TableModel *std_dev_model = check_models_->stdDev(); + if (std_dev_model == nullptr) + std_dev_model = check_models_->sigma(min_max); + if (std_dev_model) { + float std_dev = findValue(pvt, std_dev_model, from_slew, + to_slew, related_out_cap); + check_delay.setStdDev(std_dev); + } + break; + } + case PocvMode::skew_normal: { + if (check_models_->meanShift()) { + float mean_shift = findValue(pvt, check_models_->meanShift(), + from_slew, to_slew, related_out_cap); + check_delay.setMeanShift(mean_shift); + } + + if (check_models_->stdDev()) { + float std_dev = findValue(pvt, check_models_->stdDev(), + from_slew, to_slew, related_out_cap); + check_delay.setStdDev(std_dev); + } + + if (check_models_->skewness()) { + float skewness = findValue(pvt, check_models_->skewness(), + from_slew, to_slew, related_out_cap); + check_delay.setSkewness(skewness); + } + break; + } + default: + break; + } } - else - return 0.0; + return check_delay; } float CheckTableModel::findValue(const Pvt *pvt, - const TableModel *model, - float from_slew, - float to_slew, - float related_out_cap) const + const TableModel *model, + float from_slew, + float to_slew, + float related_out_cap) const { if (model) { float axis_value1, axis_value2, axis_value3; - findAxisValues(from_slew, to_slew, related_out_cap, - axis_value1, axis_value2, axis_value3); + findAxisValues(from_slew, to_slew, related_out_cap, axis_value1, axis_value2, + axis_value3); return model->findValue(cell_, pvt, axis_value1, axis_value2, axis_value3); } else return 0.0; } -string +std::string CheckTableModel::reportCheckDelay(const Pvt *pvt, - float from_slew, - const char *from_slew_annotation, - float to_slew, - float related_out_cap, - bool pocv_enabled, - int digits) const -{ - string result = reportTableDelay("Check", pvt, model_, - from_slew, from_slew_annotation, to_slew, - related_out_cap, digits); - if (pocv_enabled && sigma_models_[EarlyLate::earlyIndex()]) - result += reportTableDelay("Check sigma early", pvt, - sigma_models_[EarlyLate::earlyIndex()], - from_slew, from_slew_annotation, to_slew, - related_out_cap, digits); - if (pocv_enabled && sigma_models_[EarlyLate::lateIndex()]) - result += reportTableDelay("Check sigma late", pvt, - sigma_models_[EarlyLate::lateIndex()], - from_slew, from_slew_annotation, to_slew, - related_out_cap, digits); + float from_slew, + std::string_view from_slew_annotation, + float to_slew, + float related_out_cap, + const MinMax *min_max, + PocvMode pocv_mode, + int digits) const +{ + std::string result = reportTableDelay("Check", pvt, check_models_->model(), + from_slew, from_slew_annotation, to_slew, + related_out_cap, digits); + switch (pocv_mode) { + case PocvMode::normal: + case PocvMode::skew_normal: { + TableModel *check_table = check_models_->stdDev(); + if (check_table == nullptr) + check_table = check_models_->sigma(min_max); + if (check_table) + result += reportTableDelay("Check sigma", pvt, check_table, + from_slew, from_slew_annotation, to_slew, + related_out_cap, digits); + break; + } + default: + break; + } return result; } -string -CheckTableModel::reportTableDelay(const char *result_name, - const Pvt *pvt, - const TableModel *model, - float from_slew, - const char *from_slew_annotation, - float to_slew, - float related_out_cap, - int digits) const +std::string +CheckTableModel::reportTableDelay(std::string_view result_name, + const Pvt *pvt, + const TableModel *model, + float from_slew, + std::string_view from_slew_annotation, + float to_slew, + float related_out_cap, + int digits) const { if (model) { float axis_value1, axis_value2, axis_value3; - findAxisValues(from_slew, to_slew, related_out_cap, - axis_value1, axis_value2, axis_value3); - string result = reportPvt(cell_, pvt, digits); - result += model_->reportValue(result_name, cell_, pvt, - axis_value1, from_slew_annotation, axis_value2, - axis_value3, - cell_->libertyLibrary()->units()->timeUnit(), digits); + findAxisValues(from_slew, to_slew, related_out_cap, axis_value1, axis_value2, + axis_value3); + std::string result = reportPvt(cell_, pvt, digits); + const Unit *time_unit = cell_->libertyLibrary()->units()->timeUnit(); + result += check_models_->model()->reportValue(result_name, cell_, pvt, + axis_value1, from_slew_annotation, + axis_value2, axis_value3, + time_unit, digits); return result; } return ""; @@ -516,39 +595,40 @@ CheckTableModel::reportTableDelay(const char *result_name, void CheckTableModel::findAxisValues(float from_slew, - float to_slew, - float related_out_cap, - // Return values. - float &axis_value1, - float &axis_value2, - float &axis_value3) const -{ - switch (model_->order()) { + float to_slew, + float related_out_cap, + // Return values. + float &axis_value1, + float &axis_value2, + float &axis_value3) const +{ + TableModel *model = check_models_->model(); + switch (model->order()) { case 0: axis_value1 = 0.0; axis_value2 = 0.0; axis_value3 = 0.0; break; case 1: - axis_value1 = axisValue(model_->axis1(), from_slew, to_slew, - related_out_cap); + axis_value1 = axisValue(model->axis1(), from_slew, to_slew, + related_out_cap); axis_value2 = 0.0; axis_value3 = 0.0; break; case 2: - axis_value1 = axisValue(model_->axis1(), from_slew, to_slew, - related_out_cap); - axis_value2 = axisValue(model_->axis2(), from_slew, to_slew, - related_out_cap); + axis_value1 = axisValue(model->axis1(), from_slew, to_slew, + related_out_cap); + axis_value2 = axisValue(model->axis2(), from_slew, to_slew, + related_out_cap); axis_value3 = 0.0; break; case 3: - axis_value1 = axisValue(model_->axis1(), from_slew, to_slew, - related_out_cap); - axis_value2 = axisValue(model_->axis2(), from_slew, to_slew, - related_out_cap); - axis_value3 = axisValue(model_->axis3(), from_slew, to_slew, - related_out_cap); + axis_value1 = axisValue(model->axis1(), from_slew, to_slew, + related_out_cap); + axis_value2 = axisValue(model->axis2(), from_slew, to_slew, + related_out_cap); + axis_value3 = axisValue(model->axis3(), from_slew, to_slew, + related_out_cap); break; default: criticalError(241, "unsupported table order"); @@ -557,9 +637,9 @@ CheckTableModel::findAxisValues(float from_slew, float CheckTableModel::axisValue(const TableAxis *axis, - float from_slew, - float to_slew, - float related_out_cap) const + float from_slew, + float to_slew, + float related_out_cap) const { TableAxisVariable var = axis->variable(); if (var == TableAxisVariable::related_pin_transition) @@ -575,7 +655,7 @@ CheckTableModel::axisValue(const TableAxis *axis, } bool -CheckTableModel::checkAxes(const TablePtr table) +CheckTableModel::checkAxes(const TableModel *table) { const TableAxis *axis1 = table->axis1(); const TableAxis *axis2 = table->axis2(); @@ -595,19 +675,97 @@ CheckTableModel::checkAxis(const TableAxis *axis) { TableAxisVariable var = axis->variable(); return var == TableAxisVariable::constrained_pin_transition - || var == TableAxisVariable::related_pin_transition - || var == TableAxisVariable::related_out_total_output_net_capacitance; + || var == TableAxisVariable::related_pin_transition + || var == TableAxisVariable::related_out_total_output_net_capacitance; +} + +//////////////////////////////////////////////////////////////// + +TableModels::TableModels() : + model_(nullptr), + sigma_{}, + std_dev_(nullptr), + mean_shift_(nullptr), + skewness_(nullptr) +{ +} + +TableModels::TableModels(TableModel *model) : + model_(model), + sigma_{}, + std_dev_(nullptr), + mean_shift_(nullptr), + skewness_(nullptr) +{ +} + +TableModels::~TableModels() +{ + TableModel *sigma_early = sigma_[EarlyLate::earlyIndex()]; + TableModel *sigma_late = sigma_[EarlyLate::lateIndex()]; + if (sigma_early == sigma_late) + delete sigma_early; + else { + delete sigma_early; + delete sigma_late; + } +} + +void +TableModels::setModel(TableModel *model) +{ + model_.reset(model); +} + +TableModel * +TableModels::sigma(const EarlyLate *early_late) const +{ + return sigma_[early_late->index()]; +} + +void +TableModels::setSigma(TableModel *table, + const EarlyLate *early_late) +{ + sigma_[early_late->index()] = table; +} + +void +TableModels::setMeanShift(TableModel *table) +{ + mean_shift_.reset(table); +} + +void +TableModels::setSkewness(TableModel *table) +{ + skewness_.reset(table); +} + +void +TableModels::setStdDev(TableModel *table) +{ + std_dev_.reset(table); } //////////////////////////////////////////////////////////////// +TableModel::TableModel() : + table_(nullptr), + tbl_template_(nullptr), + scale_factor_type_(0), + rf_index_(0), + is_scaled_(false) +{ +} + TableModel::TableModel(TablePtr table, TableTemplate *tbl_template, - ScaleFactorType scale_factor_type, - const RiseFall *rf) : - table_(table), + ScaleFactorType scale_factor_type, + const RiseFall *rf) : + table_(std::move(table)), tbl_template_(tbl_template), - scale_factor_type_(int(scale_factor_type)), + scale_factor_type_(static_cast(scale_factor_type)), rf_index_(rf->index()), is_scaled_(false) { @@ -619,10 +777,16 @@ TableModel::order() const return table_->order(); } +ScaleFactorType +TableModel::scaleFactorType() const +{ + return static_cast(scale_factor_type_); +} + void TableModel::setScaleFactorType(ScaleFactorType type) { - scale_factor_type_ = int(type); + scale_factor_type_ = static_cast(type); } void @@ -659,279 +823,490 @@ TableModel::value(size_t axis_index1, float TableModel::findValue(float axis_value1, - float axis_value2, - float axis_value3) const + float axis_value2, + float axis_value3) const { return table_->findValue(axis_value1, axis_value2, axis_value3); } float TableModel::findValue(const LibertyCell *cell, - const Pvt *pvt, - float axis_value1, - float axis_value2, - float axis_value3) const + const Pvt *pvt, + float axis_value1, + float axis_value2, + float axis_value3) const { return table_->findValue(axis_value1, axis_value2, axis_value3) - * scaleFactor(cell, pvt); + * scaleFactor(cell, pvt); } float TableModel::scaleFactor(const LibertyCell *cell, - const Pvt *pvt) const + const Pvt *pvt) const { if (is_scaled_) // Scaled tables are not derated because scale factors are wrt // nominal pvt. return 1.0F; else - return cell->libertyLibrary()->scaleFactor(static_cast(scale_factor_type_), - rf_index_, cell, pvt); -} - -string -TableModel::reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *pvt, - float value1, - const char *comment1, - float value2, - float value3, + return cell->libertyLibrary()->scaleFactor( + static_cast(scale_factor_type_), rf_index_, cell, pvt); +} + +std::string +TableModel::reportValue(std::string_view result_name, + const LibertyCell *cell, + const Pvt *pvt, + float value1, + std::string_view comment1, + float value2, + float value3, const Unit *table_unit, - int digits) const + int digits) const { - string result = table_->reportValue("Table value", cell, pvt, value1, - comment1, value2, value3, table_unit, digits); + std::string result = + table_->reportValue("Table value", cell, pvt, value1, comment1, value2, value3, + table_unit, digits); result += reportPvtScaleFactor(cell, pvt, digits); - result += result_name; + result.append(result_name); result += " = "; - result += table_unit->asString(findValue(cell, pvt, value1, value2, value3), digits); + result += + table_unit->asString(findValue(cell, pvt, value1, value2, value3), digits); result += '\n'; return result; } -static string +static std::string reportPvt(const LibertyCell *cell, - const Pvt *pvt, - int digits) + const Pvt *pvt, + int digits) { const LibertyLibrary *library = cell->libertyLibrary(); if (pvt == nullptr) pvt = library->defaultOperatingConditions(); - if (pvt) { - string result; - stringPrint(result, "P = %.*f V = %.*f T = %.*f\n", - digits, pvt->process(), - digits, pvt->voltage(), - digits, pvt->temperature()); - return result; - } + if (pvt) + return sta::format("P = {:.{}f} V = {:.{}f} T = {:.{}f}\n", + pvt->process(), digits, + pvt->voltage(), digits, + pvt->temperature(), digits); return ""; } -string +std::string TableModel::reportPvtScaleFactor(const LibertyCell *cell, - const Pvt *pvt, - int digits) const + const Pvt *pvt, + int digits) const { if (pvt == nullptr) pvt = cell->libertyLibrary()->defaultOperatingConditions(); - if (pvt) { - string result; - stringPrint(result, "PVT scale factor = %.*f\n", - digits, - scaleFactor(cell, pvt)); - return result; - } + if (pvt) + return sta::formatRuntime("PVT scale factor = {:.{}f}\n", + scaleFactor(cell, pvt), digits); return ""; } //////////////////////////////////////////////////////////////// -Table0::Table0(float value) : - Table(), - value_(value) +void +Table::clear() { + if (order_ == 1) + values1_.clear(); + else if (order_ == 2 || order_ == 3) + values_table_.clear(); } -float -Table0::value(size_t, - size_t, - size_t) const +FloatSeq * +Table::values() const { - return value_; + return (order_ == 1) ? const_cast(&values1_) : nullptr; } -float -Table0::findValue(float, - float, - float) const +FloatTable * +Table::values3() { - return value_; + return (order_ >= 2) ? &values_table_ : nullptr; } -string -Table0::reportValue(const char *result_name, - const LibertyCell *, - const Pvt *, - float value1, - const char *comment1, - float value2, - float value3, - const Unit *table_unit, - int digits) const +const FloatTable * +Table::values3() const { - string result = result_name; - result += " constant = "; - result += table_unit->asString(findValue(value1, value2, value3), digits); - if (comment1) - result += comment1; - result += '\n'; - return result; + return (order_ >= 2) ? &values_table_ : nullptr; } -void -Table0::report(const Units *units, - Report *report) const +Table::Table() : + order_(0), + value_(0.0) { - int digits = 4; - const Unit *table_unit = units->timeUnit(); - report->reportLine("%s", table_unit->asString(value_, digits)); } -//////////////////////////////////////////////////////////////// +Table::Table(float value) : + order_(0), + value_(value) +{ +} -Table1::Table1() : - Table(), - values_(nullptr), - axis1_(nullptr) +Table::Table(FloatSeq *values, + TableAxisPtr axis1) : + order_(1), + value_(0.0), + values1_(std::move(*values)), + axis1_(std::move(axis1)) { + delete values; } -Table1::Table1(FloatSeq *values, - TableAxisPtr axis1) : - Table(), - values_(values), - axis1_(axis1) +Table::Table(FloatSeq &&values, + TableAxisPtr axis1) : + order_(1), + value_(0.0), + values1_(std::move(values)), + axis1_(std::move(axis1)) { } -Table1::Table1(Table1 &&table) : - Table(), - values_(table.values_), - axis1_(table.axis1_) +Table::Table(FloatTable &&values, + TableAxisPtr axis1, + TableAxisPtr axis2) : + order_(2), + value_(0.0), + values_table_(std::move(values)), + axis1_(std::move(axis1)), + axis2_(std::move(axis2)) { - table.values_ = nullptr; - table.axis1_ = nullptr; } -Table1::Table1(const Table1 &table) : - Table(), - values_(new FloatSeq(*table.values_)), - axis1_(table.axis1_) +Table::Table(FloatTable &&values, + TableAxisPtr axis1, + TableAxisPtr axis2, + TableAxisPtr axis3) : + order_(3), + value_(0.0), + values_table_(std::move(values)), + axis1_(std::move(axis1)), + axis2_(std::move(axis2)), + axis3_(std::move(axis3)) { } -Table1::~Table1() +Table::Table(Table &&table) noexcept : + order_(table.order_), + value_(table.value_), + values1_(std::move(table.values1_)), + values_table_(std::move(table.values_table_)), + axis1_(std::move(table.axis1_)), + axis2_(std::move(table.axis2_)), + axis3_(std::move(table.axis3_)) { - delete values_; } -Table1 & -Table1::operator=(Table1 &&table) +Table::Table(const Table &table) = default; + +Table & +Table::operator=(Table &&table) noexcept { - values_ = table.values_; - axis1_ = table.axis1_; - table.values_ = nullptr; - table.axis1_ = nullptr; + if (this != &table) { + order_ = table.order_; + value_ = table.value_; + values1_ = std::move(table.values1_); + values_table_ = std::move(table.values_table_); + axis1_ = table.axis1_; + axis2_ = table.axis2_; + axis3_ = table.axis3_; + } return *this; } +void +Table::setScaleFactorType(ScaleFactorType) +{ +} + +void +Table::setIsScaled(bool) +{ +} + float -Table1::value(size_t axis_index1, - size_t, - size_t) const +Table::value(size_t axis_idx1, + size_t axis_idx2, + size_t axis_idx3) const { - return value(axis_index1); + if (order_ == 0) + return value_; + if (order_ == 1) + return values1_[axis_idx1]; + if (order_ == 2) + return values_table_[axis_idx1][axis_idx2]; + // order_ == 3 + size_t row = axis_idx1 * axis2_->size() + axis_idx2; + return values_table_[row][axis_idx3]; } float -Table1::value(size_t axis_index1) const +Table::value(size_t index1) const { - return (*values_)[axis_index1]; + if (order_ != 1 || values1_.empty()) + return value_; + return values1_[index1]; } float -Table1::findValue(float axis_value1, - float, - float) const +Table::value(size_t axis_index1, + size_t axis_index2) const { - return findValue(axis_value1); + return values_table_[axis_index1][axis_index2]; +} + +float +Table::findValue(float axis_value1, + float axis_value2, + float axis_value3) const +{ + if (order_ == 0) + return value_; + if (order_ == 1) + return findValue(axis_value1); + if (order_ == 2) + return findValueOrder2(axis_value1, axis_value2); + else + return findValueOrder3(axis_value1, axis_value2, axis_value3); } float -Table1::findValue(float axis_value1) const +Table::findValueOrder2(float axis_value1, + float axis_value2) const { - if (axis1_->size() == 1) - return this->value(axis_value1); - else { + size_t size1 = axis1_->size(); + size_t size2 = axis2_->size(); + if (size1 == 1) { + if (size2 == 1) + return value(0, 0); + size_t axis_index2 = axis2_->findAxisIndex(axis_value2); + double x2 = axis_value2; + double y00 = value(0, axis_index2); + double x2l = axis2_->axisValue(axis_index2); + double x2u = axis2_->axisValue(axis_index2 + 1); + double dx2 = (x2 - x2l) / (x2u - x2l); + double y01 = value(0, axis_index2 + 1); + return (1 - dx2) * y00 + dx2 * y01; + } + if (size2 == 1) { size_t axis_index1 = axis1_->findAxisIndex(axis_value1); double x1 = axis_value1; + double y00 = value(axis_index1, 0); double x1l = axis1_->axisValue(axis_index1); double x1u = axis1_->axisValue(axis_index1 + 1); - double y1 = this->value(axis_index1); - double y2 = this->value(axis_index1 + 1); double dx1 = (x1 - x1l) / (x1u - x1l); - return (1 - dx1) * y1 + dx1 * y2; + double y10 = value(axis_index1 + 1, 0); + return (1 - dx1) * y00 + dx1 * y10; } + size_t axis_index1 = axis1_->findAxisIndex(axis_value1); + size_t axis_index2 = axis2_->findAxisIndex(axis_value2); + double x1 = axis_value1; + double x2 = axis_value2; + double y00 = value(axis_index1, axis_index2); + double x1l = axis1_->axisValue(axis_index1); + double x1u = axis1_->axisValue(axis_index1 + 1); + double dx1 = (x1 - x1l) / (x1u - x1l); + double y10 = value(axis_index1 + 1, axis_index2); + double y11 = value(axis_index1 + 1, axis_index2 + 1); + double x2l = axis2_->axisValue(axis_index2); + double x2u = axis2_->axisValue(axis_index2 + 1); + double dx2 = (x2 - x2l) / (x2u - x2l); + double y01 = value(axis_index1, axis_index2 + 1); + return (1 - dx1) * (1 - dx2) * y00 + + dx1 * (1 - dx2) * y10 + + dx1 * dx2 * y11 + + (1 - dx1) * dx2 * y01; } float -Table1::findValueClip(float axis_value1) const +Table::findValueOrder3(float axis_value1, + float axis_value2, + float axis_value3) const { - if (axis1_->size() == 1) - return this->value(axis_value1); - else { - size_t axis_index1 = axis1_->findAxisIndex(axis_value1); - double x1 = axis_value1; + size_t axis_index1 = axis1_->findAxisIndex(axis_value1); + size_t axis_index2 = axis2_->findAxisIndex(axis_value2); + size_t axis_index3 = axis3_->findAxisIndex(axis_value3); + double x1 = axis_value1; + double x2 = axis_value2; + double x3 = axis_value3; + double dx1 = 0.0; + double dx2 = 0.0; + double dx3 = 0.0; + double y000 = value(axis_index1, axis_index2, axis_index3); + double y001 = 0.0; + double y010 = 0.0; + double y011 = 0.0; + double y100 = 0.0; + double y101 = 0.0; + double y110 = 0.0; + double y111 = 0.0; + + if (axis1_->size() != 1) { double x1l = axis1_->axisValue(axis_index1); double x1u = axis1_->axisValue(axis_index1 + 1); - if (x1 < x1l) - return 0.0; - else if (x1 > x1u) - return this->value(axis1_->size() - 1); - else { - double y1 = this->value(axis_index1); - double y2 = this->value(axis_index1 + 1); - double dx1 = (x1 - x1l) / (x1u - x1l); - return (1 - dx1) * y1 + dx1 * y2; + dx1 = (x1 - x1l) / (x1u - x1l); + y100 = value(axis_index1 + 1, axis_index2, axis_index3); + if (axis3_->size() != 1) + y101 = value(axis_index1 + 1, axis_index2, axis_index3 + 1); + if (axis2_->size() != 1) { + y110 = value(axis_index1 + 1, axis_index2 + 1, axis_index3); + if (axis3_->size() != 1) + y111 = value(axis_index1 + 1, axis_index2 + 1, axis_index3 + 1); } } + if (axis2_->size() != 1) { + double x2l = axis2_->axisValue(axis_index2); + double x2u = axis2_->axisValue(axis_index2 + 1); + dx2 = (x2 - x2l) / (x2u - x2l); + y010 = value(axis_index1, axis_index2 + 1, axis_index3); + if (axis3_->size() != 1) + y011 = value(axis_index1, axis_index2 + 1, axis_index3 + 1); + } + if (axis3_->size() != 1) { + double x3l = axis3_->axisValue(axis_index3); + double x3u = axis3_->axisValue(axis_index3 + 1); + dx3 = (x3 - x3l) / (x3u - x3l); + y001 = value(axis_index1, axis_index2, axis_index3 + 1); + } + + return (1 - dx1) * (1 - dx2) * (1 - dx3) * y000 + + (1 - dx1) * (1 - dx2) * dx3 * y001 + (1 - dx1) * dx2 * (1 - dx3) * y010 + + (1 - dx1) * dx2 * dx3 * y011 + dx1 * (1 - dx2) * (1 - dx3) * y100 + + dx1 * (1 - dx2) * dx3 * y101 + dx1 * dx2 * (1 - dx3) * y110 + + dx1 * dx2 * dx3 * y111; +} + +void +Table::findValue(float axis_value1, + float &value, + bool &extrapolated) const +{ + if (axis1_->size() == 1) { + value = this->value(0); + extrapolated = false; + return; + } + size_t axis_index1 = axis1_->findAxisIndex(axis_value1); + double x1 = axis_value1; + double x1l = axis1_->axisValue(axis_index1); + double x1u = axis1_->axisValue(axis_index1 + 1); + double y1 = this->value(axis_index1); + double y2 = this->value(axis_index1 + 1); + double dx1 = (x1 - x1l) / (x1u - x1l); + value = (1 - dx1) * y1 + dx1 * y2; + extrapolated = (axis_value1 < x1l || axis_value1 > x1u); +} + +float +Table::findValue(float axis_value1) const +{ + if (axis1_->size() == 1) + return value(0); + size_t axis_index1 = axis1_->findAxisIndex(axis_value1); + double x1 = axis_value1; + double x1l = axis1_->axisValue(axis_index1); + double x1u = axis1_->axisValue(axis_index1 + 1); + double y1 = value(axis_index1); + double y2 = value(axis_index1 + 1); + double dx1 = (x1 - x1l) / (x1u - x1l); + return (1 - dx1) * y1 + dx1 * y2; +} + +float +Table::findValueClip(float axis_value1) const +{ + if (axis1_->size() == 1) + return value(0); + size_t axis_index1 = axis1_->findAxisIndex(axis_value1); + double x1 = axis_value1; + double x1l = axis1_->axisValue(axis_index1); + double x1u = axis1_->axisValue(axis_index1 + 1); + if (x1 < x1l) + return 0.0; + if (x1 > x1u) + return value(axis1_->size() - 1); + double y1 = value(axis_index1); + double y2 = value(axis_index1 + 1); + double dx1 = (x1 - x1l) / (x1u - x1l); + return (1 - dx1) * y1 + dx1 * y2; +} + +float +Table::findValue(const LibertyLibrary *, + const LibertyCell *, + const Pvt *, + float axis_value1, + float axis_value2, + float axis_value3) const +{ + return findValue(axis_value1, axis_value2, axis_value3); +} + +std::string +Table::reportValue(std::string_view result_name, + const LibertyCell *cell, + const Pvt *, + float value1, + std::string_view comment1, + float value2, + float value3, + const Unit *table_unit, + int digits) const +{ + switch (order_) { + case 0: + return reportValueOrder0(result_name, comment1, table_unit, digits); + case 1: + return reportValueOrder1(result_name, cell, value1, comment1, value2, value3, + table_unit, digits); + case 2: + return reportValueOrder2(result_name, cell, value1, comment1, value2, value3, + table_unit, digits); + case 3: + return reportValueOrder3(result_name, cell, value1, comment1, value2, value3, + table_unit, digits); + default: + return ""; + } } -string -Table1::reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *, - float value1, - const char *comment1, - float value2, - float value3, - const Unit *table_unit, - int digits) const +std::string +Table::reportValueOrder0(std::string_view result_name, + std::string_view comment1, + const Unit *table_unit, + int digits) const +{ + std::string result(result_name); + result += " constant = "; + result += table_unit->asString(value_, digits); + result.append(comment1); + result += '\n'; + return result; +} + +std::string +Table::reportValueOrder1(std::string_view result_name, + const LibertyCell *cell, + float value1, + std::string_view comment1, + float value2, + float value3, + const Unit *table_unit, + int digits) const { const Units *units = cell->libertyLibrary()->units(); const Unit *unit1 = axis1_->unit(units); - string result = "Table is indexed by\n "; + std::string result = "Table is indexed by\n "; result += axis1_->variableString(); result += " = "; result += unit1->asString(value1, digits); - if (comment1) - result += comment1; + result.append(comment1); result += '\n'; - if (axis1_->size() != 1) { size_t index1 = axis1_->findAxisIndex(value1); result += " "; @@ -939,172 +1314,44 @@ Table1::reportValue(const char *result_name, result += " "; result += unit1->asString(axis1_->axisValue(index1 + 1), digits); result += '\n'; - result += " --------------------\n"; - result += "| "; result += table_unit->asString(value(index1), digits); result += " "; - result += table_unit->asString(value(index1 + 1), - digits); + result += table_unit->asString(value(index1 + 1), digits); result += '\n'; } - - result += result_name; + result.append(result_name); result += " = "; result += table_unit->asString(findValue(value1, value2, value3), digits); result += '\n'; return result; } -void -Table1::report(const Units *units, - Report *report) const -{ - int digits = 4; - const Unit *unit1 = axis1_->unit(units); - const Unit *table_unit = units->timeUnit(); - report->reportLine("%s", tableVariableString(axis1_->variable())); - report->reportLine("------------------------------"); - string line; - for (size_t index1 = 0; index1 < axis1_->size(); index1++) { - line += unit1->asString(axis1_->axisValue(index1), digits); - line += " "; - } - report->reportLineString(line); - - line.clear(); - for (size_t index1 = 0; index1 < axis1_->size(); index1++) { - line += table_unit->asString(value(index1), digits); - line += " "; - } - report->reportLineString(line); -} - -//////////////////////////////////////////////////////////////// - -Table2::Table2(FloatTable *values, - TableAxisPtr axis1, - TableAxisPtr axis2) : - Table(), - values_(values), - axis1_(axis1), - axis2_(axis2) -{ -} - -Table2::~Table2() -{ - values_->deleteContents(); - delete values_; -} - -float -Table2::value(size_t axis_index1, - size_t axis_index2, - size_t) const -{ - return value(axis_index1, axis_index2); -} - -float -Table2::value(size_t axis_index1, - size_t axis_index2) const -{ - FloatSeq *row = (*values_)[axis_index1]; - return (*row)[axis_index2]; -} - -// Bilinear Interpolation. -float -Table2::findValue(float axis_value1, - float axis_value2, - float) const -{ - size_t size1 = axis1_->size(); - size_t size2 = axis2_->size(); - if (size1 == 1) { - if (size2 == 1) - return value(0, 0); - else { - size_t axis_index2 = axis2_->findAxisIndex(axis_value2); - double x2 = axis_value2; - double y00 = value(0, axis_index2); - double x2l = axis2_->axisValue(axis_index2); - double x2u = axis2_->axisValue(axis_index2 + 1); - double dx2 = (x2 - x2l) / (x2u - x2l); - double y01 = value(0, axis_index2 + 1); - double tbl_value - = (1 - dx2) * y00 - + dx2 * y01; - return tbl_value; - } - } - else if (size2 == 1) { - size_t axis_index1 = axis1_->findAxisIndex(axis_value1); - double x1 = axis_value1; - double y00 = value(axis_index1, 0); - double x1l = axis1_->axisValue(axis_index1); - double x1u = axis1_->axisValue(axis_index1 + 1); - double dx1 = (x1 - x1l) / (x1u - x1l); - double y10 = value(axis_index1 + 1, 0); - double tbl_value - = (1 - dx1) * y00 - + dx1 * y10; - return tbl_value; - } - else { - size_t axis_index1 = axis1_->findAxisIndex(axis_value1); - size_t axis_index2 = axis2_->findAxisIndex(axis_value2); - double x1 = axis_value1; - double x2 = axis_value2; - double y00 = value(axis_index1, axis_index2); - double x1l = axis1_->axisValue(axis_index1); - double x1u = axis1_->axisValue(axis_index1 + 1); - double dx1 = (x1 - x1l) / (x1u - x1l); - double y10 = value(axis_index1 + 1, axis_index2); - double y11 = value(axis_index1 + 1, axis_index2 + 1); - double x2l = axis2_->axisValue(axis_index2); - double x2u = axis2_->axisValue(axis_index2 + 1); - double dx2 = (x2 - x2l) / (x2u - x2l); - double y01 = value(axis_index1, axis_index2 + 1); - double tbl_value - = (1 - dx1) * (1 - dx2) * y00 - + dx1 * (1 - dx2) * y10 - + dx1 * dx2 * y11 - + (1 - dx1) * dx2 * y01; - return tbl_value; - } -} - -string -Table2::reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *, - float value1, - const char *comment1, - float value2, - float value3, - const Unit *table_unit, - int digits) const +std::string +Table::reportValueOrder2(std::string_view result_name, + const LibertyCell *cell, + float value1, + std::string_view comment1, + float value2, + float value3, + const Unit *table_unit, + int digits) const { const Units *units = cell->libertyLibrary()->units(); const Unit *unit1 = axis1_->unit(units); const Unit *unit2 = axis2_->unit(units); - string result = "------- "; - result += axis1_->variableString(), + std::string result = "------- "; + result += axis1_->variableString(); result += " = "; result += unit1->asString(value1, digits); - if (comment1) - result += comment1; + result.append(comment1); result += '\n'; - result += "| "; result += axis2_->variableString(); result += " = "; result += unit2->asString(value2, digits); result += '\n'; - size_t index1 = axis1_->findAxisIndex(value1); size_t index2 = axis2_->findAxisIndex(value2); result += "| "; @@ -1114,199 +1361,65 @@ Table2::reportValue(const char *result_name, result += unit2->asString(axis2_->axisValue(index2 + 1), digits); } result += '\n'; - result += "v --------------------\n"; result += unit1->asString(axis1_->axisValue(index1), digits); result += " | "; - result += table_unit->asString(value(index1, index2), digits); if (axis2_->size() != 1) { result += " "; result += table_unit->asString(value(index1, index2 + 1), digits); } result += '\n'; - if (axis1_->size() != 1) { result += unit1->asString(axis1_->axisValue(index1 + 1), digits); result += " | "; result += table_unit->asString(value(index1 + 1, index2), digits); if (axis2_->size() != 1) { result += " "; - result +=table_unit->asString(value(index1 + 1, index2 + 1),digits); + result += table_unit->asString(value(index1 + 1, index2 + 1), digits); } } result += '\n'; - - result += result_name; + result.append(result_name); result += " = "; result += table_unit->asString(findValue(value1, value2, value3), digits); result += '\n'; return result; } -void -Table2::report(const Units *units, - Report *report) const -{ - int digits = 4; - const Unit *table_unit = units->timeUnit(); - const Unit *unit1 = axis1_->unit(units); - const Unit *unit2 = axis2_->unit(units); - report->reportLine("%s", tableVariableString(axis2_->variable())); - report->reportLine(" ------------------------------"); - string line = " "; - for (size_t index2 = 0; index2 < axis2_->size(); index2++) { - line += unit2->asString(axis2_->axisValue(index2), digits); - line += " "; - } - report->reportLineString(line); - - for (size_t index1 = 0; index1 < axis1_->size(); index1++) { - line = unit1->asString(axis1_->axisValue(index1), digits); - line += " |"; - for (size_t index2 = 0; index2 < axis2_->size(); index2++) { - line += table_unit->asString(value(index1, index2), digits); - line += " "; - } - report->reportLineString(line); - } -} - -//////////////////////////////////////////////////////////////// - -Table3::Table3(FloatTable *values, - TableAxisPtr axis1, - TableAxisPtr axis2, - TableAxisPtr axis3) : - Table2(values, axis1, axis2), - axis3_(axis3) -{ -} - -float -Table3::value(size_t axis_index1, - size_t axis_index2, - size_t axis_index3) const -{ - size_t row = axis_index1 * axis2_->size() + axis_index2; - return values_->operator[](row)->operator[](axis_index3); -} - -// Bilinear Interpolation. -float -Table3::findValue(float axis_value1, - float axis_value2, - float axis_value3) const -{ - size_t axis_index1 = axis1_->findAxisIndex(axis_value1); - size_t axis_index2 = axis2_->findAxisIndex(axis_value2); - size_t axis_index3 = axis3_->findAxisIndex(axis_value3); - double x1 = axis_value1; - double x2 = axis_value2; - double x3 = axis_value3; - double dx1 = 0.0; - double dx2 = 0.0; - double dx3 = 0.0; - double y000 = value(axis_index1, axis_index2, axis_index3); - double y001 = 0.0; - double y010 = 0.0; - double y011 = 0.0; - double y100 = 0.0; - double y101 = 0.0; - double y110 = 0.0; - double y111 = 0.0; - - if (axis1_->size() != 1) { - double x1l = axis1_->axisValue(axis_index1); - double x1u = axis1_->axisValue(axis_index1 + 1); - dx1 = (x1 - x1l) / (x1u - x1l); - y100 = value(axis_index1 + 1, axis_index2, axis_index3); - if (axis3_->size() != 1) - y101 = value(axis_index1 + 1, axis_index2, axis_index3 + 1); - if (axis2_->size() != 1) { - y110 = value(axis_index1 + 1, axis_index2 + 1, axis_index3); - if (axis3_->size() != 1) - y111 = value(axis_index1 + 1, axis_index2 + 1, axis_index3 + 1); - } - } - if (axis2_->size() != 1) { - double x2l = axis2_->axisValue(axis_index2); - double x2u = axis2_->axisValue(axis_index2 + 1); - dx2 = (x2 - x2l) / (x2u - x2l); - y010 = value(axis_index1, axis_index2 + 1, axis_index3); - if (axis3_->size() != 1) - y011 = value(axis_index1, axis_index2 + 1, axis_index3 + 1); - } - if (axis3_->size() != 1) { - double x3l = axis3_->axisValue(axis_index3); - double x3u = axis3_->axisValue(axis_index3 + 1); - dx3 = (x3 - x3l) / (x3u - x3l); - y001 = value(axis_index1, axis_index2, axis_index3 + 1); - } - - double tbl_value - = (1 - dx1) * (1 - dx2) * (1 - dx3) * y000 - + (1 - dx1) * (1 - dx2) * dx3 * y001 - + (1 - dx1) * dx2 * (1 - dx3) * y010 - + (1 - dx1) * dx2 * dx3 * y011 - + dx1 * (1 - dx2) * (1 - dx3) * y100 - + dx1 * (1 - dx2) * dx3 * y101 - + dx1 * dx2 * (1 - dx3) * y110 - + dx1 * dx2 * dx3 * y111; - return tbl_value; -} - -// Sample output. -// -// --------- input_net_transition = 0.00 -// | ---- total_output_net_capacitance = 0.20 -// | | related_out_total_output_net_capacitance = 0.10 -// | | 0.00 0.30 -// v | -------------------- -// 0.01 v / 0.23 0.25 -// 0.00 0.20 | 0.10 0.20 -// |/ 0.30 0.32 -// 0.40 | 0.20 0.30 -string -Table3::reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *, - float value1, - const char *comment1, - float value2, - float value3, - const Unit *table_unit, - int digits) const +std::string +Table::reportValueOrder3(std::string_view result_name, + const LibertyCell *cell, + float value1, + std::string_view comment1, + float value2, + float value3, + const Unit *table_unit, + int digits) const { const Units *units = cell->libertyLibrary()->units(); const Unit *unit1 = axis1_->unit(units); const Unit *unit2 = axis2_->unit(units); const Unit *unit3 = axis3_->unit(units); - - string result = " --------- "; - result += axis1_->variableString(), + std::string result = " --------- "; + result += axis1_->variableString(); result += " = "; result += unit1->asString(value1, digits); - if (comment1) - result += comment1; + result.append(comment1); result += '\n'; - result += " | ---- "; - result += axis2_->variableString(), + result += axis2_->variableString(); result += " = "; result += unit2->asString(value2, digits); result += '\n'; - result += " | | "; result += axis3_->variableString(); result += " = "; result += unit3->asString(value3, digits); result += '\n'; - size_t axis_index1 = axis1_->findAxisIndex(value1); size_t axis_index2 = axis2_->findAxisIndex(value2); size_t axis_index3 = axis3_->findAxisIndex(value3); - result += " | | "; result += unit3->asString(axis3_->axisValue(axis_index3), digits); if (axis3_->size() != 1) { @@ -1314,134 +1427,167 @@ Table3::reportValue(const char *result_name, result += unit3->asString(axis3_->axisValue(axis_index3 + 1), digits); } result += '\n'; - result += " v | --------------------\n"; - if (axis1_->size() != 1) { result += " "; - result += unit1->asString(axis1_->axisValue(axis_index1+1), digits); + result += unit1->asString(axis1_->axisValue(axis_index1 + 1), digits); result += " v / "; - result += table_unit->asString(value(axis_index1+1,axis_index2,axis_index3), - digits); + result += table_unit->asString(value(axis_index1 + 1, axis_index2, axis_index3), + digits); if (axis3_->size() != 1) { result += " "; - result += table_unit->asString(value(axis_index1+1,axis_index2,axis_index3+1), - digits); + result += table_unit->asString( + value(axis_index1 + 1, axis_index2, axis_index3 + 1), digits); } } else { - appendSpaces(result, digits+3); + appendSpaces(result, digits + 3); result += " v / "; } result += '\n'; - result += unit1->asString(axis1_->axisValue(axis_index1), digits); result += " "; result += unit2->asString(axis2_->axisValue(axis_index2), digits); result += " | "; - result += table_unit->asString(value(axis_index1, axis_index2, axis_index3), digits); + result += + table_unit->asString(value(axis_index1, axis_index2, axis_index3), digits); if (axis3_->size() != 1) { result += " "; - result += table_unit->asString(value(axis_index1, axis_index2, axis_index3+1), - digits); + result += table_unit->asString(value(axis_index1, axis_index2, axis_index3 + 1), + digits); } result += '\n'; - result += " |/ "; - if (axis1_->size() != 1 - && axis2_->size() != 1) { - result += table_unit->asString(value(axis_index1+1,axis_index2+1,axis_index3), - digits); + if (axis1_->size() != 1 && axis2_->size() != 1) { + result += table_unit->asString( + value(axis_index1 + 1, axis_index2 + 1, axis_index3), digits); if (axis3_->size() != 1) { result += " "; - result +=table_unit->asString(value(axis_index1+1,axis_index2+1,axis_index3+1), - digits); + result += table_unit->asString( + value(axis_index1 + 1, axis_index2 + 1, axis_index3 + 1), digits); } } result += '\n'; - result += " "; result += unit2->asString(axis2_->axisValue(axis_index2 + 1), digits); result += " | "; if (axis2_->size() != 1) { - result += table_unit->asString(value(axis_index1, axis_index2+1, axis_index3), + result += table_unit->asString(value(axis_index1, axis_index2 + 1, axis_index3), digits); if (axis3_->size() != 1) { result += " "; - result +=table_unit->asString(value(axis_index1, axis_index2+1,axis_index3+1), - digits); + result += table_unit->asString( + value(axis_index1, axis_index2 + 1, axis_index3 + 1), digits); } } result += '\n'; - - result += result_name; + result.append(result_name); result += " = "; result += table_unit->asString(findValue(value1, value2, value3), digits); result += '\n'; return result; } -static void -appendSpaces(string &result, - int count) -{ - while (count--) - result += ' '; -} - void -Table3::report(const Units *units, - Report *report) const +Table::report(const Units *units, + Report *report) const { int digits = 4; const Unit *table_unit = units->timeUnit(); + if (order_ == 0) { + report->report("{}", table_unit->asString(value_, digits)); + return; + } + if (order_ == 1) { + const Unit *unit1 = axis1_->unit(units); + report->report("{}", tableVariableString(axis1_->variable())); + report->report("------------------------------"); + std::string line; + for (size_t index1 = 0; index1 < axis1_->size(); index1++) { + line += unit1->asString(axis1_->axisValue(index1), digits); + line += " "; + } + report->reportLine(line); + line.clear(); + for (size_t index1 = 0; index1 < axis1_->size(); index1++) { + line += table_unit->asString(value(index1), digits); + line += " "; + } + report->reportLine(line); + return; + } + if (order_ == 2) { + const Unit *unit1 = axis1_->unit(units); + const Unit *unit2 = axis2_->unit(units); + report->report("{}", tableVariableString(axis2_->variable())); + report->report(" ------------------------------"); + std::string line = " "; + for (size_t index2 = 0; index2 < axis2_->size(); index2++) { + line += unit2->asString(axis2_->axisValue(index2), digits); + line += " "; + } + report->reportLine(line); + for (size_t index1 = 0; index1 < axis1_->size(); index1++) { + line = unit1->asString(axis1_->axisValue(index1), digits); + line += " |"; + for (size_t index2 = 0; index2 < axis2_->size(); index2++) { + line += table_unit->asString(value(index1, index2), digits); + line += " "; + } + report->reportLine(line); + } + return; + } + // order_ == 3 const Unit *unit1 = axis1_->unit(units); const Unit *unit2 = axis2_->unit(units); const Unit *unit3 = axis3_->unit(units); for (size_t axis_index1 = 0; axis_index1 < axis1_->size(); axis_index1++) { - report->reportLine("%s %s", tableVariableString(axis1_->variable()), - unit1->asString(axis1_->axisValue(axis_index1), digits)); - - report->reportLine("%s", tableVariableString(axis3_->variable())); - report->reportLine(" ------------------------------"); - string line = " "; + report->report("{} {}", tableVariableString(axis1_->variable()), + unit1->asString(axis1_->axisValue(axis_index1), digits)); + report->report("{}", tableVariableString(axis3_->variable())); + report->report(" ------------------------------"); + std::string line = " "; for (size_t axis_index3 = 0; axis_index3 < axis3_->size(); axis_index3++) { line += unit3->asString(axis3_->axisValue(axis_index3), digits); line += " "; } - report->reportLineString(line); - + report->reportLine(line); for (size_t axis_index2 = 0; axis_index2 < axis2_->size(); axis_index2++) { - line = unit2->asString(axis2_->axisValue(axis_index2),digits); + line = unit2->asString(axis2_->axisValue(axis_index2), digits); line += " |"; for (size_t axis_index3 = 0; axis_index3 < axis3_->size(); axis_index3++) { - line += table_unit->asString(value(axis_index1, axis_index2, axis_index3),digits); + line += table_unit->asString(value(axis_index1, axis_index2, axis_index3), + digits); line += " "; } - report->reportLineString(line); + report->reportLine(line); } } } +static void +appendSpaces(std::string &result, + int count) +{ + while (count--) + result += ' '; +} + //////////////////////////////////////////////////////////////// TableAxis::TableAxis(TableAxisVariable variable, - FloatSeq *values) : + FloatSeq &&values) : variable_(variable), - values_(values) + values_(std::move(values)) { } -TableAxis::~TableAxis() -{ - delete values_; -} - float TableAxis::min() const { - if (!values_->empty()) - return (*values_)[0]; + if (!values_.empty()) + return values_[0]; else return 0.0; } @@ -1449,9 +1595,9 @@ TableAxis::min() const float TableAxis::max() const { - size_t size = values_->size(); + size_t size = values_.size(); if (size > 0) - return (*values_)[values_->size() - 1]; + return values_[values_.size() - 1]; else return 0.0; } @@ -1459,16 +1605,14 @@ TableAxis::max() const bool TableAxis::inBounds(float value) const { - size_t size = values_->size(); - return size > 1 - && value >= (*values_)[0] - && value <= (*values_)[size - 1]; + size_t size = values_.size(); + return size > 1 && value >= values_[0] && value <= values_[size - 1]; } size_t TableAxis::findAxisIndex(float value) const { - return findValueIndex(value, values_); + return findValueIndex(value, &values_); } // Bisection search. @@ -1489,9 +1633,9 @@ findValueIndex(float value, while (upper - lower > 1) { int mid = (upper + lower) >> 1; if (value >= (*values)[mid]) - lower = mid; + lower = mid; else - upper = mid; + upper = mid; } return lower; } @@ -1503,23 +1647,21 @@ TableAxis::findAxisIndex(float value, size_t &index, bool &exists) const { - size_t size = values_->size(); - if (size != 0 - && value >= (*values_)[0] - && value <= (*values_)[size - 1]) { + size_t size = values_.size(); + if (size != 0 && value >= values_[0] && value <= values_[size - 1]) { int lower = -1; int upper = size; while (upper - lower > 1) { int mid = (upper + lower) >> 1; - if (value == (*values_)[mid]) { + if (value == values_[mid]) { index = mid; exists = true; return; } - if (value > (*values_)[mid]) - lower = mid; + if (value > values_[mid]) + lower = mid; else - upper = mid; + upper = mid; } } exists = false; @@ -1528,29 +1670,29 @@ TableAxis::findAxisIndex(float value, size_t TableAxis::findAxisClosestIndex(float value) const { - size_t size = values_->size(); - if (size <= 1 || value <= (*values_)[0]) + size_t size = values_.size(); + if (size <= 1 || value <= values_[0]) return 0; - else if (value >= (*values_)[size - 1]) + else if (value >= values_[size - 1]) return size - 1; else { int lower = -1; int upper = size; while (upper - lower > 1) { int mid = (upper + lower) >> 1; - if (value >= (*values_)[mid]) - lower = mid; + if (value >= values_[mid]) + lower = mid; else - upper = mid; + upper = mid; } - if ((value - (*values_)[lower]) < ((*values_)[upper] - value)) + if ((value - values_[lower]) < (values_[upper] - value)) return lower; else return upper; } } -const char * +std::string_view TableAxis::variableString() const { return tableVariableString(variable_); @@ -1564,34 +1706,36 @@ TableAxis::unit(const Units *units) //////////////////////////////////////////////////////////////// -static EnumNameMap table_axis_variable_map = - {{TableAxisVariable::total_output_net_capacitance, "total_output_net_capacitance"}, - {TableAxisVariable::equal_or_opposite_output_net_capacitance, "equal_or_opposite_output_net_capacitance"}, - {TableAxisVariable::input_net_transition, "input_net_transition"}, - {TableAxisVariable::input_transition_time, "input_transition_time"}, - {TableAxisVariable::related_pin_transition, "related_pin_transition"}, - {TableAxisVariable::constrained_pin_transition, "constrained_pin_transition"}, - {TableAxisVariable::output_pin_transition, "output_pin_transition"}, - {TableAxisVariable::connect_delay, "connect_delay"}, - {TableAxisVariable::related_out_total_output_net_capacitance, "related_out_total_output_net_capacitance"}, - {TableAxisVariable::time, "time"}, - {TableAxisVariable::iv_output_voltage, "iv_output_voltage"}, - {TableAxisVariable::input_noise_width, "input_noise_width"}, - {TableAxisVariable::input_noise_height, "input_noise_height"}, - {TableAxisVariable::input_voltage, "input_voltage"}, - {TableAxisVariable::output_voltage, "output_voltage"}, - {TableAxisVariable::path_depth, "path_depth"}, - {TableAxisVariable::path_distance, "path_distance"}, - {TableAxisVariable::normalized_voltage, "normalized_voltage"} - }; +static EnumNameMap table_axis_variable_map = { + {TableAxisVariable::total_output_net_capacitance, + "total_output_net_capacitance"}, + {TableAxisVariable::equal_or_opposite_output_net_capacitance, + "equal_or_opposite_output_net_capacitance"}, + {TableAxisVariable::input_net_transition, "input_net_transition"}, + {TableAxisVariable::input_transition_time, "input_transition_time"}, + {TableAxisVariable::related_pin_transition, "related_pin_transition"}, + {TableAxisVariable::constrained_pin_transition, "constrained_pin_transition"}, + {TableAxisVariable::output_pin_transition, "output_pin_transition"}, + {TableAxisVariable::connect_delay, "connect_delay"}, + {TableAxisVariable::related_out_total_output_net_capacitance, + "related_out_total_output_net_capacitance"}, + {TableAxisVariable::time, "time"}, + {TableAxisVariable::iv_output_voltage, "iv_output_voltage"}, + {TableAxisVariable::input_noise_width, "input_noise_width"}, + {TableAxisVariable::input_noise_height, "input_noise_height"}, + {TableAxisVariable::input_voltage, "input_voltage"}, + {TableAxisVariable::output_voltage, "output_voltage"}, + {TableAxisVariable::path_depth, "path_depth"}, + {TableAxisVariable::path_distance, "path_distance"}, + {TableAxisVariable::normalized_voltage, "normalized_voltage"}}; TableAxisVariable -stringTableAxisVariable(const char *variable) +stringTableAxisVariable(std::string_view variable) { return table_axis_variable_map.find(variable, TableAxisVariable::unknown); } -const char * +std::string_view tableVariableString(TableAxisVariable variable) { return table_axis_variable_map.find(variable); @@ -1599,33 +1743,33 @@ tableVariableString(TableAxisVariable variable) const Unit * tableVariableUnit(TableAxisVariable variable, - const Units *units) + const Units *units) { switch (variable) { - case TableAxisVariable::total_output_net_capacitance: - case TableAxisVariable::related_out_total_output_net_capacitance: - case TableAxisVariable::equal_or_opposite_output_net_capacitance: - return units->capacitanceUnit(); - case TableAxisVariable::input_net_transition: - case TableAxisVariable::input_transition_time: - case TableAxisVariable::related_pin_transition: - case TableAxisVariable::constrained_pin_transition: - case TableAxisVariable::output_pin_transition: - case TableAxisVariable::connect_delay: - case TableAxisVariable::time: - case TableAxisVariable::input_noise_height: - return units->timeUnit(); - case TableAxisVariable::input_voltage: - case TableAxisVariable::output_voltage: - case TableAxisVariable::iv_output_voltage: - case TableAxisVariable::input_noise_width: - return units->voltageUnit(); - case TableAxisVariable::path_distance: - return units->distanceUnit(); - case TableAxisVariable::path_depth: - case TableAxisVariable::normalized_voltage: - case TableAxisVariable::unknown: - return units->scalarUnit(); + case TableAxisVariable::total_output_net_capacitance: + case TableAxisVariable::related_out_total_output_net_capacitance: + case TableAxisVariable::equal_or_opposite_output_net_capacitance: + return units->capacitanceUnit(); + case TableAxisVariable::input_net_transition: + case TableAxisVariable::input_transition_time: + case TableAxisVariable::related_pin_transition: + case TableAxisVariable::constrained_pin_transition: + case TableAxisVariable::output_pin_transition: + case TableAxisVariable::connect_delay: + case TableAxisVariable::time: + case TableAxisVariable::input_noise_height: + return units->timeUnit(); + case TableAxisVariable::input_voltage: + case TableAxisVariable::output_voltage: + case TableAxisVariable::iv_output_voltage: + case TableAxisVariable::input_noise_width: + return units->voltageUnit(); + case TableAxisVariable::path_distance: + return units->distanceUnit(); + case TableAxisVariable::path_depth: + case TableAxisVariable::normalized_voltage: + case TableAxisVariable::unknown: + return units->scalarUnit(); } // Prevent warnings from lame compilers. return nullptr; @@ -1637,22 +1781,20 @@ OutputWaveforms::OutputWaveforms(TableAxisPtr slew_axis, TableAxisPtr cap_axis, const RiseFall *rf, Table1Seq ¤t_waveforms, - Table1 *ref_times) : - slew_axis_(slew_axis), - cap_axis_(cap_axis), + Table ref_times) : + slew_axis_(std::move(slew_axis)), + cap_axis_(std::move(cap_axis)), rf_(rf), current_waveforms_(current_waveforms), - ref_times_(ref_times), - vdd_(0.0) + ref_times_(std::move(ref_times)) { } OutputWaveforms::~OutputWaveforms() { - current_waveforms_.deleteContents(); - voltage_waveforms_.deleteContents(); - voltage_currents_.deleteContents(); - delete ref_times_; + deleteContents(current_waveforms_); + deleteContents(voltage_waveforms_); + deleteContents(voltage_currents_); } bool @@ -1662,12 +1804,13 @@ OutputWaveforms::checkAxes(const TableTemplate *tbl_template) const TableAxis *axis2 = tbl_template->axis2(); const TableAxis *axis3 = tbl_template->axis3(); return (axis1 && axis1->variable() == TableAxisVariable::input_net_transition - && axis2->variable() == TableAxisVariable::time - && axis3 == nullptr) - || (axis1 && axis1->variable() == TableAxisVariable::input_net_transition - && axis2 && axis2->variable() == TableAxisVariable::total_output_net_capacitance + && axis2->variable() == TableAxisVariable::time && axis3 == nullptr) + || (axis1 && axis1->variable() == TableAxisVariable::input_net_transition + && axis2 + && axis2->variable() == TableAxisVariable::total_output_net_capacitance && axis3->variable() == TableAxisVariable::time) - || (axis1 && axis1->variable() == TableAxisVariable::total_output_net_capacitance + || (axis1 + && axis1->variable() == TableAxisVariable::total_output_net_capacitance && axis2 && axis2->variable() == TableAxisVariable::input_net_transition && axis3->variable() == TableAxisVariable::time); } @@ -1696,13 +1839,13 @@ OutputWaveforms::findVoltages(size_t wave_index, { // Integrate current waveform to find voltage waveform. // i = C dv/dt - FloatSeq *volts = new FloatSeq; - Table1 *currents = current_waveforms_[wave_index]; + FloatSeq volts; + Table *currents = current_waveforms_[wave_index]; const TableAxis *time_axis = currents->axis1(); float prev_time = time_axis->axisValue(0); float prev_current = currents->value(0); float voltage = 0.0; - volts->push_back(voltage); + volts.push_back(voltage); bool always_rise = true; bool invert = (always_rise && rf_ == RiseFall::fall()); for (size_t i = 1; i < time_axis->size(); i++) { @@ -1710,24 +1853,24 @@ OutputWaveforms::findVoltages(size_t wave_index, float current = currents->value(i); float dv = (current + prev_current) / 2.0 * (time - prev_time) / cap; voltage += invert ? -dv : dv; - volts->push_back(voltage); + volts.push_back(voltage); prev_time = time; prev_current = current; } - (*volts)[volts->size() - 1] = vdd_; - Table1 *volt_table = new Table1(volts, currents->axis1ptr()); + volts[volts.size() - 1] = vdd_; + Table *volt_table = new Table(new FloatSeq(volts), currents->axis1ptr()); voltage_waveforms_[wave_index] = volt_table; // Make voltage -> current table. - FloatSeq *axis_volts = new FloatSeq(*volts); - TableAxisPtr volt_axis = - make_shared(TableAxisVariable::input_voltage, axis_volts); + FloatSeq axis_volts = volts; + TableAxisPtr volt_axis = std::make_shared( + TableAxisVariable::input_voltage, std::move(axis_volts)); FloatSeq *currents1 = new FloatSeq(*currents->values()); - Table1 *volt_currents = new Table1(currents1, volt_axis); + Table *volt_currents = new Table(currents1, volt_axis); voltage_currents_[wave_index] = volt_currents; } -Table1 +Table OutputWaveforms::currentWaveform(float slew, float cap) { @@ -1740,11 +1883,13 @@ OutputWaveforms::currentWaveform(float slew, times->push_back(time); currents->push_back(current); } - TableAxisPtr time_axis = make_shared(TableAxisVariable::time, times); - return Table1(currents, time_axis); + TableAxisPtr time_axis = + std::make_shared(TableAxisVariable::time, std::move(*times)); + delete times; + return Table(currents, time_axis); } -const Table1 * +const Table * OutputWaveforms::currentWaveformRaw(float slew, float cap) { @@ -1821,11 +1966,8 @@ OutputWaveforms::voltageTime1(double volt, double y01 = voltageTime2(volt, wave_index01); double y10 = voltageTime2(volt, wave_index10); double y11 = voltageTime2(volt, wave_index11); - double time - = (1 - dx1) * (1 - dx2) * y00 - + dx1 * (1 - dx2) * y10 - + dx1 * dx2 * y11 - + (1 - dx1) * dx2 * y01; + double time = (1 - dx1) * (1 - dx2) * y00 + dx1 * (1 - dx2) * y10 + dx1 * dx2 * y11 + + (1 - dx1) * dx2 * y01; return time; } @@ -1833,7 +1975,7 @@ float OutputWaveforms::voltageTime2(float volt, size_t wave_index) { - const Table1 *voltage_waveform = voltage_waveforms_[wave_index]; + const Table *voltage_waveform = voltage_waveforms_[wave_index]; const FloatSeq *voltages = voltage_waveform->values(); size_t index1 = findValueIndex(volt, voltages); float volt_lo = (*voltages)[index1]; @@ -1869,10 +2011,10 @@ OutputWaveforms::waveformValue(float slew, size_t wave_index10 = (slew_index + 1) * cap_count + cap_index; size_t wave_index11 = (slew_index + 1) * cap_count + (cap_index + 1); - const Table1 *waveform00 = waveforms[wave_index00]; - const Table1 *waveform01 = waveforms[wave_index01]; - const Table1 *waveform10 = waveforms[wave_index10]; - const Table1 *waveform11 = waveforms[wave_index11]; + const Table *waveform00 = waveforms[wave_index00]; + const Table *waveform01 = waveforms[wave_index01]; + const Table *waveform10 = waveforms[wave_index10]; + const Table *waveform11 = waveforms[wave_index11]; // Interpolate waveform samples at voltage steps. size_t index1 = slew_index; @@ -1890,37 +2032,35 @@ OutputWaveforms::waveformValue(float slew, double y01 = waveform01->findValueClip(axis_value); double y10 = waveform10->findValueClip(axis_value); double y11 = waveform11->findValueClip(axis_value); - double wave_value - = (1 - dx1) * (1 - dx2) * y00 - + dx1 * (1 - dx2) * y10 - + dx1 * dx2 * y11 - + (1 - dx1) * dx2 * y01; + double wave_value = (1 - dx1) * (1 - dx2) * y00 + dx1 * (1 - dx2) * y10 + + dx1 * dx2 * y11 + (1 - dx1) * dx2 * y01; return wave_value; } float OutputWaveforms::referenceTime(float slew) { - return ref_times_->findValue(slew); + return ref_times_.findValue(slew); } -Table1 +Table OutputWaveforms::voltageWaveform(float slew, float cap) { - FloatSeq *times = new FloatSeq; - FloatSeq *volts = new FloatSeq; + FloatSeq times; + FloatSeq volts; for (size_t i = 0; i <= voltage_waveform_step_count_; i++) { float volt = i * vdd_ / voltage_waveform_step_count_; float time = voltageTime(slew, cap, volt); - times->push_back(time); - volts->push_back(volt); + times.push_back(time); + volts.push_back(volt); } - TableAxisPtr time_axis = make_shared(TableAxisVariable::time, times); - return Table1(volts, time_axis); + TableAxisPtr time_axis = + std::make_shared(TableAxisVariable::time, std::move(times)); + return Table(std::move(volts), time_axis); } -const Table1 * +const Table * OutputWaveforms::voltageWaveformRaw(float slew, float cap) { @@ -1933,8 +2073,8 @@ OutputWaveforms::voltageWaveformRaw(float slew, float OutputWaveforms::voltageTime(float slew, - float cap, - float volt) + float cap, + float volt) { size_t slew_index = slew_axis_->findAxisIndex(slew); size_t cap_index = cap_axis_->findAxisIndex(cap); @@ -1988,10 +2128,10 @@ OutputWaveforms::beginEndTime(float slew, size_t wave_index10 = (slew_index + 1) * cap_count + cap_index; size_t wave_index11 = (slew_index + 1) * cap_count + (cap_index + 1); - const Table1 *waveform00 = current_waveforms_[wave_index00]; - const Table1 *waveform01 = current_waveforms_[wave_index01]; - const Table1 *waveform10 = current_waveforms_[wave_index10]; - const Table1 *waveform11 = current_waveforms_[wave_index11]; + const Table *waveform00 = current_waveforms_[wave_index00]; + const Table *waveform01 = current_waveforms_[wave_index01]; + const Table *waveform10 = current_waveforms_[wave_index10]; + const Table *waveform11 = current_waveforms_[wave_index11]; // Interpolate waveform samples at voltage steps. size_t index1 = slew_index; @@ -2019,15 +2159,12 @@ OutputWaveforms::beginEndTime(float slew, y11 = waveform11->axis1()->max(); } - float wave_value - = (1 - dx1) * (1 - dx2) * y00 - + dx1 * (1 - dx2) * y10 - + dx1 * dx2 * y11 - + (1 - dx1) * dx2 * y01; + float wave_value = (1 - dx1) * (1 - dx2) * y00 + dx1 * (1 - dx2) * y10 + + dx1 * dx2 * y11 + (1 - dx1) * dx2 * y01; return wave_value; } -Table1 +Table OutputWaveforms::voltageCurrentWaveform(float slew, float cap) { @@ -2039,9 +2176,10 @@ OutputWaveforms::voltageCurrentWaveform(float slew, volts->push_back(volt); currents->push_back(current); } - TableAxisPtr volt_axis = - make_shared(TableAxisVariable::input_voltage, volts); - return Table1(currents, volt_axis); + TableAxisPtr volt_axis = std::make_shared( + TableAxisVariable::input_voltage, std::move(*volts)); + delete volts; + return Table(currents, volt_axis); } // Incremental resistance at final value of waveform. @@ -2054,37 +2192,38 @@ OutputWaveforms::finalResistance() size_t cap_count = cap_axis_->size(); size_t cap_index = cap_count - 1; size_t wave_index = slew_index * cap_count + cap_index; - const Table1 *voltage_currents = voltage_currents_[wave_index]; - FloatSeq *voltages = voltage_currents->axis1()->values(); + const Table *voltage_currents = voltage_currents_[wave_index]; + const FloatSeq &voltages = voltage_currents->axis1()->values(); FloatSeq *currents = voltage_currents->values(); - size_t idx_last1 = voltages->size() - 2; - return (vdd_ - (*voltages)[idx_last1]) / abs((*currents)[idx_last1]); + size_t idx_last1 = voltages.size() - 2; + return (vdd_ - voltages[idx_last1]) / std::abs((*currents)[idx_last1]); } //////////////////////////////////////////////////////////////// -DriverWaveform::DriverWaveform(const string &name, +DriverWaveform::DriverWaveform(std::string name, TablePtr waveforms) : - name_(name), - waveforms_(waveforms) + name_(std::move(name)), + waveforms_(std::move(waveforms)) { } -Table1 +Table DriverWaveform::waveform(float slew) { const TableAxis *volt_axis = waveforms_->axis2(); FloatSeq *time_values = new FloatSeq; FloatSeq *volt_values = new FloatSeq; - for (float volt : *volt_axis->values()) { + for (float volt : volt_axis->values()) { float time = waveforms_->findValue(slew, volt, 0.0); time_values->push_back(time); volt_values->push_back(volt); } - TableAxisPtr time_axis = make_shared(TableAxisVariable::time, - time_values); - Table1 waveform(volt_values, time_axis); + TableAxisPtr time_axis = + std::make_shared(TableAxisVariable::time, std::move(*time_values)); + delete time_values; + Table waveform(volt_values, time_axis); return waveform; } -} // namespace +} // namespace sta diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc index f203a4afe..dd1694bdb 100644 --- a/liberty/TimingArc.cc +++ b/liberty/TimingArc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,27 +22,36 @@ // // This notice may not be removed or altered from any source distribution. -#include "TimingModel.hh" +#include "TimingArc.hh" + +#include +#include +#include +#include +#include +#include "ContainerHelpers.hh" +#include "Delay.hh" #include "EnumNameMap.hh" +#include "Error.hh" #include "FuncExpr.hh" -#include "TimingRole.hh" +#include "LibertyClass.hh" #include "Liberty.hh" -#include "TimingArc.hh" -#include "DcalcAnalysisPt.hh" +#include "MinMax.hh" +#include "Sdc.hh" #include "TableModel.hh" +#include "TimingModel.hh" +#include "TimingRole.hh" +#include "Transition.hh" namespace sta { -using std::string; -using std::make_shared; - static bool timingArcsEquiv(const TimingArcSet *set1, - const TimingArcSet *set2); + const TimingArcSet *set2); static bool timingArcsLess(const TimingArcSet *set1, - const TimingArcSet *set2); + const TimingArcSet *set2); //////////////////////////////////////////////////////////////// @@ -50,11 +59,6 @@ TimingArcAttrs::TimingArcAttrs() : timing_type_(TimingType::combinational), timing_sense_(TimingSense::unknown), cond_(nullptr), - sdf_cond_(nullptr), - sdf_cond_start_(nullptr), - sdf_cond_end_(nullptr), - mode_name_(nullptr), - mode_value_(nullptr), ocv_arc_depth_(0.0), models_{nullptr, nullptr} { @@ -64,11 +68,6 @@ TimingArcAttrs::TimingArcAttrs(TimingSense sense) : timing_type_(TimingType::combinational), timing_sense_(sense), cond_(nullptr), - sdf_cond_(nullptr), - sdf_cond_start_(nullptr), - sdf_cond_end_(nullptr), - mode_name_(nullptr), - mode_value_(nullptr), ocv_arc_depth_(0.0), models_{nullptr, nullptr} { @@ -76,15 +75,7 @@ TimingArcAttrs::TimingArcAttrs(TimingSense sense) : TimingArcAttrs::~TimingArcAttrs() { - if (cond_) - cond_->deleteSubexprs(); - if (sdf_cond_start_ != sdf_cond_) - stringDelete(sdf_cond_start_); - if (sdf_cond_end_ != sdf_cond_) - stringDelete(sdf_cond_end_); - stringDelete(sdf_cond_); - stringDelete(mode_name_); - stringDelete(mode_value_); + delete cond_; delete models_[RiseFall::riseIndex()]; delete models_[RiseFall::fallIndex()]; } @@ -108,39 +99,34 @@ TimingArcAttrs::setCond(FuncExpr *cond) } void -TimingArcAttrs::setSdfCond(const char *cond) +TimingArcAttrs::setSdfCond(std::string_view cond) { - stringDelete(sdf_cond_); - sdf_cond_ = stringCopy(cond); + sdf_cond_ = cond; sdf_cond_start_ = sdf_cond_end_ = sdf_cond_; } void -TimingArcAttrs::setSdfCondStart(const char *cond) +TimingArcAttrs::setSdfCondStart(std::string_view cond) { - stringDelete(sdf_cond_start_); - sdf_cond_start_ = stringCopy(cond); + sdf_cond_start_ = cond; } void -TimingArcAttrs::setSdfCondEnd(const char *cond) +TimingArcAttrs::setSdfCondEnd(std::string_view cond) { - stringDelete(sdf_cond_end_); - sdf_cond_end_ = stringCopy(cond); + sdf_cond_end_ = cond; } void -TimingArcAttrs::setModeName(const char *name) +TimingArcAttrs::setModeName(std::string_view name) { - stringDelete(mode_name_); - mode_name_ = stringCopy(name); + mode_name_ = name; } void -TimingArcAttrs::setModeValue(const char *value) +TimingArcAttrs::setModeValue(std::string_view value) { - stringDelete(mode_value_); - mode_value_ = stringCopy(value); + mode_value_ = value; } TimingModel * @@ -151,7 +137,7 @@ TimingArcAttrs::model(const RiseFall *rf) const void TimingArcAttrs::setModel(const RiseFall *rf, - TimingModel *model) + TimingModel *model) { models_[rf->index()] = model; } @@ -177,9 +163,8 @@ TimingArc::intrinsicDelay() const { GateTimingModel *model = dynamic_cast(model_); if (model) { - ArcDelay arc_delay; - Slew slew; - model->gateDelay(nullptr, 0.0, 0.0, false, arc_delay, slew); + float arc_delay, slew; + model->gateDelay(nullptr, 0.0, 0.0, arc_delay, slew); return arc_delay; } else @@ -191,20 +176,20 @@ TimingArc::intrinsicDelay() const TimingArcAttrsPtr TimingArcSet::wire_timing_arc_attrs_ = nullptr; TimingArcSet *TimingArcSet::wire_timing_arc_set_ = nullptr; -TimingArcSet::TimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - const TimingRole *role, - TimingArcAttrsPtr attrs) : +TimingArcSet::TimingArcSet(LibertyCell *, + LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + const TimingRole *role, + TimingArcAttrsPtr attrs, + size_t index) : from_(from), to_(to), related_out_(related_out), role_(role), - attrs_(attrs), + attrs_(std::move(attrs)), is_cond_default_(false), - index_(cell->addTimingArcSet(this)), - is_disabled_constraint_(false), + index_(index), from_arc1_{nullptr, nullptr}, from_arc2_{nullptr, nullptr}, to_arc_{nullptr, nullptr} @@ -217,19 +202,27 @@ TimingArcSet::TimingArcSet(const TimingRole *role, to_(nullptr), related_out_(nullptr), role_(role), - attrs_(attrs), + attrs_(std::move(attrs)), is_cond_default_(false), index_(0), - is_disabled_constraint_(false), from_arc1_{nullptr, nullptr}, from_arc2_{nullptr, nullptr}, to_arc_{nullptr, nullptr} { } +std::string +TimingArcSet::to_string() const +{ + std::string str = from_->name(); + str += " -> "; + str += to_->name(); + return str; +} + TimingArcSet::~TimingArcSet() { - arcs_.deleteContents(); + deleteContents(arcs_); } bool @@ -248,12 +241,13 @@ TimingArcSet::libertyCell() const return nullptr; } -TimingArcIndex +size_t TimingArcSet::addTimingArc(TimingArc *arc) { - TimingArcIndex arc_index = arcs_.size(); + size_t arc_index = arcs_.size(); // Rise/fall to rise/fall. - if (arc_index > RiseFall::index_count * RiseFall::index_count) + if (arc_index > static_cast(RiseFall::index_count) + * RiseFall::index_count) criticalError(243, "timing arc max index exceeded\n"); arcs_.push_back(arc); @@ -304,6 +298,12 @@ TimingArcSet::setRole(const TimingRole *role) role_ = role; } +void +TimingArcSet::setIndex(size_t index) +{ + index_ = index; +} + void TimingArcSet::setIsCondDefault(bool is_default) { @@ -312,9 +312,9 @@ TimingArcSet::setIsCondDefault(bool is_default) void TimingArcSet::arcsFrom(const RiseFall *from_rf, - // Return values. - TimingArc *&arc1, - TimingArc *&arc2) const + // Return values. + TimingArc *&arc1, + TimingArc *&arc2) const { int rf_index = from_rf->index(); arc1 = from_arc1_[rf_index]; @@ -343,18 +343,12 @@ TimingArcSet::isRisingFallingEdge() const if (from_rf1 == from_rf2) return from_rf1; } - if (arcs_.size() == 1) + if (arc_count == 1) return arcs_[0]->fromEdge()->asRiseFall(); else return nullptr; } -void -TimingArcSet::setIsDisabledConstraint(bool is_disabled) -{ - is_disabled_constraint_ = is_disabled; -} - float TimingArcSet::ocvArcDepth() const { @@ -366,11 +360,11 @@ TimingArcSet::ocvArcDepth() const LibertyCell *cell = from_->libertyCell(); depth = cell->ocvArcDepth(); if (depth != 0.0) - return depth; + return depth; else { - depth = cell->libertyLibrary()->ocvArcDepth(); - if (depth != 0.0) - return depth; + depth = cell->libertyLibrary()->ocvArcDepth(); + if (depth != 0.0) + return depth; } } } @@ -380,21 +374,21 @@ TimingArcSet::ocvArcDepth() const bool TimingArcSet::equiv(const TimingArcSet *set1, - const TimingArcSet *set2) + const TimingArcSet *set2) { return LibertyPort::equiv(set1->from(), set2->from()) && LibertyPort::equiv(set1->to(), set2->to()) && set1->role() == set2->role() && FuncExpr::equiv(set1->cond(), set2->cond()) - && stringEqIf(set1->sdfCond(), set2->sdfCond()) - && stringEqIf(set1->sdfCondStart(), set2->sdfCondStart()) - && stringEqIf(set1->sdfCondEnd(), set2->sdfCondEnd()) + && set1->sdfCond() == set2->sdfCond() + && set1->sdfCondStart() == set2->sdfCondStart() + && set1->sdfCondEnd() == set2->sdfCondEnd() && timingArcsEquiv(set1, set2); } static bool timingArcsEquiv(const TimingArcSet *arc_set1, - const TimingArcSet *arc_set2) + const TimingArcSet *arc_set2) { const TimingArcSeq &arcs1 = arc_set1->arcs(); const TimingArcSeq &arcs2 = arc_set2->arcs(); @@ -414,14 +408,14 @@ timingArcsEquiv(const TimingArcSet *arc_set1, bool TimingArcSet::less(const TimingArcSet *set1, - const TimingArcSet *set2) + const TimingArcSet *set2) { return timingArcSetLess(set1, set2); } bool timingArcSetLess(const TimingArcSet *set1, - const TimingArcSet *set2) + const TimingArcSet *set2) { LibertyPort *from1 = set1->from(); LibertyPort *from2 = set2->from(); @@ -432,45 +426,45 @@ timingArcSetLess(const TimingArcSet *set1, const TimingRole *role1 = set1->role(); const TimingRole *role2 = set2->role(); if (role1 == role2) { - const FuncExpr *cond1 = set1->cond(); - const FuncExpr *cond2 = set2->cond(); - if (FuncExpr::equiv(cond1, cond2)) { - const char *sdf_cond1 = set1->sdfCond(); - const char *sdf_cond2 = set2->sdfCond(); - if (stringEqIf(sdf_cond1, sdf_cond2)) { - const char *sdf_cond_start1 = set1->sdfCondStart(); - const char *sdf_cond_start2 = set2->sdfCondStart(); - if (stringEqIf(sdf_cond_start1, sdf_cond_start2)) { - const char *sdf_cond_end1 = set1->sdfCondEnd(); - const char *sdf_cond_end2 = set2->sdfCondEnd(); - if (stringEqIf(sdf_cond_end1, sdf_cond_end2)) { - const char *mode_name1 = set1->modeName(); - const char *mode_name2 = set2->modeName(); - if (stringEqIf(mode_name1, mode_name2)) { - const char *mode_value1 = set1->modeValue(); - const char *mode_value2 = set2->modeValue(); - if (stringEqIf(mode_value1, mode_value2)) - return timingArcsLess(set1, set2); - else - return stringLessIf(mode_value1, mode_value2); - } - else - return stringLessIf(mode_name1, mode_name2); - } - else - return stringLessIf(sdf_cond_end1, sdf_cond_end2); - } - else - return stringLessIf(sdf_cond_start1, sdf_cond_start2); - } - else - return stringLessIf(sdf_cond1, sdf_cond2); - } - else - return FuncExpr::less(cond1, cond2); + const FuncExpr *cond1 = set1->cond(); + const FuncExpr *cond2 = set2->cond(); + if (FuncExpr::equiv(cond1, cond2)) { + const std::string &sdf_cond1 = set1->sdfCond(); + const std::string &sdf_cond2 = set2->sdfCond(); + if (sdf_cond1 == sdf_cond2) { + const std::string &sdf_cond_start1 = set1->sdfCondStart(); + const std::string &sdf_cond_start2 = set2->sdfCondStart(); + if (sdf_cond_start1 == sdf_cond_start2) { + const std::string &sdf_cond_end1 = set1->sdfCondEnd(); + const std::string &sdf_cond_end2 = set2->sdfCondEnd(); + if (sdf_cond_end1 == sdf_cond_end2) { + const std::string &mode_name1 = set1->modeName(); + const std::string &mode_name2 = set2->modeName(); + if (mode_name1 == mode_name2) { + const std::string &mode_value1 = set1->modeValue(); + const std::string &mode_value2 = set2->modeValue(); + if (mode_value1 == mode_value2) + return timingArcsLess(set1, set2); + else + return mode_value1 < mode_value2; + } + else + return mode_name1 < mode_name2; + } + else + return sdf_cond_end1 < sdf_cond_end2; + } + else + return sdf_cond_start1 < sdf_cond_start2; + } + else + return sdf_cond1 < sdf_cond2; + } + else + return FuncExpr::less(cond1, cond2); } else - return TimingRole::less(role1, role2); + return TimingRole::less(role1, role2); } else return LibertyPort::less(to1, to2); @@ -481,7 +475,7 @@ timingArcSetLess(const TimingArcSet *set1, static bool timingArcsLess(const TimingArcSet *arc_set1, - const TimingArcSet *arc_set2) + const TimingArcSet *arc_set2) { const TimingArcSeq &arcs1 = arc_set1->arcs(); const TimingArcSeq &arcs2 = arc_set2->arcs(); @@ -524,12 +518,12 @@ TimingArcSet::wireArcIndex(const RiseFall *rf) void TimingArcSet::init() { - wire_timing_arc_attrs_ = make_shared(TimingSense::positive_unate); + wire_timing_arc_attrs_ = std::make_shared(TimingSense::positive_unate); wire_timing_arc_set_ = new TimingArcSet(TimingRole::wire(), wire_timing_arc_attrs_); new TimingArc(wire_timing_arc_set_, Transition::rise(), - Transition::rise(), nullptr); + Transition::rise(), nullptr); new TimingArc(wire_timing_arc_set_, Transition::fall(), - Transition::fall(), nullptr); + Transition::fall(), nullptr); } void @@ -543,14 +537,13 @@ TimingArcSet::destroy() //////////////////////////////////////////////////////////////// TimingArc::TimingArc(TimingArcSet *set, - const Transition *from_rf, - const Transition *to_rf, - TimingModel *model) : + const Transition *from_rf, + const Transition *to_rf, + TimingModel *model) : set_(set), from_rf_(from_rf), to_rf_(to_rf), - model_(model), - scaled_models_(nullptr) + model_(model) { index_ = set->addTimingArc(this); } @@ -562,18 +555,18 @@ TimingArc::~TimingArc() delete scaled_models_; } -string +std::string TimingArc::to_string() const { if (set_->role()->isWire()) { - string str = "wire "; + std::string str = "wire "; str += from_rf_->to_string(); str += " -> "; str += to_rf_->to_string(); return str; } else { - string str = set_->from()->name(); + std::string str = set_->from()->name(); str += " "; str += from_rf_->to_string(); str += " -> "; @@ -585,9 +578,10 @@ TimingArc::to_string() const } GateTimingModel * -TimingArc::gateModel(const DcalcAnalysisPt *dcalc_ap) const +TimingArc::gateModel(const Scene *scene, + const MinMax *min_max) const { - return dynamic_cast(model(dcalc_ap)); + return dynamic_cast(model(scene, min_max)); } GateTableModel * @@ -597,34 +591,38 @@ TimingArc::gateTableModel() const } GateTableModel * -TimingArc::gateTableModel(const DcalcAnalysisPt *dcalc_ap) const +TimingArc::gateTableModel(const Scene *scene, + const MinMax *min_max) const { - return dynamic_cast(model(dcalc_ap)); + return dynamic_cast(model(scene, min_max)); } CheckTimingModel * -TimingArc::checkModel(const DcalcAnalysisPt *dcalc_ap) const +TimingArc::checkModel(const Scene *scene, + const MinMax *min_max) const { - return dynamic_cast(model(dcalc_ap)); + return dynamic_cast(model(scene, min_max)); } TimingModel * -TimingArc::model(const DcalcAnalysisPt *dcalc_ap) const +TimingArc::model(const Scene *scene, + const MinMax *min_max) const { - const TimingArc *corner_arc = cornerArc(dcalc_ap->libertyIndex()); - ScaledTimingModelMap *scaled_models = corner_arc->scaled_models_; + const TimingArc *scene_arc = sceneArc(scene->libertyIndex(min_max)); + ScaledTimingModelMap *scaled_models = scene_arc->scaled_models_; if (scaled_models) { - const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); - TimingModel *scaled_model = scaled_models->findKey(op_cond); + const OperatingConditions *op_cond = + scene->sdc()->operatingConditions(min_max); + TimingModel *scaled_model = findKey(*scaled_models, op_cond); if (scaled_model) return scaled_model; } - return corner_arc->model(); + return scene_arc->model(); } void TimingArc::addScaledModel(const OperatingConditions *op_cond, - TimingModel *scaled_model) + TimingModel *scaled_model) { if (scaled_models_ == nullptr) scaled_models_ = new ScaledTimingModelMap; @@ -633,36 +631,36 @@ TimingArc::addScaledModel(const OperatingConditions *op_cond, bool TimingArc::equiv(const TimingArc *arc1, - const TimingArc *arc2) + const TimingArc *arc2) { return arc1->fromEdge() == arc2->fromEdge() && arc1->toEdge() == arc2->toEdge(); } void -TimingArc::setIndex(unsigned index) +TimingArc::setIndex(size_t index) { index_ = index; } const TimingArc * -TimingArc::cornerArc(int ap_index) const +TimingArc::sceneArc(size_t lib_ap_index) const { - if (ap_index < static_cast(corner_arcs_.size())) { - TimingArc *corner_arc = corner_arcs_[ap_index]; - if (corner_arc) - return corner_arc; + if (lib_ap_index < scene_arcs_.size()) { + TimingArc *scene_arc = scene_arcs_[lib_ap_index]; + if (scene_arc) + return scene_arc; } return this; } void -TimingArc::setCornerArc(TimingArc *corner_arc, - int ap_index) +TimingArc::setSceneArc(TimingArc *scene_arc, + size_t lib_ap_index) { - if (ap_index >= static_cast(corner_arcs_.size())) - corner_arcs_.resize(ap_index + 1); - corner_arcs_[ap_index] = corner_arc; + if (lib_ap_index >= scene_arcs_.size()) + scene_arcs_.resize(lib_ap_index + 1); + scene_arcs_[lib_ap_index] = scene_arc; } //////////////////////////////////////////////////////////////// @@ -673,12 +671,12 @@ TimingArc::sense() const if ((from_rf_ == Transition::rise() && to_rf_ == Transition::rise()) || (from_rf_ == Transition::fall() - && to_rf_ == Transition::fall())) + && to_rf_ == Transition::fall())) return TimingSense::positive_unate; else if ((from_rf_ == Transition::rise() && to_rf_ == Transition::fall()) || (from_rf_ == Transition::fall() - && to_rf_ == Transition::rise())) + && to_rf_ == Transition::rise())) return TimingSense::negative_unate; else return TimingSense::non_unate; @@ -692,7 +690,7 @@ static EnumNameMap timing_sense_name_map = {TimingSense::unknown, "unknown"} }; -const char * +const std::string & to_string(TimingSense sense) { return timing_sense_name_map.find(sense); @@ -760,14 +758,14 @@ EnumNameMap timing_type_name_map = {TimingType::unknown, "unknown"} }; -const char * +std::string_view timingTypeString(TimingType type) { return timing_type_name_map.find(type); } TimingType -findTimingType(const char *type_name) +findTimingType(std::string_view type_name) { return timing_type_name_map.find(type_name, TimingType::unknown); } @@ -858,4 +856,4 @@ timingTypeScaleFactorType(TimingType type) return ScaleFactorType::unknown; } -} // namespace +} // namespace sta diff --git a/liberty/TimingModel.cc b/liberty/TimingModel.cc index fe9550050..5e31059ac 100644 --- a/liberty/TimingModel.cc +++ b/liberty/TimingModel.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,8 @@ #include "TimingModel.hh" +#include "LibertyClass.hh" + namespace sta { GateTimingModel::GateTimingModel(LibertyCell *cell) : @@ -36,4 +38,4 @@ CheckTimingModel::CheckTimingModel(LibertyCell *cell) : { } -} // namespace +} // namespace sta diff --git a/liberty/TimingRole.cc b/liberty/TimingRole.cc index 819ba9297..672a6498a 100644 --- a/liberty/TimingRole.cc +++ b/liberty/TimingRole.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,8 @@ #include "TimingRole.hh" +#include "MinMax.hh" + namespace sta { TimingRoleMap TimingRole::timing_roles_; @@ -90,12 +92,12 @@ const TimingRole TimingRole::clock_tree_path_max_("max clock tree path", false, false, MinMax::max(), nullptr, 28); TimingRole::TimingRole(const char *name, - bool is_sdf_iopath, - bool is_timing_check, - bool is_non_seq_check, - const MinMax *path_min_max, - const TimingRole *generic_role, - int index) : + bool is_sdf_iopath, + bool is_timing_check, + bool is_non_seq_check, + const MinMax *path_min_max, + const TimingRole *generic_role, + int index) : name_(name), is_timing_check_(is_timing_check), is_sdf_iopath_(is_sdf_iopath), @@ -163,6 +165,12 @@ TimingRole::isLatchDtoQ() const return this == &latch_d_q_; } +bool +TimingRole::isLatchEnToQ() const +{ + return this == &latch_en_q_; +} + bool TimingRole::isTimingCheckBetween() const { @@ -173,9 +181,9 @@ TimingRole::isTimingCheckBetween() const bool TimingRole::less(const TimingRole *role1, - const TimingRole *role2) + const TimingRole *role2) { return role1->index() < role2->index(); } -} // namespace +} // namespace sta diff --git a/liberty/Units.cc b/liberty/Units.cc index 8dcbb4f0f..6d1c3f061 100644 --- a/liberty/Units.cc +++ b/liberty/Units.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,15 +26,13 @@ #include // abs +#include "Format.hh" #include "StringUtil.hh" #include "MinMax.hh" // INF #include "Fuzzy.hh" namespace sta { -using std::abs; - - Unit::Unit(const char *suffix) : scale_(1.0), suffix_(suffix), @@ -44,8 +42,8 @@ Unit::Unit(const char *suffix) : } Unit::Unit(float scale, - const char *suffix, - int digits) : + const char *suffix, + int digits) : scale_(scale), suffix_(suffix), digits_(digits) @@ -59,15 +57,6 @@ Unit::setScaleAbbrevSuffix() scale_abbrev_suffix_ = scaleAbbreviation() + suffix_; } -void -Unit::operator=(const Unit &unit) -{ - scale_ = unit.scale_; - suffix_ = unit.suffix_; - scale_abbrev_suffix_ = unit.scale_abbrev_suffix_; - digits_ = unit.digits_; -} - double Unit::staToUser(double value) { @@ -130,7 +119,7 @@ Unit::scaleString() const else if (fuzzyEqual(scale_, 1E-15)) return "1f"; else - return stdstrPrint("%.1e", scale_); + return sta::format("{:.1e}", scale_); } std::string @@ -158,31 +147,25 @@ Unit::width() const return digits_ + 2; } -const char * +std::string Unit::asString(float value) const { return asString(value, digits_); } -const char * -Unit::asString(double value) const -{ - return asString(static_cast(value), digits_); -} - -const char * +std::string Unit::asString(float value, - int digits) const + int digits) const { // Special case INF because it blows up otherwise. - if (abs(value) >= INF * .1) + if (std::abs(value) >= INF * .1) return (value > 0.0) ? "INF" : "-INF"; else { float scaled_value = value / scale_; // prevent "-0.00" on slowaris - if (abs(scaled_value) < 1E-6) + if (std::abs(scaled_value) < 1E-6) scaled_value = 0.0; - return stringPrintTmp("%.*f", digits, scaled_value); + return sta::formatRuntime("{:.{}f}", scaled_value, digits); } } @@ -201,27 +184,27 @@ Units::Units() : } Unit * -Units::find(const char *unit_name) +Units::find(std::string_view unit_name) { - if (stringEq(unit_name, "time")) + if (stringEqual(unit_name, "time")) return &time_unit_; - else if (stringEq(unit_name, "resistance")) + else if (stringEqual(unit_name, "resistance")) return &resistance_unit_; - else if (stringEq(unit_name, "capacitance")) + else if (stringEqual(unit_name, "capacitance")) return &capacitance_unit_; - else if (stringEq(unit_name, "voltage")) + else if (stringEqual(unit_name, "voltage")) return &voltage_unit_; - else if (stringEq(unit_name, "current")) + else if (stringEqual(unit_name, "current")) return ¤t_unit_; - else if (stringEq(unit_name, "power")) + else if (stringEqual(unit_name, "power")) return &power_unit_; - else if (stringEq(unit_name, "distance")) + else if (stringEqual(unit_name, "distance")) return &distance_unit_; else return nullptr; } -void +Units & Units::operator=(const Units &units) { time_unit_ = *units.timeUnit(); @@ -232,6 +215,7 @@ Units::operator=(const Units &units) power_unit_ = *units.powerUnit(); distance_unit_ = *units.distanceUnit(); scalar_unit_ = *units.scalarUnit(); + return *this; } -} // namespace +} // namespace sta diff --git a/liberty/Wireload.cc b/liberty/Wireload.cc index 9c8ea3e5b..e416ebb33 100644 --- a/liberty/Wireload.cc +++ b/liberty/Wireload.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,15 +25,20 @@ #include "Wireload.hh" #include +#include +#include +#include +#include -#include "StringUtil.hh" +#include "ContainerHelpers.hh" +#include "LibertyClass.hh" #include "Liberty.hh" namespace sta { -Wireload::Wireload(const char *name, - LibertyLibrary *library) : - name_(stringCopy(name)), +Wireload::Wireload(std::string name, + LibertyLibrary *library) : + name_(std::move(name)), library_(library), area_(0.0F), resistance_(0.0F), @@ -42,13 +47,13 @@ Wireload::Wireload(const char *name, { } -Wireload::Wireload(const char *name, - LibertyLibrary *library, - float area, - float resistance, - float capacitance, - float slope) : - name_(stringCopy(name)), +Wireload::Wireload(std::string name, + LibertyLibrary *library, + float area, + float resistance, + float capacitance, + float slope) : + name_(std::move(name)), library_(library), area_(area), resistance_(resistance), @@ -59,8 +64,7 @@ Wireload::Wireload(const char *name, Wireload::~Wireload() { - fanout_lengths_.deleteContents(); - stringDelete(name_); + deleteContents(fanout_lengths_); } void @@ -90,7 +94,7 @@ Wireload::setSlope(float slope) struct FanoutLess { bool operator()(FanoutLength *fanout1, - FanoutLength *fanout2) const + FanoutLength *fanout2) const { return fanout1->first < fanout2->first; } @@ -98,7 +102,7 @@ struct FanoutLess void Wireload::addFanoutLength(float fanout, - float length) + float length) { FanoutLength *fanout_length = new FanoutLength(fanout, length); fanout_lengths_.push_back(fanout_length); @@ -110,9 +114,9 @@ Wireload::addFanoutLength(float fanout, void Wireload::findWireload(float fanout, - const OperatingConditions *op_cond, - float &cap, - float &res) const + const OperatingConditions *op_cond, + float &cap, + float &res) const { size_t size = fanout_lengths_.size(); float length; @@ -125,8 +129,7 @@ Wireload::findWireload(float fanout, if (fanout < fanout0) { // Extrapolate from lowest fanout entry. length = fanout_lengths_[0]->second - (fanout0 - fanout) * slope_; - if (length < 0) - length = 0; + length = std::max(length, 0.0F); } else if (fanout == fanout0) length = fanout_lengths_[0]->second; @@ -138,11 +141,11 @@ Wireload::findWireload(float fanout, int lower = -1; int upper = size; while (upper - lower > 1) { - int mid = (upper + lower) >> 1; - if (fanout >= fanout_lengths_[mid]->first) - lower = mid; - else - upper = mid; + int mid = (upper + lower) >> 1; + if (fanout >= fanout_lengths_[mid]->first) + lower = mid; + else + upper = mid; } // Interpolate between lower and lower+1 entries. float fanout1 = fanout_lengths_[lower]->first; @@ -165,8 +168,8 @@ class WireloadForArea { public: WireloadForArea(float min_area, - float max_area, - const Wireload *wireload); + float max_area, + const Wireload *wireload); float minArea() const { return min_area_; } float maxArea() const { return max_area_; } const Wireload *wireload() const { return wireload_; } @@ -178,29 +181,28 @@ class WireloadForArea }; WireloadForArea::WireloadForArea(float min_area, - float max_area, - const Wireload *wireload) : + float max_area, + const Wireload *wireload) : min_area_(min_area), max_area_(max_area), wireload_(wireload) { } -WireloadSelection::WireloadSelection(const char *name) : - name_(stringCopy(name)) +WireloadSelection::WireloadSelection(std::string name) : + name_(std::move(name)) { } WireloadSelection::~WireloadSelection() { - wireloads_.deleteContents(); - stringDelete(name_); + deleteContents(wireloads_); } struct WireloadForAreaMinLess { bool operator()(WireloadForArea *wireload1, - WireloadForArea *wireload2) const + WireloadForArea *wireload2) const { return wireload1->minArea() < wireload2->minArea(); } @@ -208,11 +210,11 @@ struct WireloadForAreaMinLess void WireloadSelection::addWireloadFromArea(float min_area, - float max_area, - const Wireload *wireload) + float max_area, + const Wireload *wireload) { WireloadForArea *wireload_area = new WireloadForArea(min_area, max_area, - wireload); + wireload); wireloads_.push_back(wireload_area); // Keep wireloads sorted by area for lookup. if (wireloads_.size() > 1 @@ -264,13 +266,13 @@ wireloadTreeString(WireloadTree tree) } WireloadTree -stringWireloadTree(const char *wire_load_type) +stringWireloadTree(std::string_view wire_load_type) { - if (stringEq(wire_load_type, "worst_case_tree")) + if (wire_load_type == "worst_case_tree") return WireloadTree::worst_case; - else if (stringEq(wire_load_type, "best_case_tree")) + else if (wire_load_type == "best_case_tree") return WireloadTree::best_case; - else if (stringEq(wire_load_type, "balanced_tree")) + else if (wire_load_type == "balanced_tree") return WireloadTree::balanced; else return WireloadTree::unknown; @@ -294,16 +296,16 @@ wireloadModeString(WireloadMode wire_load_mode) } WireloadMode -stringWireloadMode(const char *wire_load_mode) +stringWireloadMode(std::string_view wire_load_mode) { - if (stringEq(wire_load_mode, "top")) + if (wire_load_mode == "top") return WireloadMode::top; - else if (stringEq(wire_load_mode, "enclosed")) + else if (wire_load_mode == "enclosed") return WireloadMode::enclosed; - else if (stringEq(wire_load_mode, "segmented")) + else if (wire_load_mode == "segmented") return WireloadMode::segmented; else return WireloadMode::unknown; } -} // namespace +} // namespace sta diff --git a/network/ConcreteLibrary.cc b/network/ConcreteLibrary.cc index 2dedc9f4c..77b335a1d 100644 --- a/network/ConcreteLibrary.cc +++ b/network/ConcreteLibrary.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,46 +24,39 @@ #include "ConcreteLibrary.hh" +#include #include #include +#include "ConcreteNetwork.hh" +#include "ContainerHelpers.hh" +#include "ParseBus.hh" #include "PatternMatch.hh" #include "PortDirection.hh" -#include "ParseBus.hh" -#include "ConcreteNetwork.hh" namespace sta { -using std::string; -using std::map; -using std::min; -using std::max; -using std::abs; -using std::swap; - static constexpr char escape_ = '\\'; -ConcreteLibrary::ConcreteLibrary(const char *name, - const char *filename, - bool is_liberty) : +ConcreteLibrary::ConcreteLibrary(std::string_view name, + std::string_view filename, + bool is_liberty) : name_(name), id_(ConcreteNetwork::nextObjectId()), - filename_(filename ? filename : ""), - is_liberty_(is_liberty), - bus_brkt_left_('['), - bus_brkt_right_(']') + filename_(filename), + is_liberty_(is_liberty) { } ConcreteLibrary::~ConcreteLibrary() { - cell_map_.deleteContents(); + deleteContents(cell_map_); } ConcreteCell * -ConcreteLibrary::makeCell(const char *name, - bool is_leaf, - const char *filename) +ConcreteLibrary::makeCell(std::string_view name, + bool is_leaf, + std::string_view filename) { ConcreteCell *cell = new ConcreteCell(name, filename, is_leaf, this); addCell(cell); @@ -77,11 +70,9 @@ ConcreteLibrary::addCell(ConcreteCell *cell) } void -ConcreteLibrary::renameCell(ConcreteCell *cell, - const char *cell_name) +ConcreteLibrary::removeCell(ConcreteCell *cell) { cell_map_.erase(cell->name()); - cell_map_[cell_name] = cell; } void @@ -98,19 +89,17 @@ ConcreteLibrary::cellIterator() const } ConcreteCell * -ConcreteLibrary::findCell(const char *name) const +ConcreteLibrary::findCell(std::string_view name) const { - return cell_map_.findKey(name); + return findStringKey(cell_map_, name); } CellSeq ConcreteLibrary::findCellsMatching(const PatternMatch *pattern) const { CellSeq matches; - ConcreteLibraryCellIterator cell_iter=ConcreteLibraryCellIterator(cell_map_); - while (cell_iter.hasNext()) { - ConcreteCell *cell = cell_iter.next(); - if (pattern->match(cell->name())) + for (auto [name, cell] : cell_map_) { + if (pattern->match(name)) matches.push_back(reinterpret_cast(cell)); } return matches; @@ -118,7 +107,7 @@ ConcreteLibrary::findCellsMatching(const PatternMatch *pattern) const void ConcreteLibrary::setBusBrkts(char left, - char right) + char right) { bus_brkt_left_ = left; bus_brkt_right_ = right; @@ -126,31 +115,29 @@ ConcreteLibrary::setBusBrkts(char left, //////////////////////////////////////////////////////////////// -ConcreteCell::ConcreteCell(const char *name, - const char *filename, - bool is_leaf, +ConcreteCell::ConcreteCell(std::string_view name, + std::string_view filename, + bool is_leaf, ConcreteLibrary *library) : name_(name), id_(ConcreteNetwork::nextObjectId()), - filename_(filename ? filename : ""), + filename_(filename), library_(library), - liberty_cell_(nullptr), - ext_cell_(nullptr), - port_bit_count_(0), is_leaf_(is_leaf) { } ConcreteCell::~ConcreteCell() { - ports_.deleteContents(); + deleteContents(ports_); } void -ConcreteCell::setName(const char *name) +ConcreteCell::setName(std::string_view name) { - library_->renameCell(this, name); + library_->removeCell(this); name_ = name; + library_->addCell(this); } void @@ -166,18 +153,20 @@ ConcreteCell::setExtCell(void *ext_cell) } ConcretePort * -ConcreteCell::makePort(const char *name) +ConcreteCell::makePort(std::string_view name) { - ConcretePort *port = new ConcretePort(name, false, -1, -1, false, nullptr, this); + ConcretePort *port = new ConcretePort(name, false, -1, -1, + false, nullptr, this); addPort(port); return port; } ConcretePort * -ConcreteCell::makeBundlePort(const char *name, - ConcretePortSeq *members) +ConcreteCell::makeBundlePort(std::string_view name, + ConcretePortSeq *members) { - ConcretePort *port = new ConcretePort(name, false, -1, -1, true, members, this); + ConcretePort *port = new ConcretePort(name, false, -1, -1, true, + members, this); addPort(port); for (ConcretePort *member : *members) member->setBundlePort(port); @@ -185,67 +174,67 @@ ConcreteCell::makeBundlePort(const char *name, } ConcretePort * -ConcreteCell::makeBusPort(const char *name, - int from_index, - int to_index) +ConcreteCell::makeBusPort(std::string_view name, + int from_index, + int to_index) { ConcretePort *port = new ConcretePort(name, true, from_index, to_index, - false, new ConcretePortSeq, this); + false, new ConcretePortSeq, this); addPort(port); - makeBusPortBits(port, name, from_index, to_index); + makeBusPortBits(port, port->name(), from_index, to_index); return port; } ConcretePort * -ConcreteCell::makeBusPort(const char *name, - int from_index, - int to_index, - ConcretePortSeq *members) +ConcreteCell::makeBusPort(std::string_view name, + int from_index, + int to_index, + ConcretePortSeq *members) { ConcretePort *port = new ConcretePort(name, true, from_index, to_index, - false, members, this); + false, members, this); addPort(port); return port; } void ConcreteCell::makeBusPortBits(ConcretePort *bus_port, - const char *name, - int from_index, - int to_index) + std::string_view bus_name, + int from_index, + int to_index) { if (from_index < to_index) { for (int index = from_index; index <= to_index; index++) - makeBusPortBit(bus_port, name, index); + makeBusPortBit(bus_port, bus_name, index); } else { for (int index = from_index; index >= to_index; index--) - makeBusPortBit(bus_port, name, index); + makeBusPortBit(bus_port, bus_name, index); } } void ConcreteCell::makeBusPortBit(ConcretePort *bus_port, - const char *bus_name, - int bit_index) -{ - string bit_name; - stringPrint(bit_name, "%s%c%d%c", - bus_name, - library_->busBrktLeft(), - bit_index, - library_->busBrktRight()); - ConcretePort *port = makePort(bit_name.c_str(), bit_index); + std::string_view bus_name, + int bit_index) +{ + std::string bit_name; + bit_name.append(bus_name); + bit_name += library_->busBrktLeft(); + bit_name += std::to_string(bit_index); + bit_name += library_->busBrktRight(); + ConcretePort *port = makePort(bit_name, bit_index); bus_port->addPortBit(port); addPortBit(port); } ConcretePort * -ConcreteCell::makePort(const char *bit_name, - int bit_index) +ConcreteCell::makePort(std::string_view bit_name, + int bit_index) { - ConcretePort *port = new ConcretePort(bit_name, false, bit_index, - bit_index, false, nullptr, this); + ConcretePort *port = new ConcretePort(bit_name, false, + bit_index, bit_index, false, + nullptr, this); addPortBit(port); return port; } @@ -273,14 +262,14 @@ ConcreteCell::setIsLeaf(bool is_leaf) } void -ConcreteCell::setAttribute(const string &key, - const string &value) +ConcreteCell::setAttribute(std::string_view key, + std::string_view value) { - attribute_map_[key] = value; + attribute_map_[std::string(key)] = value; } -string -ConcreteCell::getAttribute(const string &key) const +std::string +ConcreteCell::getAttribute(std::string_view key) const { const auto &itr = attribute_map_.find(key); if (itr != attribute_map_.end()) @@ -289,9 +278,9 @@ ConcreteCell::getAttribute(const string &key) const } ConcretePort * -ConcreteCell::findPort(const char *name) const +ConcreteCell::findPort(std::string_view name) const { - return port_map_.findKey(name); + return findStringKey(port_map_, name); } size_t @@ -351,19 +340,19 @@ void BusPort::addBusBit(ConcretePort *port, int index) { - from_ = min(from_, index); - to_ = max(to_, index); + from_ = std::min(from_, index); + to_ = std::max(to_, index); members_.push_back(port); } void -ConcreteCell::groupBusPorts(const char bus_brkt_left, - const char bus_brkt_right, - std::function port_msb_first) +ConcreteCell::groupBusPorts(char bus_brkt_left, + char bus_brkt_right, + const std::function &port_msb_first) { const char bus_brkts_left[2]{bus_brkt_left, '\0'}; const char bus_brkts_right[2]{bus_brkt_right, '\0'}; - map bus_map; + std::map bus_map; // Find ungrouped bus ports. // Remove bus bit ports from the ports_ vector during the scan by // keeping an index to the next insertion index and skipping over @@ -371,16 +360,15 @@ ConcreteCell::groupBusPorts(const char bus_brkt_left, ConcretePortSeq ports = ports_; ports_.clear(); for (ConcretePort *port : ports) { - const char *port_name = port->name(); bool is_bus; - string bus_name; + std::string bus_name; int index; - parseBusName(port_name, bus_brkts_left, bus_brkts_right, escape_, - is_bus, bus_name, index); + parseBusName(port->name(), bus_brkts_left, bus_brkts_right, escape_, + is_bus, bus_name, index); if (is_bus) { if (!port->isBusBit()) { BusPort &bus_port = bus_map[bus_name]; - bus_port.addBusBit(port, index); + bus_port.addBusBit(port, index); port->setBusBitIndex(index); bus_port.setDirection(port->direction()); } @@ -394,7 +382,7 @@ ConcreteCell::groupBusPorts(const char bus_brkt_left, int from = bus_port.from(); int to = bus_port.to(); size_t size = to - from + 1; - bool msb_first = port_msb_first(bus_name.c_str()); + bool msb_first = port_msb_first(bus_name); ConcretePortSeq *members = new ConcretePortSeq(size); // Index the bus bit ports. for (ConcretePort *bus_bit : bus_port.members()) { @@ -403,34 +391,30 @@ ConcreteCell::groupBusPorts(const char bus_brkt_left, (*members)[member_index] = bus_bit; } if (msb_first) - swap(from, to); - ConcretePort *port = makeBusPort(bus_name.c_str(), from, to, members); + std::swap(from, to); + ConcretePort *port = makeBusPort(bus_name, from, to, members); port->setDirection(bus_port.direction()); } } //////////////////////////////////////////////////////////////// -ConcretePort::ConcretePort(const char *name, - bool is_bus, - int from_index, - int to_index, - bool is_bundle, - ConcretePortSeq *member_ports, +ConcretePort::ConcretePort(std::string_view name, + bool is_bus, + int from_index, + int to_index, + bool is_bundle, + ConcretePortSeq *member_ports, ConcreteCell *cell) : name_(name), id_(ConcreteNetwork::nextObjectId()), cell_(cell), direction_(PortDirection::unknown()), - liberty_port_(nullptr), - ext_port_(nullptr), - pin_index_(-1), is_bundle_(is_bundle), is_bus_(is_bus), from_index_(from_index), to_index_(to_index), - member_ports_(member_ports), - bundle_port_(nullptr) + member_ports_(member_ports) { } @@ -439,7 +423,7 @@ ConcretePort::~ConcretePort() // The member ports of a bus are owned by the bus port. // The member ports of a bundle are NOT owned by the bus port. if (is_bus_) - member_ports_->deleteContents(); + deleteContents(member_ports_); delete member_ports_; } @@ -467,17 +451,17 @@ ConcretePort::setExtPort(void *port) ext_port_ = port; } -const char * +std::string ConcretePort::busName() const { if (is_bus_) { ConcreteLibrary *lib = cell_->library(); - return stringPrintTmp("%s%c%d:%d%c", - name(), - lib->busBrktLeft(), - from_index_, - to_index_, - lib->busBrktRight()); + return sta::format("{}{}{}:{}{}", + name(), + lib->busBrktLeft(), + from_index_, + to_index_, + lib->busBrktRight()); } else return name(); @@ -506,7 +490,7 @@ int ConcretePort::size() const { if (is_bus_) - return abs(to_index_ - from_index_) + 1; + return std::abs(to_index_ - from_index_) + 1; else if (is_bundle_) return static_cast(member_ports_->size()); else @@ -548,8 +532,8 @@ ConcretePort::findBusBit(int index) const && index >= from_index_) return (*member_ports_)[index - from_index_]; else if (from_index_ >= to_index_ - && index >= to_index_ - && index <= from_index_) + && index >= to_index_ + && index <= from_index_) return (*member_ports_)[from_index_ - index]; else return nullptr; @@ -559,11 +543,11 @@ bool ConcretePort::busIndexInRange(int index) const { return (from_index_ <= to_index_ - && index <= to_index_ - && index >= from_index_) + && index <= to_index_ + && index >= from_index_) || (from_index_ > to_index_ - && index >= to_index_ - && index <= from_index_); + && index >= to_index_ + && index <= from_index_); } bool @@ -580,11 +564,9 @@ ConcretePort::memberIterator() const //////////////////////////////////////////////////////////////// -ConcreteCellPortBitIterator::ConcreteCellPortBitIterator(const ConcreteCell* - cell) : - port_iter_(cell->ports_), - member_iter_(nullptr), - next_(nullptr) +ConcreteCellPortBitIterator::ConcreteCellPortBitIterator(const ConcreteCell* cell) : + ports_(cell->ports_), + port_iter_(ports_.begin()) { findNext(); } @@ -616,8 +598,8 @@ ConcreteCellPortBitIterator::findNext() member_iter_ = nullptr; } } - while (port_iter_.hasNext()) { - ConcretePort *next = port_iter_.next(); + while (port_iter_ != ports_.end()) { + ConcretePort *next = *port_iter_++; if (next->isBus()) { member_iter_ = next->memberIterator(); next_ = member_iter_->next(); @@ -632,4 +614,4 @@ ConcreteCellPortBitIterator::findNext() next_ = nullptr; } -} // namespace +} // namespace sta diff --git a/network/ConcreteNetwork.cc b/network/ConcreteNetwork.cc index d165e9f31..df99ff9d7 100644 --- a/network/ConcreteNetwork.cc +++ b/network/ConcreteNetwork.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,30 +24,32 @@ #include "ConcreteNetwork.hh" -#include "PatternMatch.hh" -#include "Report.hh" -#include "Liberty.hh" -#include "PortDirection.hh" +#include +#include +#include + #include "ConcreteLibrary.hh" +#include "Liberty.hh" #include "Network.hh" +#include "PatternMatch.hh" +#include "PortDirection.hh" +#include "Report.hh" namespace sta { -using std::string; - static void makeChildNetwork(Instance *proto, - Instance *parent, - ConcreteBindingTbl *parent_bindings, - NetworkReader *network); + Instance *parent, + ConcreteBindingTbl *parent_bindings, + NetworkReader *network); static void makeClonePins(Instance *proto, - Instance *clone, - Instance *clone_view, - ConcreteBindingTbl *bindings, - Instance *parent, - ConcreteBindingTbl *parent_bindings, - NetworkReader *network); + Instance *clone, + Instance *clone_view, + ConcreteBindingTbl *bindings, + Instance *parent, + ConcreteBindingTbl *parent_bindings, + NetworkReader *network); //////////////////////////////////////////////////////////////// @@ -61,66 +63,75 @@ makeConcreteNetwork() class ConcreteInstanceChildIterator : public InstanceChildIterator { public: - explicit ConcreteInstanceChildIterator(ConcreteInstanceChildMap *map); - bool hasNext(); - Instance *next(); + ConcreteInstanceChildIterator(ConcreteInstanceChildMap *map); + bool hasNext() override; + Instance *next() override; private: - ConcreteInstanceChildMap::ConstIterator iter_; + ConcreteInstanceChildMap *map_; + ConcreteInstanceChildMap::const_iterator iter_; }; ConcreteInstanceChildIterator:: ConcreteInstanceChildIterator(ConcreteInstanceChildMap *map) : - iter_(map) + map_(map) { + if (map_) + iter_ = map_->begin(); } bool ConcreteInstanceChildIterator::hasNext() { - return iter_.hasNext(); + return map_ && iter_ != map_->end(); } Instance * ConcreteInstanceChildIterator::next() { - return reinterpret_cast(iter_.next()); + Instance *next = reinterpret_cast(iter_->second); + iter_++; + return next; } class ConcreteInstanceNetIterator : public InstanceNetIterator { public: - explicit ConcreteInstanceNetIterator(ConcreteInstanceNetMap *nets); - bool hasNext(); - Net *next(); + ConcreteInstanceNetIterator(ConcreteInstanceNetMap *nets); + bool hasNext() override; + Net *next() override; private: void findNext(); - ConcreteInstanceNetMap::Iterator iter_; - ConcreteNet *next_; + ConcreteInstanceNetMap *nets_; + ConcreteInstanceNetMap::iterator iter_; + ConcreteNet *next_{nullptr}; }; ConcreteInstanceNetIterator:: ConcreteInstanceNetIterator(ConcreteInstanceNetMap *nets): - iter_(nets), - next_(nullptr) + nets_(nets) { - findNext(); + if (nets) { + iter_ = nets->begin(); + findNext(); + } } bool ConcreteInstanceNetIterator::hasNext() { - return next_ != nullptr; + return nets_ && next_ != nullptr; } // Skip nets that have been merged. void ConcreteInstanceNetIterator::findNext() { - while (iter_.hasNext()) { - next_ = iter_.next(); + while (iter_ != nets_->end()) { + next_ = iter_->second; + iter_++; if (next_->mergedInto() == nullptr) return; } @@ -141,25 +152,24 @@ class ConcreteInstancePinIterator : public InstancePinIterator { public: ConcreteInstancePinIterator(const ConcreteInstance *inst, - int pin_count); - bool hasNext(); - Pin *next(); + int pin_count); + bool hasNext() override; + Pin *next() override; private: void findNext(); const ConcretePinSeq &pins_; int pin_count_; - int pin_index_; + int pin_index_{0}; ConcretePin *next_; }; ConcreteInstancePinIterator:: ConcreteInstancePinIterator(const ConcreteInstance *inst, - int pin_count) : + int pin_count) : pins_(inst->pins_), - pin_count_(pin_count), - pin_index_(0) + pin_count_(pin_count) { findNext(); } @@ -195,9 +205,9 @@ ConcreteInstancePinIterator::findNext() class ConcreteNetPinIterator : public NetPinIterator { public: - explicit ConcreteNetPinIterator(const ConcreteNet *net); - bool hasNext(); - Pin *next(); + ConcreteNetPinIterator(const ConcreteNet *net); + bool hasNext() override; + Pin *next() override; private: ConcretePin *next_; @@ -227,9 +237,9 @@ ConcreteNetPinIterator::next() class ConcreteNetTermIterator : public NetTermIterator { public: - explicit ConcreteNetTermIterator(const ConcreteNet *net); - bool hasNext(); - Term *next(); + ConcreteNetTermIterator(const ConcreteNet *net); + bool hasNext() override; + Term *next() override; private: ConcreteTerm *next_; @@ -260,24 +270,31 @@ ObjectId ConcreteNetwork::object_id_ = 0; ConcreteNetwork::ConcreteNetwork() : NetworkReader(), - top_instance_(nullptr), - constant_nets_{NetSet(this), NetSet(this)}, link_func_(nullptr) { } ConcreteNetwork::~ConcreteNetwork() { - clear(); + // Cannot call virtual functions in destructor. + clearImpl(); } void -ConcreteNetwork::clear() +ConcreteNetwork::clearImpl() { - deleteTopInstance(); - deleteCellNetworkViews(); - library_seq_.deleteContentsClear(); + if (top_instance_) + deleteInstanceImpl(top_instance_); + top_instance_ = nullptr; + deleteCellNetworkViewsImpl(); + deleteContents(library_seq_); library_map_.clear(); +} + +void +ConcreteNetwork::clear() +{ + clearImpl(); Network::clear(); } @@ -293,9 +310,13 @@ ConcreteNetwork::deleteTopInstance() void ConcreteNetwork::deleteCellNetworkViews() { - CellNetworkViewMap::Iterator view_iter(cell_network_view_map_); - while (view_iter.hasNext()) { - Instance *view = view_iter.next(); + deleteCellNetworkViewsImpl(); +} + +void +ConcreteNetwork::deleteCellNetworkViewsImpl() +{ + for (auto [cell, view] : cell_network_view_map_) { if (view) deleteInstance(view); } @@ -313,29 +334,31 @@ ConcreteNetwork::topInstance() const class ConcreteLibraryIterator1 : public Iterator { public: - explicit ConcreteLibraryIterator1(const ConcreteLibrarySeq &lib_seq_); - virtual bool hasNext(); - virtual Library *next(); + ConcreteLibraryIterator1(const ConcreteLibrarySeq &libs); + bool hasNext() override; + Library *next() override; private: - ConcreteLibraryIterator iter_; + const ConcreteLibrarySeq &libs_; + ConcreteLibrarySeq::const_iterator iter_; }; -ConcreteLibraryIterator1::ConcreteLibraryIterator1(const ConcreteLibrarySeq &lib_seq_): - iter_(lib_seq_) +ConcreteLibraryIterator1::ConcreteLibraryIterator1(const ConcreteLibrarySeq &libs): + libs_(libs), + iter_(libs.begin()) { } bool ConcreteLibraryIterator1::hasNext() { - return iter_.hasNext(); + return iter_ != libs_.end(); } Library * ConcreteLibraryIterator1::next() { - return reinterpret_cast(iter_.next()); + return reinterpret_cast(*iter_++); } LibraryIterator * @@ -349,30 +372,26 @@ ConcreteNetwork::libraryIterator() const class ConcreteLibertyLibraryIterator : public Iterator { public: - explicit ConcreteLibertyLibraryIterator(const ConcreteNetwork *network); - virtual ~ConcreteLibertyLibraryIterator(); - virtual bool hasNext(); - virtual LibertyLibrary *next(); + ConcreteLibertyLibraryIterator(const ConcreteNetwork *network); + bool hasNext() override; + LibertyLibrary *next() override; private: void findNext(); - ConcreteLibrarySeq::ConstIterator iter_; - LibertyLibrary *next_; + const ConcreteLibrarySeq &libs_; + ConcreteLibrarySeq::const_iterator iter_; + LibertyLibrary *next_{nullptr}; }; ConcreteLibertyLibraryIterator:: ConcreteLibertyLibraryIterator(const ConcreteNetwork *network): - iter_(network->library_seq_), - next_(nullptr) + libs_(network->library_seq_), + iter_(libs_.begin()) { findNext(); } -ConcreteLibertyLibraryIterator::~ConcreteLibertyLibraryIterator() -{ -} - bool ConcreteLibertyLibraryIterator::hasNext() { @@ -391,13 +410,13 @@ void ConcreteLibertyLibraryIterator::findNext() { next_ = nullptr; - while (iter_.hasNext()) { - ConcreteLibrary *lib = iter_.next(); + while (iter_ != libs_.end()) { + ConcreteLibrary *lib = *iter_++; if (lib->isLiberty()) { LibertyLibrary *liberty = static_cast(lib); if (liberty) { - next_ = liberty; - break; + next_ = liberty; + break; } } } @@ -412,19 +431,21 @@ ConcreteNetwork::libertyLibraryIterator() const //////////////////////////////////////////////////////////////// Library * -ConcreteNetwork::makeLibrary(const char *name, - const char *filename) +ConcreteNetwork::makeLibrary(std::string_view name, + std::string_view filename) { - ConcreteLibrary *library = new ConcreteLibrary(name, filename, false); + ConcreteLibrary *library = new ConcreteLibrary(std::string(name), + std::string(filename), false); addLibrary(library); return reinterpret_cast(library); } LibertyLibrary * -ConcreteNetwork::makeLibertyLibrary(const char *name, - const char *filename) +ConcreteNetwork::makeLibertyLibrary(std::string_view name, + std::string_view filename) { - LibertyLibrary *library = new LibertyLibrary(name, filename); + LibertyLibrary *library = new LibertyLibrary(std::string(name), + std::string(filename)); addLibrary(library); return library; } @@ -437,9 +458,9 @@ ConcreteNetwork::addLibrary(ConcreteLibrary *library) } Library * -ConcreteNetwork::findLibrary(const char *name) +ConcreteNetwork::findLibrary(std::string_view name) { - return reinterpret_cast(library_map_.findKey(name)); + return reinterpret_cast(findStringKey(library_map_, name)); } void @@ -447,11 +468,11 @@ ConcreteNetwork::deleteLibrary(Library *library) { ConcreteLibrary *clib = reinterpret_cast(library); library_map_.erase(clib->name()); - library_seq_.eraseObject(clib); + library_seq_.erase(std::ranges::find(library_seq_, clib)); delete clib; } -const char * +std::string ConcreteNetwork::name(const Library *library) const { const ConcreteLibrary *clib = @@ -468,18 +489,18 @@ ConcreteNetwork::id(const Library *library) const } LibertyLibrary * -ConcreteNetwork::findLiberty(const char *name) +ConcreteNetwork::findLiberty(std::string_view name) { - ConcreteLibrary *lib = library_map_.findKey(name); + ConcreteLibrary *lib = findStringKey(library_map_, name); if (lib) { if (lib->isLiberty()) return static_cast(lib); // Potential name conflict else { for (ConcreteLibrary *lib : library_seq_) { - if (stringEq(lib->name(), name) - && lib->isLiberty()) - return static_cast(lib); + if (lib->name() == name + && lib->isLiberty()) + return static_cast(lib); } } } @@ -488,9 +509,9 @@ ConcreteNetwork::findLiberty(const char *name) Cell * ConcreteNetwork::makeCell(Library *library, - const char *name, - bool is_leaf, - const char *filename) + std::string_view name, + bool is_leaf, + std::string_view filename) { ConcreteLibrary *clib = reinterpret_cast(library); return reinterpret_cast(clib->makeCell(name, is_leaf, filename)); @@ -498,7 +519,7 @@ ConcreteNetwork::makeCell(Library *library, Cell * ConcreteNetwork::findCell(const Library *library, - const char *name) const + std::string_view name) const { const ConcreteLibrary *clib = reinterpret_cast(library); @@ -506,11 +527,9 @@ ConcreteNetwork::findCell(const Library *library, } Cell * -ConcreteNetwork::findAnyCell(const char *name) +ConcreteNetwork::findAnyCell(std::string_view name) { - ConcreteLibrarySeq::Iterator lib_iter(library_seq_); - while (lib_iter.hasNext()) { - ConcreteLibrary *lib = lib_iter.next(); + for (ConcreteLibrary *lib : library_seq_) { ConcreteCell *cell = lib->findCell(name); if (cell) return reinterpret_cast(cell); @@ -520,7 +539,7 @@ ConcreteNetwork::findAnyCell(const char *name) CellSeq ConcreteNetwork::findCellsMatching(const Library *library, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { const ConcreteLibrary *clib = reinterpret_cast(library); @@ -537,7 +556,7 @@ ConcreteNetwork::deleteCell(Cell *cell) //////////////////////////////////////////////////////////////// -const char * +std::string ConcreteNetwork::name(const Cell *cell) const { const ConcreteCell *ccell = reinterpret_cast(cell); @@ -553,7 +572,7 @@ ConcreteNetwork::id(const Cell *cell) const void ConcreteNetwork::setName(Cell *cell, - const char *name) + std::string_view name) { ConcreteCell *ccell = reinterpret_cast(cell); ccell->setName(name); @@ -561,7 +580,7 @@ ConcreteNetwork::setName(Cell *cell, void ConcreteNetwork::setIsLeaf(Cell *cell, - bool is_leaf) + bool is_leaf) { ConcreteCell *ccell = reinterpret_cast(cell); ccell->setIsLeaf(is_leaf); @@ -569,8 +588,8 @@ ConcreteNetwork::setIsLeaf(Cell *cell, void ConcreteNetwork::setAttribute(Cell *cell, - const string &key, - const string &value) + std::string_view key, + std::string_view value) { ConcreteCell *ccell = reinterpret_cast(cell); ccell->setAttribute(key, value); @@ -609,16 +628,16 @@ ConcreteNetwork::cell(const LibertyCell *cell) const return reinterpret_cast(cell); } -const char * -ConcreteNetwork::filename(const Cell *cell) +std::string_view +ConcreteNetwork::filename(const Cell *cell) const { const ConcreteCell *ccell = reinterpret_cast(cell); return ccell->filename(); } -string +std::string ConcreteNetwork::getAttribute(const Cell *cell, - const string &key) const + std::string_view key) const { const ConcreteCell *ccell = reinterpret_cast(cell); return ccell->getAttribute(key); @@ -633,7 +652,7 @@ ConcreteNetwork::attributeMap(const Cell *cell) const Port * ConcreteNetwork::findPort(const Cell *cell, - const char *name) const + std::string_view name) const { const ConcreteCell *ccell = reinterpret_cast(cell); return reinterpret_cast(ccell->findPort(name)); @@ -648,7 +667,7 @@ ConcreteNetwork::isLeaf(const Cell *cell) const Port * ConcreteNetwork::makePort(Cell *cell, - const char *name) + std::string_view name) { ConcreteCell *ccell = reinterpret_cast(cell); ConcretePort *port = ccell->makePort(name); @@ -657,9 +676,9 @@ ConcreteNetwork::makePort(Cell *cell, Port * ConcreteNetwork::makeBusPort(Cell *cell, - const char *name, - int from_index, - int to_index) + std::string_view name, + int from_index, + int to_index) { ConcreteCell *ccell = reinterpret_cast(cell); ConcretePort *port = ccell->makeBusPort(name, from_index, to_index); @@ -668,7 +687,7 @@ ConcreteNetwork::makeBusPort(Cell *cell, void ConcreteNetwork::groupBusPorts(Cell *cell, - std::function port_msb_first) + std::function port_msb_first) { Library *lib = library(cell); ConcreteLibrary *clib = reinterpret_cast(lib); @@ -679,8 +698,8 @@ ConcreteNetwork::groupBusPorts(Cell *cell, Port * ConcreteNetwork::makeBundlePort(Cell *cell, - const char *name, - PortSeq *members) + std::string_view name, + PortSeq *members) { ConcreteCell *ccell = reinterpret_cast(cell); ConcretePortSeq *cmembers = reinterpret_cast(members); @@ -690,7 +709,7 @@ ConcreteNetwork::makeBundlePort(Cell *cell, void ConcreteNetwork::setDirection(Port *port, - PortDirection *dir) + PortDirection *dir) { ConcretePort *cport = reinterpret_cast(port); cport->setDirection(dir); @@ -707,10 +726,10 @@ ConcreteNetwork::nextObjectId() class ConcreteCellPortIterator1 : public CellPortIterator { public: - explicit ConcreteCellPortIterator1(const ConcreteCell *cell); - ~ConcreteCellPortIterator1(); - virtual bool hasNext() { return iter_->hasNext(); } - virtual Port *next(); + ConcreteCellPortIterator1(const ConcreteCell *cell); + ~ConcreteCellPortIterator1() override; + bool hasNext() override { return iter_->hasNext(); } + Port *next() override; private: ConcreteCellPortIterator *iter_; @@ -744,10 +763,10 @@ ConcreteNetwork::portIterator(const Cell *cell) const class ConcreteCellPortBitIterator1 : public CellPortIterator { public: - explicit ConcreteCellPortBitIterator1(const ConcreteCell *cell); - ~ConcreteCellPortBitIterator1(); - virtual bool hasNext() { return iter_->hasNext(); } - virtual Port *next(); + ConcreteCellPortBitIterator1(const ConcreteCell *cell); + ~ConcreteCellPortBitIterator1() override; + bool hasNext() override { return iter_->hasNext(); } + Port *next() override; private: ConcreteCellPortBitIterator *iter_; @@ -785,7 +804,7 @@ ConcreteNetwork::portBitCount(const Cell *cell) const //////////////////////////////////////////////////////////////// -const char * +std::string ConcreteNetwork::name(const Port *port) const { const ConcretePort *cport = reinterpret_cast(port); @@ -834,7 +853,7 @@ ConcreteNetwork::isBus(const Port *port) const return cport->isBus(); } -const char * +std::string ConcreteNetwork::busName(const Port *port) const { const ConcretePort *cport = reinterpret_cast(port); @@ -864,7 +883,7 @@ ConcreteNetwork::toIndex(const Port *port) const Port * ConcreteNetwork::findBusBit(const Port *port, - int index) const + int index) const { const ConcretePort *cport = reinterpret_cast(port); return reinterpret_cast(cport->findBusBit(index)); @@ -872,7 +891,7 @@ ConcreteNetwork::findBusBit(const Port *port, Port * ConcreteNetwork::findMember(const Port *port, - int index) const + int index) const { const ConcretePort *cport = reinterpret_cast(port); return reinterpret_cast(cport->findMember(index)); @@ -891,20 +910,19 @@ ConcreteNetwork::hasMembers(const Port *port) const class ConcretePortMemberIterator1 : public PortMemberIterator { public: - explicit ConcretePortMemberIterator1(const ConcretePort *port); - ~ConcretePortMemberIterator1(); - virtual bool hasNext(); - virtual Port *next(); + ConcretePortMemberIterator1(const ConcretePort *port); + ~ConcretePortMemberIterator1() override; + bool hasNext() override; + Port *next() override; private: ConcretePortMemberIterator *iter_; - ConcretePort *next_; + ConcretePort *next_{nullptr}; }; ConcretePortMemberIterator1::ConcretePortMemberIterator1(const ConcretePort * - port) : - iter_(port->memberIterator()), - next_(nullptr) + port) : + iter_(port->memberIterator()) { } @@ -939,12 +957,12 @@ ConcreteNetwork::memberIterator(const Port *port) const //////////////////////////////////////////////////////////////// -const char * +std::string ConcreteNetwork::name(const Instance *instance) const { const ConcreteInstance *inst = reinterpret_cast(instance); - return inst->name(); + return std::string(inst->name()); } ObjectId @@ -955,9 +973,9 @@ ConcreteNetwork::id(const Instance *instance) const return inst->id(); } -string +std::string ConcreteNetwork::getAttribute(const Instance *inst, - const string &key) const + std::string_view key) const { const ConcreteInstance *cinst = reinterpret_cast(inst); return cinst->getAttribute(key); @@ -997,7 +1015,7 @@ ConcreteNetwork::isLeaf(const Instance *instance) const Instance * ConcreteNetwork::findChild(const Instance *parent, - const char *name) const + std::string_view name) const { const ConcreteInstance *inst = reinterpret_cast(parent); @@ -1006,7 +1024,7 @@ ConcreteNetwork::findChild(const Instance *parent, Pin * ConcreteNetwork::findPin(const Instance *instance, - const char *port_name) const + std::string_view port_name) const { const ConcreteInstance *inst = reinterpret_cast(instance); @@ -1015,7 +1033,7 @@ ConcreteNetwork::findPin(const Instance *instance, Pin * ConcreteNetwork::findPin(const Instance *instance, - const Port *port) const + const Port *port) const { const ConcreteInstance *inst = reinterpret_cast(instance); @@ -1024,7 +1042,7 @@ ConcreteNetwork::findPin(const Instance *instance, Net * ConcreteNetwork::findNet(const Instance *instance, - const char *net_name) const + std::string_view net_name) const { const ConcreteInstance *inst = reinterpret_cast(instance); @@ -1033,12 +1051,12 @@ ConcreteNetwork::findNet(const Instance *instance, void ConcreteNetwork::findInstNetsMatching(const Instance *instance, - const PatternMatch *pattern, - NetSeq &matches) const + const PatternMatch *pattern, + NetSeq &matches) const { const ConcreteInstance *inst = reinterpret_cast(instance); - return inst->findNetsMatching(pattern, matches); + inst->findNetsMatching(pattern, matches); } //////////////////////////////////////////////////////////////// @@ -1123,7 +1141,7 @@ ConcreteNetwork::vertexId(const Pin *pin) const void ConcreteNetwork::setVertexId(Pin *pin, - VertexId id) + VertexId id) { ConcretePin *cpin = reinterpret_cast(pin); cpin->setVertexId(id); @@ -1154,11 +1172,11 @@ ConcreteNetwork::pin(const Term *term) const //////////////////////////////////////////////////////////////// -const char * +std::string ConcreteNetwork::name(const Net *net) const { const ConcreteNet *cnet = reinterpret_cast(net); - return cnet->name(); + return std::string(cnet->name()); } ObjectId @@ -1178,13 +1196,13 @@ ConcreteNetwork::instance(const Net *net) const bool ConcreteNetwork::isPower(const Net *net) const { - return constant_nets_[int(LogicValue::one)].hasKey(const_cast(net)); + return constant_nets_[static_cast(LogicValue::one)].contains(const_cast(net)); } bool ConcreteNetwork::isGround(const Net *net) const { - return constant_nets_[int(LogicValue::zero)].hasKey(const_cast(net)); + return constant_nets_[static_cast(LogicValue::zero)].contains(const_cast(net)); } NetPinIterator * @@ -1203,7 +1221,7 @@ ConcreteNetwork::termIterator(const Net *net) const void ConcreteNetwork::mergeInto(Net *net, - Net *into_net) + Net *into_net) { ConcreteNet *cnet = reinterpret_cast(net); ConcreteNet *cinto_net = reinterpret_cast(into_net); @@ -1228,8 +1246,8 @@ ConcreteInstance::cell() const Instance * ConcreteNetwork::makeInstance(Cell *cell, - const char *name, - Instance *parent) + std::string_view name, + Instance *parent) { ConcreteCell *ccell = reinterpret_cast(cell); return makeConcreteInstance(ccell, name, parent); @@ -1237,16 +1255,16 @@ ConcreteNetwork::makeInstance(Cell *cell, Instance * ConcreteNetwork::makeInstance(LibertyCell *cell, - const char *name, - Instance *parent) + std::string_view name, + Instance *parent) { return makeConcreteInstance(cell, name, parent); } Instance * ConcreteNetwork::makeConcreteInstance(ConcreteCell *cell, - const char *name, - Instance *parent) + std::string_view name, + Instance *parent) { ConcreteInstance *cparent = reinterpret_cast(parent); @@ -1269,7 +1287,7 @@ ConcreteNetwork::makePins(Instance *inst) void ConcreteNetwork::replaceCell(Instance *inst, - Cell *cell) + Cell *cell) { ConcreteCell *ccell = reinterpret_cast(cell); int port_count = ccell->portBitCount(); @@ -1296,21 +1314,29 @@ ConcreteNetwork::replaceCell(Instance *inst, void ConcreteNetwork::deleteInstance(Instance *inst) { - ConcreteInstance *cinst = reinterpret_cast(inst); + deleteInstanceImpl(inst); +} - // Delete nets first (so children pin deletes are not required). - ConcreteInstanceNetMap::Iterator net_iter(cinst->nets_); - while (net_iter.hasNext()) { - ConcreteNet *cnet = net_iter.next(); - Net *net = reinterpret_cast(cnet); - // Delete terminals connected to net. - NetTermIterator *term_iter = termIterator(net); - while (term_iter->hasNext()) { - ConcreteTerm *term = reinterpret_cast(term_iter->next()); - delete term; +void +ConcreteNetwork::deleteInstanceImpl(Instance *inst) +{ + ConcreteInstance *cinst = reinterpret_cast(inst); + ConcreteInstanceNetMap *nets = cinst->nets_; + if (nets) { + // Delete nets first (so children pin deletes are not required). + for (auto itr = nets->begin(); itr != nets->end(); /*no incr*/) { + auto [name, cnet] = *itr; + Net *net = reinterpret_cast(cnet); + // Delete terminals connected to net. + NetTermIterator *term_iter = termIterator(net); + while (term_iter->hasNext()) { + ConcreteTerm *term = reinterpret_cast(term_iter->next()); + delete term; + } + delete term_iter; + itr = nets->erase(itr); + deleteNet(net); } - delete term_iter; - deleteNet(net); } // Delete children. @@ -1339,8 +1365,8 @@ ConcreteNetwork::deleteInstance(Instance *inst) Pin * ConcreteNetwork::makePin(Instance *inst, - Port *port, - Net *net) + Port *port, + Net *net) { ConcreteInstance *cinst = reinterpret_cast(inst); ConcretePort *cport = reinterpret_cast(port); @@ -1354,7 +1380,7 @@ ConcreteNetwork::makePin(Instance *inst, Term * ConcreteNetwork::makeTerm(Pin *pin, - Net *net) + Net *net) { ConcretePin *cpin = reinterpret_cast(pin); ConcreteNet *cnet = reinterpret_cast(net); @@ -1367,16 +1393,16 @@ ConcreteNetwork::makeTerm(Pin *pin, Pin * ConcreteNetwork::connect(Instance *inst, - LibertyPort *port, - Net *net) + LibertyPort *port, + Net *net) { return connect(inst, reinterpret_cast(port), net); } void ConcreteNetwork::setAttribute(Instance *inst, - const string &key, - const string &value) + std::string_view key, + std::string_view value) { ConcreteInstance *cinst = reinterpret_cast(inst); cinst->setAttribute(key, value); @@ -1384,8 +1410,8 @@ ConcreteNetwork::setAttribute(Instance *inst, Pin * ConcreteNetwork::connect(Instance *inst, - Port *port, - Net *net) + Port *port, + Net *net) { ConcreteNet *cnet = reinterpret_cast(net); ConcreteInstance *cinst = reinterpret_cast(inst); @@ -1417,7 +1443,7 @@ ConcreteNetwork::connect(Instance *inst, void ConcreteNetwork::connectNetPin(ConcreteNet *cnet, - ConcretePin *cpin) + ConcretePin *cpin) { cnet->addPin(cpin); @@ -1427,9 +1453,9 @@ ConcreteNetwork::connectNetPin(ConcreteNet *cnet, if (isDriver(pin)) { if (cnet->terms_ == nullptr) { Net *net = reinterpret_cast(cnet); - PinSet *drvrs = net_drvr_pin_map_.findKey(net); + PinSet *drvrs = findKey(net_drvr_pin_map_, net); if (drvrs) - drvrs->insert(pin); + drvrs->insert(pin); } else clearNetDrvrPinMap(); @@ -1445,8 +1471,8 @@ ConcreteNetwork::disconnectPin(Pin *pin) if (cterm) { ConcreteNet *cnet = cterm->net_; if (cnet) { - cnet->deleteTerm(cterm); - clearNetDrvrPinMap(); + cnet->deleteTerm(cterm); + clearNetDrvrPinMap(); } cpin->term_ = nullptr; delete cterm; @@ -1462,7 +1488,7 @@ ConcreteNetwork::disconnectPin(Pin *pin) void ConcreteNetwork::disconnectNetPin(ConcreteNet *cnet, - ConcretePin *cpin) + ConcretePin *cpin) { cnet->deletePin(cpin); @@ -1473,9 +1499,9 @@ ConcreteNetwork::disconnectNetPin(ConcreteNet *cnet, // and it is safe to incrementally update the drivers. if (cnet->terms_ == nullptr) { Net *net = reinterpret_cast(cnet); - PinSet *drvrs = net_drvr_pin_map_.findKey(net); + PinSet *drvrs = findKey(net_drvr_pin_map_, net); if (drvrs) - drvrs->erase(pin); + drvrs->erase(pin); } else clearNetDrvrPinMap(); @@ -1489,16 +1515,15 @@ ConcreteNetwork::deletePin(Pin *pin) ConcreteNet *cnet = cpin->net(); if (cnet) disconnectNetPin(cnet, cpin); - ConcreteInstance *cinst = - reinterpret_cast(cpin->instance()); + ConcreteInstance *cinst = cpin->instance(); if (cinst) cinst->deletePin(cpin); delete cpin; } Net * -ConcreteNetwork::makeNet(const char *name, - Instance *parent) +ConcreteNetwork::makeNet(std::string_view name, + Instance *parent) { ConcreteInstance *cparent = reinterpret_cast(parent); ConcreteNet *net = new ConcreteNet(name, cparent); @@ -1518,16 +1543,15 @@ ConcreteNetwork::deleteNet(Net *net) pin->net_ = nullptr; } - constant_nets_[int(LogicValue::zero)].erase(net); - constant_nets_[int(LogicValue::one)].erase(net); - PinSet *drvrs = net_drvr_pin_map_.findKey(net); + constant_nets_[static_cast(LogicValue::zero)].erase(net); + constant_nets_[static_cast(LogicValue::one)].erase(net); + PinSet *drvrs = findKey(net_drvr_pin_map_, net); if (drvrs) { delete drvrs; net_drvr_pin_map_.erase(net); } - ConcreteInstance *cinst = - reinterpret_cast(cnet->instance()); + ConcreteInstance *cinst = cnet->instance(); cinst->deleteNet(cnet); delete cnet; } @@ -1535,25 +1559,25 @@ ConcreteNetwork::deleteNet(Net *net) void ConcreteNetwork::clearConstantNets() { - constant_nets_[int(LogicValue::zero)].clear(); - constant_nets_[int(LogicValue::one)].clear(); + constant_nets_[static_cast(LogicValue::zero)].clear(); + constant_nets_[static_cast(LogicValue::one)].clear(); } void ConcreteNetwork::addConstantNet(Net *net, - LogicValue value) + LogicValue value) { if (value == LogicValue::zero || value == LogicValue::one) - constant_nets_[int(value)].insert(net); + constant_nets_[static_cast(value)].insert(net); } ConstantPinIterator * ConcreteNetwork::constantPinIterator() { return new NetworkConstantPinIterator(this, - constant_nets_[int(LogicValue::zero)], - constant_nets_[int(LogicValue::one)]); + constant_nets_[static_cast(LogicValue::zero)], + constant_nets_[static_cast(LogicValue::one)]); } //////////////////////////////////////////////////////////////// @@ -1561,22 +1585,22 @@ ConcreteNetwork::constantPinIterator() // Optimized version of Network::visitConnectedPins. void ConcreteNetwork::visitConnectedPins(const Net *net, - PinVisitor &visitor, - NetSet &visited_nets) const + PinVisitor &visitor, + NetSet &visited_nets) const { - if (!visited_nets.hasKey(net)) { + if (!visited_nets.contains(net)) { visited_nets.insert(net); // Search up from net terminals. const ConcreteNet *cnet = reinterpret_cast(net); for (ConcreteTerm *term = cnet->terms_; term; term = term->net_next_) { ConcretePin *above_pin = term->pin_; if (above_pin) { - ConcreteNet *above_net = above_pin->net_; - if (above_net) - visitConnectedPins(reinterpret_cast(above_net), - visitor, visited_nets); - else - visitor(reinterpret_cast(above_pin)); + ConcreteNet *above_net = above_pin->net_; + if (above_net) + visitConnectedPins(reinterpret_cast(above_net), + visitor, visited_nets); + else + visitor(reinterpret_cast(above_pin)); } } @@ -1585,10 +1609,10 @@ ConcreteNetwork::visitConnectedPins(const Net *net, visitor(reinterpret_cast(pin)); ConcreteTerm *below_term = pin->term_; if (below_term) { - ConcreteNet *below_net = below_term->net_; - if (below_net) - visitConnectedPins(reinterpret_cast(below_net), - visitor, visited_nets); + ConcreteNet *below_net = below_term->net_; + if (below_net) + visitConnectedPins(reinterpret_cast(below_net), + visitor, visited_nets); } } } @@ -1596,15 +1620,13 @@ ConcreteNetwork::visitConnectedPins(const Net *net, //////////////////////////////////////////////////////////////// -ConcreteInstance::ConcreteInstance(const char *name, - ConcreteCell *cell, +ConcreteInstance::ConcreteInstance(std::string_view name, + ConcreteCell *cell, ConcreteInstance *parent) : - name_(stringCopy(name)), + name_(name), id_(ConcreteNetwork::nextObjectId()), cell_(cell), - parent_(parent), - children_(nullptr), - nets_(nullptr) + parent_(parent) { initPins(); } @@ -1618,22 +1640,21 @@ ConcreteInstance::initPins() ConcreteInstance::~ConcreteInstance() { - stringDelete(name_); delete children_; delete nets_; } Instance * -ConcreteInstance::findChild(const char *name) const +ConcreteInstance::findChild(std::string_view name) const { if (children_) - return reinterpret_cast(children_->findKey(name)); + return reinterpret_cast(findStringKey(*children_, name)); else return nullptr; } ConcretePin * -ConcreteInstance::findPin(const char *port_name) const +ConcreteInstance::findPin(std::string_view port_name) const { ConcreteCell *ccell = reinterpret_cast(cell_); const ConcretePort *cport = @@ -1657,15 +1678,15 @@ ConcreteInstance::findPin(const Port *port) const } ConcreteNet * -ConcreteInstance::findNet(const char *net_name) const +ConcreteInstance::findNet(std::string_view net_name) const { ConcreteNet *net = nullptr; if (nets_) { - net = nets_->findKey(net_name); + net = findStringKey(*nets_, net_name); // Follow merge pointer to surviving net. if (net) { while (net->mergedInto()) - net = net->mergedInto(); + net = net->mergedInto(); } } return net; @@ -1675,20 +1696,18 @@ void ConcreteInstance::findNetsMatching(const PatternMatch *pattern, NetSeq &matches) const { - if (pattern->hasWildcards()) { - ConcreteInstanceNetMap::Iterator net_iter(nets_); - while (net_iter.hasNext()) { - const char *net_name; - ConcreteNet *cnet; - net_iter.next(net_name, cnet); - if (pattern->match(net_name)) - matches.push_back(reinterpret_cast(cnet)); + if (nets_) { + if (pattern->hasWildcards()) { + for (auto [net_name, cnet] : *nets_) { + if (pattern->match(net_name)) + matches.push_back(reinterpret_cast(cnet)); + } + } + else { + ConcreteNet *cnet = findNet(pattern->pattern()); + if (cnet) + matches.push_back(reinterpret_cast(cnet)); } - } - else { - ConcreteNet *cnet = findNet(pattern->pattern()); - if (cnet) - matches.push_back(reinterpret_cast(cnet)); } } @@ -1706,14 +1725,14 @@ ConcreteInstance::childIterator() const } void -ConcreteInstance::setAttribute(const string &key, - const string &value) +ConcreteInstance::setAttribute(std::string_view key, + std::string_view value) { - attribute_map_[key] = value; + attribute_map_[std::string(key)] = value; } -string -ConcreteInstance::getAttribute(const string &key) const +std::string +ConcreteInstance::getAttribute(std::string_view key) const { const auto &itr = attribute_map_.find(key); if (itr != attribute_map_.end()) @@ -1761,12 +1780,12 @@ ConcreteInstance::addNet(ConcreteNet *net) } void -ConcreteInstance::addNet(const char *name, - ConcreteNet *net) +ConcreteInstance::addNet(std::string_view, + ConcreteNet *net) { if (nets_ == nullptr) nets_ = new ConcreteInstanceNetMap; - (*nets_)[name] = net; + (*nets_)[net->name()] = net; } void @@ -1784,20 +1803,16 @@ ConcreteInstance::setCell(ConcreteCell *cell) //////////////////////////////////////////////////////////////// ConcretePin::ConcretePin(ConcreteInstance *instance, - ConcretePort *port, - ConcreteNet *net) : + ConcretePort *port, + ConcreteNet *net) : instance_(instance), port_(port), net_(net), - term_(nullptr), - id_(ConcreteNetwork::nextObjectId()), - net_next_(nullptr), - net_prev_(nullptr), - vertex_id_(vertex_id_null) + id_(ConcreteNetwork::nextObjectId()) { } -const char * +const std::string& ConcretePin::name() const { return port_->name(); @@ -1811,40 +1826,30 @@ ConcretePin::setVertexId(VertexId id) //////////////////////////////////////////////////////////////// -const char * +const std::string & ConcreteTerm::name() const { ConcretePin *cpin = reinterpret_cast(pin_); - const ConcretePort *cport = - reinterpret_cast(cpin->port()); + const ConcretePort *cport = cpin->port(); return cport->name(); } ConcreteTerm::ConcreteTerm(ConcretePin *pin, - ConcreteNet *net) : + ConcreteNet *net) : pin_(pin), net_(net), - id_(ConcreteNetwork::nextObjectId()), - net_next_(nullptr) + id_(ConcreteNetwork::nextObjectId()) { } //////////////////////////////////////////////////////////////// -ConcreteNet::ConcreteNet(const char *name, - ConcreteInstance *instance) : - name_(stringCopy(name)), +ConcreteNet::ConcreteNet(std::string_view name, + ConcreteInstance *instance) : + name_(name), id_(ConcreteNetwork::nextObjectId()), - instance_(instance), - pins_(nullptr), - terms_(nullptr), - merged_into_(nullptr) -{ -} - -ConcreteNet::~ConcreteNet() + instance_(instance) { - stringDelete(name_); } // Merged nets are kept around to serve as name aliases. @@ -1912,9 +1917,9 @@ ConcreteNet::deleteTerm(ConcreteTerm *term) for (ConcreteTerm *net_term=terms_;net_term;net_term=net_term->net_next_) { if (net_term == term) { if (net_prev_term) - net_prev_term->net_next_ = term->net_next_; + net_prev_term->net_next_ = term->net_next_; else - terms_ = term->net_next_; + terms_ = term->net_next_; break; } net_prev_term = net_term; @@ -1923,18 +1928,18 @@ ConcreteNet::deleteTerm(ConcreteTerm *term) //////////////////////////////////////////////////////////////// -typedef Map BindingMap; +using BindingMap = std::map ; // Binding table used for linking/expanding network. class ConcreteBindingTbl { public: - explicit ConcreteBindingTbl(NetworkEdit *network); + ConcreteBindingTbl(NetworkEdit *network); Net *ensureBinding(Net *proto_net, - Instance *parent); + Instance *parent); Net *find(Net *net); void bind(Net *proto_net, - Net *clone_net); + Net *clone_net); private: BindingMap map_; @@ -1943,7 +1948,7 @@ class ConcreteBindingTbl void ConcreteNetwork::setCellNetworkView(Cell *cell, - Instance *inst) + Instance *inst) { cell_network_view_map_[cell] = inst; } @@ -1951,7 +1956,7 @@ ConcreteNetwork::setCellNetworkView(Cell *cell, Instance * ConcreteNetwork::cellNetworkView(Cell *cell) { - return cell_network_view_map_.findKey(cell); + return findKey(cell_network_view_map_, cell); } void @@ -1980,28 +1985,29 @@ ConcreteNetwork::setLinkFunc(LinkNetworkFunc link) } bool -ConcreteNetwork::linkNetwork(const char *top_cell_name, - bool make_black_boxes, - Report *report) +ConcreteNetwork::linkNetwork(std::string_view top_cell_name, + bool make_black_boxes, + Report *report) { if (link_func_) { clearConstantNets(); deleteTopInstance(); - top_instance_ = link_func_(top_cell_name, make_black_boxes); + top_instance_ = link_func_(top_cell_name, + make_black_boxes); if (top_instance_) - checkNetworkLibertyCorners(); + checkNetworkLibertyScenes(); return top_instance_ != nullptr; } else { - report->error(1000, "cell type %s can not be linked.", top_cell_name); + report->error(1000, "cell type {} can not be linked.", top_cell_name); return false; } } Instance * linkReaderNetwork(Cell *top_cell, - bool, Report *, - NetworkReader *network) + bool, Report *, + NetworkReader *network) { Instance *view = network->cellNetworkView(top_cell); if (view) { @@ -2023,13 +2029,13 @@ linkReaderNetwork(Cell *top_cell, static void makeChildNetwork(Instance *proto, - Instance *parent, - ConcreteBindingTbl *parent_bindings, - NetworkReader *network) + Instance *parent, + ConcreteBindingTbl *parent_bindings, + NetworkReader *network) { Cell *proto_cell = network->cell(proto); Instance *clone = network->makeInstance(proto_cell, network->name(proto), - parent); + parent); if (network->isLeaf(proto_cell)) makeClonePins(proto, clone, nullptr, nullptr, parent, parent_bindings, network); else { @@ -2037,12 +2043,12 @@ makeChildNetwork(Instance *proto, ConcreteBindingTbl bindings(network); Instance *clone_view = network->cellNetworkView(proto_cell); makeClonePins(proto, clone, clone_view, &bindings, parent, - parent_bindings, network); + parent_bindings, network); if (clone_view) { InstanceChildIterator *child_iter = network->childIterator(clone_view); while (child_iter->hasNext()) { - Instance *child = child_iter->next(); - makeChildNetwork(child, clone, &bindings, network); + Instance *child = child_iter->next(); + makeChildNetwork(child, clone, &bindings, network); } delete child_iter; } @@ -2051,12 +2057,12 @@ makeChildNetwork(Instance *proto, static void makeClonePins(Instance *proto, - Instance *clone, - Instance *clone_view, - ConcreteBindingTbl *bindings, - Instance *parent, - ConcreteBindingTbl *parent_bindings, - NetworkReader *network) + Instance *clone, + Instance *clone_view, + ConcreteBindingTbl *bindings, + Instance *parent, + ConcreteBindingTbl *parent_bindings, + NetworkReader *network) { InstancePinIterator *proto_pin_iter = network->pinIterator(proto); while (proto_pin_iter->hasNext()) { @@ -2072,7 +2078,7 @@ makeClonePins(Instance *proto, Net *clone_proto_net = network->net(clone_proto_pin); Net *clone_child_net = nullptr; if (clone_proto_net) - clone_child_net = bindings->ensureBinding(clone_proto_net, clone); + clone_child_net = bindings->ensureBinding(clone_proto_net, clone); network->makeTerm(clone_pin, clone_child_net); } } @@ -2092,7 +2098,7 @@ ConcreteBindingTbl::ConcreteBindingTbl(NetworkEdit *network) : Net * ConcreteBindingTbl::find(Net *proto_net) { - ConcreteNet *net = reinterpret_cast(map_.findKey(proto_net)); + ConcreteNet *net = reinterpret_cast(findKey(map_, proto_net)); while (net && net->mergedInto()) net = net->mergedInto(); return reinterpret_cast(net); @@ -2100,14 +2106,14 @@ ConcreteBindingTbl::find(Net *proto_net) void ConcreteBindingTbl::bind(Net *proto_net, - Net *net) + Net *net) { map_[proto_net] = net; } Net * ConcreteBindingTbl::ensureBinding(Net *proto_net, - Instance *parent) + Instance *parent) { Net *net = find(proto_net); if (net == nullptr) { @@ -2117,4 +2123,4 @@ ConcreteBindingTbl::ensureBinding(Net *proto_net, return net; } -} // namespace +} // namespace sta diff --git a/network/HpinDrvrLoad.cc b/network/HpinDrvrLoad.cc index 944777390..8aaf57b4a 100644 --- a/network/HpinDrvrLoad.cc +++ b/network/HpinDrvrLoad.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,39 +25,41 @@ #include "HpinDrvrLoad.hh" #include +#include +#include "ContainerHelpers.hh" #include "Network.hh" namespace sta { -typedef Set HpinDrvrLoads; +using HpinDrvrLoads = std::set; static void visitPinsAboveNet2(const Pin *hpin, - Net *above_net, - NetSet &visited, - HpinDrvrLoads &above_drvrs, - HpinDrvrLoads &above_loads, - PinSet *hpin_path, - const Network *network); + Net *above_net, + NetSet &visited, + HpinDrvrLoads &above_drvrs, + HpinDrvrLoads &above_loads, + PinSet *hpin_path, + const Network *network); static void visitPinsBelowNet2(const Pin *hpin, - Net *above_net, - Net *below_net, - NetSet &visited, - HpinDrvrLoads &below_drvrs, - HpinDrvrLoads &below_loads, - PinSet *hpin_path, - const Network *network); + Net *above_net, + Net *below_net, + NetSet &visited, + HpinDrvrLoads &below_drvrs, + HpinDrvrLoads &below_loads, + PinSet *hpin_path, + const Network *network); static void -visitHpinDrvrLoads(HpinDrvrLoads drvrs, - HpinDrvrLoads loads, - HpinDrvrLoadVisitor *visitor); +visitHpinDrvrLoads(HpinDrvrLoads &drvrs, + HpinDrvrLoads &loads, + HpinDrvrLoadVisitor *visitor); void visitHpinDrvrLoads(const Pin *pin, - const Network *network, - HpinDrvrLoadVisitor *visitor) + const Network *network, + HpinDrvrLoadVisitor *visitor) { NetSet visited(network); HpinDrvrLoads above_drvrs; @@ -66,8 +68,8 @@ visitHpinDrvrLoads(const Pin *pin, Net *above_net = network->net(pin); if (above_net) { visitPinsAboveNet2(pin, above_net, visited, - above_drvrs, above_loads, - &hpin_path, network); + above_drvrs, above_loads, + &hpin_path, network); } // Search down from hpin terminal. @@ -78,8 +80,8 @@ visitHpinDrvrLoads(const Pin *pin, Net *below_net = network->net(term); if (below_net) visitPinsBelowNet2(pin, above_net, below_net, visited, - below_drvrs, below_loads, - &hpin_path, network); + below_drvrs, below_loads, + &hpin_path, network); } if (network->isHierarchical(pin)) { visitHpinDrvrLoads(above_drvrs, below_loads, visitor); @@ -102,20 +104,20 @@ visitHpinDrvrLoads(const Pin *pin, visitHpinDrvrLoads(above_drvrs, loads, visitor); } } - above_drvrs.deleteContents(); - above_loads.deleteContents(); - below_drvrs.deleteContents(); - below_loads.deleteContents(); + deleteContents(above_drvrs); + deleteContents(above_loads); + deleteContents(below_drvrs); + deleteContents(below_loads); } static void visitPinsAboveNet2(const Pin *hpin, - Net *above_net, - NetSet &visited, - HpinDrvrLoads &above_drvrs, - HpinDrvrLoads &above_loads, - PinSet *hpin_path, - const Network *network) + Net *above_net, + NetSet &visited, + HpinDrvrLoads &above_drvrs, + HpinDrvrLoads &above_loads, + PinSet *hpin_path, + const Network *network) { visited.insert(above_net); // Visit above net pins. @@ -124,25 +126,25 @@ visitPinsAboveNet2(const Pin *hpin, const Pin *above_pin = pin_iter->next(); if (above_pin != hpin) { if (network->isDriver(above_pin)) { - HpinDrvrLoad *drvr = new HpinDrvrLoad(above_pin, nullptr, - hpin_path, nullptr); - above_drvrs.insert(drvr); + HpinDrvrLoad *drvr = new HpinDrvrLoad(above_pin, nullptr, + hpin_path, nullptr); + above_drvrs.insert(drvr); } if (network->isLoad(above_pin)) { - HpinDrvrLoad *load = new HpinDrvrLoad(nullptr, above_pin, - nullptr, hpin_path); - above_loads.insert(load); + HpinDrvrLoad *load = new HpinDrvrLoad(nullptr, above_pin, + nullptr, hpin_path); + above_loads.insert(load); } Term *above_term = network->term(above_pin); if (above_term) { - Net *above_net1 = network->net(above_term); - if (above_net1 && !visited.hasKey(above_net1)) { - hpin_path->insert(above_pin); - visitPinsAboveNet2(above_pin, above_net1, visited, - above_drvrs, above_loads, - hpin_path, network); - hpin_path->erase(above_pin); - } + Net *above_net1 = network->net(above_term); + if (above_net1 && !visited.contains(above_net1)) { + hpin_path->insert(above_pin); + visitPinsAboveNet2(above_pin, above_net1, visited, + above_drvrs, above_loads, + hpin_path, network); + hpin_path->erase(above_pin); + } } } } @@ -154,25 +156,25 @@ visitPinsAboveNet2(const Pin *hpin, Term *term = term_iter->next(); Pin *above_pin = network->pin(term); if (above_pin - && above_pin != hpin) { + && above_pin != hpin) { Net *above_net1 = network->net(above_pin); - if (above_net1 && !visited.hasKey(above_net1)) { - hpin_path->insert(above_pin); - visitPinsAboveNet2(above_pin, above_net1, visited, - above_drvrs, above_loads, - hpin_path, network); - hpin_path->erase(above_pin); + if (above_net1 && !visited.contains(above_net1)) { + hpin_path->insert(above_pin); + visitPinsAboveNet2(above_pin, above_net1, visited, + above_drvrs, above_loads, + hpin_path, network); + hpin_path->erase(above_pin); } if (network->isDriver(above_pin)) { - HpinDrvrLoad *drvr = new HpinDrvrLoad(above_pin, nullptr, - hpin_path, nullptr); - above_drvrs.insert(drvr); + HpinDrvrLoad *drvr = new HpinDrvrLoad(above_pin, nullptr, + hpin_path, nullptr); + above_drvrs.insert(drvr); } if (network->isLoad(above_pin)) { - HpinDrvrLoad *load = new HpinDrvrLoad(nullptr, above_pin, - nullptr, hpin_path); - above_loads.insert(load); + HpinDrvrLoad *load = new HpinDrvrLoad(nullptr, above_pin, + nullptr, hpin_path); + above_loads.insert(load); } } } @@ -181,13 +183,13 @@ visitPinsAboveNet2(const Pin *hpin, static void visitPinsBelowNet2(const Pin *hpin, - Net *above_net, - Net *below_net, - NetSet &visited, - HpinDrvrLoads &below_drvrs, - HpinDrvrLoads &below_loads, - PinSet *hpin_path, - const Network *network) + Net *above_net, + Net *below_net, + NetSet &visited, + HpinDrvrLoads &below_drvrs, + HpinDrvrLoads &below_loads, + PinSet *hpin_path, + const Network *network) { visited.insert(below_net); // Visit below net pins. @@ -195,32 +197,32 @@ visitPinsBelowNet2(const Pin *hpin, while (pin_iter->hasNext()) { const Pin *below_pin = pin_iter->next(); if (below_pin != hpin) { - if (above_net && !visited.hasKey(above_net)) - visitPinsAboveNet2(below_pin, above_net, - visited, below_drvrs, below_loads, - hpin_path, network); + if (above_net && !visited.contains(above_net)) + visitPinsAboveNet2(below_pin, above_net, + visited, below_drvrs, below_loads, + hpin_path, network); if (network->isDriver(below_pin)) { - HpinDrvrLoad *drvr = new HpinDrvrLoad(below_pin, nullptr, - hpin_path, nullptr); - below_drvrs.insert(drvr); + HpinDrvrLoad *drvr = new HpinDrvrLoad(below_pin, nullptr, + hpin_path, nullptr); + below_drvrs.insert(drvr); } if (network->isLoad(below_pin)) { - HpinDrvrLoad *load = new HpinDrvrLoad(nullptr, below_pin, - nullptr, hpin_path); - below_loads.insert(load); + HpinDrvrLoad *load = new HpinDrvrLoad(nullptr, below_pin, + nullptr, hpin_path); + below_loads.insert(load); } if (network->isHierarchical(below_pin)) { - Term *term = network->term(below_pin); - if (term) { - Net *below_net1 = network->net(term); - if (below_net1 && !visited.hasKey(below_net1)) { - hpin_path->insert(below_pin); - visitPinsBelowNet2(below_pin, below_net, below_net1, visited, - below_drvrs, below_loads, - hpin_path, network); - hpin_path->erase(below_pin); - } - } + Term *term = network->term(below_pin); + if (term) { + Net *below_net1 = network->net(term); + if (below_net1 && !visited.contains(below_net1)) { + hpin_path->insert(below_pin); + visitPinsBelowNet2(below_pin, below_net, below_net1, visited, + below_drvrs, below_loads, + hpin_path, network); + hpin_path->erase(below_pin); + } + } } } } @@ -232,14 +234,14 @@ visitPinsBelowNet2(const Pin *hpin, Term *term = term_iter->next(); Pin *above_pin = network->pin(term); if (above_pin - && above_pin != hpin) { + && above_pin != hpin) { Net *above_net1 = network->net(above_pin); - if (above_net1 && !visited.hasKey(above_net1)) { - hpin_path->insert(above_pin); - visitPinsAboveNet2(above_pin, above_net1, - visited, below_drvrs, below_loads, - hpin_path, network); - hpin_path->erase(above_pin); + if (above_net1 && !visited.contains(above_net1)) { + hpin_path->insert(above_pin); + visitPinsAboveNet2(above_pin, above_net1, + visited, below_drvrs, below_loads, + hpin_path, network); + hpin_path->erase(above_pin); } } } @@ -247,20 +249,16 @@ visitPinsBelowNet2(const Pin *hpin, } static void -visitHpinDrvrLoads(HpinDrvrLoads drvrs, - HpinDrvrLoads loads, - HpinDrvrLoadVisitor *visitor) +visitHpinDrvrLoads(HpinDrvrLoads &drvrs, + HpinDrvrLoads &loads, + HpinDrvrLoadVisitor *visitor) { - HpinDrvrLoads::Iterator drvr_iter(drvrs); - while (drvr_iter.hasNext()) { - HpinDrvrLoad *drvr = drvr_iter.next(); - HpinDrvrLoads::Iterator load_iter(loads); - while (load_iter.hasNext()) { - HpinDrvrLoad *load = load_iter.next(); + for (HpinDrvrLoad *drvr : drvrs) { + for (HpinDrvrLoad *load : loads) { HpinDrvrLoad clone(drvr->drvr(), - load->load(), - drvr->hpinsFromDrvr(), - load->hpinsToLoad()); + load->load(), + drvr->hpinsFromDrvr(), + load->hpinsToLoad()); visitor->visit(&clone); } } @@ -269,9 +267,9 @@ visitHpinDrvrLoads(HpinDrvrLoads drvrs, //////////////////////////////////////////////////////////////// HpinDrvrLoad::HpinDrvrLoad(const Pin *drvr, - const Pin *load, - PinSet *hpins_from_drvr, - PinSet *hpins_to_load) : + const Pin *load, + PinSet *hpins_from_drvr, + PinSet *hpins_to_load) : drvr_(drvr), load_(load), hpins_from_drvr_(hpins_from_drvr ? new PinSet(*hpins_from_drvr) : nullptr), @@ -280,7 +278,7 @@ HpinDrvrLoad::HpinDrvrLoad(const Pin *drvr, } HpinDrvrLoad::HpinDrvrLoad(const Pin *drvr, - const Pin *load) : + const Pin *load) : drvr_(drvr), load_(load) { @@ -295,21 +293,16 @@ HpinDrvrLoad::~HpinDrvrLoad() void HpinDrvrLoad::report(const Network *network) { - printf("%s -> %s: ", - drvr_ ? network->pathName(drvr_) : "-", - load_ ? network->pathName(load_) : "-"); - PinSet::Iterator pin_iter(hpins_from_drvr_); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - printf("%s ", network->pathName(pin)); - } - printf("* "); - PinSet::Iterator pin_iter2(hpins_to_load_); - while (pin_iter2.hasNext()) { - const Pin *pin = pin_iter2.next(); - printf("%s ", network->pathName(pin)); - } - printf("\n"); + Report *report = network->report(); + std::string line = sta::format("{} -> {}: ", + drvr_ ? network->pathName(drvr_) : "-", + load_ ? network->pathName(load_) : "-"); + for (const Pin *pin : *hpins_from_drvr_) + line += sta::format("{} ", network->pathName(pin)); + line += "* "; + for (const Pin *pin : *hpins_to_load_) + line += sta::format("{} ", network->pathName(pin)); + report->report(line); } void @@ -320,7 +313,7 @@ HpinDrvrLoad::setDrvr(const Pin *drvr) bool HpinDrvrLoadLess::operator()(const HpinDrvrLoad *drvr_load1, - const HpinDrvrLoad *drvr_load2) const + const HpinDrvrLoad *drvr_load2) const { const Pin *load1 = drvr_load1->load(); const Pin *load2 = drvr_load2->load(); @@ -333,4 +326,4 @@ HpinDrvrLoadLess::operator()(const HpinDrvrLoad *drvr_load1, return load1 < load2; } -} // namespace +} // namespace sta diff --git a/network/Link.tcl b/network/Link.tcl index 4dd5bae49..721960870 100644 --- a/network/Link.tcl +++ b/network/Link.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/network/Network.cc b/network/Network.cc index 98d172897..720711dae 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -30,27 +30,22 @@ #include "Sequential.hh" #include "FuncExpr.hh" -#include "StringUtil.hh" -#include "PatternMatch.hh" +#include +#include + +#include "ContainerHelpers.hh" #include "Liberty.hh" -#include "PortDirection.hh" -#include "Corner.hh" #include "ParseBus.hh" +#include "PatternMatch.hh" +#include "PortDirection.hh" +#include "Scene.hh" +#include "StringUtil.hh" namespace sta { -using std::string; - -Network::Network() : - default_liberty_(nullptr), - divider_('/'), - escape_('\\') -{ -} - Network::~Network() { - net_drvr_pin_map_.deleteContents(); + deleteContents(net_drvr_pin_map_); } void @@ -79,12 +74,12 @@ Network::findPortsMatching(const Cell *cell, { PortSeq matches; bool is_bus, is_range, subscript_wild; - string bus_name; + std::string bus_name; int from, to; parseBusName(pattern->pattern(), '[', ']', '\\', is_bus, is_range, bus_name, from, to, subscript_wild); if (is_bus) { - PatternMatch bus_pattern(bus_name.c_str(), pattern); + PatternMatch bus_pattern(bus_name, pattern); CellPortIterator *port_iter = portIterator(cell); while (port_iter->hasNext()) { Port *port = port_iter->next(); @@ -111,8 +106,8 @@ Network::findPortsMatching(const Cell *cell, else { // bus[0] Port *port_bit = findBusBit(port, from); - if (port_bit != nullptr) - matches.push_back(port_bit); + if (port_bit != nullptr) + matches.push_back(port_bit); } } } @@ -143,18 +138,18 @@ Network::readLibertyAfter(LibertyLibrary *) } LibertyCell * -Network::findLibertyCell(const char *name) const +Network::findLibertyCell(std::string_view name) const { - LibertyLibraryIterator *iter = libertyLibraryIterator(); - while (iter->hasNext()) { - LibertyLibrary *lib = iter->next(); + LibertyLibraryIterator *lib_iter = libertyLibraryIterator(); + while (lib_iter->hasNext()) { + LibertyLibrary *lib = lib_iter->next(); LibertyCell *cell = lib->findLibertyCell(name); if (cell) { - delete iter; + delete lib_iter; return cell; } } - delete iter; + delete lib_iter; return nullptr; } @@ -171,9 +166,9 @@ Network::setDefaultLibertyLibrary(LibertyLibrary *library) } void -Network::checkLibertyCorners() +Network::checkLibertyScenes() { - if (corners_->count() > 1) { + if (multiScene()) { LibertyLibraryIterator *lib_iter = libertyLibraryIterator(); LibertyCellSet cells; while (lib_iter->hasNext()) { @@ -188,14 +183,14 @@ Network::checkLibertyCorners() delete lib_iter; for (LibertyCell *cell : cells) - LibertyLibrary::checkCorners(cell, corners_, report_); + LibertyLibrary::checkScenes(cell, scenes_, report_); } } void -Network::checkNetworkLibertyCorners() +Network::checkNetworkLibertyScenes() { - if (corners_->count() > 1) { + if (multiScene()) { LibertyCellSet network_cells; LeafInstanceIterator *leaf_iter = network_->leafInstanceIterator(); while (leaf_iter->hasNext()) { @@ -207,18 +202,17 @@ Network::checkNetworkLibertyCorners() delete leaf_iter; for (LibertyCell *cell : network_cells) - LibertyLibrary::checkCorners(cell, corners_, report_); + LibertyLibrary::checkScenes(cell, scenes_, report_); } } -// Only used by Sta::setMinLibrary so linear search is acceptable. LibertyLibrary * -Network::findLibertyFilename(const char *filename) +Network::findLibertyFilename(std::string_view filename) { LibertyLibraryIterator *lib_iter = libertyLibraryIterator(); while (lib_iter->hasNext()) { LibertyLibrary *lib = lib_iter->next(); - if (stringEq(lib->filename(), filename)) { + if (lib->filename() == filename) { delete lib_iter; return lib; } @@ -245,16 +239,16 @@ Network::libertyPort(const Pin *pin) const bool Network::busIndexInRange(const Port *port, - int index) + int index) { int from_index = fromIndex(port); int to_index = toIndex(port); return (from_index <= to_index - && index <= to_index - && index >= from_index) + && index <= to_index + && index >= from_index) || (from_index > to_index - && index >= to_index - && index <= from_index); + && index >= to_index + && index <= from_index); } bool @@ -263,44 +257,32 @@ Network::hasMembers(const Port *port) const return isBus(port) || isBundle(port); } -const char * +std::string Network::pathName(const Instance *instance) const { InstanceSeq inst_path; path(instance, inst_path); - size_t name_length = 0; - InstanceSeq::Iterator path_iter1(inst_path); - while (path_iter1.hasNext()) { - const Instance *inst = path_iter1.next(); - name_length += strlen(name(inst)) + 1; - } - char *path_name = makeTmpString(name_length + 1); - char *path_ptr = path_name; - // Top instance has null string name, so terminate the string here. - *path_name = '\0'; - while (inst_path.size()) { + std::string path_name; + while (!inst_path.empty()) { const Instance *inst = inst_path.back(); - const char *inst_name = name(inst); - strcpy(path_ptr, inst_name); - path_ptr += strlen(inst_name); + path_name += name(inst); inst_path.pop_back(); - if (inst_path.size()) - *path_ptr++ = pathDivider(); - *path_ptr = '\0'; + if (!inst_path.empty()) + path_name += pathDivider(); } return path_name; } bool Network::pathNameLess(const Instance *inst1, - const Instance *inst2) const + const Instance *inst2) const { return pathNameCmp(inst1, inst2) < 0; } int Network::pathNameCmp(const Instance *inst1, - const Instance *inst2) const + const Instance *inst2) const { if (inst1 == nullptr && inst2) return -1; @@ -316,9 +298,9 @@ Network::pathNameCmp(const Instance *inst1, while (!path1.empty() && !path2.empty()) { const Instance *inst1 = path1.back(); const Instance *inst2 = path2.back(); - int cmp = strcmp(name(inst1), name(inst2)); + auto cmp = name(inst1) <=> name(inst2); if (cmp != 0) - return cmp; + return cmp < 0 ? -1 : 1; path1.pop_back(); path2.pop_back(); } @@ -333,8 +315,8 @@ Network::pathNameCmp(const Instance *inst1, void Network::path(const Instance *inst, - // Return value. - InstanceSeq &path) const + // Return value. + InstanceSeq &path) const { while (!isTopInstance(inst)) { path.push_back(inst); @@ -350,7 +332,7 @@ Network::isTopInstance(const Instance *inst) const bool Network::isInside(const Instance *inst, - const Instance *hier_inst) const + const Instance *hier_inst) const { while (inst) { if (inst == hier_inst) @@ -368,34 +350,26 @@ Network::isHierarchical(const Instance *instance) const //////////////////////////////////////////////////////////////// -const char * +std::string Network::name(const Pin *pin) const { return pathName(pin); } -const char * +std::string Network::portName(const Pin *pin) const { return name(port(pin)); } -const char * +std::string Network::pathName(const Pin *pin) const { const Instance *inst = instance(pin); if (inst && inst != topInstance()) { - const char *inst_name = pathName(inst); - size_t inst_name_length = strlen(inst_name); - const char *port_name = portName(pin); - size_t port_name_length = strlen(port_name); - size_t path_name_length = inst_name_length + port_name_length + 2; - char *path_name = makeTmpString(path_name_length); - char *path_ptr = path_name; - strcpy(path_ptr, inst_name); - path_ptr += inst_name_length; - *path_ptr++ = pathDivider(); - strcpy(path_ptr, port_name); + std::string path_name(pathName(inst)); + path_name += pathDivider(); + path_name += portName(pin); return path_name; } else @@ -404,25 +378,31 @@ Network::pathName(const Pin *pin) const bool Network::pathNameLess(const Pin *pin1, - const Pin *pin2) const + const Pin *pin2) const { return pathNameCmp(pin1, pin2) < 0; } int Network::pathNameCmp(const Pin *pin1, - const Pin *pin2) const + const Pin *pin2) const { int inst_cmp = pathNameCmp(instance(pin1), instance(pin2)); - if (inst_cmp == 0) - return strcmp(portName(pin1), portName(pin2)); + if (inst_cmp == 0) { + auto cmp = portName(pin1) <=> portName(pin2); + if (cmp < 0) + return -1; + if (cmp > 0) + return 1; + return 0; + } else return inst_cmp; } bool Network::isInside(const Net *net, - const Instance *hier_inst) const + const Instance *hier_inst) const { return isInside(instance(net), hier_inst); } @@ -447,63 +427,61 @@ Network::isTopLevelPort(const Pin *pin) const bool Network::isInside(const Pin *pin, - const Pin *hier_pin) const + const Pin *hier_pin) const { return isInside(pin, instance(hier_pin)); } bool Network::isInside(const Pin *pin, - const Instance *hier_inst) const + const Instance *hier_inst) const { return isInside(instance(pin), hier_inst); } bool Network::pinLess(const Pin *pin1, - const Pin *pin2) const + const Pin *pin2) const { return pathNameLess(pin1, pin2); } //////////////////////////////////////////////////////////////// -const char * +std::string Network::pathName(const Net *net) const { const Instance *inst = instance(net); if (inst && inst != topInstance()) { - const char *inst_name = pathName(inst); - size_t inst_name_length = strlen(inst_name); - const char *net_name = name(net); - size_t net_name_length = strlen(net_name); - size_t path_name_length = inst_name_length + net_name_length + 2; - char *path_name = makeTmpString(path_name_length); - char *path_ptr = path_name; - strcpy(path_ptr, inst_name); - path_ptr += inst_name_length; - *path_ptr++ = pathDivider(); - strcpy(path_ptr, net_name); + std::string path_name(pathName(inst)); + path_name += pathDivider(); + path_name += name(net); return path_name; } else - return name(net); + return std::string(name(net)); } bool Network::pathNameLess(const Net *net1, - const Net *net2) const + const Net *net2) const { return pathNameCmp(net1, net2) < 0; } int Network::pathNameCmp(const Net *net1, - const Net *net2) const + const Net *net2) const { int inst_cmp = pathNameCmp(instance(net1), instance(net2)); - if (inst_cmp == 0) - return strcmp(name(net1), name(net2)); + if (inst_cmp == 0) { + auto cmp = name(net1) <=> name(net2); + if (cmp < 0) + return -1; + if (cmp > 0) + return 1; + return 0; + } else return inst_cmp; } @@ -520,8 +498,8 @@ Network::highestNetAbove(Net *net) const if (above_pin) { Net *above_net = this->net(above_pin); if (above_net) { - highest_net = highestNetAbove(above_net); - break; + highest_net = highestNetAbove(above_net); + break; } } } @@ -536,13 +514,11 @@ Network::highestConnectedNet(Net *net) const connectedNets(net, &nets); const Net *highest_net = net; int highest_level = hierarchyLevel(net); - NetSet::Iterator net_iter(nets); - while (net_iter.hasNext()) { - const Net *net1 = net_iter.next(); + for (const Net *net1 : nets) { int level = hierarchyLevel(net1); if (level < highest_level - || (level == highest_level - && stringLess(pathName(net1), pathName(highest_net)))) { + || (level == highest_level + && pathName(net1) < pathName(highest_net))) { highest_net = net1; highest_level = level; } @@ -552,9 +528,9 @@ Network::highestConnectedNet(Net *net) const void Network::connectedNets(Net *net, - NetSet *nets) const + NetSet *nets) const { - if (!nets->hasKey(net)) { + if (!nets->contains(net)) { nets->insert(net); // Search up from net terminals. NetTermIterator *term_iter = termIterator(net); @@ -562,9 +538,9 @@ Network::connectedNets(Net *net, Term *term = term_iter->next(); Pin *above_pin = pin(term); if (above_pin) { - Net *above_net = this->net(above_pin); - if (above_net) - connectedNets(above_net, nets); + Net *above_net = this->net(above_pin); + if (above_net) + connectedNets(above_net, nets); } } delete term_iter; @@ -575,9 +551,9 @@ Network::connectedNets(Net *net, const Pin *pin1 = pin_iter->next(); Term *below_term = term(pin1); if (below_term) { - Net *below_net = this->net(below_term); - if (below_net) - connectedNets(below_net, nets); + Net *below_net = this->net(below_term); + if (below_net) + connectedNets(below_net, nets); } } delete pin_iter; @@ -586,7 +562,7 @@ Network::connectedNets(Net *net, void Network::connectedNets(const Pin *pin, - NetSet *nets) const + NetSet *nets) const { Net *net = this->net(pin); if (net) @@ -596,7 +572,7 @@ Network::connectedNets(const Pin *pin, if (term) { Net *below_net = this->net(term); if (below_net) - connectedNets(below_net, nets); + connectedNets(below_net, nets); } } } @@ -611,8 +587,8 @@ Network::hierarchyLevel(const Net *net) const if (pin) { Net *above_net = network_->net(pin); if (above_net) { - delete term_iter; - return hierarchyLevel(above_net) + 1; + delete term_iter; + return hierarchyLevel(above_net) + 1; } } } @@ -665,7 +641,7 @@ Network::isRiseEdgeTriggered(const Pin *pin) const // Assumption: the clock function is exactly CLK for rising-edge triggered // sequentials. To be updated as necessary. - return clk_func->op() == FuncExpr::op_port; + return clk_func->op() == FuncExpr::Op::port; } bool @@ -684,7 +660,7 @@ Network::isFallEdgeTriggered(const Pin *pin) const // Assumption: the clock function is exactly !CLK for falling-edge triggered // sequentials. To be updated as necessary. - return clk_func->op() == FuncExpr::op_not; + return clk_func->op() == FuncExpr::Op::not_; } @@ -714,19 +690,19 @@ Network::isLatchData(const Pin *pin) const //////////////////////////////////////////////////////////////// -const char * +std::string Network::name(const Term *term) const { return name(pin(term)); } -const char * +std::string Network::pathName(const Term *term) const { return pathName(pin(term)); } -const char * +std::string Network::portName(const Term *term) const { return portName(pin(term)); @@ -734,40 +710,35 @@ Network::portName(const Term *term) const //////////////////////////////////////////////////////////////// -const char * +std::string Network::cellName(const Instance *inst) const { return name(cell(inst)); } Instance * -Network::findInstance(const char *path_name) const +Network::findInstance(std::string_view path_name) const { return findInstanceRelative(topInstance(), path_name); } Instance * Network::findInstanceRelative(const Instance *inst, - const char *path_name) const + std::string_view path_name) const { - char *first, *tail; + std::string first, tail; pathNameFirst(path_name, first, tail); - if (first) { + if (!first.empty()) { Instance *inst1 = findChild(inst, first); - stringDelete(first); - while (inst1 && tail) { - char *next_tail; + while (inst1 && !tail.empty()) { + std::string next_tail; pathNameFirst(tail, first, next_tail); - if (first) { - inst1 = findChild(inst1, first); - stringDelete(first); - } + if (!first.empty()) + inst1 = findChild(inst1, first); else - inst1 = findChild(inst1, tail); - stringDelete(tail); + inst1 = findChild(inst1, tail); tail = next_tail; } - stringDelete(tail); return inst1; } else @@ -776,14 +747,14 @@ Network::findInstanceRelative(const Instance *inst, InstanceSeq Network::findInstancesMatching(const Instance *context, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { InstanceSeq matches; if (pattern->hasWildcards()) { size_t context_name_length = 0; if (context != topInstance()) // Add one for the trailing divider. - context_name_length = strlen(pathName(context)) + 1; + context_name_length = pathName(context).size() + 1; findInstancesMatching1(context, context_name_length, pattern, matches); } else { @@ -796,16 +767,17 @@ Network::findInstancesMatching(const Instance *context, void Network::findInstancesMatching1(const Instance *context, - size_t context_name_length, - const PatternMatch *pattern, - InstanceSeq &matches) const + size_t context_name_length, + const PatternMatch *pattern, + InstanceSeq &matches) const { InstanceChildIterator *child_iter = childIterator(context); while (child_iter->hasNext()) { Instance *child = child_iter->next(); - const char *child_name = pathName(child); + std::string child_name = pathName(child); // Remove context prefix from the name. - const char *child_context_name = &child_name[context_name_length]; + std::string_view child_context_name = + std::string_view(child_name).substr(context_name_length); if (pattern->match(child_context_name)) matches.push_back(child); if (!isLeaf(child)) @@ -816,7 +788,7 @@ Network::findInstancesMatching1(const Instance *context, InstanceSeq Network::findInstancesHierMatching(const Instance *instance, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { InstanceSeq matches; findInstancesHierMatching1(instance, pattern, matches); @@ -841,15 +813,15 @@ Network::findInstancesHierMatching1(const Instance *instance, void Network::findChildrenMatching(const Instance *parent, - const PatternMatch *pattern, - InstanceSeq &matches) const + const PatternMatch *pattern, + InstanceSeq &matches) const { if (pattern->hasWildcards()) { InstanceChildIterator *child_iter = childIterator(parent); while (child_iter->hasNext()) { Instance *child = child_iter->next(); if (pattern->match(name(child))) - matches.push_back(child); + matches.push_back(child); } delete child_iter; } @@ -861,27 +833,22 @@ Network::findChildrenMatching(const Instance *parent, } Pin * -Network::findPin(const char *path_name) const +Network::findPin(std::string_view path_name) const { return findPinRelative(topInstance(), path_name); } Pin * Network::findPinRelative(const Instance *inst, - const char *path_name) const + std::string_view path_name) const { - char *inst_path, *port_name; - pathNameLast(path_name, inst_path, port_name); - if (inst_path) { + std::string inst_path, port_name; + std::string path_storage(path_name); + pathNameLast(path_storage, inst_path, port_name); + if (!inst_path.empty()) { Instance *pin_inst = findInstanceRelative(inst, inst_path); - if (pin_inst) { - Pin *pin = findPin(pin_inst, port_name); - stringDelete(inst_path); - stringDelete(port_name); - return pin; - } - stringDelete(inst_path); - stringDelete(port_name); + if (pin_inst) + return findPin(pin_inst, port_name); return nullptr; } else @@ -891,12 +858,12 @@ Network::findPinRelative(const Instance *inst, Pin * Network::findPinLinear(const Instance *instance, - const char *port_name) const + std::string_view port_name) const { InstancePinIterator *pin_iter = pinIterator(instance); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - if (stringEq(port_name, portName(pin))) { + if (port_name == portName(pin)) { delete pin_iter; return pin; } @@ -907,40 +874,35 @@ Network::findPinLinear(const Instance *instance, Pin * Network::findPin(const Instance *instance, - const Port *port) const + const Port *port) const { return findPin(instance, name(port)); } Pin * Network::findPin(const Instance *instance, - const LibertyPort *port) const + const LibertyPort *port) const { return findPin(instance, port->name()); } Net * -Network::findNet(const char *path_name) const +Network::findNet(std::string_view path_name) const { return findNetRelative(topInstance(), path_name); } Net * Network::findNetRelative(const Instance *inst, - const char *path_name) const + std::string_view path_name) const { - char *inst_path, *net_name; - pathNameLast(path_name, inst_path, net_name); - if (inst_path) { + std::string inst_path, net_name; + std::string path_storage(path_name); + pathNameLast(path_storage, inst_path, net_name); + if (!inst_path.empty()) { Instance *net_inst = findInstanceRelative(inst, inst_path); - if (net_inst) { - Net *net = findNet(net_inst, net_name); - stringDelete(inst_path); - stringDelete(net_name); - return net; - } - stringDelete(inst_path); - stringDelete(net_name); + if (net_inst) + return findNet(net_inst, net_name); return nullptr; } else @@ -950,12 +912,12 @@ Network::findNetRelative(const Instance *inst, Net * Network::findNetLinear(const Instance *instance, - const char *net_name) const + std::string_view net_name) const { InstanceNetIterator *net_iter = netIterator(instance); while (net_iter->hasNext()) { Net *net = net_iter->next(); - if (stringEq(name(net), net_name)) { + if (name(net) == net_name) { delete net_iter; return net; } @@ -966,7 +928,7 @@ Network::findNetLinear(const Instance *instance, NetSeq Network::findNetsMatching(const Instance *context, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { NetSeq matches; findNetsMatching(context, pattern, matches); @@ -975,23 +937,18 @@ Network::findNetsMatching(const Instance *context, void Network::findNetsMatching(const Instance *context, - const PatternMatch *pattern, + const PatternMatch *pattern, NetSeq &matches) const { if (pattern->hasWildcards()) { - char *inst_path, *net_name; + std::string inst_path, net_name; pathNameLast(pattern->pattern(), inst_path, net_name); - if (inst_path) { + if (!inst_path.empty()) { PatternMatch inst_pattern(inst_path, pattern); PatternMatch net_pattern(net_name, pattern); InstanceSeq insts = findInstancesMatching(context, &inst_pattern); - InstanceSeq::Iterator inst_iter(insts); - while (inst_iter.hasNext()) { - const Instance *inst = inst_iter.next(); - findNetsMatching(inst, &net_pattern, matches); - } - stringDelete(inst_path); - stringDelete(net_name); + for (const Instance *inst : insts) + findNetsMatching(inst, &net_pattern, matches); } else // Top level net. @@ -1006,7 +963,7 @@ Network::findNetsMatching(const Instance *context, NetSeq Network::findNetsHierMatching(const Instance *instance, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { NetSeq matches; findNetsHierMatching(instance, pattern, matches); @@ -1015,7 +972,7 @@ Network::findNetsHierMatching(const Instance *instance, void Network::findNetsHierMatching(const Instance *instance, - const PatternMatch *pattern, + const PatternMatch *pattern, NetSeq &matches) const { findInstNetsMatching(instance, pattern, matches); @@ -1029,7 +986,7 @@ Network::findNetsHierMatching(const Instance *instance, NetSeq Network::findNetsMatchingLinear(const Instance *instance, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { NetSeq matches; InstanceNetIterator *net_iter = netIterator(instance); @@ -1044,23 +1001,18 @@ Network::findNetsMatchingLinear(const Instance *instance, PinSeq Network::findPinsMatching(const Instance *instance, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { PinSeq matches; if (pattern->hasWildcards()) { - char *inst_path, *port_name; + std::string inst_path, port_name; pathNameLast(pattern->pattern(), inst_path, port_name); - if (inst_path) { + if (!inst_path.empty()) { PatternMatch inst_pattern(inst_path, pattern); PatternMatch port_pattern(port_name, pattern); InstanceSeq insts = findInstancesMatching(instance, &inst_pattern); - InstanceSeq::Iterator inst_iter(insts); - while (inst_iter.hasNext()) { - const Instance *inst = inst_iter.next(); - findInstPinsMatching(inst, &port_pattern, matches); - } - stringDelete(inst_path); - stringDelete(port_name); + for (const Instance *inst : insts) + findInstPinsMatching(inst, &port_pattern, matches); } else // Top level pin. @@ -1076,7 +1028,7 @@ Network::findPinsMatching(const Instance *instance, PinSeq Network::findPinsHierMatching(const Instance *instance, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { PinSeq matches; findPinsHierMatching(instance, pattern, matches); @@ -1085,7 +1037,7 @@ Network::findPinsHierMatching(const Instance *instance, void Network::findPinsHierMatching(const Instance *instance, - const PatternMatch *pattern, + const PatternMatch *pattern, // Return value. PinSeq &matches) const { @@ -1100,17 +1052,17 @@ Network::findPinsHierMatching(const Instance *instance, void Network::findInstPinsHierMatching(const Instance *instance, - const PatternMatch *pattern, - // Return value. - PinSeq &matches) const + const PatternMatch *pattern, + // Return value. + PinSeq &matches) const { - string inst_name = name(instance); + std::string inst_name(name(instance)); InstancePinIterator *pin_iter = pinIterator(instance); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); - const char *port_name = name(port(pin)); - string pin_name = inst_name + divider_ + port_name; - if (pattern->match(pin_name.c_str())) + std::string port_name = name(port(pin)); + std::string pin_name = inst_name + divider_ + std::string(port_name); + if (pattern->match(pin_name)) matches.push_back(pin); } delete pin_iter; @@ -1118,15 +1070,15 @@ Network::findInstPinsHierMatching(const Instance *instance, void Network::findInstPinsMatching(const Instance *instance, - const PatternMatch *pattern, - PinSeq &matches) const + const PatternMatch *pattern, + PinSeq &matches) const { if (pattern->hasWildcards()) { InstancePinIterator *pin_iter = pinIterator(instance); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); if (pattern->match(name(pin))) - matches.push_back(pin); + matches.push_back(pin); } delete pin_iter; } @@ -1139,10 +1091,10 @@ Network::findInstPinsMatching(const Instance *instance, void Network::location(const Pin *, - // Return values. - double &x, - double &y, - bool &exists) const + // Return values. + double &x, + double &y, + bool &exists) const { x = y = 0.0; exists = false; @@ -1282,15 +1234,15 @@ void Network::addGeneratedClockPinToCell(const char *pinName, LibertyCell *cell) //////////////////////////////////////////////////////////////// -typedef Vector InstanceChildIteratorSeq; +using InstanceChildIteratorSeq = std::vector; class LeafInstanceIterator1 : public LeafInstanceIterator { public: LeafInstanceIterator1(const Instance *inst, - const Network *network); - bool hasNext() { return next_; } - Instance *next(); + const Network *network); + bool hasNext() override { return next_; } + Instance *next() override; private: void nextInst(); @@ -1298,15 +1250,14 @@ class LeafInstanceIterator1 : public LeafInstanceIterator const Network *network_; InstanceChildIteratorSeq pending_child_iters_; InstanceChildIterator *child_iter_; - Instance *next_; + Instance *next_{nullptr}; }; LeafInstanceIterator1::LeafInstanceIterator1(const Instance *inst, - const Network *network) : + const Network *network) : network_(network), - child_iter_(network->childIterator(inst)), - next_(nullptr) + child_iter_(network->childIterator(inst)) { pending_child_iters_.reserve(8); nextInst(); @@ -1362,7 +1313,7 @@ Network::leafInstanceIterator(const Instance *hier_inst) const void Network::visitConnectedPins(const Pin *pin, - PinVisitor &visitor) const + PinVisitor &visitor) const { NetSet visited_nets(network_); Net *pin_net = net(pin); @@ -1383,7 +1334,7 @@ Network::visitConnectedPins(const Pin *pin, void Network::visitConnectedPins(const Net *net, - PinVisitor &visitor) const + PinVisitor &visitor) const { NetSet visited_nets(this); visitConnectedPins(net, visitor, visited_nets); @@ -1391,10 +1342,10 @@ Network::visitConnectedPins(const Net *net, void Network::visitConnectedPins(const Net *net, - PinVisitor &visitor, - NetSet &visited_nets) const + PinVisitor &visitor, + NetSet &visited_nets) const { - if (!visited_nets.hasKey(net)) { + if (!visited_nets.contains(net)) { visited_nets.insert(net); // Search up from net terminals. NetTermIterator *term_iter = termIterator(net); @@ -1402,11 +1353,11 @@ Network::visitConnectedPins(const Net *net, Term *term = term_iter->next(); Pin *above_pin = pin(term); if (above_pin) { - Net *above_net = this->net(above_pin); - if (above_net) - visitConnectedPins(above_net, visitor, visited_nets); - else - visitor(above_pin); + Net *above_net = this->net(above_pin); + if (above_net) + visitConnectedPins(above_net, visitor, visited_nets); + else + visitor(above_pin); } } delete term_iter; @@ -1418,9 +1369,9 @@ Network::visitConnectedPins(const Net *net, visitor(pin); Term *below_term = term(pin); if (below_term) { - Net *below_net = this->net(below_term); - if (below_net) - visitConnectedPins(below_net, visitor, visited_nets); + Net *below_net = this->net(below_term); + if (below_net) + visitConnectedPins(below_net, visitor, visited_nets); } } delete pin_iter; @@ -1432,42 +1383,44 @@ Network::visitConnectedPins(const Net *net, class ConnectedPinIterator1 : public ConnectedPinIterator { public: - explicit ConnectedPinIterator1(PinSet *pins); - virtual ~ConnectedPinIterator1(); - virtual bool hasNext(); - virtual const Pin *next(); + ConnectedPinIterator1(PinSet *pins); + ~ConnectedPinIterator1() override; + bool hasNext() override; + const Pin *next() override; protected: - PinSet::Iterator pin_iter_; + PinSet *pins_; + PinSet::iterator pin_iter_; }; ConnectedPinIterator1::ConnectedPinIterator1(PinSet *pins) : - pin_iter_(pins) + pins_(pins), + pin_iter_(pins_->begin()) { } ConnectedPinIterator1::~ConnectedPinIterator1() { - delete pin_iter_.container(); + delete pins_; } bool ConnectedPinIterator1::hasNext() { - return pin_iter_.hasNext(); + return pin_iter_ != pins_->end(); } const Pin * ConnectedPinIterator1::next() { - return pin_iter_.next(); + return *pin_iter_++; } class FindConnectedPins : public PinVisitor { public: - explicit FindConnectedPins(PinSet *pins); - virtual void operator()(const Pin *pin); + FindConnectedPins(PinSet *pins); + void operator()(const Pin *pin) override; protected: PinSet *pins_; @@ -1519,7 +1472,7 @@ Network::connectedPinIterator(const Pin *pin) const bool Network::isConnected(const Net *net, - const Pin *pin) const + const Pin *pin) const { if (this->net(pin) == net) return true; @@ -1531,10 +1484,10 @@ Network::isConnected(const Net *net, bool Network::isConnected(const Net *net, - const Pin *pin, - NetSet &nets) const + const Pin *pin, + NetSet &nets) const { - if (!nets.hasKey(net)) { + if (!nets.contains(net)) { nets.insert(net); // Search up from net terminals. NetTermIterator *term_iter = termIterator(net); @@ -1542,17 +1495,17 @@ Network::isConnected(const Net *net, Term *term = term_iter->next(); Pin *above_pin = this->pin(term); if (above_pin) { - if (above_pin == pin) { - delete term_iter; - return true; - } - else { - Net *above_net = this->net(above_pin); - if (above_net && isConnected(above_net, pin, nets)) { - delete term_iter; - return true; - } - } + if (above_pin == pin) { + delete term_iter; + return true; + } + else { + Net *above_net = this->net(above_pin); + if (above_net && isConnected(above_net, pin, nets)) { + delete term_iter; + return true; + } + } } } delete term_iter; @@ -1562,18 +1515,18 @@ Network::isConnected(const Net *net, while (pin_iter->hasNext()) { const Pin *pin1 = pin_iter->next(); if (pin1 == pin) { - delete pin_iter; - return true; + delete pin_iter; + return true; } else { - Term *below_term = term(pin1); - if (below_term) { - Net *below_net = this->net(below_term); - if (below_net && isConnected(below_net, pin, nets)) { - delete pin_iter; - return true; - } - } + Term *below_term = term(pin1); + if (below_term) { + Net *below_net = this->net(below_term); + if (below_net && isConnected(below_net, pin, nets)) { + delete pin_iter; + return true; + } + } } } delete pin_iter; @@ -1583,7 +1536,7 @@ Network::isConnected(const Net *net, bool Network::isConnected(const Net *net1, - const Net *net2) const + const Net *net2) const { NetSet nets(this); return isConnected(net1, net2, nets); @@ -1591,12 +1544,12 @@ Network::isConnected(const Net *net1, bool Network::isConnected(const Net *net1, - const Net *net2, - NetSet &nets) const + const Net *net2, + NetSet &nets) const { if (net1 == net2) return true; - else if (!nets.hasKey(net1)) { + else if (!nets.contains(net1)) { nets.insert(net1); // Search up from net terminals. NetTermIterator *term_iter = termIterator(net1); @@ -1604,11 +1557,11 @@ Network::isConnected(const Net *net1, Term *term = term_iter->next(); Pin *above_pin = pin(term); if (above_pin) { - Net *above_net = net(above_pin); - if (above_net && isConnected(above_net, net2, nets)) { - delete term_iter; - return true; - } + Net *above_net = net(above_pin); + if (above_net && isConnected(above_net, net2, nets)) { + delete term_iter; + return true; + } } } delete term_iter; @@ -1619,11 +1572,11 @@ Network::isConnected(const Net *net1, const Pin *pin1 = pin_iter->next(); Term *below_term = term(pin1); if (below_term) { - Net *below_net = net(below_term); - if (below_net && isConnected(below_net, net2, nets)) { - delete pin_iter; - return true; - } + Net *below_net = net(below_term); + if (below_net && isConnected(below_net, net2, nets)) { + delete pin_iter; + return true; + } } } delete pin_iter; @@ -1636,9 +1589,9 @@ Network::isConnected(const Net *net1, class FindDrvrPins : public PinVisitor { public: - explicit FindDrvrPins(PinSet *pins, - const Network *network); - virtual void operator()(const Pin *pin); + FindDrvrPins(PinSet *pins, + const Network *network); + void operator()(const Pin *pin) override; protected: PinSet *pins_; @@ -1646,7 +1599,7 @@ class FindDrvrPins : public PinVisitor }; FindDrvrPins::FindDrvrPins(PinSet *pins, - const Network *network) : + const Network *network) : PinVisitor(), pins_(pins), network_(network) @@ -1673,13 +1626,13 @@ Network::drivers(const Pin *pin) void Network::clearNetDrvrPinMap() { - net_drvr_pin_map_.deleteContentsClear(); + deleteContents(net_drvr_pin_map_); } PinSet * Network::drivers(const Net *net) { - PinSet *drvrs = net_drvr_pin_map_.findKey(net); + PinSet *drvrs = findKey(net_drvr_pin_map_, net); if (drvrs == nullptr) { drvrs = new PinSet(this); FindDrvrPins visitor(drvrs, this); @@ -1692,109 +1645,75 @@ Network::drivers(const Net *net) //////////////////////////////////////////////////////////////// void -Network::pathNameFirst(const char *path_name, - char *&first, - char *&tail) const +Network::pathNameFirst(std::string_view path_name, + std::string &first, + std::string &tail) const { + first.clear(); + tail.clear(); + char escape = pathEscape(); char divider = pathDivider(); - const char *d = strchr(path_name, divider); - // Skip escaped dividers. - while (d != nullptr - && d > path_name - && d[-1] == escape) - d = strchr(d + 1, divider); - if (d) { - first = new char[d - path_name + 1]; - strncpy(first, path_name, d - path_name); - first[d - path_name] = '\0'; - - tail = new char[strlen(d)]; - // Chop off the leading divider. - strcpy(tail, d + 1); - } - else { - // No divider in path_name. - first = nullptr; - tail = nullptr; + size_t i = 0; + while (i < path_name.size()) { + size_t d = path_name.find(divider, i); + while (d != std::string_view::npos && d > 0 + && path_name[d - 1] == escape) + d = path_name.find(divider, d + 1); + if (d != std::string_view::npos) { + first = path_name.substr(0, d); + tail = path_name.substr(d + 1); + return; + } + break; } } void -Network::pathNameLast(const char *path_name, - char *&head, - char *&last) const +Network::pathNameLast(std::string_view path_name, + std::string &head, + std::string &last) const { + head.clear(); + last.clear(); + char escape = pathEscape(); char divider = pathDivider(); - const char *d = strrchr(path_name, divider); - // Search for a non-escaped divider. - if (d) { - while (d > path_name - && (d[0] != divider - || (d[0] == divider - && d > &path_name[1] - && d[-1] == escape))) - d--; - } - if (d && d != path_name) { - head = new char[d - path_name + 1]; - strncpy(head, path_name, d - path_name); - head[d - path_name] = '\0'; - - last = new char[strlen(d)]; - // Chop off the last divider. - strcpy(last, d + 1); - } - else { - // No divider in path_name. - head = nullptr; - last = nullptr; - } -} - -//////////////////////////////////////////////////////////////// -NetworkEdit::NetworkEdit() : - Network() -{ -} - -void -NetworkEdit::connectPin(Pin *pin, - Net *net) -{ - connect(instance(pin), port(pin), net); + size_t div_pos = path_name.rfind(divider); + while (div_pos > 0) { + if (div_pos == std::string_view::npos) + return; + if (path_name[div_pos - 1] != escape) { + // Found the last non-escaped divider. + head = path_name.substr(0, div_pos); + last = path_name.substr(div_pos + 1); + return; + } + div_pos = path_name.rfind(divider, div_pos - 1); + } } //////////////////////////////////////////////////////////////// NetworkConstantPinIterator:: NetworkConstantPinIterator(const Network *network, - NetSet &zero_nets, - NetSet &one_nets) : + NetSet &zero_nets, + NetSet &one_nets) : ConstantPinIterator(), - network_(network), - constant_pins_{PinSet(network), PinSet(network)} + network_(network) { findConstantPins(zero_nets, constant_pins_[0]); findConstantPins(one_nets, constant_pins_[1]); value_ = LogicValue::zero; - pin_iter_ = new PinSet::Iterator(constant_pins_[0]); -} - -NetworkConstantPinIterator::~NetworkConstantPinIterator() -{ - delete pin_iter_; + pin_iter_ = constant_pins_[0].begin(); } void NetworkConstantPinIterator::findConstantPins(NetSet &nets, - PinSet &pins) + PinSet &pins) { - NetSet::Iterator net_iter(nets); - while (net_iter.hasNext()) { - const Net *net = net_iter.next(); + for (const Net *net : nets) { NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); @@ -1807,13 +1726,12 @@ NetworkConstantPinIterator::findConstantPins(NetSet &nets, bool NetworkConstantPinIterator::hasNext() { - if (pin_iter_->hasNext()) + if (pin_iter_ != constant_pins_[static_cast(value_)].end()) return true; else if (value_ == LogicValue::zero) { - delete pin_iter_; value_ = LogicValue::one; - pin_iter_ = new PinSet::Iterator(constant_pins_[1]); - return pin_iter_->hasNext(); + pin_iter_ = constant_pins_[1].begin(); + return pin_iter_ != constant_pins_[1].end(); } else return false; @@ -1821,19 +1739,19 @@ NetworkConstantPinIterator::hasNext() void NetworkConstantPinIterator::next(const Pin *&pin, - LogicValue &value) + LogicValue &value) { - pin = pin_iter_->next(); + pin = *pin_iter_++; value = value_; } //////////////////////////////////////////////////////////////// FindNetDrvrLoads::FindNetDrvrLoads(const Pin *drvr_pin, - PinSet &visited_drvrs, - PinSeq &loads, - PinSeq &drvrs, - const Network *network) : + PinSet &visited_drvrs, + PinSeq &loads, + PinSeq &drvrs, + const Network *network) : drvr_pin_(drvr_pin), visited_drvrs_(visited_drvrs), loads_(loads), @@ -1858,11 +1776,11 @@ FindNetDrvrLoads::operator()(const Pin *pin) static void visitPinsAboveNet1(const Pin *hpin, - Net *above_net, - NetSet &visited, - PinSet &above_drvrs, - PinSet &above_loads, - const Network *network) + Net *above_net, + NetSet &visited, + PinSet &above_drvrs, + PinSet &above_loads, + const Network *network) { visited.insert(above_net); // Visit above net pins. @@ -1871,15 +1789,15 @@ visitPinsAboveNet1(const Pin *hpin, const Pin *above_pin = pin_iter->next(); if (above_pin != hpin) { if (network->isDriver(above_pin)) - above_drvrs.insert(above_pin); + above_drvrs.insert(above_pin); if (network->isLoad(above_pin)) - above_loads.insert(above_pin); + above_loads.insert(above_pin); Term *above_term = network->term(above_pin); if (above_term) { - Net *above_net1 = network->net(above_term); - if (above_net1 && !visited.hasKey(above_net1)) - visitPinsAboveNet1(above_pin, above_net1, visited, - above_drvrs, above_loads, network); + Net *above_net1 = network->net(above_term); + if (above_net1 && !visited.contains(above_net1)) + visitPinsAboveNet1(above_pin, above_net1, visited, + above_drvrs, above_loads, network); } } } @@ -1891,15 +1809,15 @@ visitPinsAboveNet1(const Pin *hpin, Term *term = term_iter->next(); Pin *above_pin = network->pin(term); if (above_pin - && above_pin != hpin) { + && above_pin != hpin) { Net *above_net1 = network->net(above_pin); - if (above_net1 && !visited.hasKey(above_net1)) - visitPinsAboveNet1(above_pin, above_net1, visited, - above_drvrs, above_loads, network); + if (above_net1 && !visited.contains(above_net1)) + visitPinsAboveNet1(above_pin, above_net1, visited, + above_drvrs, above_loads, network); if (network->isDriver(above_pin)) - above_drvrs.insert(above_pin); + above_drvrs.insert(above_pin); if (network->isLoad(above_pin)) - above_loads.insert(above_pin); + above_loads.insert(above_pin); } } delete term_iter; @@ -1907,11 +1825,11 @@ visitPinsAboveNet1(const Pin *hpin, static void visitPinsBelowNet1(const Pin *hpin, - Net *below_net, - NetSet &visited, - PinSet &below_drvrs, - PinSet &below_loads, - const Network *network) + Net *below_net, + NetSet &visited, + PinSet &below_drvrs, + PinSet &below_loads, + const Network *network) { visited.insert(below_net); // Visit below net pins. @@ -1921,17 +1839,17 @@ visitPinsBelowNet1(const Pin *hpin, if (below_pin != hpin) { NetSet visited_above(network); if (network->isDriver(below_pin)) - below_drvrs.insert(below_pin); + below_drvrs.insert(below_pin); if (network->isLoad(below_pin)) - below_loads.insert(below_pin); + below_loads.insert(below_pin); if (network->isHierarchical(below_pin)) { - Term *term = network->term(below_pin); - if (term) { - Net *below_net1 = network->net(term); - if (below_net1 && !visited.hasKey(below_net1)) - visitPinsBelowNet1(below_pin, below_net1, visited, - below_drvrs, below_loads, network); - } + Term *term = network->term(below_pin); + if (term) { + Net *below_net1 = network->net(term); + if (below_net1 && !visited.contains(below_net1)) + visitPinsBelowNet1(below_pin, below_net1, visited, + below_drvrs, below_loads, network); + } } } } @@ -1939,25 +1857,20 @@ visitPinsBelowNet1(const Pin *hpin, } static void -visitDrvrLoads(PinSet drvrs, - PinSet loads, - HierPinThruVisitor *visitor) -{ - PinSet::Iterator drvr_iter(drvrs); - while (drvr_iter.hasNext()) { - const Pin *drvr = drvr_iter.next(); - PinSet::Iterator load_iter(loads); - while (load_iter.hasNext()) { - const Pin *load = load_iter.next(); +visitDrvrLoads(PinSet &drvrs, + PinSet &loads, + HierPinThruVisitor *visitor) +{ + for (const Pin *drvr : drvrs) { + for (const Pin *load : loads) visitor->visit(drvr, load); - } } } void visitDrvrLoadsThruHierPin(const Pin *hpin, - const Network *network, - HierPinThruVisitor *visitor) + const Network *network, + HierPinThruVisitor *visitor) { Net *above_net = network->net(hpin); if (above_net) { @@ -1966,17 +1879,17 @@ visitDrvrLoadsThruHierPin(const Pin *hpin, if (term) { Net *below_net = network->net(term); if (below_net) { - NetSet visited(network); - PinSet above_drvrs(network); - PinSet above_loads(network); - visitPinsAboveNet1(hpin, above_net, visited, - above_drvrs, above_loads, network); - PinSet below_drvrs(network); - PinSet below_loads(network); - visitPinsBelowNet1(hpin, below_net, visited, - below_drvrs, below_loads, network); - visitDrvrLoads(above_drvrs, below_loads, visitor); - visitDrvrLoads(below_drvrs, above_loads, visitor); + NetSet visited(network); + PinSet above_drvrs(network); + PinSet above_loads(network); + visitPinsAboveNet1(hpin, above_net, visited, + above_drvrs, above_loads, network); + PinSet below_drvrs(network); + PinSet below_loads(network); + visitPinsBelowNet1(hpin, below_net, visited, + below_drvrs, below_loads, network); + visitDrvrLoads(above_drvrs, below_loads, visitor); + visitDrvrLoads(below_drvrs, above_loads, visitor); } } } @@ -1984,8 +1897,8 @@ visitDrvrLoadsThruHierPin(const Pin *hpin, void visitDrvrLoadsThruNet(const Net *net, - const Network *network, - HierPinThruVisitor *visitor) + const Network *network, + HierPinThruVisitor *visitor) { NetSet visited(network); PinSet above_drvrs(network); @@ -2001,18 +1914,18 @@ visitDrvrLoadsThruNet(const Net *net, // Search down from pin terminal. const Term *term = network->term(pin); if (term) { - Net *below_net = network->net(term); - if (below_net) { - visitPinsBelowNet1(pin, below_net, visited, - below_drvrs, below_loads, network); - } + Net *below_net = network->net(term); + if (below_net) { + visitPinsBelowNet1(pin, below_net, visited, + below_drvrs, below_loads, network); + } } } else { if (network->isDriver(pin)) - net_drvrs.insert(pin); + net_drvrs.insert(pin); if (network->isLoad(pin)) - net_loads.insert(pin); + net_loads.insert(pin); } } delete pin_iter; @@ -2023,13 +1936,13 @@ visitDrvrLoadsThruNet(const Net *net, Pin *above_pin = network->pin(term); if (above_pin) { if (network->isDriver(above_pin)) - above_drvrs.insert(above_pin); + above_drvrs.insert(above_pin); if (network->isLoad(above_pin)) - above_loads.insert(above_pin); + above_loads.insert(above_pin); Net *above_net = network->net(above_pin); if (above_net) - visitPinsAboveNet1(above_pin, above_net, visited, - above_drvrs, above_loads, network); + visitPinsAboveNet1(above_pin, above_net, visited, + above_drvrs, above_loads, network); } } delete term_iter; @@ -2047,8 +1960,8 @@ visitDrvrLoadsThruNet(const Net *net, char logicValueString(LogicValue value) { - static char str[] = "01X^v"; - return str[int(value)]; + static char names[] = "01X^v"; + return names[static_cast(value)]; } //////////////////////////////////////////////////////////////// @@ -2127,152 +2040,43 @@ NetIdLess::operator()(const Net *net1, //////////////////////////////////////////////////////////////// CellSet::CellSet(const Network *network) : - Set(CellIdLess(network)) + std::set(CellIdLess(network)) { } PortSet::PortSet(const Network *network) : - Set(PortIdLess(network)) + std::set(PortIdLess(network)) { } InstanceSet::InstanceSet() : - Set(InstanceIdLess(nullptr)) + std::set(InstanceIdLess(nullptr)) { } InstanceSet::InstanceSet(const Network *network) : - Set(InstanceIdLess(network)) -{ -} - -int -InstanceSet::compare(const InstanceSet *set1, - const InstanceSet *set2, - const Network *network) -{ - size_t size1 = set1 ? set1->size() : 0; - size_t size2 = set2 ? set2->size() : 0; - if (size1 == size2) { - InstanceSet::ConstIterator iter1(set1); - InstanceSet::ConstIterator iter2(set2); - while (iter1.hasNext() && iter2.hasNext()) { - const Instance *inst1 = iter1.next(); - const Instance *inst2 = iter2.next(); - ObjectId id1 = network->id(inst1); - ObjectId id2 = network->id(inst2); - if (id1 < id2) - return -1; - else if (id1 > id2) - return 1; - } - // Sets are equal. - return 0; - } - else - return (size1 > size2) ? 1 : -1; -} - -bool -InstanceSet::intersects(const InstanceSet *set1, - const InstanceSet *set2, - const Network *network) + std::set(InstanceIdLess(network)) { - return Set::intersects(set1, set2, InstanceIdLess(network)); } -//////////////////////////////////////////////////////////////// - PinSet::PinSet() : - Set(PinIdLess(nullptr)) + std::set(PinIdLess(nullptr)) { } PinSet::PinSet(const Network *network) : - Set(PinIdLess(network)) -{ -} - -int -PinSet::compare(const PinSet *set1, - const PinSet *set2, - const Network *network) -{ - size_t size1 = set1 ? set1->size() : 0; - size_t size2 = set2 ? set2->size() : 0; - if (size1 == size2) { - PinSet::ConstIterator iter1(set1); - PinSet::ConstIterator iter2(set2); - while (iter1.hasNext() && iter2.hasNext()) { - const Pin *pin1 = iter1.next(); - const Pin *pin2 = iter2.next(); - ObjectId id1 = network->id(pin1); - ObjectId id2 = network->id(pin2); - if (id1 < id2) - return -1; - else if (id1 > id2) - return 1; - } - // Sets are equal. - return 0; - } - else - return (size1 > size2) ? 1 : -1; -} - -bool -PinSet::intersects(const PinSet *set1, - const PinSet *set2, - const Network *network) + std::set(PinIdLess(network)) { - return Set::intersects(set1, set2, PinIdLess(network)); } -//////////////////////////////////////////////////////////////// - NetSet::NetSet() : - Set(NetIdLess(nullptr)) + std::set(NetIdLess(nullptr)) { } NetSet::NetSet(const Network *network) : - Set(NetIdLess(network)) -{ -} - -int -NetSet::compare(const NetSet *set1, - const NetSet *set2, - const Network *network) -{ - size_t size1 = set1 ? set1->size() : 0; - size_t size2 = set2 ? set2->size() : 0; - if (size1 == size2) { - NetSet::ConstIterator iter1(set1); - NetSet::ConstIterator iter2(set2); - while (iter1.hasNext() && iter2.hasNext()) { - const Net *net1 = iter1.next(); - const Net *net2 = iter2.next(); - ObjectId id1 = network->id(net1); - ObjectId id2 = network->id(net2); - if (id1 < id2) - return -1; - else if (id1 > id2) - return 1; - } - // Sets are equal. - return 0; - } - else - return (size1 > size2) ? 1 : -1; -} - -bool -NetSet::intersects(const NetSet *set1, - const NetSet *set2, - const Network *network) + std::set(NetIdLess(network)) { - return Set::intersects(set1, set2, NetIdLess(network)); } -} // namespace +} // namespace sta diff --git a/network/Network.i b/network/Network.i index 8ff2413f8..b1b6f3b03 100644 --- a/network/Network.i +++ b/network/Network.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,11 +22,16 @@ // // This notice may not be removed or altered from any source distribution. -%module network +%include %{ #include "Network.hh" #include "FindObjects.hh" +#include "FilterObjects.hh" + +#include + +#include "StringUtil.hh" %} //////////////////////////////////////////////////////////////// @@ -235,8 +240,8 @@ library_iterator() CellSeq find_cells_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Network *network = Sta::sta()->ensureLinked(); PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); @@ -253,12 +258,12 @@ find_cells_matching(const char *pattern, } void -set_cmd_namespace_cmd(const char *namespc) +set_cmd_namespace_cmd(std::string namespc) { Sta *sta = Sta::sta(); - if (stringEq(namespc, "sdc")) + if (namespc == "sdc") sta->setCmdNamespace(CmdNamespace::sdc); - else if (stringEq(namespc, "sta")) + else if (namespc == "sta") sta->setCmdNamespace(CmdNamespace::sta); else sta->report()->warn(2120, "unknown namespace"); @@ -285,22 +290,22 @@ leaf_instance_iterator() return network->leafInstanceIterator(); } -const char * +std::string_view port_direction(const Port *port) { return Sta::sta()->ensureLinked()->direction(port)->name(); } - -const char * + +std::string pin_direction(const Pin *pin) { - return Sta::sta()->ensureLinked()->direction(pin)->name(); + return std::string(Sta::sta()->ensureLinked()->direction(pin)->name()); } PortSeq find_ports_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -311,11 +316,11 @@ find_ports_matching(const char *pattern, PortSeq matches; for (const Port *port : matches1) { if (network->isBus(port) - || network->isBundle(port)) { + || network->isBundle(port)) { PortMemberIterator *member_iter = network->memberIterator(port); while (member_iter->hasNext()) { - Port *member = member_iter->next(); - matches.push_back(member); + Port *member = member_iter->next(); + matches.push_back(member); } delete member_iter; } @@ -327,8 +332,8 @@ find_ports_matching(const char *pattern, PinSeq find_port_pins_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -339,20 +344,20 @@ find_port_pins_matching(const char *pattern, PinSeq pins; for (const Port *port : ports) { if (network->isBus(port) - || network->isBundle(port)) { + || network->isBundle(port)) { PortMemberIterator *member_iter = network->memberIterator(port); while (member_iter->hasNext()) { - Port *member = member_iter->next(); - Pin *pin = network->findPin(top_inst, member); - if (pin) - pins.push_back(pin); + Port *member = member_iter->next(); + Pin *pin = network->findPin(top_inst, member); + if (pin) + pins.push_back(pin); } delete member_iter; } else { Pin *pin = network->findPin(top_inst, port); if (pin) - pins.push_back(pin); + pins.push_back(pin); } } return pins; @@ -376,18 +381,18 @@ const char pin_typename[] = "pin"; PinSeq * find_pins_complete(PinSeq *collection, - StringSeq *patterns, - bool regexp, - bool nocase, - bool hier, - bool quiet, - const char *filter_expression = nullptr) + StringSeq patterns, + bool regexp, + bool nocase, + bool hier, + bool quiet, + const char *filter_expression = nullptr) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); Instance *current_instance = sta->currentInstance(); - return find_objects_complete( + return find_objects_complete( collection, patterns, regexp, @@ -411,8 +416,8 @@ find_pins_complete(PinSeq *collection, PinSeq find_pins_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -424,8 +429,8 @@ find_pins_matching(const char *pattern, PinSeq find_pins_hier_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -449,8 +454,8 @@ network_leaf_instances() InstanceSeq find_instances_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -464,18 +469,18 @@ const char instance_typename[] = "instance"; InstanceSeq * find_instances_complete(InstanceSeq *collection, - StringSeq *patterns, - bool regexp, - bool nocase, - bool hier, - bool quiet, - const char *filter_expression = nullptr) + StringSeq patterns, + bool regexp, + bool nocase, + bool hier, + bool quiet, + const char *filter_expression = nullptr) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); Instance *current_instance = sta->currentInstance(); - return find_objects_complete( + return find_objects_complete( collection, patterns, regexp, @@ -493,17 +498,17 @@ const char port_typename[] = "instance"; PortSeq * find_ports_complete(PortSeq *collection, - StringSeq *patterns, - bool regexp, - bool nocase, - bool quiet, - const char *filter_expression = nullptr) + StringSeq patterns, + bool regexp, + bool nocase, + bool quiet, + const char *filter_expression = nullptr) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); Cell *top_cell = network->cell(network->topInstance()); - return find_objects_complete( + return find_objects_complete( collection, patterns, regexp, @@ -531,8 +536,8 @@ find_ports_complete(PortSeq *collection, InstanceSeq find_instances_hier_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -542,72 +547,6 @@ find_instances_hier_matching(const char *pattern, return matches; } -InstanceSet -find_register_instances(ClockSet *clks, - const RiseFallBoth *clk_tr, - bool edge_triggered, - bool latches) -{ - Sta *sta = Sta::sta(); - InstanceSet insts = sta->findRegisterInstances(clks, clk_tr, - edge_triggered, - latches); - delete clks; - return insts; -} - -PinSet -find_register_data_pins(ClockSet *clks, - const RiseFallBoth *clk_tr, - bool edge_triggered, - bool latches) -{ - Sta *sta = Sta::sta(); - PinSet pins = sta->findRegisterDataPins(clks, clk_tr, - edge_triggered, latches); - delete clks; - return pins; -} - -PinSet -find_register_clk_pins(ClockSet *clks, - const RiseFallBoth *clk_tr, - bool edge_triggered, - bool latches) -{ - Sta *sta = Sta::sta(); - PinSet pins = sta->findRegisterClkPins(clks, clk_tr, - edge_triggered, latches); - delete clks; - return pins; -} - -PinSet -find_register_async_pins(ClockSet *clks, - const RiseFallBoth *clk_tr, - bool edge_triggered, - bool latches) -{ - Sta *sta = Sta::sta(); - PinSet pins = sta->findRegisterAsyncPins(clks, clk_tr, - edge_triggered, latches); - delete clks; - return pins; -} - -PinSet -find_register_output_pins(ClockSet *clks, - const RiseFallBoth *clk_tr, - bool edge_triggered, - bool latches) -{ - Sta *sta = Sta::sta(); - PinSet pins = sta->findRegisterOutputPins(clks, clk_tr, - edge_triggered, latches); - delete clks; - return pins; -} - Net * find_net(char *path_name) { @@ -618,18 +557,18 @@ const char net_typename[] = "net"; NetSeq * find_nets_complete(NetSeq *collection, - StringSeq *patterns, - bool regexp, - bool nocase, - bool hier, - bool quiet, - const char *filter_expression = nullptr) + StringSeq patterns, + bool regexp, + bool nocase, + bool hier, + bool quiet, + const char *filter_expression = nullptr) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); Instance *current_instance = sta->currentInstance(); - return find_objects_complete( + return find_objects_complete( collection, patterns, regexp, @@ -645,8 +584,8 @@ find_nets_complete(NetSeq *collection, NetSeq find_nets_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -658,8 +597,8 @@ find_nets_matching(const char *pattern, NetSeq find_nets_hier_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -713,7 +652,7 @@ net_pins(Net *net) return pins; } -const char * +std::string pin_location(const Pin *pin) { Network *network = Sta::sta()->ensureLinked(); @@ -722,12 +661,12 @@ pin_location(const Pin *pin) network->location(pin, x, y, exists); // return x/y as tcl list if (exists) - return sta::stringPrintTmp("%f %f", x, y); + return sta::format("{} {}", x, y); else return ""; } -const char * +std::string port_location(const Port *port) { Network *network = Sta::sta()->ensureLinked(); @@ -744,7 +683,7 @@ port_location(const Port *port) //////////////////////////////////////////////////////////////// %extend Library { -const char *name() +std::string name() { return Sta::sta()->ensureLinked()->name(self); } @@ -757,8 +696,8 @@ find_cell(const char *name) CellSeq find_cells_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -776,13 +715,14 @@ void finish() { delete self; } } // LibraryIterator methods %extend Cell { -const char *name() { return Sta::sta()->cmdNetwork()->name(self); } +std::string name() { return Sta::sta()->cmdNetwork()->name(self); } Library *library() { return Sta::sta()->cmdNetwork()->library(self); } LibertyCell *liberty_cell() { return Sta::sta()->cmdNetwork()->libertyCell(self); } bool is_leaf() { return Sta::sta()->cmdNetwork()->isLeaf(self); } CellPortIterator * port_iterator() { return Sta::sta()->cmdNetwork()->portIterator(self); } -string get_attribute(const char *key) +std::string +get_attribute(const char *key) { return Sta::sta()->cmdNetwork()->getAttribute(self, key); } @@ -795,8 +735,8 @@ find_port(const char *name) PortSeq find_ports_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -813,7 +753,7 @@ void finish() { delete self; } } // CellPortIterator methods %extend Port { -const char *bus_name() { return Sta::sta()->ensureLinked()->busName(self); } +std::string bus_name() { return Sta::sta()->ensureLinked()->busName(self); } Cell *cell() { return Sta::sta()->ensureLinked()->cell(self); } LibertyPort *liberty_port() { return Sta::sta()->ensureLibLinked()->libertyPort(self); } bool is_bus() { return Sta::sta()->ensureLinked()->isBus(self); } @@ -839,12 +779,16 @@ InstancePinIterator * pin_iterator() { return Sta::sta()->ensureLinked()->pinIterator(self); } InstanceNetIterator * net_iterator() { return Sta::sta()->ensureLinked()->netIterator(self); } + Pin * find_pin(const char *name) { return Sta::sta()->ensureLinked()->findPin(self, name); } -string get_attribute(const char *key) { + +std::string +get_attribute(const char *key) +{ return Sta::sta()->ensureLinked()->getAttribute(self, key); } @@ -875,7 +819,7 @@ void finish() { delete self; } } // InstanceNetIterator methods %extend Pin { -const char *port_name() { return Sta::sta()->ensureLinked()->portName(self); } +std::string port_name() { return Sta::sta()->ensureLinked()->portName(self); } Instance *instance() { return Sta::sta()->ensureLinked()->instance(self); } Net *net() { return Sta::sta()->ensureLinked()->net(self); } Port *port() { return Sta::sta()->ensureLinked()->port(self); } @@ -941,35 +885,35 @@ bool is_power() { return Sta::sta()->ensureLinked()->isPower(self);} bool is_ground() { return Sta::sta()->ensureLinked()->isGround(self);} float -capacitance(Corner *corner, - const MinMax *min_max) +capacitance(Scene *scene, + const MinMax *min_max) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); float pin_cap, wire_cap; - sta->connectedCap(self, corner, min_max, pin_cap, wire_cap); + sta->connectedCap(self, scene, min_max, pin_cap, wire_cap); return pin_cap + wire_cap; } float -pin_capacitance(Corner *corner, - const MinMax *min_max) +pin_capacitance(Scene *scene, + const MinMax *min_max) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); float pin_cap, wire_cap; - sta->connectedCap(self, corner, min_max, pin_cap, wire_cap); + sta->connectedCap(self, scene, min_max, pin_cap, wire_cap); return pin_cap; } float -wire_capacitance(Corner *corner, - const MinMax *min_max) +wire_capacitance(Scene *scene, + const MinMax *min_max) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); float pin_cap, wire_cap; - sta->connectedCap(self, corner, min_max, pin_cap, wire_cap); + sta->connectedCap(self, scene, min_max, pin_cap, wire_cap); return wire_cap; } diff --git a/network/Network.tcl b/network/Network.tcl index b9d958a69..376bd289e 100644 --- a/network/Network.tcl +++ b/network/Network.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -87,8 +87,8 @@ proc report_instance_pins1 {instance header header_optional dirs} { set dir [pin_direction $pin] if { [lsearch $dirs $dir] != -1 } { if { !$header_shown } { - report_line $header - set header_shown 1 + report_line $header + set header_shown 1 } report_instance_pin $pin } @@ -140,31 +140,17 @@ proc instance_sorted_children { instance } { ################################################################ -define_cmd_args "report_net" {[-corner corner] [-digits digits]\ +define_cmd_args "report_net" {[-scene scene] [-digits digits]\ net_path [> filename] [>> filename]} # -hpins to show hierarchical pins proc_redirect report_net { global sta_report_default_digits - parse_key_args "report_net" args keys {-corner -digits} \ - flags {-connections -verbose -hier_pins} + parse_key_args "report_net" args keys {-corner -scene -digits} flags {} check_argc_eq1 "report_net" $args - if { [info exists flags(-connections)] } { - # deprecated 2024-01-17 - sta_warn 235 "report_net -connections is deprecated." - } - if { [info exists flags(-verbose)] } { - # deprecated 2024-01-17 - sta_warn 236 "report_net -verbose is deprecated." - } - if { [info exists flags(-hier_pins)] } { - # deprecated 2024-01-17 - sta_warn 237 "report_net -hier_pins is deprecated." - } - - set corner [parse_corner_or_all keys] + set scene [parse_scene_or_default keys] set digits $sta_report_default_digits if { [info exists keys(-digits)] } { set digits $keys(-digits) @@ -173,15 +159,15 @@ proc_redirect report_net { set net_path [lindex $args 0] set net [find_net $net_path] if { $net != "NULL" } { - report_net1 $net $corner $digits + report_net1 $net $scene $digits } else { set pin [find_pin $net_path] if { $pin != "NULL" } { set net [$pin net] if { $net != "NULL" } { - report_net1 $net $corner $digits + report_net1 $net $scene $digits } else { - sta_error 231 "net $net_path not found." + sta_error 231 "net $net_path not found." } } else { sta_error 232 "net $net_path not found." @@ -189,14 +175,14 @@ proc_redirect report_net { } } -proc report_net1 { net corner digits } { +proc report_net1 { net scene digits } { report_line "Net [get_full_name $net]" set pins [net_connected_pins_sorted $net] - report_net_caps $net $pins $corner $digits - report_net_pins $pins "Driver pins" "is_driver" $corner $digits - report_net_pins $pins "Load pins" "is_load" $corner $digits - report_net_pins $pins "Hierarchical pins" "is_hierarchical" $corner $digits - report_net_other_pins $pins $corner $digits + report_net_caps $net $pins $scene $digits + report_net_pins $pins "Driver pins" "is_driver" $scene $digits + report_net_pins $pins "Load pins" "is_load" $scene $digits + report_net_pins $pins "Hierarchical pins" "is_hierarchical" $scene $digits + report_net_other_pins $pins $scene $digits } proc net_connected_pins_sorted { net } { @@ -211,10 +197,10 @@ proc net_connected_pins_sorted { net } { return $pins } -proc report_net_caps { net pins corner digits } { - report_net_cap $net "Pin" "pin_capacitance" $corner $digits - report_net_cap $net "Wire" "wire_capacitance" $corner $digits - report_net_cap $net "Total" "capacitance" $corner $digits +proc report_net_caps { net pins scene digits } { + report_net_cap $net "Pin" "pin_capacitance" $scene $digits + report_net_cap $net "Wire" "wire_capacitance" $scene $digits + report_net_cap $net "Total" "capacitance" $scene $digits set pin_count 0 set driver_count 0 @@ -236,13 +222,13 @@ proc report_net_caps { net pins corner digits } { report_line "" } -proc report_net_cap { net caption cap_msg corner digits } { - set cap_min [$net $cap_msg $corner "min"] - set cap_max [$net $cap_msg $corner "max"] +proc report_net_cap { net caption cap_msg scene digits } { + set cap_min [$net $cap_msg $scene "min"] + set cap_max [$net $cap_msg $scene "max"] report_line " $caption capacitance: [capacitance_range_str $cap_min $cap_max $digits]" } -proc report_net_pins { pins header pin_pred corner digits } { +proc report_net_pins { pins header pin_pred scene digits } { set found 0 foreach pin $pins { if {[$pin $pin_pred]} { @@ -250,7 +236,7 @@ proc report_net_pins { pins header pin_pred corner digits } { report_line $header set found 1 } - report_net_pin $pin $corner $digits + report_net_pin $pin $scene $digits } } if { $found } { @@ -258,43 +244,42 @@ proc report_net_pins { pins header pin_pred corner digits } { } } -proc report_net_other_pins { pins corner digits } { +proc report_net_other_pins { pins scene digits } { set header 0 foreach pin $pins { if { !([$pin is_driver] || [$pin is_load] || [$pin is_hierarchical]) } { if { !$header } { - report_line "" - report_line "Other pins" - set header 1 + report_line "" + report_line "Other pins" + set header 1 } - report_net_pin $pin $corner $digits + report_net_pin $pin $scene $digits } } } -proc report_net_pin { pin corner digits } { +proc report_net_pin { pin scene digits } { if [$pin is_leaf] { set cell_name [get_name [[$pin instance] cell]] set cap "" set liberty_port [$pin liberty_port] if { $liberty_port != "NULL" } { - set cap [port_capacitance_str $liberty_port $corner $digits] + set cap [port_capacitance_str $liberty_port $scene $digits] } report_line " [get_full_name $pin] [pin_direction $pin] ($cell_name)$cap[pin_location_str $pin]" } elseif [$pin is_top_level_port] { set wire_cap "" set pin_cap "" - set corner [sta::cmd_corner] set port [$pin port] - set cap_min [port_ext_wire_cap $port $corner "min"] - set cap_max [port_ext_wire_cap $port $corner "max"] + set cap_min [port_ext_wire_cap $port "min"] + set cap_max [port_ext_wire_cap $port "max"] if { $cap_min > 0 || $cap_max > 0 } { set wire_cap " wire [capacitance_range_str $cap_min $cap_max $digits]" } - set cap_min [port_ext_pin_cap $port $corner "min"] - set cap_max [port_ext_pin_cap $port $corner "max"] + set cap_min [port_ext_pin_cap $port "min"] + set cap_max [port_ext_pin_cap $port "max"] if { $cap_min > 0 || $cap_max > 0} { set pin_cap " pin [capacitance_range_str $cap_min $cap_max $digits]" } @@ -317,10 +302,10 @@ proc pin_location_str { pin } { ################################################################ -proc report_pin_ { pin corner digits } { +proc report_pin_ { pin scene digits } { set liberty_port [$pin liberty_port] if { $liberty_port != "NULL" } { - set cap [port_capacitance_str $liberty_port $corner $digits] + set cap [port_capacitance_str $liberty_port $scene $digits] } else { set cap "" } @@ -354,9 +339,9 @@ proc pin_direction_desc { pin } { } # Do not preceed this field by a space in the caller. -proc port_capacitance_str { liberty_port corner digits } { - set cap_min [$liberty_port capacitance $corner "min"] - set cap_max [$liberty_port capacitance $corner "max"] +proc port_capacitance_str { liberty_port scene digits } { + set cap_min [$liberty_port capacitance $scene "min"] + set cap_max [$liberty_port capacitance $scene "max"] if { $cap_min > 0 || $cap_max > 0 } { return " [capacitance_range_str $cap_min $cap_max $digits]" } else { @@ -374,13 +359,13 @@ proc capacitance_range_str { cap_min cap_max digits } { proc capacitances_str { cap_r_min cap_r_max cap_f_min cap_f_max digits } { if { $cap_r_min == $cap_r_max \ - && $cap_f_min == $cap_f_max \ - && $cap_r_max == $cap_f_max } { + && $cap_f_min == $cap_f_max \ + && $cap_r_max == $cap_f_max } { # All 4 values are the same. set cap $cap_r_min return "[format_capacitance $cap $digits]" } elseif { $cap_r_min == $cap_r_max \ - && $cap_f_min == $cap_f_max } { + && $cap_f_min == $cap_f_max } { # Mins equal maxes. return "r [format_capacitance $cap_r_min $digits] f [format_capacitance $cap_f_min $digits]" } else { diff --git a/network/NetworkCmp.cc b/network/NetworkCmp.cc index e00df1316..2d0db2dbf 100644 --- a/network/NetworkCmp.cc +++ b/network/NetworkCmp.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,9 +26,9 @@ #include -#include "StringUtil.hh" #include "Liberty.hh" #include "Network.hh" +#include "StringUtil.hh" namespace sta { @@ -39,9 +39,9 @@ PortNameLess::PortNameLess(const Network *network) : bool PortNameLess::operator()(const Port *port1, - const Port *port2) const + const Port *port2) const { - return stringLess(network_->name(port1), network_->name(port2)); + return network_->name(port1) < network_->name(port2); } PinPathNameLess::PinPathNameLess(const Network *network) : @@ -51,7 +51,7 @@ PinPathNameLess::PinPathNameLess(const Network *network) : bool PinPathNameLess::operator()(const Pin *pin1, - const Pin *pin2) const + const Pin *pin2) const { return network_->pathNameLess(pin1, pin2); } @@ -63,7 +63,7 @@ NetPathNameLess::NetPathNameLess(const Network *network) : bool NetPathNameLess::operator()(const Net *net1, - const Net *net2) const + const Net *net2) const { return network_->pathNameLess(net1, net2); } @@ -75,7 +75,7 @@ InstancePathNameLess::InstancePathNameLess(const Network *network) : bool InstancePathNameLess::operator()(const Instance *inst1, - const Instance *inst2) const + const Instance *inst2) const { return network_->pathNameLess(inst1, inst2); } @@ -93,6 +93,17 @@ sortByPathName(const PinSet *set, return pins; } +PinSeq +sortByPathName(const PinUnorderedSet *set, + const Network *network) +{ + PinSeq pins; + for (const Pin *pin : *set) + pins.push_back(pin); + sort(pins, PinPathNameLess(network)); + return pins; +} + PortSeq sortByName(const PortSet *set, const Network *network) @@ -126,4 +137,4 @@ sortByPathName(NetSet *set, return nets; } -} // namespace +} // namespace sta diff --git a/network/NetworkEdit.i b/network/NetworkEdit.i index c0fa6527f..7c515f577 100644 --- a/network/NetworkEdit.i +++ b/network/NetworkEdit.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,8 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module NetworkEdit - %{ using sta::Cell; using sta::Instance; @@ -44,8 +42,8 @@ using sta::NetworkEdit; Instance * make_instance_cmd(const char *name, - LibertyCell *cell, - Instance *parent) + LibertyCell *cell, + Instance *parent) { return Sta::sta()->makeInstance(name, cell, parent); } @@ -58,14 +56,14 @@ delete_instance_cmd(Instance *inst) void replace_cell_cmd(Instance *inst, - LibertyCell *to_cell) + LibertyCell *to_cell) { Sta::sta()->replaceCell(inst, to_cell); } Net * make_net_cmd(const char *name, - Instance *parent) + Instance *parent) { return Sta::sta()->makeNet(name, parent); } @@ -85,8 +83,8 @@ delete_net_cmd(Net *net) void connect_pin_cmd(Instance *inst, - Port *port, - Net *net) + Port *port, + Net *net) { Sta::sta()->connectPin(inst, port, net); } @@ -104,4 +102,11 @@ network_changed() Sta::sta()->networkChanged(); } +// Notify STA of network change without touching SDC network references. +void +network_changed_non_sdc() +{ + Sta::sta()->networkChangedNonSdc(); +} + %} // inline diff --git a/network/NetworkEdit.tcl b/network/NetworkEdit.tcl index 929c82e42..cfddb7530 100644 --- a/network/NetworkEdit.tcl +++ b/network/NetworkEdit.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -35,11 +35,11 @@ proc make_instance { inst_path lib_cell } { if {[regexp $path_regexp $inst_path ignore path_name inst_name]} { set parent [find_instance $path_name] if { $parent == "NULL" } { - # Parent instance not found. This could be a typo, but since - # SDC does not escape hierarchy dividers it can also be - # an escaped name. - set inst_name $inst_path - set parent [top_instance] + # Parent instance not found. This could be a typo, but since + # SDC does not escape hierarchy dividers it can also be + # an escaped name. + set inst_name $inst_path + set parent [top_instance] } } else { set inst_name $inst_path @@ -118,7 +118,7 @@ proc parse_connect_pin { arg } { if {[regexp $path_regexp $arg ignore path_name port_name]} { set inst [find_instance $path_name] if { $inst == "NULL" } { - return 0 + return 0 } } else { set inst [top_instance] @@ -134,8 +134,8 @@ proc parse_connect_pin { arg } { # Make sure the pin is not currently connected to a net. if { $pin != "NULL" \ - && ![$pin is_hierarchical] \ - && [$pin net] != "NULL" } { + && ![$pin is_hierarchical] \ + && [$pin net] != "NULL" } { return 0 } return [list $inst $port] @@ -217,7 +217,7 @@ proc replace_cell { instance lib_cell } { set inst [get_instance_error "instance" $instance] set inst_cell [$inst liberty_cell] if { $inst_cell == "NULL" \ - || ![equiv_cell_ports $inst_cell $cell] } { + || ![equiv_cell_ports $inst_cell $cell] } { return 0 } replace_cell_cmd $inst $cell diff --git a/network/ParseBus.cc b/network/ParseBus.cc index 18ca03f30..8205dead7 100644 --- a/network/ParseBus.cc +++ b/network/ParseBus.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,112 +24,105 @@ #include "ParseBus.hh" -#include -#include #include +#include #include "StringUtil.hh" namespace sta { -using std::string; - bool -isBusName(const char *name, - const char brkt_left, - const char brkt_right, - char escape) +isBusName(std::string_view name, + const char brkt_left, + const char brkt_right, + char escape) { - size_t len = strlen(name); + size_t len = name.size(); // Shortest bus name is a[0]. if (len >= 4 // Escaped bus brackets are not buses. && name[len - 2] != escape && name[len - 1] == brkt_right) { - const char *left = strrchr(name, brkt_left); - return left != nullptr; + size_t left = name.rfind(brkt_left); + return left != std::string_view::npos; } else return false; } void -parseBusName(const char *name, - const char brkt_left, - const char brkt_right, - const char escape, - // Return values. - bool &is_bus, - string &bus_name, - int &index) +parseBusName(std::string_view name, + const char brkt_left, + const char brkt_right, + const char escape, + // Return values. + bool &is_bus, + std::string &bus_name, + int &index) { - const char brkts_left[2] = {brkt_left, '\0'}; - const char brkts_right[2] = {brkt_right, '\0'}; - parseBusName(name, brkts_left, brkts_right, escape, + parseBusName(name, std::string_view(&brkt_left, 1), + std::string_view(&brkt_right, 1), escape, is_bus, bus_name, index); } void -parseBusName(const char *name, - const char *brkts_left, - const char *brkts_right, - char escape, - // Return values. - bool &is_bus, - string &bus_name, - int &index) +parseBusName(std::string_view name, + std::string_view brkts_left, + std::string_view brkts_right, + char escape, + // Return values. + bool &is_bus, + std::string &bus_name, + int &index) { is_bus = false; - size_t len = strlen(name); + size_t len = name.size(); // Shortest bus name is a[0]. if (len >= 4 // Escaped bus brackets are not buses. && name[len - 2] != escape) { char last_ch = name[len - 1]; - const char *brkt_right_ptr = strchr(brkts_right, last_ch); - if (brkt_right_ptr) { - size_t brkt_index = brkt_right_ptr - brkts_right; - char brkt_left = brkts_left[brkt_index]; - const char *left = strrchr(name, brkt_left); - if (left) { + size_t brkt_index = brkts_right.find(last_ch); + if (brkt_index != std::string_view::npos) { + char brkt_left_ch = brkts_left[brkt_index]; + size_t left = name.rfind(brkt_left_ch); + if (left != std::string_view::npos) { is_bus = true; - size_t bus_name_len = left - name; - bus_name.append(name, bus_name_len); - // Simple bus subscript. - index = atoi(left + 1); + bus_name.append(name.substr(0, left)); + // Simple bus subscript. + index = std::stoi(std::string(name.substr(left + 1))); } } } } void -parseBusName(const char *name, +parseBusName(std::string_view name, const char brkt_left, const char brkt_right, char escape, // Return values. bool &is_bus, bool &is_range, - string &bus_name, + std::string &bus_name, int &from, int &to, bool &subscript_wild) { - const char brkts_left[2] = {brkt_left, '\0'}; - const char brkts_right[2] = {brkt_right, '\0'}; - parseBusName(name, brkts_left, brkts_right, escape, + parseBusName(name, std::string_view(&brkt_left, 1), + std::string_view(&brkt_right, 1), escape, is_bus, is_range, bus_name, from, to, subscript_wild); } void -parseBusName(const char *name, - const char *brkts_left, - const char *brkts_right, +parseBusName(std::string_view name, + std::string_view brkts_left, + std::string_view brkts_right, char escape, // Return values. bool &is_bus, bool &is_range, - string &bus_name, + std::string &bus_name, int &from, int &to, bool &subscript_wild) @@ -137,59 +130,55 @@ parseBusName(const char *name, is_bus = false; is_range = false; subscript_wild = false; - size_t len = strlen(name); + size_t len = name.size(); // Shortest bus is a[0]. if (len >= 4 // Escaped bus brackets are not buses. && name[len - 2] != escape) { char last_ch = name[len - 1]; - const char *brkt_right_ptr = strchr(brkts_right, last_ch); - if (brkt_right_ptr) { - size_t brkt_index = brkt_right_ptr - brkts_right; - char brkt_left = brkts_left[brkt_index]; - const char *left = strrchr(name, brkt_left); - if (left) { + size_t brkt_index = brkts_right.find(last_ch); + if (brkt_index != std::string_view::npos) { + char brkt_left_ch = brkts_left[brkt_index]; + size_t left = name.rfind(brkt_left_ch); + if (left != std::string_view::npos) { is_bus = true; - // Check for bus range. - const char range_sep = ':'; - const char *range = strchr(name, range_sep); - if (range) { + bus_name.append(name.substr(0, left)); + // Check for bus range. + size_t range = name.find(':', left); + if (range != std::string_view::npos) { is_range = true; - bus_name.append(name, left - name); - // No need to terminate bus subscript because atoi stops - // scanning at first non-digit character. - from = atoi(left + 1); - to = atoi(range + 1); - } + from = std::stoi(std::string(name.substr(left + 1))); + to = std::stoi(std::string(name.substr(range + 1))); + } else { - bus_name.append(name, left - name); - if (left[1] == '*') + if (left + 1 < len && name[left + 1] == '*') subscript_wild = true; else - from = to = atoi(left + 1); + from = to = std::stoi(std::string(name.substr(left + 1))); } } } } } -string -escapeChars(const char *token, - const char ch1, - const char ch2, - const char escape) +std::string +escapeChars(std::string_view token, + const char ch1, + const char ch2, + const char escape) { - string escaped; - for (const char *s = token; *s; s++) { - char ch = *s; + std::string escaped; + escaped.reserve(token.size()); + for (size_t i = 0; i < token.size(); i++) { + char ch = token[i]; if (ch == escape) { - char next_ch = s[1]; - // Make sure we don't skip the null if escape is the last char. - if (next_ch != '\0') { + if (i + 1 < token.size()) { escaped += ch; - escaped += next_ch; - s++; + escaped += token[i + 1]; + i++; } + else + escaped += ch; } else if (ch == ch1 || ch == ch2) { escaped += escape; @@ -201,4 +190,4 @@ escapeChars(const char *token, return escaped; } -} // namespace +} // namespace sta diff --git a/network/PortDirection.cc b/network/PortDirection.cc index 64176b092..9d6761656 100644 --- a/network/PortDirection.cc +++ b/network/PortDirection.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -35,6 +35,7 @@ PortDirection *PortDirection::bidirect_; PortDirection *PortDirection::internal_; PortDirection *PortDirection::ground_; PortDirection *PortDirection::power_; +PortDirection *PortDirection::well_; PortDirection *PortDirection::unknown_; void @@ -47,7 +48,8 @@ PortDirection::init() internal_ = new PortDirection("internal", 4); ground_ = new PortDirection("ground", 5); power_ = new PortDirection("power", 6); - unknown_ = new PortDirection("unknown", 7); + well_ = new PortDirection("well", 7); + unknown_ = new PortDirection("unknown", 8); } void @@ -67,12 +69,14 @@ PortDirection::destroy() ground_ = nullptr; delete power_; power_ = nullptr; + delete well_; + well_ = nullptr; delete unknown_; unknown_ = nullptr; } PortDirection::PortDirection(const char *name, - int index) : + size_t index) : name_(name), index_(index) { @@ -95,6 +99,8 @@ PortDirection::find(const char *dir_name) return ground_; else if (stringEqual(dir_name, "power")) return power_; + else if (stringEqual(dir_name, "well")) + return well_; else return nullptr; } @@ -125,7 +131,8 @@ bool PortDirection::isPowerGround() const { return this == ground_ - || this == power_; + || this == power_ + || this == well_; } -} // namespace +} // namespace sta diff --git a/network/SdcNetwork.cc b/network/SdcNetwork.cc index 3967a82b7..0542f95aa 100644 --- a/network/SdcNetwork.cc +++ b/network/SdcNetwork.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,33 +24,22 @@ #include "SdcNetwork.hh" -#include "StringUtil.hh" -#include "PatternMatch.hh" #include "ParseBus.hh" +#include "PatternMatch.hh" +#include "StringUtil.hh" namespace sta { -using std::string; -using std::to_string; - -static string -escapeDividers(const char *token, - const Network *network); -static string -escapeBrackets(const char *token, - const Network *network); - NetworkNameAdapter::NetworkNameAdapter(Network *network) : - NetworkEdit(), network_(network), network_edit_(dynamic_cast(network)) { } bool -NetworkNameAdapter::linkNetwork(const char *top_cell_name, - bool make_black_boxes, - Report *report) +NetworkNameAdapter::linkNetwork(std::string_view top_cell_name, + bool make_black_boxes, + Report *report) { return network_->linkNetwork(top_cell_name, make_black_boxes, report); } @@ -80,24 +69,24 @@ NetworkNameAdapter::libertyLibraryIterator() const } Library * -NetworkNameAdapter::findLibrary(const char *name) +NetworkNameAdapter::findLibrary(std::string_view name) { return network_->findLibrary(name); } LibertyLibrary * -NetworkNameAdapter::findLiberty(const char *name) +NetworkNameAdapter::findLiberty(std::string_view name) { return network_->findLiberty(name); } LibertyLibrary * -NetworkNameAdapter::findLibertyFilename(const char *filename) +NetworkNameAdapter::findLibertyFilename(std::string_view filename) { return network_->findLibertyFilename(filename); } -const char * +std::string NetworkNameAdapter::name(const Library *library) const { return network_->name(library); @@ -111,21 +100,21 @@ NetworkNameAdapter::id(const Library *library) const Cell * NetworkNameAdapter::findCell(const Library *library, - const char *name) const + std::string_view name) const { return network_->findCell(library, name); } CellSeq NetworkNameAdapter::findCellsMatching(const Library *library, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { return network_->findCellsMatching(library, pattern); } //////////////////////////////////////////////////////////////// -const char * +std::string NetworkNameAdapter::name(const Cell *cell) const { return network_->name(cell); @@ -137,9 +126,9 @@ NetworkNameAdapter::id(const Cell *cell) const return network_->id(cell); } -string +std::string NetworkNameAdapter::getAttribute(const Cell *cell, - const string &key) const + std::string_view key) const { return network_->getAttribute(cell, key); } @@ -156,8 +145,8 @@ NetworkNameAdapter::library(const Cell *cell) const return network_->library(cell); } -const char * -NetworkNameAdapter::filename(const Cell *cell) +std::string_view +NetworkNameAdapter::filename(const Cell *cell) const { return network_->filename(cell); } @@ -188,14 +177,14 @@ NetworkNameAdapter::cell(const LibertyCell *cell) const Port * NetworkNameAdapter::findPort(const Cell *cell, - const char *name) const + std::string_view name) const { return network_->findPort(cell, name); } PortSeq NetworkNameAdapter::findPortsMatching(const Cell *cell, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { return network_->findPortsMatching(cell, pattern); } @@ -226,7 +215,7 @@ NetworkNameAdapter::portBitCount(const Cell *cell) const //////////////////////////////////////////////////////////////// -const char * +std::string NetworkNameAdapter::name(const Port *port) const { return network_->name(port); @@ -264,17 +253,17 @@ NetworkNameAdapter::vertexId(const Pin *pin) const void NetworkNameAdapter::setVertexId(Pin *pin, - VertexId id) + VertexId id) { network_->setVertexId(pin, id); } void NetworkNameAdapter::location(const Pin *pin, - // Return values. - double &x, - double &y, - bool &exists) const + // Return values. + double &x, + double &y, + bool &exists) const { network_->location(pin, x, y, exists); } @@ -291,7 +280,7 @@ NetworkNameAdapter::isBus(const Port *port) const return network_->isBus(port); } -const char * +std::string NetworkNameAdapter::busName(const Port *port) const { return network_->busName(port); @@ -299,7 +288,7 @@ NetworkNameAdapter::busName(const Port *port) const Port * NetworkNameAdapter::findBusBit(const Port *port, - int index) const + int index) const { return network_->findMember(port, index); } @@ -330,7 +319,7 @@ NetworkNameAdapter::hasMembers(const Port *port) const Port * NetworkNameAdapter::findMember(const Port *port, - int index) const + int index) const { return network_->findMember(port, index); } @@ -355,9 +344,9 @@ NetworkNameAdapter::cell(const Instance *instance) const return network_->cell(instance); } -string +std::string NetworkNameAdapter::getAttribute(const Instance *inst, - const string &key) const + std::string_view key) const { return network_->getAttribute(inst, key); } @@ -382,14 +371,14 @@ NetworkNameAdapter::isLeaf(const Instance *instance) const Pin * NetworkNameAdapter::findPin(const Instance *instance, - const Port *port) const + const Port *port) const { return network_->findPin(instance, port); } Pin * NetworkNameAdapter::findPin(const Instance *instance, - const LibertyPort *port) const + const LibertyPort *port) const { return network_->findPin(instance, port); } @@ -540,16 +529,16 @@ NetworkNameAdapter::isEditable() const LibertyLibrary * -NetworkNameAdapter::makeLibertyLibrary(const char *name, - const char *filename) +NetworkNameAdapter::makeLibertyLibrary(std::string_view name, + std::string_view filename) { return network_edit_->makeLibertyLibrary(name, filename); } Instance * NetworkNameAdapter::makeInstance(LibertyCell *cell, - const char *name, - Instance *parent) + std::string_view name, + Instance *parent) { return network_edit_->makeInstance(cell, name, parent); } @@ -562,7 +551,7 @@ NetworkNameAdapter::makePins(Instance *inst) void NetworkNameAdapter::mergeInto(Net *net, - Net *into_net) + Net *into_net) { network_edit_->mergeInto(net, into_net); } @@ -574,31 +563,31 @@ NetworkNameAdapter::mergedInto(Net *net) } Net * -NetworkNameAdapter::makeNet(const char *name, - Instance *parent) +NetworkNameAdapter::makeNet(std::string_view name, + Instance *parent) { return network_edit_->makeNet(name, parent); } void NetworkNameAdapter::replaceCell(Instance *inst, - Cell *to_cell) + Cell *to_cell) { network_edit_->replaceCell(inst, to_cell); } Pin * NetworkNameAdapter::connect(Instance *inst, - Port *port, - Net *net) + Port *port, + Net *net) { return network_edit_->connect(inst, port, net); } Pin * NetworkNameAdapter::connect(Instance *inst, - LibertyPort *port, - Net *net) + LibertyPort *port, + Net *net) { return network_edit_->connect(inst, port, net); } @@ -642,59 +631,58 @@ SdcNetwork::SdcNetwork(Network *network) : // Translate sta namespace to sdc namespace. // Remove all escapes. -const char * -SdcNetwork::staToSdc(const char *sta_name) const +std::string +SdcNetwork::staToSdc(std::string_view sta_name) const { char escape = pathEscape(); - char *sdc_name = makeTmpString(strlen(sta_name) + 1); - char *d = sdc_name; - for (const char *s = sta_name; *s; s++) { - char ch = s[0]; + size_t sta_length = sta_name.length(); + std::string sdc_name; + for (size_t i = 0; i < sta_length; i++) { + char ch = sta_name[i]; if (ch == escape) { - char next_ch = s[1]; + char next_ch = sta_name[i + 1]; // Escaped escape. if (next_ch == escape) { - *d++ = ch; - *d++ = next_ch; - s++; + sdc_name += ch; + sdc_name += next_ch; + i++; } } else // Non escape. - *d++ = ch; + sdc_name += ch; } - *d++ = '\0'; return sdc_name; } Port * SdcNetwork::findPort(const Cell *cell, - const char *name) const + std::string_view name) const { Port *port = network_->findPort(cell, name); if (port == nullptr) { // Look for matches after escaping brackets. bool is_bus; - string bus_name; + std::string bus_name; int index; parseBusName(name, '[', ']', pathEscape(), is_bus, bus_name, index); if (is_bus) { - string escaped1 = escapeBrackets(name, this); - port = network_->findPort(cell, escaped1.c_str()); + std::string escaped1 = escapeBrackets(std::string(name), this); + port = network_->findPort(cell, escaped1); if (port == nullptr) { - // Try escaping base foo\[0\][1] - string escaped2; - string escaped_bus_name = escapeBrackets(bus_name.c_str(), this); - stringPrint(escaped2, "%s[%d]", - escaped_bus_name.c_str(), - index); - port = network_->findPort(cell, escaped2.c_str()); + // Try escaping base foo\[0\][1] + std::string escaped_bus_name = escapeBrackets(bus_name, this); + std::string escaped2 = escaped_bus_name + + '[' + + std::to_string(index) + + ']'; + port = network_->findPort(cell, escaped2); } } else { // Try escaping brackets foo\[0\].bar - string escaped = escapeBrackets(name, this); - port = network_->findPort(cell, escaped.c_str()); + std::string escaped = escapeBrackets(std::string(name), this); + port = network_->findPort(cell, escaped); } } return port; @@ -702,83 +690,83 @@ SdcNetwork::findPort(const Cell *cell, PortSeq SdcNetwork::findPortsMatching(const Cell *cell, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { PortSeq matches = network_->findPortsMatching(cell, pattern); if (matches.empty()) { // Look for matches after escaping brackets. bool is_bus; - string bus_name; + std::string bus_name; int index; parseBusName(pattern->pattern(), '[', ']', pathEscape(), is_bus, bus_name, index); if (is_bus) { - string escaped1 = escapeBrackets(pattern->pattern(), this); - PatternMatch escaped_pattern1(escaped1.c_str(), pattern); + std::string escaped1 = escapeBrackets(pattern->pattern(), this); + PatternMatch escaped_pattern1(escaped1, pattern); matches = network_->findPortsMatching(cell, &escaped_pattern1); if (matches.empty()) { - // Try escaping base foo\[0\][1] - string escaped_name = escapeBrackets(bus_name.c_str(), this); + // Try escaping base foo\[0\][1] + std::string escaped_name = escapeBrackets(bus_name, this); escaped_name += '['; - escaped_name += to_string(index); + escaped_name += std::to_string(index); escaped_name += ']'; - PatternMatch escaped_pattern2(escaped_name.c_str(), pattern); - matches = network_->findPortsMatching(cell, &escaped_pattern2); + PatternMatch escaped_pattern2(escaped_name, pattern); + matches = network_->findPortsMatching(cell, &escaped_pattern2); } } else { // Try escaping brackets foo\[0\].bar - string escaped = escapeBrackets(pattern->pattern(), this); - PatternMatch escaped_pattern(escaped.c_str(), pattern); + std::string escaped = escapeBrackets(pattern->pattern(), this); + PatternMatch escaped_pattern(escaped, pattern); matches = network_->findPortsMatching(cell, &escaped_pattern); } } return matches; } -const char * +std::string SdcNetwork::name(const Port *port) const { return staToSdc(network_->name(port)); } -const char * +std::string SdcNetwork::busName(const Port *port) const { return staToSdc(network_->busName(port)); } -const char * +std::string SdcNetwork::name(const Instance *instance) const { return staToSdc(network_->name(instance)); } -const char * +std::string SdcNetwork::pathName(const Instance *instance) const { return staToSdc(network_->pathName(instance)); } -const char * +std::string SdcNetwork::pathName(const Pin *pin) const { return staToSdc(network_->pathName(pin)); } -const char * +std::string SdcNetwork::portName(const Pin *pin) const { return staToSdc(network_->portName(pin)); } -const char * +std::string SdcNetwork::name(const Net *net) const { return staToSdc(network_->name(net)); } -const char * +std::string SdcNetwork::pathName(const Net *net) const { return staToSdc(network_->pathName(net)); @@ -787,32 +775,32 @@ SdcNetwork::pathName(const Net *net) const //////////////////////////////////////////////////////////////// Instance * -SdcNetwork::findInstance(const char *path_name) const +SdcNetwork::findInstance(std::string_view path_name) const { - const char *child_name; + std::string child_name; Instance *parent; parsePath(path_name, parent, child_name); if (parent == nullptr) parent = network_->topInstance(); Instance *child = findChild(parent, child_name); if (child == nullptr) { - string escaped_name = escapeDividers(child_name, this); - child = findChild(parent, escaped_name.c_str()); + std::string escaped_name = escapeDividers(child_name, this); + child = findChild(parent, escaped_name); } return child; } Instance * SdcNetwork::findInstanceRelative(const Instance *inst, - const char *path_name) const + std::string_view path_name) const { Instance *inst1 = network_->findInstanceRelative(inst, path_name); if (inst1 == nullptr) { - string path_name1 = escapeBrackets(path_name, this); - inst1 = network_->findInstanceRelative(inst, path_name1.c_str()); + std::string path_name1 = escapeBrackets(std::string(path_name), this); + inst1 = network_->findInstanceRelative(inst, path_name1); if (inst1 == nullptr) { - string path_name2 = escapeDividers(path_name1.c_str(), network_); - inst1 = network_->findInstanceRelative(inst, path_name2.c_str()); + std::string path_name2 = escapeDividers(path_name1, network_); + inst1 = network_->findInstanceRelative(inst, path_name2); } } return inst1; @@ -820,7 +808,7 @@ SdcNetwork::findInstanceRelative(const Instance *inst, InstanceSeq SdcNetwork::findInstancesMatching(const Instance *context, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { InstanceSeq matches; findInstancesMatching1(context, pattern, matches); @@ -833,23 +821,22 @@ SdcNetwork::findInstancesMatching1(const Instance *context, InstanceSeq &matches) const { visitMatches(context, pattern, - [&](const Instance *instance, - const PatternMatch *tail) - { - size_t match_count = matches.size(); - network_->findChildrenMatching(instance, tail, matches); - return matches.size() != match_count; - }); + [&] (const Instance *instance, + const PatternMatch *tail) { + size_t match_count = matches.size(); + network_->findChildrenMatching(instance, tail, matches); + return matches.size() != match_count; + }); } Instance * SdcNetwork::findChild(const Instance *parent, - const char *name) const + std::string_view name) const { Instance *child = network_->findChild(parent, name); if (child == nullptr) { - string escaped = escapeBrackets(name, this); - child = network_->findChild(parent, escaped.c_str()); + std::string escaped = escapeBrackets(std::string(name), this); + child = network_->findChild(parent, escaped); } return child; } @@ -857,9 +844,9 @@ SdcNetwork::findChild(const Instance *parent, //////////////////////////////////////////////////////////////// Net * -SdcNetwork::findNet(const char *path_name) const +SdcNetwork::findNet(std::string_view path_name) const { - const char *net_name; + std::string net_name; Instance *inst; parsePath(path_name, inst, net_name); if (inst == nullptr) @@ -869,33 +856,33 @@ SdcNetwork::findNet(const char *path_name) const Net * SdcNetwork::findNet(const Instance *instance, - const char *net_name) const + std::string_view net_name) const { Net *net = network_->findNet(instance, net_name); if (net == nullptr) { - string net_name1 = escapeBrackets(net_name, this); - string net_name2 = escapeDividers(net_name1.c_str(), network_); - net = network_->findNet(instance, net_name2.c_str()); + std::string net_name1 = escapeBrackets(std::string(net_name), this); + std::string net_name2 = escapeDividers(net_name1, network_); + net = network_->findNet(instance, net_name2); } return net; } Net * SdcNetwork::findNetRelative(const Instance *inst, - const char *path_name) const + std::string_view path_name) const { Net *net = network_->findNetRelative(inst, path_name); if (net == nullptr) { - string path_name1 = escapeDividers(path_name, network_); - net = network_->findNetRelative(inst, path_name1.c_str()); + std::string path_name1 = escapeDividers(std::string(path_name), network_); + net = network_->findNetRelative(inst, path_name1); if (net == nullptr) { - string path_name2 = escapeBrackets(path_name, network_); - net = network_->findNetRelative(inst, path_name2.c_str()); + std::string path_name2 = escapeBrackets(std::string(path_name), network_); + net = network_->findNetRelative(inst, path_name2); if (net == nullptr) { - string path_name3 = escapeDividers(path_name2.c_str(), network_); - net = network_->findNetRelative(inst, path_name3.c_str()); + std::string path_name3 = escapeDividers(path_name2, network_); + net = network_->findNetRelative(inst, path_name3); } } } @@ -904,35 +891,35 @@ SdcNetwork::findNetRelative(const Instance *inst, NetSeq SdcNetwork::findNetsMatching(const Instance *parent, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { NetSeq matches; visitMatches(parent, pattern, - [&](const Instance *instance, - const PatternMatch *tail) - { - size_t match_count = matches.size(); - network_->findInstNetsMatching(instance, tail, matches); - return matches.size() != match_count; - }); + [&](const Instance *instance, + const PatternMatch *tail) + { + size_t match_count = matches.size(); + network_->findInstNetsMatching(instance, tail, matches); + return matches.size() != match_count; + }); return matches; } void SdcNetwork::findInstNetsMatching(const Instance *instance, - const PatternMatch *pattern, - NetSeq &matches) const + const PatternMatch *pattern, + NetSeq &matches) const { network_->findInstNetsMatching(instance, pattern, matches); if (matches.empty()) { // Look for matches after escaping path dividers. - string escaped_pattern = escapeDividers(pattern->pattern(), this); - const PatternMatch escaped_dividers(escaped_pattern.c_str(), pattern); + std::string escaped_pattern = escapeDividers(pattern->pattern(), this); + const PatternMatch escaped_dividers(escaped_pattern, pattern); network_->findInstNetsMatching(instance, &escaped_dividers, matches); if (matches.empty()) { // Look for matches after escaping brackets. - string escaped_pattern2 = escapeBrackets(pattern->pattern(),this); - const PatternMatch escaped_brkts(escaped_pattern2.c_str(), pattern); + std::string escaped_pattern2 = escapeBrackets(pattern->pattern(),this); + const PatternMatch escaped_brkts(escaped_pattern2, pattern); network_->findInstNetsMatching(instance, &escaped_brkts, matches); } } @@ -941,9 +928,9 @@ SdcNetwork::findInstNetsMatching(const Instance *instance, //////////////////////////////////////////////////////////////// Pin * -SdcNetwork::findPin(const char *path_name) const +SdcNetwork::findPin(std::string_view path_name) const { - const char *port_name; + std::string port_name; Instance *inst; parsePath(path_name, inst, port_name); if (inst == nullptr) @@ -953,31 +940,33 @@ SdcNetwork::findPin(const char *path_name) const Pin * SdcNetwork::findPin(const Instance *instance, - const char *port_name) const + std::string_view port_name) const { Pin *pin = network_->findPin(instance, port_name); if (pin == nullptr) { // Look for match after escaping brackets. bool is_bus; - string bus_name; + std::string bus_name; int index; parseBusName(port_name, '[', ']', pathEscape(), is_bus, bus_name, index); if (is_bus) { - string escaped1 = escapeBrackets(port_name, this); - pin = network_->findPin(instance, escaped1.c_str()); + std::string escaped1 = escapeBrackets(std::string(port_name), this); + pin = network_->findPin(instance, escaped1); if (pin == nullptr) { - // Try escaping base foo\[0\][1] - string escaped_bus_name = escapeBrackets(bus_name.c_str(), this); - string escaped2; - stringPrint(escaped2, "%s[%d]", escaped_bus_name.c_str(), index); - pin = network_->findPin(instance, escaped2.c_str()); + // Try escaping base foo\[0\][1] + std::string escaped_bus_name = escapeBrackets(bus_name, this); + std::string escaped2 = escaped_bus_name + + '[' + + std::to_string(index) + + ']'; + pin = network_->findPin(instance, escaped2); } } else { // Try escaping port brackets foo\[0\].bar - string escaped = escapeBrackets(port_name, this); - pin = network_->findPin(instance, escaped.c_str()); + std::string escaped = escapeBrackets(std::string(port_name), this); + pin = network_->findPin(instance, escaped); } } return pin; @@ -986,18 +975,18 @@ SdcNetwork::findPin(const Instance *instance, // Top level ports are not considered pins by get_pins. PinSeq SdcNetwork::findPinsMatching(const Instance *instance, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { PinSeq matches; - if (stringEq(pattern->pattern(), "*")) { + if (pattern->pattern() == "*") { // Pattern of '*' matches all child instance pins. InstanceChildIterator *child_iter = childIterator(instance); while (child_iter->hasNext()) { Instance *child = child_iter->next(); InstancePinIterator *pin_iter = pinIterator(child); while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - matches.push_back(pin); + const Pin *pin = pin_iter->next(); + matches.push_back(pin); } delete pin_iter; } @@ -1005,18 +994,18 @@ SdcNetwork::findPinsMatching(const Instance *instance, } else visitMatches(instance, pattern, - [&](const Instance *instance, - const PatternMatch *tail) - { - return visitPinTail(instance, tail, matches); - }); + [&](const Instance *instance, + const PatternMatch *tail) + { + return visitPinTail(instance, tail, matches); + }); return matches; } bool SdcNetwork::visitPinTail(const Instance *instance, - const PatternMatch *tail, - PinSeq &matches) const + const PatternMatch *tail, + PinSeq &matches) const { bool found_match = false; if (instance != network_->topInstance()) { @@ -1024,42 +1013,45 @@ SdcNetwork::visitPinTail(const Instance *instance, CellPortIterator *port_iter = network_->portIterator(cell); while (port_iter->hasNext()) { Port *port = port_iter->next(); - const char *port_name = network_->name(port); + std::string port_name = network_->name(port); if (network_->hasMembers(port)) { - bool bus_matches = tail->match(port_name); + bool bus_matches = tail->match(port_name); if (!bus_matches) { - string escaped_name = escapeDividers(port_name, network_); - bus_matches = tail->match(escaped_name); + std::string escaped_name = escapeDividers(std::string(port_name), + network_); + bus_matches = tail->match(escaped_name); } - PortMemberIterator *member_iter = network_->memberIterator(port); - while (member_iter->hasNext()) { - Port *member_port = member_iter->next(); - const Pin *pin = network_->findPin(instance, member_port); - if (pin) { - if (bus_matches) { - matches.push_back(pin); - found_match = true; - } - else { - const char *member_name = network_->name(member_port); - bool member_matches = tail->match(member_name); + PortMemberIterator *member_iter = network_->memberIterator(port); + while (member_iter->hasNext()) { + Port *member_port = member_iter->next(); + const Pin *pin = network_->findPin(instance, member_port); + if (pin) { + if (bus_matches) { + matches.push_back(pin); + found_match = true; + } + else { + std::string member_name = network_->name(member_port); + bool member_matches = tail->match(member_name); if (!member_matches) { - string escaped_name = escapeDividers(member_name, network_); + std::string escaped_name = escapeDividers(std::string(member_name), + network_); member_matches = tail->match(escaped_name); } if (member_matches) { - matches.push_back(pin); - found_match = true; - } - } - } - } - delete member_iter; + matches.push_back(pin); + found_match = true; + } + } + } + } + delete member_iter; } else { bool port_matches = tail->match(port_name); if (!port_matches) { - string escaped_name = escapeDividers(port_name, network_); + std::string escaped_name = escapeDividers(std::string(port_name), + network_); port_matches = tail->match(escaped_name); } if (port_matches) { @@ -1078,19 +1070,19 @@ SdcNetwork::visitPinTail(const Instance *instance, Instance * SdcNetwork::makeInstance(LibertyCell *cell, - const char *name, - Instance *parent) + std::string_view name, + Instance *parent) { - string escaped_name = escapeDividers(name, this); - return network_edit_->makeInstance(cell, escaped_name.c_str(), parent); + std::string escaped_name = escapeDividers(std::string(name), this); + return network_edit_->makeInstance(cell, escaped_name, parent); } Net * -SdcNetwork::makeNet(const char *name, - Instance *parent) +SdcNetwork::makeNet(std::string_view name, + Instance *parent) { - string escaped_name = escapeDividers(name, this); - return network_edit_->makeNet(escaped_name.c_str(), parent); + std::string escaped_name = escapeDividers(std::string(name), this); + return network_edit_->makeNet(escaped_name, parent); } //////////////////////////////////////////////////////////////// @@ -1103,10 +1095,10 @@ SdcNetwork::makeNet(const char *name, // a\/b // a\/b\/c void -SdcNetwork::parsePath(const char *path, - // Return values. - Instance *&inst, - const char *&path_tail) const +SdcNetwork::parsePath(std::string_view path, + // Return values. + Instance *&inst, + std::string &path_tail) const { int divider_count, path_length; scanPath(path, divider_count, path_length); @@ -1120,21 +1112,21 @@ SdcNetwork::parsePath(const char *path, // Scan the path for unescaped dividers. void -SdcNetwork::scanPath(const char *path, - // Return values. - // Unescaped divider count. - int ÷r_count, - int &path_length) const +SdcNetwork::scanPath(std::string_view path, + // Return values. + // Unescaped divider count. + int ÷r_count, + int &path_length) const { divider_count = 0; path_length = 0; - for (const char *s = path; *s; s++) { - char ch = *s; + for (size_t i = 0; i < path.size(); i++) { + char ch = path[i]; if (ch == escape_) { // Make sure we don't skip the null if escape is the last char. - if (s[1] != '\0') { - s++; - path_length++; + if (i != path.size() - 1) { + i++; + path_length++; } } else if (ch == divider_) @@ -1144,54 +1136,47 @@ SdcNetwork::scanPath(const char *path, } void -SdcNetwork::parsePath(const char *path, - int divider_count, - int path_length, - // Return values. - Instance *&inst, - const char *&path_tail) const +SdcNetwork::parsePath(std::string_view path, + int divider_count, + int path_length, + // Return values. + Instance *&inst, + std::string &path_tail) const { Instance *parent = topInstance(); - // Leave room to escape all the dividers and '\0'. - int inst_path_length = path_length + divider_count + 1; - char *inst_path = new char[inst_path_length]; + std::string inst_path; + // Leave room to escape all the dividers. + inst_path.reserve(path_length + divider_count); inst = nullptr; path_tail = path; - char *p = inst_path; - for (const char *s = path; *s; s++) { - char ch = *s; + for (size_t i = 0; i < path.size(); i++) { + char ch = path[i]; if (ch == escape_) { // Make sure we don't skip the null if escape is the last char. - if (s[1] != '\0') { - *p++ = ch; - *p++ = s[1]; - s++; + if (i < path.size() - 1) { + inst_path += ch; + inst_path += path[i + 1]; + i++; } } else if (ch == divider_) { - // Terminate the sub-path up to this divider. - *p = '\0'; Instance *child = findChild(parent, inst_path); if (child) { - // Found an instance for the sub-path up to this divider. - parent = inst = child; - // Reset the instance path. - p = inst_path; - path_tail = s + 1; + // Found an instance for the sub-path up to this divider. + parent = inst = child; + // Reset the instance path. + inst_path.clear(); + path_tail = path.substr(i + 1); } else { - // No match for sub-path. Escape the divider and keep looking. - *p++ = escape_; - *p++ = divider_; + // No match for sub-path. Escape the divider and keep looking. + inst_path += escape_; + inst_path += divider_; } } else - *p++ = ch; - if (p - inst_path + 1 > inst_path_length) - report_->critical(1500, "inst path string lenth estimate busted"); + inst_path += ch; } - *p = '\0'; - stringDelete(inst_path); } // Helper to visit instance path matches. @@ -1203,95 +1188,85 @@ SdcNetwork::parsePath(const char *path, // a\/b\/c bool SdcNetwork::visitMatches(const Instance *parent, - const PatternMatch *pattern, - const std::function - visit_tail) const + const PatternMatch *pattern, + const std::function + &visit_tail) const { int divider_count, path_length; scanPath(pattern->pattern(), divider_count, path_length); - + std::string inst_path; // Leave room to escape all the dividers and '\0'. - int inst_path_length = path_length + divider_count + 1; - char *inst_path = new char[inst_path_length]; - char *p = inst_path; + inst_path.reserve(path_length + divider_count + 1); bool has_brkts = false; bool found_match = false; - for (const char *s = pattern->pattern(); *s; s++) { - char ch = *s; + const std::string &pattern_str = pattern->pattern(); + for (size_t i = 0; i < pattern_str.size(); i++) { + char ch = pattern_str[i]; if (ch == escape_) { // Make sure we don't skip the null if escape is the last char. - if (s[1] != '\0') { - *p++ = ch; - *p++ = s[1]; - s++; + if (i < pattern_str.size() - 1) { + inst_path += ch; + inst_path += pattern_str[i + 1]; + i++; } } else if (ch == divider_) { - // Terminate the sub-path up to this divider. - *p = '\0'; PatternMatch matcher(inst_path, pattern); InstanceSeq matches; network_->findChildrenMatching(parent, &matcher, matches); if (has_brkts && matches.empty()) { - // Look for matches after escaping brackets. - string escaped_brkts = escapeBrackets(inst_path, this); - const PatternMatch escaped_pattern(escaped_brkts, pattern); - network_->findChildrenMatching(parent, &escaped_pattern, matches); + // Look for matches after escaping brackets. + std::string escaped_brkts = escapeBrackets(inst_path, this); + const PatternMatch escaped_pattern(escaped_brkts, pattern); + network_->findChildrenMatching(parent, &escaped_pattern, matches); } if (!matches.empty()) { - // Found instance matches for the sub-path up to this divider. - const PatternMatch tail_pattern(s + 1, pattern); - InstanceSeq::Iterator match_iter(matches); - while (match_iter.hasNext()) { - const Instance *match = match_iter.next(); - // Recurse to save the iterator state so we can iterate over - // multiple nested partial matches. - found_match |= visitMatches(match, &tail_pattern, visit_tail); - } + // Found instance matches for the sub-path up to this divider. + const PatternMatch tail_pattern(pattern_str.substr(i + 1), pattern); + for (const Instance *match : matches) + // Recurse to save the iterator state so we can iterate over + // multiple nested partial matches. + found_match |= visitMatches(match, &tail_pattern, visit_tail); } // Escape the divider and keep looking. - *p++ = escape_; - *p++ = divider_; + inst_path += escape_; + inst_path += divider_; } else { if (ch == '[' || ch == ']') - has_brkts = true; - *p++ = ch; + has_brkts = true; + inst_path += ch; } - if (p - inst_path + 1 > inst_path_length) - report_->critical(1501, "inst path string lenth estimate exceeded"); } - *p = '\0'; if (!found_match) { PatternMatch tail_pattern(inst_path, pattern); found_match |= visit_tail(parent, &tail_pattern); if (!found_match && has_brkts) { // Look for matches after escaping brackets. - string escaped_path = escapeBrackets(inst_path, this); + std::string escaped_path = escapeBrackets(inst_path, this); const PatternMatch escaped_tail(escaped_path, pattern); found_match |= visit_tail(parent, &escaped_tail); } } - stringDelete(inst_path); return found_match; } //////////////////////////////////////////////////////////////// -static string -escapeDividers(const char *token, - const Network *network) +std::string +escapeDividers(std::string_view name, + const Network *network) { - return escapeChars(token, network->pathDivider(), '\0', - network->pathEscape()); + return escapeChars(name, network->pathDivider(), '\0', + network->pathEscape()); } -static string -escapeBrackets(const char *token, - const Network *network) +std::string +escapeBrackets(std::string_view name, + const Network *network) { - return escapeChars(token, '[', ']', network->pathEscape()); + return escapeChars(name, '[', ']', network->pathEscape()); } -} // namespace +} // namespace sta diff --git a/network/VerilogNamespace.cc b/network/VerilogNamespace.cc index 94cc2f3f4..ed63cd3e6 100644 --- a/network/VerilogNamespace.cc +++ b/network/VerilogNamespace.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,81 +26,87 @@ #include -#include "StringUtil.hh" #include "ParseBus.hh" +#include "StringUtil.hh" namespace sta { -using std::string; - constexpr char verilog_escape = '\\'; -static string -staToVerilog(const char *sta_name); -static string -staToVerilog2(const char *sta_name); -static string -verilogToSta(const string *verilog_name); +static std::string +staToVerilog(std::string_view sta_name); +static std::string +staToVerilog2(std::string_view sta_name); +static std::string +verilogToSta(std::string_view verilog_name); -string -cellVerilogName(const char *sta_name) +std::string +cellVerilogName(std::string_view sta_name) { return staToVerilog(sta_name); } -string -instanceVerilogName(const char *sta_name) +std::string +instanceVerilogName(std::string_view sta_name) { return staToVerilog(sta_name); } -string -netVerilogName(const char *sta_name) +std::string +netVerilogName(std::string_view sta_name) { bool is_bus; - string bus_name; + std::string bus_name; int index; parseBusName(sta_name, '[', ']', verilog_escape, is_bus, bus_name, index); if (is_bus) { - string bus_vname = staToVerilog(bus_name.c_str()); - string vname; - stringPrint(vname, "%s[%d]", bus_vname.c_str(), index); + std::string bus_vname = staToVerilog(bus_name); + std::string vname = bus_vname + '[' + std::to_string(index) + ']'; return vname; } else return staToVerilog2(sta_name); } -string -portVerilogName(const char *sta_name) +std::string +portVerilogName(std::string_view sta_name) { return staToVerilog2(sta_name); } -static string -staToVerilog(const char *sta_name) +// functions expect a value representable as unsigned char or EOF. +static bool +isAlnumUnderscore(char ch) +{ + return std::isalnum(static_cast(ch)) != 0 || ch == '_'; +} + +static std::string +staToVerilog(std::string_view sta_name) { // Leave room for leading escape and trailing space if the name // needs to be escaped. // Assume the name has to be escaped and start copying while scanning. - string escaped_name = "\\"; + std::string escaped_name = "\\"; bool escaped = false; - for (const char *s = sta_name; *s ; s++) { - char ch = s[0]; + size_t sta_length = sta_name.size(); + for (size_t i = 0; i < sta_length; i++) { + char ch = sta_name[i]; if (ch == verilog_escape) { - char next_ch = s[1]; - if (next_ch == verilog_escape) { - escaped_name += ch; - escaped_name += next_ch; - s++; + escaped = true; + if (i + 1 < sta_length) { + char next_ch = sta_name[i + 1]; + if (next_ch == verilog_escape) { + escaped_name += next_ch; + i++; + } } else - // Skip escape. - escaped = true; + escaped_name += ch; } else { - if ((!(isalnum(ch) || ch == '_'))) - escaped = true; + if (!isAlnumUnderscore(ch)) + escaped = true; escaped_name += ch; } } @@ -110,37 +116,38 @@ staToVerilog(const char *sta_name) return escaped_name; } else - return string(sta_name); + return std::string(sta_name); } -static string -staToVerilog2(const char *sta_name) +static std::string +staToVerilog2(std::string_view sta_name) { constexpr char bus_brkt_left = '['; constexpr char bus_brkt_right = ']'; // Leave room for leading escape and trailing space if the name // needs to be escaped. - string escaped_name = "\\"; + std::string escaped_name = "\\"; // Assume the name has to be escaped and start copying while scanning. bool escaped = false; - for (const char *s = sta_name; *s ; s++) { - char ch = s[0]; + size_t sta_length = sta_name.size(); + for (size_t i = 0; i < sta_length; i++) { + char ch = sta_name[i]; if (ch == verilog_escape) { - char next_ch = s[1]; - if (next_ch == verilog_escape) { - escaped_name += ch; - escaped_name += next_ch; - s++; + escaped = true; + if (i + 1 < sta_length) { + char next_ch = sta_name[i + 1]; + if (next_ch == verilog_escape) { + escaped_name += next_ch; + i++; + } } else - // Skip escape. - escaped = true; + escaped_name += ch; } else { bool is_brkt = (ch == bus_brkt_left || ch == bus_brkt_right); - if ((!(isalnum(ch) || ch == '_') && !is_brkt) - || is_brkt) - escaped = true; + if ((!isAlnumUnderscore(ch) && !is_brkt) || is_brkt) + escaped = true; escaped_name += ch; } } @@ -150,62 +157,66 @@ staToVerilog2(const char *sta_name) return escaped_name; } else - return string(sta_name); + return std::string(sta_name); } //////////////////////////////////////////////////////////////// -string -moduleVerilogToSta(const string *module_name) +std::string +moduleVerilogToSta(std::string_view module_name) { return verilogToSta(module_name); } -string -instanceVerilogToSta(const string *inst_name) +std::string +instanceVerilogToSta(std::string_view inst_name) { return verilogToSta(inst_name); } -string -netVerilogToSta(const string *net_name) +std::string +netVerilogToSta(std::string_view net_name) { return verilogToSta(net_name); } -string -portVerilogToSta(const string *port_name) +std::string +portVerilogToSta(std::string_view port_name) { return verilogToSta(port_name); } -static string -verilogToSta(const string *verilog_name) +static std::string +verilogToSta(std::string_view verilog_name) { - if (verilog_name->front() == '\\') { + if (verilog_name.empty()) + return std::string(verilog_name); + + if (verilog_name.front() == '\\') { constexpr char divider = '/'; constexpr char bus_brkt_left = '['; constexpr char bus_brkt_right = ']'; - size_t verilog_name_length = verilog_name->size(); - if (isspace(verilog_name->back())) + size_t verilog_name_length = verilog_name.size(); + if (verilog_name_length > 1 + && std::isspace(static_cast(verilog_name.back())) != 0) verilog_name_length--; - string sta_name; + std::string sta_name; // Ignore leading '\'. for (size_t i = 1; i < verilog_name_length; i++) { - char ch = verilog_name->at(i); + char ch = verilog_name[i]; if (ch == bus_brkt_left || ch == bus_brkt_right || ch == divider || ch == verilog_escape) // Escape bus brackets, dividers and escapes. - sta_name += verilog_escape; + sta_name += verilog_escape; sta_name += ch; } return sta_name; } else - return string(*verilog_name); + return std::string(verilog_name); } -} // namespace +} // namespace sta diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index 9d7f58447..6ab2bcbb0 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,20 +26,18 @@ #include // max -#include "Report.hh" +#include "ConcreteParasiticsPvt.hh" #include "Debug.hh" #include "Error.hh" -#include "Mutex.hh" -#include "Set.hh" +#include "Liberty.hh" #include "MinMax.hh" +#include "Mutex.hh" #include "Network.hh" -#include "Wireload.hh" -#include "Liberty.hh" -#include "Sdc.hh" #include "Parasitics.hh" -#include "MakeConcreteParasitics.hh" -#include "ConcreteParasiticsPvt.hh" -#include "Corner.hh" +#include "Report.hh" +#include "Scene.hh" +#include "Sdc.hh" +#include "Wireload.hh" // Multiple inheritance is used to share elmore and pi model base // classes, but care is taken to make sure there are no loops in the @@ -47,12 +45,6 @@ namespace sta { -using std::max; - -ConcreteParasitic::~ConcreteParasitic() -{ -} - bool ConcreteParasitic::isPiElmore() const { @@ -85,15 +77,15 @@ ConcreteParasitic::isParasiticNetwork() const void ConcreteParasitic::piModel(float &, - float &, - float &) const + float &, + float &) const { } void ConcreteParasitic::setPiModel(float, - float, - float) + float, + float) { } @@ -110,15 +102,15 @@ ConcreteParasitic::setIsReduced(bool) void ConcreteParasitic::findElmore(const Pin *, - float &, - bool &exists) const + float &, + bool &exists) const { exists = false; } void ConcreteParasitic::setElmore(const Pin *, - float) + float) { } @@ -130,20 +122,19 @@ ConcreteParasitic::findPoleResidue(const Pin *) const void ConcreteParasitic::setPoleResidue(const Pin *, - ComplexFloatSeq *, - ComplexFloatSeq *) + ComplexFloatSeq *, + ComplexFloatSeq *) { } //////////////////////////////////////////////////////////////// ConcretePi::ConcretePi(float c2, - float rpi, - float c1) : + float rpi, + float c1) : c2_(c2), rpi_(rpi), - c1_(c1), - is_reduced_(false) + c1_(c1) { } @@ -155,8 +146,8 @@ ConcretePi::capacitance() const void ConcretePi::setPiModel(float c2, - float rpi, - float c1) + float rpi, + float c1) { c2_ = c2; rpi_ = rpi; @@ -165,8 +156,8 @@ ConcretePi::setPiModel(float c2, void ConcretePi::piModel(float &c2, - float &rpi, - float &c1) const + float &rpi, + float &c1) const { c2 = c2_; rpi = rpi_; @@ -182,8 +173,8 @@ ConcretePi::setIsReduced(bool reduced) //////////////////////////////////////////////////////////////// ConcretePiElmore::ConcretePiElmore(float c2, - float rpi, - float c1) : + float rpi, + float c1) : ConcretePi(c2, rpi, c1) { } @@ -196,16 +187,16 @@ ConcretePiElmore::capacitance() const void ConcretePiElmore::piModel(float &c2, - float &rpi, - float &c1) const + float &rpi, + float &c1) const { ConcretePi::piModel(c2, rpi, c1); } void ConcretePiElmore::setPiModel(float c2, - float rpi, - float c1) + float rpi, + float c1) { ConcretePi::setPiModel(c2, rpi, c1); } @@ -224,8 +215,8 @@ ConcretePiElmore::setIsReduced(bool reduced) void ConcretePiElmore::findElmore(const Pin *load_pin, - float &elmore, - bool &exists) const + float &elmore, + bool &exists) const { auto itr = loads_.find(load_pin); if (itr == loads_.end()) @@ -238,7 +229,7 @@ ConcretePiElmore::findElmore(const Pin *load_pin, void ConcretePiElmore::setElmore(const Pin *load_pin, - float elmore) + float elmore) { loads_[load_pin] = elmore; } @@ -261,13 +252,6 @@ ConcretePiElmore::unannotatedLoads(const Pin *drvr_pin, //////////////////////////////////////////////////////////////// -ConcretePoleResidue:: -ConcretePoleResidue() : - poles_(nullptr), - residues_(nullptr) -{ -} - ConcretePoleResidue::~ConcretePoleResidue() { delete poles_; @@ -282,8 +266,8 @@ ConcretePoleResidue::poleResidueCount() const void ConcretePoleResidue::poleResidue(int index, - ComplexFloat &pole, - ComplexFloat &residue) const + ComplexFloat &pole, + ComplexFloat &residue) const { pole = (*poles_)[index]; residue = (*residues_)[index]; @@ -291,7 +275,7 @@ ConcretePoleResidue::poleResidue(int index, void ConcretePoleResidue::setPoleResidue(ComplexFloatSeq *poles, - ComplexFloatSeq *residues) + ComplexFloatSeq *residues) { poles_ = poles; residues_ = residues; @@ -307,8 +291,8 @@ ConcretePoleResidue::unannotatedLoads(const Pin *, //////////////////////////////////////////////////////////////// ConcretePiPoleResidue::ConcretePiPoleResidue(float c2, - float rpi, - float c1) : + float rpi, + float c1) : ConcretePi(c2, rpi, c1) { } @@ -321,16 +305,16 @@ ConcretePiPoleResidue::capacitance() const void ConcretePiPoleResidue::piModel(float &c2, - float &rpi, - float &c1) const + float &rpi, + float &c1) const { ConcretePi::piModel(c2, rpi, c1); } void ConcretePiPoleResidue::setPiModel(float c2, - float rpi, - float c1) + float rpi, + float c1) { ConcretePi::setPiModel(c2, rpi, c1); } @@ -359,8 +343,8 @@ ConcretePiPoleResidue::findPoleResidue(const Pin *load_pin) const void ConcretePiPoleResidue::setPoleResidue(const Pin *load_pin, - ComplexFloatSeq *poles, - ComplexFloatSeq *residues) + ComplexFloatSeq *poles, + ComplexFloatSeq *residues) { ConcretePoleResidue &pole_residue = load_pole_residue_[load_pin]; pole_residue.setPoleResidue(poles, residues); @@ -385,7 +369,7 @@ ConcretePiPoleResidue::unannotatedLoads(const Pin *drvr_pin, //////////////////////////////////////////////////////////////// ConcreteParasiticNode::ConcreteParasiticNode(const Net *net, - int id, + uint32_t id, bool is_external) : is_net_(true), is_external_(is_external), @@ -411,12 +395,14 @@ ConcreteParasiticNode::incrCapacitance(float cap) cap_ += cap; } -const char * +std::string ConcreteParasiticNode::name(const Network *network) const { if (is_net_) { - const char *net_name = network->pathName(net_pin_.net_); - return stringPrintTmp("%s:%d", net_name, id_); + std::string name = std::string(network->pathName(net_pin_.net_)) + + ':' + + std::to_string(id_); + return name; } else return network->pathName(net_pin_.pin_); @@ -452,10 +438,10 @@ ConcreteParasiticNode::net(const Network *network) const //////////////////////////////////////////////////////////////// -ConcreteParasiticDevice::ConcreteParasiticDevice(size_t id, - float value, - ConcreteParasiticNode *node1, - ConcreteParasiticNode *node2) : +ConcreteParasiticDevice::ConcreteParasiticDevice(uint32_t id, + float value, + ConcreteParasiticNode *node1, + ConcreteParasiticNode *node2) : id_(id), value_(value), node1_(node1), @@ -473,7 +459,7 @@ ConcreteParasiticDevice::replaceNode(ConcreteParasiticNode *from_node, node2_ = to_node; } -ConcreteParasiticResistor::ConcreteParasiticResistor(size_t id, +ConcreteParasiticResistor::ConcreteParasiticResistor(uint32_t id, float value, ConcreteParasiticNode *node1, ConcreteParasiticNode *node2) : @@ -481,7 +467,7 @@ ConcreteParasiticResistor::ConcreteParasiticResistor(size_t id, { } -ConcreteParasiticCapacitor::ConcreteParasiticCapacitor(size_t id, +ConcreteParasiticCapacitor::ConcreteParasiticCapacitor(uint32_t id, float value, ConcreteParasiticNode *node1, ConcreteParasiticNode *node2) : @@ -497,11 +483,20 @@ ConcreteParasiticNetwork::ConcreteParasiticNetwork(const Net *net, net_(net), sub_nodes_(network), pin_nodes_(network), - max_node_id_(0), includes_pin_caps_(includes_pin_caps) { } +ConcreteParasiticNetwork::ConcreteParasiticNetwork(ConcreteParasiticNetwork &¶sitic) + noexcept : + net_(parasitic.net_), + sub_nodes_(std::move(parasitic.sub_nodes_)), + pin_nodes_(std::move(parasitic.pin_nodes_)), + max_node_id_(parasitic.max_node_id_), + includes_pin_caps_(parasitic.includes_pin_caps_) +{ +} + ConcreteParasiticNetwork::~ConcreteParasiticNetwork() { deleteDevices(); @@ -580,7 +575,7 @@ ConcreteParasiticNetwork::capacitance() const ConcreteParasiticNode * ConcreteParasiticNetwork::findParasiticNode(const Net *net, - int id, + uint32_t id, const Network *) const { NetIdPair net_id(net, id); @@ -603,7 +598,7 @@ ConcreteParasiticNetwork::findParasiticNode(const Pin *pin) const ConcreteParasiticNode * ConcreteParasiticNetwork::ensureParasiticNode(const Net *net, - int id, + uint32_t id, const Network *network) { ConcreteParasiticNode *node; @@ -614,7 +609,7 @@ ConcreteParasiticNetwork::ensureParasiticNode(const Net *net, node = new ConcreteParasiticNode(net, id, network->highestNetAbove(net1) != net_); sub_nodes_[net_id] = node; if (net == net_) - max_node_id_ = max((int) max_node_id_, id); + max_node_id_ = std::max(max_node_id_, id); } else node = id_node->second; @@ -679,12 +674,12 @@ ConcreteParasiticNetwork::unannotatedLoads(ParasiticNode *node, visited_nodes.insert(node); ParasiticResistorSeq &resistors = resistor_map[node]; for (ParasiticResistor *resistor : resistors) { - if (loop_resistors.find(resistor) == loop_resistors.end()) { + if (!loop_resistors.contains(resistor)) { ParasiticNode *onode = parasitics->otherNode(resistor, node); // One commercial extractor creates resistors with identical from/to nodes. if (onode != node - && resistor != from_res) { - if (visited_nodes.find(onode) == visited_nodes.end()) + && resistor != from_res) { + if (!visited_nodes.contains(onode)) unannotatedLoads(onode, resistor, loads, visited_nodes, loop_resistors, resistor_map, parasitics); else @@ -700,7 +695,7 @@ ConcreteParasiticNetwork::unannotatedLoads(ParasiticNode *node, void ConcreteParasiticNetwork::disconnectPin(const Pin *pin, - const Net *net, + const Net *net, const Network *network) { auto pin_node = pin_nodes_.find(pin); @@ -737,31 +732,27 @@ bool NetIdPairLess::operator()(const NetIdPair &net_id1, const NetIdPair &net_id2) const { - const Net *net1 = net_id1.first; - const Net *net2 = net_id2.first; - int id1 = net_id1.second; - int id2 = net_id2.second; + const auto& [net1, id1] = net_id1; + const auto& [net2, id2] = net_id2; return net_less_(net1, net2) || (net1 == net2 - && id1 < id2); + && id1 < id2); } //////////////////////////////////////////////////////////////// -Parasitics * -makeConcreteParasitics(StaState *sta) -{ - return new ConcreteParasitics(sta); -} - -ConcreteParasitics::ConcreteParasitics(StaState *sta) : - Parasitics(sta) +ConcreteParasitics::ConcreteParasitics(std::string_view name, + std::string_view filename, + StaState *sta) : + Parasitics(sta), + name_(name), + filename_(filename) { } ConcreteParasitics::~ConcreteParasitics() { - clear(); + deleteParasiticsImpl(); } bool @@ -777,78 +768,44 @@ ConcreteParasitics::clear() deleteParasitics(); } -int -ConcreteParasitics::parasiticAnalysisPtIndex(const ParasiticAnalysisPt *ap, - const RiseFall *rf) const +void +ConcreteParasitics::deleteParasitics() { - return ap->index() * RiseFall::index_count + rf->index(); + deleteParasiticsImpl(); } void -ConcreteParasitics::deleteParasitics() +ConcreteParasitics::deleteParasiticsImpl() { - int ap_count = corners_->parasiticAnalysisPtCount(); - int ap_rf_count = ap_count * RiseFall::index_count; - for (const auto [drvr, parasitics] : drvr_parasitic_map_) { - if (parasitics) { - for (int i = 0; i < ap_rf_count; i++) - delete parasitics[i]; - delete [] parasitics; - } + for (auto &[drvr, parasitics] : drvr_parasitic_map_) { + for (size_t i = 0; i < min_max_rise_fall_count; i++) + delete parasitics[i]; } drvr_parasitic_map_.clear(); - for (const auto [net, parasitics] : parasitic_network_map_) { - if (parasitics) { - for (int i = 0; i < ap_count; i++) - delete parasitics[i]; - delete [] parasitics; - } - } parasitic_network_map_.clear(); } -void -ConcreteParasitics::deleteParasitics(const Pin *drvr_pin, - const ParasiticAnalysisPt *ap) -{ - ConcreteParasitic **parasitics = drvr_parasitic_map_[drvr_pin]; - if (parasitics) { - for (auto rf : RiseFall::range()) { - int ap_rf_index = parasiticAnalysisPtIndex(ap, rf); - delete parasitics[ap_rf_index]; - parasitics[ap_rf_index] = nullptr; - } - } -} - void ConcreteParasitics::deleteParasitics(const Pin *drvr_pin) { - ConcreteParasitic **parasitics = drvr_parasitic_map_[drvr_pin]; - if (parasitics) { - int ap_count = corners_->parasiticAnalysisPtCount(); - int ap_rf_count = ap_count * RiseFall::index_count; - for (int i = 0; i < ap_rf_count; i++) { + auto itr = drvr_parasitic_map_.find(drvr_pin); + if (itr != drvr_parasitic_map_.end()) { + const MinMaxRiseFallParasitics ¶sitics = itr->second; + for (size_t i = 0; i < min_max_rise_fall_count; i++) delete parasitics[i]; - parasitics[i] = nullptr; - } + drvr_parasitic_map_.erase(itr); } } void -ConcreteParasitics::deleteParasitics(const Net *net, - const ParasiticAnalysisPt *ap) +ConcreteParasitics::deleteParasitics(const Net *net) { PinSet *drivers = network_->drivers(net); for (auto drvr_pin : *drivers) - deleteParasitics(drvr_pin, ap); + deleteParasitics(drvr_pin); - ConcreteParasiticNetwork **parasitics = parasitic_network_map_[net]; - if (parasitics) { - delete parasitics[ap->index()]; - parasitics[ap->index()] = nullptr; - } + parasitic_network_map_.erase(net); } float @@ -867,30 +824,24 @@ ConcreteParasitics::isReducedParasiticNetwork(const Parasitic *parasitic) const void ConcreteParasitics::setIsReducedParasiticNetwork(Parasitic *parasitic, - bool is_reduced) + bool is_reduced) { ConcreteParasitic *cparasitic = static_cast(parasitic); cparasitic->setIsReduced(is_reduced); } void -ConcreteParasitics::disconnectPinBefore(const Pin *pin, - const Network *network) +ConcreteParasitics::disconnectPinBefore(const Pin *pin) { if (haveParasitics()) { deleteReducedParasitics(pin); const Net *net = findParasiticNet(pin); if (net) { - ConcreteParasiticNetwork **parasitics = parasitic_network_map_[net]; - if (parasitics) { - int ap_count = corners_->parasiticAnalysisPtCount(); - for (int i = 0; i < ap_count; i++) { - ConcreteParasiticNetwork *parasitic = parasitics[i]; - if (parasitic) - parasitic->disconnectPin(pin, net, network); - } - } + ConcreteParasiticNetwork *parasitic = + static_cast(findParasiticNetwork(pin)); + if (parasitic) + parasitic->disconnectPin(pin, net, network_); } } } @@ -899,7 +850,7 @@ void ConcreteParasitics::deletePinBefore(const Pin *pin) { // Actions are the same. - disconnectPinBefore(pin, network_); + disconnectPinBefore(pin); } void @@ -910,14 +861,13 @@ ConcreteParasitics::loadPinCapacitanceChanged(const Pin *pin) } void -ConcreteParasitics::deleteReducedParasitics(const Net *net, - const ParasiticAnalysisPt *ap) +ConcreteParasitics::deleteReducedParasitics(const Net *net) { if (!drvr_parasitic_map_.empty()) { PinSet *drivers = network_->drivers(net); if (drivers) { for (auto drvr_pin : *drivers) - deleteDrvrReducedParasitics(drvr_pin, ap); + deleteDrvrReducedParasitics(drvr_pin); } } } @@ -930,7 +880,7 @@ ConcreteParasitics::deleteReducedParasitics(const Pin *pin) PinSet *drivers = network_->drivers(pin); if (drivers) { for (auto drvr_pin : *drivers) - deleteDrvrReducedParasitics(drvr_pin); + deleteDrvrReducedParasitics(drvr_pin); } } } @@ -938,29 +888,7 @@ ConcreteParasitics::deleteReducedParasitics(const Pin *pin) void ConcreteParasitics::deleteDrvrReducedParasitics(const Pin *drvr_pin) { - LockGuard lock(lock_); - ConcreteParasitic **parasitics = drvr_parasitic_map_[drvr_pin]; - if (parasitics) { - int ap_count = corners_->parasiticAnalysisPtCount(); - int ap_rf_count = ap_count * RiseFall::index_count; - for (int i = 0; i < ap_rf_count; i++) - delete parasitics[i]; - delete [] parasitics; - } - drvr_parasitic_map_[drvr_pin] = nullptr; -} - -void -ConcreteParasitics::deleteDrvrReducedParasitics(const Pin *drvr_pin, - const ParasiticAnalysisPt *ap) -{ - LockGuard lock(lock_); - ConcreteParasitic **parasitics = drvr_parasitic_map_[drvr_pin]; - if (parasitics) { - int ap_index = ap->index(); - delete parasitics[ap_index]; - parasitics[ap_index] = nullptr; - } + deleteParasitics(drvr_pin); } //////////////////////////////////////////////////////////////// @@ -972,59 +900,61 @@ ConcreteParasitics::isPiElmore(const Parasitic *parasitic) const return cparasitic && cparasitic->isPiElmore(); } +size_t +minMaxRiseFallIndex(const MinMax *min_max, + const RiseFall *rf) +{ + return min_max->index() * RiseFall::index_count + rf->index(); +} + Parasitic * ConcreteParasitics::findPiElmore(const Pin *drvr_pin, - const RiseFall *rf, - const ParasiticAnalysisPt *ap) const + const RiseFall *rf, + const MinMax *min_max) const { LockGuard lock(lock_); - if (!drvr_parasitic_map_.empty()) { - int ap_rf_index = parasiticAnalysisPtIndex(ap, rf); - ConcreteParasitic **parasitics = drvr_parasitic_map_.findKey(drvr_pin); - if (parasitics) { - ConcreteParasitic *parasitic = parasitics[ap_rf_index]; - if (parasitic && parasitic->isPiElmore()) - return parasitic; - } + auto itr = drvr_parasitic_map_.find(drvr_pin); + if (itr != drvr_parasitic_map_.end()) { + const MinMaxRiseFallParasitics ¶sitics = itr->second; + ConcreteParasitic *parasitic = parasitics[minMaxRiseFallIndex(min_max, rf)]; + if (parasitic && parasitic->isPiElmore()) + return parasitic; } return nullptr; } Parasitic * ConcreteParasitics::makePiElmore(const Pin *drvr_pin, - const RiseFall *rf, - const ParasiticAnalysisPt *ap, - float c2, - float rpi, - float c1) + const RiseFall *rf, + const MinMax *min_max, + float c2, + float rpi, + float c1) { LockGuard lock(lock_); - ConcreteParasitic **parasitics = drvr_parasitic_map_.findKey(drvr_pin); - if (parasitics == nullptr) { - int ap_count = corners_->parasiticAnalysisPtCount(); - int ap_rf_count = ap_count * RiseFall::index_count; - parasitics = new ConcreteParasitic*[ap_rf_count]; - for (int i = 0; i < ap_rf_count; i++) - parasitics[i] = nullptr; - drvr_parasitic_map_[drvr_pin] = parasitics; - } - int ap_rf_index = parasiticAnalysisPtIndex(ap, rf); - ConcreteParasitic *parasitic = parasitics[ap_rf_index]; + auto itr = drvr_parasitic_map_.find(drvr_pin); ConcretePiElmore *pi_elmore = nullptr; - if (parasitic) { - if (parasitic->isPiElmore()) { + size_t mm_rf_index = minMaxRiseFallIndex(min_max, rf); + if (itr != drvr_parasitic_map_.end()) { + MinMaxRiseFallParasitics ¶sitics = itr->second; + ConcreteParasitic *parasitic = parasitics[mm_rf_index]; + if (parasitic && parasitic->isPiElmore()) { pi_elmore = dynamic_cast(parasitic); pi_elmore->setPiModel(c2, rpi, c1); + pi_elmore->loads().clear(); } else { delete parasitic; pi_elmore = new ConcretePiElmore(c2, rpi, c1); - parasitics[ap_rf_index] = pi_elmore; + parasitics[mm_rf_index] = pi_elmore; } } else { + MinMaxRiseFallParasitics ¶sitics = drvr_parasitic_map_[drvr_pin]; + for (size_t i = 0; i < min_max_rise_fall_count; i++) + parasitics[i] = nullptr; pi_elmore = new ConcretePiElmore(c2, rpi, c1); - parasitics[ap_rf_index] = pi_elmore; + parasitics[mm_rf_index] = pi_elmore; } return pi_elmore; } @@ -1040,31 +970,37 @@ ConcreteParasitics::isPiModel(const Parasitic *parasitic) const void ConcreteParasitics::piModel(const Parasitic *parasitic, - float &c2, - float &rpi, - float &c1) const + float &c2, + float &rpi, + float &c1) const { const ConcreteParasitic *cparasitic = static_cast(parasitic); - cparasitic->piModel(c2, rpi, c1); + if (cparasitic->isPiModel()) + cparasitic->piModel(c2, rpi, c1); + else + criticalError(2700, "piModel called on non-PiElmore parasitic."); } void ConcreteParasitics::setPiModel(Parasitic *parasitic, - float c2, - float rpi, - float c1) + float c2, + float rpi, + float c1) { ConcreteParasitic *cparasitic = static_cast(parasitic); - cparasitic->setPiModel(c2, rpi, c1); + if (cparasitic->isPiModel()) + cparasitic->setPiModel(c2, rpi, c1); + else + criticalError(2701, "setPiModel called on non-PiElmore parasitic."); } //////////////////////////////////////////////////////////////// void ConcreteParasitics::findElmore(const Parasitic *parasitic, - const Pin *load_pin, - float &elmore, - bool &exists) const + const Pin *load_pin, + float &elmore, + bool &exists) const { const ConcreteParasitic *cparasitic = static_cast(parasitic); cparasitic->findElmore(load_pin, elmore, exists); @@ -1072,8 +1008,8 @@ ConcreteParasitics::findElmore(const Parasitic *parasitic, void ConcreteParasitics::setElmore(Parasitic *parasitic, - const Pin *load_pin, - float elmore) + const Pin *load_pin, + float elmore) { ConcreteParasitic *cparasitic = static_cast(parasitic); cparasitic->setElmore(load_pin, elmore); @@ -1090,68 +1026,60 @@ ConcreteParasitics::isPiPoleResidue(const Parasitic* parasitic) const Parasitic * ConcreteParasitics::findPiPoleResidue(const Pin *drvr_pin, - const RiseFall *rf, - const ParasiticAnalysisPt *ap) const + const RiseFall *rf, + const MinMax *min_max) const { - if (!drvr_parasitic_map_.empty()) { - int ap_rf_index = parasiticAnalysisPtIndex(ap, rf); - LockGuard lock(lock_); - ConcreteParasitic **parasitics = drvr_parasitic_map_.findKey(drvr_pin); - if (parasitics) { - ConcreteParasitic *parasitic = parasitics[ap_rf_index]; - if (parasitic == nullptr && rf == RiseFall::fall()) { - ap_rf_index = parasiticAnalysisPtIndex(ap, RiseFall::rise()); - parasitic = parasitics[ap_rf_index]; - } - if (parasitic && parasitic->isPiPoleResidue()) - return parasitic; - } + LockGuard lock(lock_); + auto itr = drvr_parasitic_map_.find(drvr_pin); + if (itr != drvr_parasitic_map_.end()) { + const MinMaxRiseFallParasitics ¶sitics = itr->second; + size_t mm_rf_index = minMaxRiseFallIndex(min_max, rf); + ConcreteParasitic *parasitic = parasitics[mm_rf_index]; + if (parasitic && parasitic->isPiPoleResidue()) + return parasitic; } return nullptr; } Parasitic * ConcreteParasitics::makePiPoleResidue(const Pin *drvr_pin, - const RiseFall *rf, - const ParasiticAnalysisPt *ap, - float c2, - float rpi, - float c1) + const RiseFall *rf, + const MinMax *min_max, + float c2, + float rpi, + float c1) { LockGuard lock(lock_); - ConcreteParasitic **parasitics = drvr_parasitic_map_.findKey(drvr_pin); - if (parasitics == nullptr) { - int ap_count = corners_->parasiticAnalysisPtCount(); - int ap_rf_count = ap_count * RiseFall::index_count; - parasitics = new ConcreteParasitic*[ap_rf_count]; - for (int i = 0; i < ap_rf_count; i++) - parasitics[i] = nullptr; - drvr_parasitic_map_[drvr_pin] = parasitics; - } - int ap_rf_index = parasiticAnalysisPtIndex(ap, rf); - ConcreteParasitic *parasitic = parasitics[ap_rf_index]; + auto itr = drvr_parasitic_map_.find(drvr_pin); ConcretePiPoleResidue *pi_pole_residue = nullptr; - if (parasitic) { - if (parasitic->isPiElmore()) { + size_t mm_rf_index = minMaxRiseFallIndex(min_max, rf); + if (itr != drvr_parasitic_map_.end()) { + MinMaxRiseFallParasitics ¶sitics = itr->second; + ConcreteParasitic *parasitic = parasitics[mm_rf_index]; + if (parasitic && parasitic->isPoleResidue()) { pi_pole_residue = dynamic_cast(parasitic); pi_pole_residue->setPiModel(c2, rpi, c1); + pi_pole_residue->loadResidues().clear(); } else { delete parasitic; pi_pole_residue = new ConcretePiPoleResidue(c2, rpi, c1); - parasitics[ap_rf_index] = pi_pole_residue; + parasitics[mm_rf_index] = pi_pole_residue; } } else { + MinMaxRiseFallParasitics ¶sitics = drvr_parasitic_map_[drvr_pin]; + for (size_t i = 0; i < min_max_rise_fall_count; i++) + parasitics[i] = nullptr; pi_pole_residue = new ConcretePiPoleResidue(c2, rpi, c1); - parasitics[ap_rf_index] = pi_pole_residue; + parasitics[mm_rf_index] = pi_pole_residue; } return pi_pole_residue; } Parasitic * ConcreteParasitics::findPoleResidue(const Parasitic *parasitic, - const Pin *load_pin) const + const Pin *load_pin) const { const ConcreteParasitic *cparasitic = static_cast(parasitic); @@ -1160,9 +1088,9 @@ ConcreteParasitics::findPoleResidue(const Parasitic *parasitic, void ConcreteParasitics::setPoleResidue(Parasitic *parasitic, - const Pin *load_pin, - ComplexFloatSeq *poles, - ComplexFloatSeq *residues) + const Pin *load_pin, + ComplexFloatSeq *poles, + ComplexFloatSeq *residues) { ConcreteParasitic *cparasitic = static_cast(parasitic); @@ -1189,9 +1117,9 @@ ConcreteParasitics::poleResidueCount(const Parasitic *parasitic) const void ConcreteParasitics::poleResidue(const Parasitic *parasitic, - int pole_index, - ComplexFloat &pole, - ComplexFloat &residue) const + int pole_index, + ComplexFloat &pole, + ComplexFloat &residue) const { const ConcretePoleResidue *pr_parasitic = static_cast(parasitic); @@ -1208,39 +1136,29 @@ ConcreteParasitics::isParasiticNetwork(const Parasitic *parasitic) const } Parasitic * -ConcreteParasitics::findParasiticNetwork(const Net *net, - const ParasiticAnalysisPt *ap) const +ConcreteParasitics::findParasiticNetwork(const Net *net) { - if (!parasitic_network_map_.empty()) { - LockGuard lock(lock_); - if (!parasitic_network_map_.empty()) { - ConcreteParasiticNetwork **parasitics=parasitic_network_map_.findKey(net); - if (parasitics) { - ConcreteParasiticNetwork *parasitic = parasitics[ap->index()]; - if (parasitic == nullptr) - parasitic = parasitics[ap->indexMax()]; - return parasitic; - } - } - } - return nullptr; + LockGuard lock(lock_); + auto itr = parasitic_network_map_.find(net); + if (itr != parasitic_network_map_.end()) + return &itr->second; + else + return nullptr; } Parasitic * -ConcreteParasitics::findParasiticNetwork(const Pin *pin, - const ParasiticAnalysisPt *ap) const +ConcreteParasitics::findParasiticNetwork(const Pin *pin) { if (!parasitic_network_map_.empty()) { LockGuard lock(lock_); if (!parasitic_network_map_.empty()) { // Only call findParasiticNet if parasitics exist. const Net *net = findParasiticNet(pin); - ConcreteParasiticNetwork **parasitics=parasitic_network_map_.findKey(net); - if (parasitics) { - ConcreteParasiticNetwork *parasitic = parasitics[ap->index()]; - if (parasitic == nullptr) - parasitic = parasitics[ap->indexMax()]; - return parasitic; + + if (!parasitic_network_map_.empty()) { + auto itr = parasitic_network_map_.find(net); + if (itr != parasitic_network_map_.end()) + return &itr->second; } } } @@ -1249,74 +1167,25 @@ ConcreteParasitics::findParasiticNetwork(const Pin *pin, Parasitic * ConcreteParasitics::makeParasiticNetwork(const Net *net, - bool includes_pin_caps, - const ParasiticAnalysisPt *ap) + bool includes_pin_caps) { LockGuard lock(lock_); - ConcreteParasiticNetwork **parasitics = parasitic_network_map_.findKey(net); - if (parasitics == nullptr) { - int ap_count = corners_->parasiticAnalysisPtCount(); - parasitics = new ConcreteParasiticNetwork*[ap_count]; - for (int i = 0; i < ap_count; i++) - parasitics[i] = nullptr; - parasitic_network_map_[net] = parasitics; + auto itr = parasitic_network_map_.find(net); + if (itr != parasitic_network_map_.end()) { + parasitic_network_map_.erase(itr); + for (const Pin *drvr_pin : *network_->drivers(net)) + deleteParasitics(drvr_pin); } - int ap_index = ap->index(); - ConcreteParasiticNetwork *parasitic = parasitics[ap_index]; - if (parasitic) { - delete parasitic; - if (net) { - for (const Pin *drvr_pin : *network_->drivers(net)) - deleteParasitics(drvr_pin); - } - } - parasitic = new ConcreteParasiticNetwork(net, includes_pin_caps, network_); - parasitics[ap_index] = parasitic; - return parasitic; + parasitic_network_map_.emplace(net, ConcreteParasiticNetwork(net, includes_pin_caps, + network_)); + return ¶sitic_network_map_.find(net)->second; } void -ConcreteParasitics::deleteParasiticNetwork(const Net *net, - const ParasiticAnalysisPt *ap) +ConcreteParasitics::deleteParasiticNetwork(const Net *net) { - if (!parasitic_network_map_.empty()) { - LockGuard lock(lock_); - ConcreteParasiticNetwork **parasitics = parasitic_network_map_.findKey(net); - if (parasitics) { - int ap_index = ap->index(); - delete parasitics[ap_index]; - parasitics[ap_index] = nullptr; - - int ap_count = corners_->parasiticAnalysisPtCount(); - bool have_parasitics = false; - for (int i = 0; i < ap_count; i++) { - if (parasitics[i]) { - have_parasitics = true; - break; - } - } - if (!have_parasitics) { - delete [] parasitics; - parasitic_network_map_.erase(net); - } - } - } -} - -void -ConcreteParasitics::deleteParasiticNetworks(const Net *net) -{ - if (!parasitic_network_map_.empty()) { - LockGuard lock(lock_); - ConcreteParasiticNetwork **parasitics = parasitic_network_map_.findKey(net); - if (parasitics) { - int ap_count = corners_->parasiticAnalysisPtCount(); - for (int i = 0; i < ap_count; i++) - delete parasitics[i]; - delete [] parasitics; - parasitic_network_map_.erase(net); - } - } + LockGuard lock(lock_); + parasitic_network_map_.erase(net); } const Net * @@ -1341,7 +1210,7 @@ ConcreteParasitics::includesPinCaps(const Parasitic *parasitic) const ParasiticNode * ConcreteParasitics::findParasiticNode(Parasitic *parasitic, const Net *net, - int id, + uint32_t id, const Network *network) const { const ConcreteParasiticNetwork *cparasitic = @@ -1351,8 +1220,8 @@ ConcreteParasitics::findParasiticNode(Parasitic *parasitic, ParasiticNode * ConcreteParasitics::ensureParasiticNode(Parasitic *parasitic, - const Net *net, - int id, + const Net *net, + uint32_t id, const Network *network) { ConcreteParasiticNetwork *cparasitic = @@ -1371,7 +1240,7 @@ ConcreteParasitics::findParasiticNode(const Parasitic *parasitic, ParasiticNode * ConcreteParasitics::ensureParasiticNode(Parasitic *parasitic, - const Pin *pin, + const Pin *pin, const Network *network) { ConcreteParasiticNetwork *cparasitic = @@ -1381,7 +1250,7 @@ ConcreteParasitics::ensureParasiticNode(Parasitic *parasitic, void ConcreteParasitics::incrCap(ParasiticNode *node, - float cap) + float cap) { ConcreteParasiticNode *cnode = static_cast(node); cnode->incrCapacitance(cap); @@ -1389,7 +1258,7 @@ ConcreteParasitics::incrCap(ParasiticNode *node, void ConcreteParasitics::makeCapacitor(Parasitic *parasitic, - size_t index, + uint32_t id, float cap, ParasiticNode *node1, ParasiticNode *node2) @@ -1397,7 +1266,7 @@ ConcreteParasitics::makeCapacitor(Parasitic *parasitic, ConcreteParasiticNode *cnode1 = static_cast(node1); ConcreteParasiticNode *cnode2 = static_cast(node2); ConcreteParasiticCapacitor *capacitor = - new ConcreteParasiticCapacitor(index, cap, cnode1, cnode2); + new ConcreteParasiticCapacitor(id, cap, cnode1, cnode2); ConcreteParasiticNetwork *cparasitic = static_cast(parasitic); cparasitic->addCapacitor(capacitor); @@ -1405,14 +1274,14 @@ ConcreteParasitics::makeCapacitor(Parasitic *parasitic, void ConcreteParasitics::makeResistor(Parasitic *parasitic, - size_t index, + uint32_t id, float res, - ParasiticNode *node1, - ParasiticNode *node2) + ParasiticNode *node1, + ParasiticNode *node2) { ConcreteParasiticNode *cnode1 = static_cast(node1); ConcreteParasiticNode *cnode2 = static_cast(node2); - ParasiticResistor *resistor = new ConcreteParasiticResistor(index, res, + ParasiticResistor *resistor = new ConcreteParasiticResistor(id, res, cnode1, cnode2); ConcreteParasiticNetwork *cparasitic = static_cast(parasitic); @@ -1444,7 +1313,7 @@ ConcreteParasitics::capacitors(const Parasitic *parasitic) const } -const char * +std::string ConcreteParasitics::name(const ParasiticNode *node) const { const ConcreteParasiticNode *cnode = @@ -1495,7 +1364,7 @@ ConcreteParasitics::isExternal(const ParasiticNode *node) const //////////////////////////////////////////////////////////////// -size_t +uint32_t ConcreteParasitics::id(const ParasiticResistor *resistor) const { const ConcreteParasiticResistor *cresistor = @@ -1529,7 +1398,7 @@ ConcreteParasitics::node2(const ParasiticResistor *resistor) const //////////////////////////////////////////////////////////////// -size_t +uint32_t ConcreteParasitics::id(const ParasiticCapacitor *capacitor) const { const ConcreteParasiticCapacitor *ccapacitor = @@ -1571,4 +1440,4 @@ ConcreteParasitics::unannotatedLoads(const Parasitic *parasitic, return cparasitic->unannotatedLoads(drvr_pin, this); } -} // namespace +} // namespace sta diff --git a/parasitics/ConcreteParasitics.hh b/parasitics/ConcreteParasitics.hh index 9a06058d6..936609c17 100644 --- a/parasitics/ConcreteParasitics.hh +++ b/parasitics/ConcreteParasitics.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,10 +24,11 @@ #pragma once +#include +#include #include +#include -#include "Map.hh" -#include "Set.hh" #include "MinMax.hh" #include "Parasitics.hh" @@ -36,24 +37,31 @@ namespace sta { class ConcreteParasitic; class ConcreteParasiticNetwork; -typedef Map ConcreteParasiticMap; -typedef Map ConcreteParasiticNetworkMap; +constexpr size_t min_max_rise_fall_count = MinMax::index_count * RiseFall::index_count; +// Min/maxrise/fall reduced parasitics for each driver pin. +// When min parastitic network != max parasitic network only +// the min values are populated for the min parasitics and +// max values for max parasitics. +using MinMaxRiseFallParasitics = std::array; +using ConcreteParasiticMap = std::map; +using ConcreteParasiticNetworkMap = std::map; // This class acts as a BUILDER for parasitics. class ConcreteParasitics : public Parasitics { public: - ConcreteParasitics(StaState *sta); - virtual ~ConcreteParasitics(); + ~ConcreteParasitics() override; + ConcreteParasitics(std::string_view name, + std::string_view filename, + StaState *sta); + const std::string &name() const override { return name_; }; + const std::string &filename() const override { return filename_; }; bool haveParasitics() override; void clear() override; void deleteParasitics() override; - void deleteParasitics(const Net *net, - const ParasiticAnalysisPt *ap) override; - void deleteParasitics(const Pin *drvr_pin, - const ParasiticAnalysisPt *ap) override; - void deleteParasitics(const Pin *drvr_pin); + void deleteParasitics(const Net *net) override; + void deleteParasitics(const Pin *drvr_pin) override; bool isReducedParasiticNetwork(const Parasitic *parasitic) const override; void setIsReducedParasiticNetwork(Parasitic *parasitic, @@ -64,10 +72,10 @@ public: bool isPiElmore(const Parasitic *parasitic) const override; Parasitic *findPiElmore(const Pin *drvr_pin, const RiseFall *rf, - const ParasiticAnalysisPt *ap) const override; + const MinMax *min_max) const override; Parasitic *makePiElmore(const Pin *drvr_pin, const RiseFall *rf, - const ParasiticAnalysisPt *ap, + const MinMax *min_max, float c2, float rpi, float c1) override; @@ -93,13 +101,15 @@ public: bool isPiPoleResidue(const Parasitic* parasitic) const override; Parasitic *findPiPoleResidue(const Pin *drvr_pin, const RiseFall *rf, - const ParasiticAnalysisPt *ap) const override; + const MinMax *min_max) const override; Parasitic *findPoleResidue(const Parasitic *parasitic, const Pin *load_pin) const override; Parasitic *makePiPoleResidue(const Pin *drvr_pin, const RiseFall *rf, - const ParasiticAnalysisPt *ap, - float c2, float rpi, float c1) override; + const MinMax *min_max, + float c2, + float rpi, + float c1) override; void setPoleResidue(Parasitic *parasitic, const Pin *load_pin, ComplexFloatSeq *poles, ComplexFloatSeq *residues) override; @@ -111,25 +121,20 @@ public: ComplexFloat &residue) const override; bool isParasiticNetwork(const Parasitic *parasitic) const override; - Parasitic *findParasiticNetwork(const Net *net, - const ParasiticAnalysisPt *ap) const override; - Parasitic *findParasiticNetwork(const Pin *pin, - const ParasiticAnalysisPt *ap) const override; + Parasitic *findParasiticNetwork(const Net *net) override; + Parasitic *findParasiticNetwork(const Pin *pin) override; Parasitic *makeParasiticNetwork(const Net *net, - bool includes_pin_caps, - const ParasiticAnalysisPt *ap) override; - void deleteParasiticNetwork(const Net *net, - const ParasiticAnalysisPt *ap) override; - void deleteParasiticNetworks(const Net *net) override; + bool includes_pin_caps) override; + void deleteParasiticNetwork(const Net *net) override; const Net *net(const Parasitic *parasitic) const override; bool includesPinCaps(const Parasitic *parasitic) const override; ParasiticNode *findParasiticNode(Parasitic *parasitic, const Net *net, - int id, + uint32_t id, const Network *network) const override; ParasiticNode *ensureParasiticNode(Parasitic *parasitic, const Net *net, - int id, + uint32_t id, const Network *network) override; ParasiticNode *findParasiticNode(const Parasitic *parasitic, const Pin *pin) const override; @@ -139,7 +144,7 @@ public: ParasiticNodeSeq nodes(const Parasitic *parasitic) const override; void incrCap(ParasiticNode *node, float cap) override; - const char *name(const ParasiticNode *node) const override; + std::string name(const ParasiticNode *node) const override; const Pin *pin(const ParasiticNode *node) const override; const Net *net(const ParasiticNode *node, const Network *network) const override; @@ -149,21 +154,21 @@ public: ParasiticResistorSeq resistors(const Parasitic *parasitic) const override; void makeResistor(Parasitic *parasitic, - size_t id, + uint32_t id, float res, ParasiticNode *node1, ParasiticNode *node2) override; - size_t id(const ParasiticResistor *resistor) const override; + uint32_t id(const ParasiticResistor *resistor) const override; float value(const ParasiticResistor *resistor) const override; ParasiticNode *node1(const ParasiticResistor *resistor) const override; ParasiticNode *node2(const ParasiticResistor *resistor) const override; ParasiticCapacitorSeq capacitors(const Parasitic *parasitic) const override; void makeCapacitor(Parasitic *parasitic, - size_t id, + uint32_t id, float cap, ParasiticNode *node1, ParasiticNode *node2) override; - size_t id(const ParasiticCapacitor *capacitor) const override; + uint32_t id(const ParasiticCapacitor *capacitor) const override; float value(const ParasiticCapacitor *capacitor) const override; ParasiticNode *node1(const ParasiticCapacitor *capacitor) const override; ParasiticNode *node2(const ParasiticCapacitor *capacitor) const override; @@ -171,23 +176,21 @@ public: PinSet unannotatedLoads(const Parasitic *parasitic, const Pin *drvr_pin) const override; - void disconnectPinBefore(const Pin *pin, - const Network *network) override; + void disconnectPinBefore(const Pin *pin) override; void deletePinBefore(const Pin *pin) override; void loadPinCapacitanceChanged(const Pin *pin) override; - void deleteReducedParasitics(const Net *net, - const ParasiticAnalysisPt *ap) override; + void deleteReducedParasitics(const Net *net) override; void deleteDrvrReducedParasitics(const Pin *drvr_pin) override; protected: - int parasiticAnalysisPtIndex(const ParasiticAnalysisPt *ap, - const RiseFall *rf) const; + void deleteParasiticsImpl(); Parasitic *ensureRspf(const Pin *drvr_pin); void makeAnalysisPtAfter(); void deleteReducedParasitics(const Pin *pin); - void deleteDrvrReducedParasitics(const Pin *drvr_pin, - const ParasiticAnalysisPt *ap); + + std::string name_; + std::string filename_; // Driver pin to array of parasitics indexed by analysis pt index // and transition. @@ -200,4 +203,4 @@ protected: friend class ConcreteParasiticNetwork; }; -} // namespace +} // namespace sta diff --git a/parasitics/ConcreteParasiticsPvt.hh b/parasitics/ConcreteParasiticsPvt.hh index 72c44a9c9..8b36ff4ab 100644 --- a/parasitics/ConcreteParasiticsPvt.hh +++ b/parasitics/ConcreteParasiticsPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,6 +26,7 @@ #include #include +#include #include "Parasitics.hh" @@ -35,26 +36,27 @@ class ConcretePoleResidue; class ConcreteParasiticDevice; class ConcreteParasiticNode; -typedef std::pair NetIdPair; +using NetIdPair = std::pair; + class NetIdPairLess { public: NetIdPairLess(const Network *network); bool operator()(const NetIdPair &net_id1, - const NetIdPair &net_id2) const; + const NetIdPair &net_id2) const; private: const NetIdLess net_less_; }; -typedef std::map ConcreteElmoreLoadMap; -typedef std::map ConcretePoleResidueMap; -typedef std::map ConcreteParasiticSubNodeMap; -typedef std::map ConcreteParasiticPinNodeMap; -typedef std::set ParasiticNodeSet; -typedef std::set ParasiticResistorSet; -typedef std::vector ParasiticResistorSeq; +using ConcreteElmoreLoadMap = std::map; +using ConcretePoleResidueMap = std::map; +using ConcreteParasiticSubNodeMap = std::map; +using ConcreteParasiticPinNodeMap = std::map; +using ParasiticNodeSet = std::set; +using ParasiticResistorSet = std::set; +using ParasiticResistorSeq = std::vector; // Empty base class definitions so casts are not required on returned // objects. @@ -68,7 +70,7 @@ class ParasiticNetwork {}; class ConcreteParasitic : public Parasitic { public: - virtual ~ConcreteParasitic(); + virtual ~ConcreteParasitic() = default; virtual float capacitance() const = 0; virtual bool isPiElmore() const; virtual bool isPiModel() const; @@ -76,22 +78,22 @@ public: virtual bool isPoleResidue() const; virtual bool isParasiticNetwork() const; virtual void piModel(float &c2, - float &rpi, - float &c1) const; + float &rpi, + float &c1) const; virtual void setPiModel(float c2, - float rpi, - float c1); + float rpi, + float c1); virtual bool isReducedParasiticNetwork() const; virtual void setIsReduced(bool reduced); virtual void findElmore(const Pin *load_pin, - float &elmore, - bool &exists) const; + float &elmore, + bool &exists) const; virtual void setElmore(const Pin *load_pin, - float elmore); + float elmore); virtual Parasitic *findPoleResidue(const Pin *load_pin) const; virtual void setPoleResidue(const Pin *load_pin, - ComplexFloatSeq *poles, - ComplexFloatSeq *residues); + ComplexFloatSeq *poles, + ComplexFloatSeq *residues); virtual PinSet unannotatedLoads(const Pin *drvr_pin, const Parasitics *parasitics) const = 0; }; @@ -101,15 +103,15 @@ class ConcretePi { public: ConcretePi(float c2, - float rpi, - float c1); + float rpi, + float c1); float capacitance() const; void piModel(float &c2, - float &rpi, - float &c1) const; + float &rpi, + float &c1) const; void setPiModel(float c2, - float rpi, - float c1); + float rpi, + float c1); bool isReducedParasiticNetwork() const { return is_reduced_; } void setIsReduced(bool reduced); @@ -117,17 +119,17 @@ protected: float c2_; float rpi_; float c1_; - bool is_reduced_; + bool is_reduced_{false}; }; // Pi model for a driver pin and the elmore delay to each load. class ConcretePiElmore : public ConcretePi, - public ConcreteParasitic + public ConcreteParasitic { public: ConcretePiElmore(float c2, - float rpi, - float c1); + float rpi, + float c1); bool isPiElmore() const override { return true; } bool isPiModel() const override { return true; } float capacitance() const override; @@ -147,6 +149,7 @@ public: PinSet unannotatedLoads(const Pin *drvr_pin, const Parasitics *parasitics) const override; void deleteLoad(const Pin *load_pin); + ConcreteElmoreLoadMap &loads() { return loads_; } private: ConcreteElmoreLoadMap loads_; @@ -155,9 +158,8 @@ private: class ConcretePoleResidue : public ConcreteParasitic { public: - ConcretePoleResidue(); - virtual ~ConcretePoleResidue(); - virtual bool isPoleResidue() const override { return true; } + ~ConcretePoleResidue() override; + bool isPoleResidue() const override { return true; } float capacitance() const override { return 0.0; } PinSet unannotatedLoads(const Pin *drvr_pin, const Parasitics *parasitics) const override; @@ -171,72 +173,74 @@ public: using ConcreteParasitic::setPoleResidue; private: - ComplexFloatSeq *poles_; - ComplexFloatSeq *residues_; + ComplexFloatSeq *poles_{nullptr}; + ComplexFloatSeq *residues_{nullptr}; }; // Pi model for a driver pin and the pole/residues to each load. class ConcretePiPoleResidue : public ConcretePi, - public ConcreteParasitic + public ConcreteParasitic { public: ConcretePiPoleResidue(float c2, - float rpi, - float c1); - virtual bool isPiPoleResidue() const override { return true; } - virtual bool isPiModel() const override { return true; } - virtual float capacitance() const override; - virtual void piModel(float &c2, - float &rpi, - float &c1) const override; - virtual void setPiModel(float c2, - float rpi, - float c1) override; - virtual bool isReducedParasiticNetwork() const override; - virtual void setIsReduced(bool reduced) override; - virtual Parasitic *findPoleResidue(const Pin *load_pin) const override; - virtual void setPoleResidue(const Pin *load_pin, - ComplexFloatSeq *poles, - ComplexFloatSeq *residues) override; - virtual PinSet unannotatedLoads(const Pin *drvr_pin, - const Parasitics *parasitics) const override; + float rpi, + float c1); + bool isPiPoleResidue() const override { return true; } + bool isPiModel() const override { return true; } + float capacitance() const override; + void piModel(float &c2, + float &rpi, + float &c1) const override; + void setPiModel(float c2, + float rpi, + float c1) override; + bool isReducedParasiticNetwork() const override; + void setIsReduced(bool reduced) override; + Parasitic *findPoleResidue(const Pin *load_pin) const override; + void setPoleResidue(const Pin *load_pin, + ComplexFloatSeq *poles, + ComplexFloatSeq *residues) override; + PinSet unannotatedLoads(const Pin *drvr_pin, + const Parasitics *parasitics) const override; void deleteLoad(const Pin *load_pin); + ConcretePoleResidueMap &loadResidues() { return load_pole_residue_; } private: ConcretePoleResidueMap load_pole_residue_; }; class ConcreteParasiticNetwork : public ParasiticNetwork, - public ConcreteParasitic + public ConcreteParasitic { public: ConcreteParasiticNetwork(const Net *net, bool includes_pin_caps, const Network *network); - virtual ~ConcreteParasiticNetwork(); - virtual bool isParasiticNetwork() const { return true; } + ConcreteParasiticNetwork(ConcreteParasiticNetwork &¶sitic) noexcept; + ~ConcreteParasiticNetwork() override; + bool isParasiticNetwork() const override { return true; } const Net *net() const { return net_; } bool includesPinCaps() const { return includes_pin_caps_; } ConcreteParasiticNode *findParasiticNode(const Net *net, - int id, + uint32_t id, const Network *network) const; ConcreteParasiticNode *ensureParasiticNode(const Net *net, - int id, + uint32_t id, const Network *network); ConcreteParasiticNode *findParasiticNode(const Pin *pin) const; ConcreteParasiticNode *ensureParasiticNode(const Pin *pin, const Network *network); - virtual float capacitance() const; + float capacitance() const override; ParasiticNodeSeq nodes() const; void disconnectPin(const Pin *pin, - const Net *net, + const Net *net, const Network *network); ParasiticResistorSeq resistors() const { return resistors_; } void addResistor(ParasiticResistor *resistor); ParasiticCapacitorSeq capacitors() const { return capacitors_; } void addCapacitor(ParasiticCapacitor *capacitor); - virtual PinSet unannotatedLoads(const Pin *drvr_pin, - const Parasitics *parasitics) const; + PinSet unannotatedLoads(const Pin *drvr_pin, + const Parasitics *parasitics) const override; private: void unannotatedLoads(ParasiticNode *node, @@ -255,7 +259,7 @@ private: ConcreteParasiticPinNodeMap pin_nodes_; ParasiticResistorSeq resistors_; ParasiticCapacitorSeq capacitors_; - unsigned max_node_id_:31; + unsigned max_node_id_:31{0}; bool includes_pin_caps_:1; }; @@ -263,12 +267,12 @@ class ConcreteParasiticNode : public ParasiticNode { public: ConcreteParasiticNode(const Net *net, - int id, + uint32_t id, bool is_external); ConcreteParasiticNode(const Pin *pin, bool is_external); float capacitance() const { return cap_; } - const char *name(const Network *network) const; + std::string name(const Network *network) const; const Net *net(const Network *network) const; unsigned id() const { return id_; } bool isExternal() const { return is_external_; } @@ -293,11 +297,11 @@ protected: class ConcreteParasiticDevice { public: - ConcreteParasiticDevice(size_t id, + ConcreteParasiticDevice(uint32_t id, float value, ConcreteParasiticNode *node1, ConcreteParasiticNode *node2); - int id() const { return id_; } + uint32_t id() const { return id_; } float value() const { return value_; } ConcreteParasiticNode *node1() const { return node1_; } ConcreteParasiticNode *node2() const { return node2_; } @@ -305,7 +309,7 @@ public: ConcreteParasiticNode *to_node); protected: - size_t id_; + uint32_t id_; float value_; ConcreteParasiticNode *node1_; ConcreteParasiticNode *node2_; @@ -315,7 +319,7 @@ class ConcreteParasiticResistor : public ParasiticResistor, public ConcreteParasiticDevice { public: - ConcreteParasiticResistor(size_t id, + ConcreteParasiticResistor(uint32_t id, float value, ConcreteParasiticNode *node1, ConcreteParasiticNode *node2); @@ -325,10 +329,10 @@ class ConcreteParasiticCapacitor : public ParasiticCapacitor, public ConcreteParasiticDevice { public: - ConcreteParasiticCapacitor(size_t id, + ConcreteParasiticCapacitor(uint32_t id, float value, ConcreteParasiticNode *node1, ConcreteParasiticNode *node2); }; -} // namespace +} // namespace sta diff --git a/parasitics/EstimateParasitics.cc b/parasitics/EstimateParasitics.cc index 5f656cfbb..2dfa1c82a 100644 --- a/parasitics/EstimateParasitics.cc +++ b/parasitics/EstimateParasitics.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,12 +24,14 @@ #include "EstimateParasitics.hh" -#include "Wireload.hh" +#include + #include "Liberty.hh" -#include "PortDirection.hh" #include "Network.hh" -#include "Sdc.hh" #include "Parasitics.hh" +#include "PortDirection.hh" +#include "Sdc.hh" +#include "Wireload.hh" namespace sta { @@ -42,20 +44,21 @@ EstimateParasitics::EstimateParasitics(StaState *sta) : // loads when driven by a different pin. void EstimateParasitics::estimatePiElmore(const Pin *drvr_pin, - const RiseFall *rf, - const Wireload *wireload, - float fanout, - float net_pin_cap, - const Corner *corner, - const MinMax *min_max, - float &c2, - float &rpi, - float &c1, - float &elmore_res, - float &elmore_cap, - bool &elmore_use_load_cap) + const RiseFall *rf, + const Wireload *wireload, + float fanout, + float net_pin_cap, + const Scene *scene, + const MinMax *min_max, + float &c2, + float &rpi, + float &c1, + float &elmore_res, + float &elmore_cap, + bool &elmore_use_load_cap) { - const OperatingConditions *op_cond = sdc_->operatingConditions(min_max); + const Sdc *sdc = scene->sdc(); + const OperatingConditions *op_cond = sdc->operatingConditions(min_max); float wireload_cap, wireload_res; wireload->findWireload(fanout, op_cond, wireload_cap, wireload_res); @@ -65,22 +68,22 @@ EstimateParasitics::estimatePiElmore(const Pin *drvr_pin, switch (tree) { case WireloadTree::worst_case: estimatePiElmoreWorst(drvr_pin, wireload_cap, wireload_res, - fanout, net_pin_cap, rf, corner, min_max, - c2, rpi, c1, elmore_res, - elmore_cap, elmore_use_load_cap); + fanout, net_pin_cap, rf, scene, min_max, + c2, rpi, c1, elmore_res, + elmore_cap, elmore_use_load_cap); break; case WireloadTree::balanced: case WireloadTree::unknown: estimatePiElmoreBalanced(drvr_pin, wireload_cap, wireload_res, - fanout, net_pin_cap, rf, corner, min_max, - c2, rpi, c1, elmore_res, - elmore_cap, elmore_use_load_cap); + fanout, net_pin_cap, rf, scene, min_max, + c2, rpi, c1, elmore_res, + elmore_cap, elmore_use_load_cap); break; case WireloadTree::best_case: estimatePiElmoreBest(drvr_pin, wireload_cap, net_pin_cap, - rf, corner, min_max, - c2, rpi, c1, elmore_res, elmore_cap, - elmore_use_load_cap); + rf, scene, min_max, + c2, rpi, c1, elmore_res, elmore_cap, + elmore_use_load_cap); break; } } @@ -88,17 +91,17 @@ EstimateParasitics::estimatePiElmore(const Pin *drvr_pin, // No wire resistance, so load is lumped capacitance. void EstimateParasitics::estimatePiElmoreBest(const Pin *, - float wireload_cap, - float net_pin_cap, - const RiseFall *, - const Corner *, - const MinMax *, - float &c2, - float &rpi, - float &c1, - float &elmore_res, - float &elmore_cap, - bool &elmore_use_load_cap) const + float wireload_cap, + float net_pin_cap, + const RiseFall *, + const Scene *, + const MinMax *, + float &c2, + float &rpi, + float &c1, + float &elmore_res, + float &elmore_cap, + bool &elmore_use_load_cap) const { c2 = wireload_cap + net_pin_cap; rpi = 0.0; @@ -112,22 +115,23 @@ EstimateParasitics::estimatePiElmoreBest(const Pin *, // the resistor. void EstimateParasitics::estimatePiElmoreWorst(const Pin *drvr_pin, - float wireload_cap, - float wireload_res, - float, - float net_pin_cap, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float &c2, - float &rpi, - float &c1, - float &elmore_res, - float &elmore_cap, - bool &elmore_use_load_cap) + float wireload_cap, + float wireload_res, + float, + float net_pin_cap, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + float &c2, + float &rpi, + float &c1, + float &elmore_res, + float &elmore_cap, + bool &elmore_use_load_cap) { + const Sdc *sdc = scene->sdc(); float drvr_pin_cap = 0.0; - drvr_pin_cap = sdc_->pinCapacitance(drvr_pin, rf, corner, min_max); + drvr_pin_cap = sdc->pinCapacitance(drvr_pin, rf, scene, min_max); c2 = drvr_pin_cap; rpi = wireload_res; c1 = net_pin_cap - drvr_pin_cap + wireload_cap; @@ -141,19 +145,19 @@ EstimateParasitics::estimatePiElmoreWorst(const Pin *drvr_pin, // Use O'Brien/Savarino reduction to rspf (pi elmore) model. void EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin, - float wireload_cap, - float wireload_res, - float fanout, - float net_pin_cap, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float &c2, - float &rpi, - float &c1, - float &elmore_res, - float &elmore_cap, - bool &elmore_use_load_cap) + float wireload_cap, + float wireload_res, + float fanout, + float net_pin_cap, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + float &c2, + float &rpi, + float &c1, + float &elmore_res, + float &elmore_cap, + bool &elmore_use_load_cap) { if (wireload_res == 0.0 || fanout == 0.0) { @@ -172,7 +176,8 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin, double y1 = 0.0; double y2 = 0.0; double y3 = 0.0; - y1 = sdc_->pinCapacitance(drvr_pin, rf, corner, min_max); + const Sdc *sdc = scene->sdc(); + y1 = sdc->pinCapacitance(drvr_pin, rf, scene, min_max); PinConnectedPinIterator *load_iter = network_->connectedPinIterator(drvr_pin); while (load_iter->hasNext()) { @@ -181,11 +186,11 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin, double cap = 0.0; // Bidirects don't count themselves as loads. if (load_pin == drvr_pin) - cap = sdc_->portExtCap(port, rf, corner, min_max); + cap = sdc->portExtCap(port, rf, min_max); else if (network_->isLeaf(load_pin)) - cap = sdc_->pinCapacitance(load_pin, rf, corner, min_max) + cap_fanout; + cap = sdc->pinCapacitance(load_pin, rf, scene, min_max) + cap_fanout; else if (network_->isTopLevelPort(load_pin)) - cap = sdc_->portExtCap(port, rf, corner, min_max) + cap_fanout; + cap = sdc->portExtCap(port, rf, min_max) + cap_fanout; double y2_ = res_fanout * cap * cap; y1 += cap; y2 += -y2_; @@ -202,8 +207,7 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin, else { c1 = static_cast(y2 * y2 / y3); c2 = static_cast(y1 - y2 * y2 / y3); - if (c2 < 0.0) - c2 = 0.0; + c2 = std::max(c2, 0.0f); rpi = static_cast(-y3 * y3 / (y2 * y2 * y2)); } elmore_res = static_cast(res_fanout); @@ -212,28 +216,4 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin, } } -#if 0 -static void -selectWireload(Network *network) -{ - // Look for a default wireload selection group. - WireloadSelection *selection; - float area = instanceArea(network->topInstance(), network); - Wireload *wireload = selection->findWireload(area); -} - -static float -instanceArea(Instance *inst, - Network *network) -{ - float area = 0.0; - LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); - while (network->hasNext(inst_iter)) { - Instance *leaf = network->next(inst_iter); - area += network->cell(leaf)->area(); - } - return area; -} -#endif - -} // namespace +} // namespace sta diff --git a/parasitics/EstimateParasitics.hh b/parasitics/EstimateParasitics.hh index 689183eaa..f762198f1 100644 --- a/parasitics/EstimateParasitics.hh +++ b/parasitics/EstimateParasitics.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,15 +24,15 @@ #pragma once -#include "StaState.hh" #include "LibertyClass.hh" #include "NetworkClass.hh" -#include "SdcClass.hh" #include "ParasiticsClass.hh" +#include "SdcClass.hh" +#include "StaState.hh" namespace sta { -class Corner; +class Scene; class StaState; class EstimateParasitics : public StaState @@ -41,58 +41,58 @@ public: EstimateParasitics(StaState *sta); // Helper function for wireload estimation. void estimatePiElmore(const Pin *drvr_pin, - const RiseFall *rf, - const Wireload *wireload, - float fanout, - float net_pin_cap, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &c2, - float &rpi, - float &c1, - float &elmore_res, - float &elmore_cap, - bool &elmore_use_load_cap); + const RiseFall *rf, + const Wireload *wireload, + float fanout, + float net_pin_cap, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &c2, + float &rpi, + float &c1, + float &elmore_res, + float &elmore_cap, + bool &elmore_use_load_cap); protected: void estimatePiElmoreBest(const Pin *drvr_pin, - float net_pin_cap, - float wireload_cap, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &c2, - float &rpi, - float &c1, - float &elmore_res, - float &elmore_cap, - bool &elmore_use_load_cap) const; + float wireload_cap, + float net_pin_cap, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &c2, + float &rpi, + float &c1, + float &elmore_res, + float &elmore_cap, + bool &elmore_use_load_cap) const; void estimatePiElmoreWorst(const Pin *drvr_pin, - float wireload_cap, - float wireload_res, - float fanout, - float net_pin_cap, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &c2, float &rpi, float &c1, - float &elmore_res, float &elmore_cap, - bool &elmore_use_load_cap); + float wireload_cap, + float wireload_res, + float fanout, + float net_pin_cap, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &c2, float &rpi, float &c1, + float &elmore_res, float &elmore_cap, + bool &elmore_use_load_cap); void estimatePiElmoreBalanced(const Pin *drvr_pin, - float wireload_cap, - float wireload_res, - float fanout, - float net_pin_cap, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &c2, float &rpi, float &c1, - float &elmore_res, float &elmore_cap, - bool &elmore_use_load_cap); + float wireload_cap, + float wireload_res, + float fanout, + float net_pin_cap, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &c2, float &rpi, float &c1, + float &elmore_res, float &elmore_cap, + bool &elmore_use_load_cap); }; -} // namespace +} // namespace sta diff --git a/parasitics/Parasitics.cc b/parasitics/Parasitics.cc index 400e9239f..086ad9b45 100644 --- a/parasitics/Parasitics.cc +++ b/parasitics/Parasitics.cc @@ -1,40 +1,40 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Parasitics.hh" -#include "Error.hh" #include "Debug.hh" -#include "Units.hh" +#include "Error.hh" +#include "EstimateParasitics.hh" #include "Liberty.hh" -#include "Wireload.hh" #include "Network.hh" #include "PortDirection.hh" -#include "Sdc.hh" -#include "Corner.hh" #include "ReduceParasitics.hh" -#include "EstimateParasitics.hh" +#include "Scene.hh" +#include "Sdc.hh" +#include "Units.hh" +#include "Wireload.hh" namespace sta { @@ -48,38 +48,29 @@ Parasitics::report(const Parasitic *parasitic) const { if (isParasiticNetwork(parasitic)) { const Unit *cap_unit = units_->capacitanceUnit(); - report_->reportLine("Net %s %s", - network_->pathName(net(parasitic)), - cap_unit->asString(capacitance(parasitic))); - report_->reportLine("Nodes:"); + report_->report("Net {} {}", network_->pathName(net(parasitic)), + cap_unit->asString(capacitance(parasitic))); + report_->report("Nodes:"); for (ParasiticNode *node : nodes(parasitic)) - report_->reportLine("%s%s %s", - name(node), - isExternal(node) ? " (ext)" : "", - cap_unit->asString(nodeGndCap(node))); - report_->reportLine("Resistors:"); + report_->report("{}{} {}", name(node), isExternal(node) ? " (ext)" : "", + cap_unit->asString(nodeGndCap(node))); + report_->report("Resistors:"); for (ParasiticResistor *res : resistors(parasitic)) { ParasiticNode *node1 = this->node1(res); ParasiticNode *node2 = this->node2(res); - report_->reportLine("%zu %s%s %s%s %s", - id(res), - name(node1), - isExternal(node1) ? " (ext)" : "", - name(node2), - isExternal(node2) ? " (ext)" : "", - units_->resistanceUnit()->asString(value(res))); + report_->report("{} {}{} {}{} {}", id(res), name(node1), + isExternal(node1) ? " (ext)" : "", name(node2), + isExternal(node2) ? " (ext)" : "", + units_->resistanceUnit()->asString(value(res))); } - report_->reportLine("Coupling Capacitors:"); + report_->report("Coupling Capacitors:"); for (ParasiticCapacitor *cap : capacitors(parasitic)) { ParasiticNode *node1 = this->node1(cap); ParasiticNode *node2 = this->node2(cap); - report_->reportLine("%zu %s%s %s%s %s", - id(cap), - name(node1), - isExternal(node1) ? " (ext)" : "", - name(node2), - isExternal(node2) ? " (ext)" : "", - cap_unit->asString(value(cap))); + report_->report("{} {}{} {}{} {}", id(cap), name(node1), + isExternal(node1) ? " (ext)" : "", name(node2), + isExternal(node2) ? " (ext)" : "", + cap_unit->asString(value(cap))); } } } @@ -137,10 +128,10 @@ Parasitics::parasiticNodeResistorMap(const Parasitic *parasitic) const return resistor_map; } -ParasiticNodeCapacitorMap +ParasiticNodeCapacitorMap Parasitics::parasiticNodeCapacitorMap(const Parasitic *parasitic) const { - ParasiticNodeCapacitorMap capacitor_map; + ParasiticNodeCapacitorMap capacitor_map; for (ParasiticCapacitor *capacitor : capacitors(parasitic)) { ParasiticNode *n1 = node1(capacitor); ParasiticNode *n2 = node2(capacitor); @@ -182,26 +173,22 @@ Parasitic * Parasitics::reduceToPiElmore(const Parasitic *parasitic, const Pin *drvr_pin, const RiseFall *rf, - const Corner *corner, - const MinMax *cnst_min_max, - const ParasiticAnalysisPt *ap) + const Scene *scene, + const MinMax *min_max) { - return sta::reduceToPiElmore(parasitic, drvr_pin, rf, ap->couplingCapFactor(), - corner, cnst_min_max, ap, this); + return sta::reduceToPiElmore(parasitic, drvr_pin, rf, coupling_cap_factor_, scene, + min_max, this); } Parasitic * Parasitics::reduceToPiPoleResidue2(const Parasitic *parasitic, const Pin *drvr_pin, const RiseFall *rf, - const Corner *corner, - const MinMax *cnst_min_max, - const ParasiticAnalysisPt *ap) + const Scene *scene, + const MinMax *min_max) { - return sta::reduceToPiPoleResidue2(parasitic, drvr_pin, rf, - ap->couplingCapFactor(), - corner, cnst_min_max, - ap, this); + return sta::reduceToPiPoleResidue2(parasitic, drvr_pin, rf, coupling_cap_factor_, + scene, min_max, this); } //////////////////////////////////////////////////////////////// @@ -212,27 +199,26 @@ Parasitics::estimatePiElmore(const Pin *drvr_pin, const Wireload *wireload, float fanout, float net_pin_cap, - const Corner *corner, + const Scene *scene, const MinMax *min_max) { EstimateParasitics estimate(this); float c2, rpi, c1, elmore_res, elmore_cap; bool elmore_use_load_cap; - estimate.estimatePiElmore(drvr_pin, rf, wireload, fanout, net_pin_cap, - corner, min_max, - c2, rpi, c1, - elmore_res, elmore_cap, elmore_use_load_cap); + estimate.estimatePiElmore(drvr_pin, rf, wireload, fanout, net_pin_cap, scene, + min_max, c2, rpi, c1, elmore_res, elmore_cap, + elmore_use_load_cap); if (c1 > 0.0 || c2 > 0.0) { - ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(min_max); - Parasitic *parasitic = makePiElmore(drvr_pin, rf, ap, c2, rpi, c1); + Parasitic *parasitic = makePiElmore(drvr_pin, rf, min_max, c2, rpi, c1); + const Sdc *sdc = scene->sdc(); NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(drvr_pin); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); if (network_->isLoad(pin)) { float load_cap = 0.0; if (elmore_use_load_cap) - load_cap = sdc_->pinCapacitance(pin, rf, corner, min_max); + load_cap = sdc->pinCapacitance(pin, rf, scene, min_max); float elmore = elmore_res * (elmore_cap + load_cap); setElmore(parasitic, pin, elmore); } @@ -248,16 +234,17 @@ Parasitics::estimatePiElmore(const Pin *drvr_pin, Parasitic * Parasitics::makeWireloadNetwork(const Pin *drvr_pin, - const Wireload *wireload, - float fanout, - const MinMax *min_max, - const ParasiticAnalysisPt *ap) + const Wireload *wireload, + float fanout, + const Scene *scene, + const MinMax *min_max) { Parasitic *parasitic = nullptr; const Net *net = findParasiticNet(drvr_pin); if (net) { - parasitic = makeParasiticNetwork(net, false, ap); - const OperatingConditions *op_cond = sdc_->operatingConditions(min_max); + parasitic = makeParasiticNetwork(net, false); + const Sdc *sdc = scene->sdc(); + const OperatingConditions *op_cond = sdc->operatingConditions(min_max); float wireload_cap, wireload_res; wireload->findWireload(fanout, op_cond, wireload_cap, wireload_res); @@ -265,19 +252,19 @@ Parasitics::makeWireloadNetwork(const Pin *drvr_pin, if (op_cond) tree = op_cond->wireloadTree(); switch (tree) { - case WireloadTree::worst_case: - makeWireloadNetworkWorst(parasitic, drvr_pin, net, wireload_cap, - wireload_res, fanout); - break; - case WireloadTree::balanced: - makeWireloadNetworkBalanced(parasitic, drvr_pin, wireload_cap, - wireload_res, fanout); - break; - case WireloadTree::best_case: - case WireloadTree::unknown: - makeWireloadNetworkBest(parasitic, drvr_pin, wireload_cap, - wireload_res, fanout); - break; + case WireloadTree::worst_case: + makeWireloadNetworkWorst(parasitic, drvr_pin, net, wireload_cap, + wireload_res, fanout); + break; + case WireloadTree::unknown: + case WireloadTree::balanced: + makeWireloadNetworkBalanced(parasitic, drvr_pin, wireload_cap, wireload_res, + fanout); + break; + case WireloadTree::best_case: + makeWireloadNetworkBest(parasitic, drvr_pin, wireload_cap, wireload_res, + fanout); + break; } } return parasitic; @@ -287,23 +274,21 @@ Parasitics::makeWireloadNetwork(const Pin *drvr_pin, // the resistor. void Parasitics::makeWireloadNetworkWorst(Parasitic *parasitic, - const Pin *drvr_pin, + const Pin *drvr_pin, const Net *net, - float wireload_cap, - float wireload_res, - float /* fanout */) + float wireload_cap, + float wireload_res, + float /* fanout */) { ParasiticNode *drvr_node = ensureParasiticNode(parasitic, drvr_pin, network_); size_t resistor_index = 1; ParasiticNode *load_node = ensureParasiticNode(parasitic, net, 0, network_); makeResistor(parasitic, resistor_index++, wireload_res, drvr_node, load_node); - parasitics_->incrCap(load_node, wireload_cap); - PinConnectedPinIterator *load_iter = - network_->connectedPinIterator(drvr_pin); + incrCap(load_node, wireload_cap); + PinConnectedPinIterator *load_iter = network_->connectedPinIterator(drvr_pin); while (load_iter->hasNext()) { const Pin *load_pin = load_iter->next(); - if (load_pin != drvr_pin - && network_->isLoad(load_pin)) { + if (load_pin != drvr_pin && network_->isLoad(load_pin)) { ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin, network_); makeResistor(parasitic, resistor_index++, 0.0, load_node, load_node1); } @@ -313,20 +298,18 @@ Parasitics::makeWireloadNetworkWorst(Parasitic *parasitic, // No wire resistance, so load is lumped capacitance. void Parasitics::makeWireloadNetworkBest(Parasitic *parasitic, - const Pin *drvr_pin, - float wireload_cap, - float /* wireload_res */, - float /* fanout */) + const Pin *drvr_pin, + float wireload_cap, + float /* wireload_res */, + float /* fanout */) { ParasiticNode *drvr_node = ensureParasiticNode(parasitic, drvr_pin, network_); - parasitics_->incrCap(drvr_node, wireload_cap); - PinConnectedPinIterator *load_iter = - network_->connectedPinIterator(drvr_pin); + incrCap(drvr_node, wireload_cap); + PinConnectedPinIterator *load_iter = network_->connectedPinIterator(drvr_pin); size_t resistor_index = 1; while (load_iter->hasNext()) { const Pin *load_pin = load_iter->next(); - if (load_pin != drvr_pin - && network_->isLoad(load_pin)) { + if (load_pin != drvr_pin && network_->isLoad(load_pin)) { ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin, network_); makeResistor(parasitic, resistor_index++, 0.0, drvr_node, load_node1); } @@ -337,48 +320,40 @@ Parasitics::makeWireloadNetworkBest(Parasitic *parasitic, // connecting it to the driver. void Parasitics::makeWireloadNetworkBalanced(Parasitic *parasitic, - const Pin *drvr_pin, - float wireload_cap, - float wireload_res, - float fanout) + const Pin *drvr_pin, + float wireload_cap, + float wireload_res, + float fanout) { float fanout_cap = wireload_cap / fanout; float fanout_res = wireload_res / fanout; ParasiticNode *drvr_node = ensureParasiticNode(parasitic, drvr_pin, network_); - PinConnectedPinIterator *load_iter = - network_->connectedPinIterator(drvr_pin); + PinConnectedPinIterator *load_iter = network_->connectedPinIterator(drvr_pin); size_t resistor_index = 1; while (load_iter->hasNext()) { const Pin *load_pin = load_iter->next(); - if (load_pin != drvr_pin - && network_->isLoad(load_pin)) { + if (load_pin != drvr_pin && network_->isLoad(load_pin)) { ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin, network_); - makeResistor(parasitic, resistor_index++, fanout_res, drvr_node, load_node1); - parasitics_->incrCap(load_node1, fanout_cap); + makeResistor(parasitic, resistor_index++, fanout_res, drvr_node, load_node1); + incrCap(load_node1, fanout_cap); } } } -//////////////////////////////////////////////////////////////// - -ParasiticAnalysisPt::ParasiticAnalysisPt(const char *name, - int index, - int index_max) : - name_(name), - index_(index), - index_max_(index_max), - coupling_cap_factor_(1.0) -{ -} - void -ParasiticAnalysisPt::setCouplingCapFactor(float factor) +Parasitics::setCouplingCapFactor(float factor) { coupling_cap_factor_ = factor; } //////////////////////////////////////////////////////////////// +ParasiticNodeLess::ParasiticNodeLess() : + parasitics_(nullptr), + network_(nullptr) +{ +} + ParasiticNodeLess::ParasiticNodeLess(const Parasitics *parasitics, const Network *network) : parasitics_(parasitics), @@ -386,12 +361,6 @@ ParasiticNodeLess::ParasiticNodeLess(const Parasitics *parasitics, { } -ParasiticNodeLess::ParasiticNodeLess(const ParasiticNodeLess &less) : - parasitics_(less.parasitics_), - network_(less.network_) -{ -} - bool ParasiticNodeLess::operator()(const ParasiticNode *node1, const ParasiticNode *node2) const @@ -403,12 +372,10 @@ ParasiticNodeLess::operator()(const ParasiticNode *node1, unsigned id1 = parasitics_->netId(node1); unsigned id2 = parasitics_->netId(node2); return (pin1 == nullptr && pin2) - || (pin1 && pin2 - && network_->id(pin1) < network_->id(pin2)) - || (pin1 == nullptr && pin2 == nullptr - && (network_->id(net1) < network_->id(net2) - || (net1 == net2 - && id1 < id2))); + || (pin1 && pin2 && network_->id(pin1) < network_->id(pin2)) + || (pin1 == nullptr && pin2 == nullptr + && (network_->id(net1) < network_->id(net2) + || (net1 == net2 && id1 < id2))); } -} // namespace +} // namespace sta diff --git a/parasitics/Parasitics.i b/parasitics/Parasitics.i index 2854d38eb..ab794949e 100644 --- a/parasitics/Parasitics.i +++ b/parasitics/Parasitics.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,8 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module parasitics - %{ #include "Sta.hh" @@ -38,31 +36,33 @@ using sta::Pin; %inline %{ bool -read_spef_cmd(const char *filename, - Instance *instance, - const Corner *corner, +read_spef_cmd(const char *name, + const char *filename, + Instance *instance, + Scene *scene, const MinMaxAll *min_max, - bool pin_cap_included, - bool keep_coupling_caps, - float coupling_cap_factor, - bool reduce) + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + bool reduce) { - return Sta::sta()->readSpef(filename, instance, corner, min_max, - pin_cap_included, keep_coupling_caps, + return Sta::sta()->readSpef(name, filename, instance, + scene, min_max, + pin_cap_included, keep_coupling_caps, coupling_cap_factor, reduce); } void -report_parasitic_annotation_cmd(bool report_unannotated, - const Corner *corner) +report_parasitic_annotation_cmd(const char *spef_name, + bool report_unannotated) { - Sta::sta()->reportParasiticAnnotation(report_unannotated, corner); + Sta::sta()->reportParasiticAnnotation(spef_name, report_unannotated); } FloatSeq find_pi_elmore(Pin *drvr_pin, - RiseFall *rf, - MinMax *min_max) + RiseFall *rf, + MinMax *min_max) { float c2, rpi, c1; bool exists; @@ -78,9 +78,9 @@ find_pi_elmore(Pin *drvr_pin, float find_elmore(Pin *drvr_pin, - Pin *load_pin, - RiseFall *rf, - MinMax *min_max) + Pin *load_pin, + RiseFall *rf, + MinMax *min_max) { float elmore = 0.0; bool exists; @@ -90,21 +90,21 @@ find_elmore(Pin *drvr_pin, void set_pi_model_cmd(Pin *drvr_pin, - RiseFall *rf, - MinMaxAll *min_max, - float c2, - float rpi, - float c1) + RiseFall *rf, + MinMaxAll *min_max, + float c2, + float rpi, + float c1) { Sta::sta()->makePiElmore(drvr_pin, rf, min_max, c2, rpi, c1); } void set_elmore_cmd(Pin *drvr_pin, - Pin *load_pin, - RiseFall *rf, - MinMaxAll *min_max, - float elmore) + Pin *load_pin, + RiseFall *rf, + MinMaxAll *min_max, + float elmore) { Sta::sta()->setElmore(drvr_pin, load_pin, rf, min_max, elmore); } diff --git a/parasitics/Parasitics.tcl b/parasitics/Parasitics.tcl index 15e8e81d4..e09f496be 100644 --- a/parasitics/Parasitics.tcl +++ b/parasitics/Parasitics.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -25,7 +25,8 @@ namespace eval sta { define_cmd_args "read_spef" \ - {[-corner corner]\ + {[-name spef_name] + [-corner corner]\ [-min]\ [-max]\ [-path path]\ @@ -33,16 +34,19 @@ define_cmd_args "read_spef" \ [-keep_capacitive_coupling]\ [-coupling_reduction_factor factor]\ [-reduce]\ - [-delete_after_reduce]\ filename} +# -scene/-min/-max are for compatibilty, Deprecated 11/21/2025 proc_redirect read_spef { parse_key_args "read_spef" args \ - keys {-path -coupling_reduction_factor -corner -name} \ + keys {-name -path -coupling_reduction_factor -corner} \ flags {-min -max -increment -pin_cap_included -keep_capacitive_coupling -reduce} - check_argc_eq1 "read_spef" $args - set reduce [info exists flags(-reduce)] + check_argc_eq1 "read_spef" $args + set name "" + if [info exists keys(-name)] { + set name $keys(-name) + } set instance [top_instance] if [info exists keys(-path)] { set path $keys(-path) @@ -51,7 +55,7 @@ proc_redirect read_spef { sta_error 276 "path instance '$path' not found." } } - set corner [parse_corner_or_all keys] + set scene [parse_scene_or_null keys] set min_max [parse_min_max_all_flags flags] set coupling_reduction_factor 1.0 if [info exists keys(-coupling_reduction_factor)] { @@ -60,22 +64,28 @@ proc_redirect read_spef { } set keep_coupling_caps [info exists flags(-keep_capacitive_coupling)] set pin_cap_included [info exists flags(-pin_cap_included)] + set reduce [info exists flags(-reduce)] set filename [file nativename [lindex $args 0]] - return [read_spef_cmd $filename $instance $corner $min_max \ - $pin_cap_included $keep_coupling_caps \ + return [read_spef_cmd $name $filename $instance $scene $min_max \ + $pin_cap_included $keep_coupling_caps \ $coupling_reduction_factor $reduce] } -define_cmd_args "report_parasitic_annotation" {[-report_unannotated]} +define_cmd_args "report_parasitic_annotation" {[-name spef_name]\ + [-report_unannotated]} proc_redirect report_parasitic_annotation { parse_key_args "report_parasitic_annotation" args \ - keys {} flags {-report_unannotated} + keys {-name} flags {-report_unannotated} check_argc_eq0 "report_parasitic_annotation" $args + set spef_name "" + if { [info exists keys(-name)] } { + set spef_name $keys(-name) + } set report_unannotated [info exists flags(-report_unannotated)] - report_parasitic_annotation_cmd $report_unannotated [sta::cmd_corner] + report_parasitic_annotation_cmd $spef_name $report_unannotated } # set_pi_model [-min] [-max] drvr_pin c2 rpi c1 diff --git a/parasitics/ReduceParasitics.cc b/parasitics/ReduceParasitics.cc index 7f2f4ef1d..231cdebae 100644 --- a/parasitics/ReduceParasitics.cc +++ b/parasitics/ReduceParasitics.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,23 +24,25 @@ #include "ReduceParasitics.hh" -#include "Error.hh" +#include +#include +#include + #include "Debug.hh" -#include "MinMax.hh" +#include "Error.hh" #include "Liberty.hh" +#include "MinMax.hh" #include "Network.hh" -#include "Sdc.hh" -#include "Corner.hh" #include "Parasitics.hh" +#include "Scene.hh" +#include "Sdc.hh" namespace sta { -using std::max; - -typedef Map ParasiticNodeValueMap; -typedef Map ResistorCurrentMap; -typedef Set ParasiticResistorSet; -typedef Set ParasiticNodeSet; +using ParasiticNodeValueMap = std::map; +using ResistorCurrentMap = std::map; +using ParasiticResistorSet = std::set; +using ParasiticNodeSet = std::set; class ReduceToPi : public StaState { @@ -48,59 +50,53 @@ class ReduceToPi : public StaState ReduceToPi(StaState *sta); void reduceToPi(const Parasitic *parasitic_network, const Pin *drvr_pin, - ParasiticNode *drvr_node, - float coupling_cap_factor, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap, - float &c2, - float &rpi, - float &c1); + ParasiticNode *drvr_node, + float coupling_cap_factor, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + float &c2, + float &rpi, + float &c1); bool pinCapsOneValue() { return pin_caps_one_value_; } protected: void reducePiDfs(const Pin *drvr_pin, - ParasiticNode *node, - ParasiticResistor *from_res, + ParasiticNode *node, + ParasiticResistor *from_res, double src_resistance, - double &y1, - double &y2, - double &y3, - double &dwn_cap, + double &y1, + double &y2, + double &y3, + double &dwn_cap, double &max_resistance); void visit(ParasiticNode *node); bool isVisited(ParasiticNode *node); void leave(ParasiticNode *node); void setDownstreamCap(ParasiticNode *node, - float cap); + float cap); float downstreamCap(ParasiticNode *node); float pinCapacitance(ParasiticNode *node); bool isLoopResistor(ParasiticResistor *resistor); void markLoopResistor(ParasiticResistor *resistor); + Parasitics *parasitics_; bool includes_pin_caps_; - float coupling_cap_multiplier_; - const RiseFall *rf_; - const Corner *corner_; - const MinMax *min_max_; - const ParasiticAnalysisPt *ap_; + float coupling_cap_multiplier_ {1.0}; + const RiseFall *rf_ {nullptr}; + const Scene *scene_ {nullptr}; + const MinMax *min_max_ {nullptr}; ParasiticNodeResistorMap resistor_map_; ParasiticNodeCapacitorMap capacitor_map_; ParasiticNodeSet visited_nodes_; ParasiticNodeValueMap node_values_; ParasiticResistorSet loop_resistors_; - bool pin_caps_one_value_; + bool pin_caps_one_value_ {true}; }; ReduceToPi::ReduceToPi(StaState *sta) : - StaState(sta), - coupling_cap_multiplier_(1.0), - rf_(nullptr), - corner_(nullptr), - min_max_(nullptr), - pin_caps_one_value_(true) + StaState(sta) { } @@ -111,22 +107,21 @@ ReduceToPi::ReduceToPi(StaState *sta) : void ReduceToPi::reduceToPi(const Parasitic *parasitic_network, const Pin *drvr_pin, - ParasiticNode *drvr_node, - float coupling_cap_factor, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap, - float &c2, - float &rpi, - float &c1) + ParasiticNode *drvr_node, + float coupling_cap_factor, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + float &c2, + float &rpi, + float &c1) { - includes_pin_caps_ = parasitics_->includesPinCaps(parasitic_network), coupling_cap_multiplier_ = coupling_cap_factor; rf_ = rf; - corner_ = corner; + scene_ = scene; min_max_ = min_max; - ap_ = ap; + parasitics_ = scene_->parasitics(min_max); + includes_pin_caps_ = parasitics_->includesPinCaps(parasitic_network), resistor_map_ = parasitics_->parasiticNodeResistorMap(parasitic_network); capacitor_map_ = parasitics_->parasiticNodeCapacitorMap(parasitic_network); @@ -147,20 +142,20 @@ ReduceToPi::reduceToPi(const Parasitic *parasitic_network, rpi = -y3 * y3 / (y2 * y2 * y2); } debugPrint(debug_, "parasitic_reduce", 2, - " Pi model c2=%.3g rpi=%.3g c1=%.3g max_r=%.3g", + " Pi model c2={:.3g} rpi={:.3g} c1={:.3g} max_r={:.3g}", c2, rpi, c1, max_resistance); } // Find admittance moments. void ReduceToPi::reducePiDfs(const Pin *drvr_pin, - ParasiticNode *node, - ParasiticResistor *from_res, - double src_resistance, - double &y1, - double &y2, - double &y3, - double &dwn_cap, + ParasiticNode *node, + ParasiticResistor *from_res, + double src_resistance, + double &y1, + double &y2, + double &y3, + double &dwn_cap, double &max_resistance) { double coupling_cap = 0.0; @@ -173,7 +168,7 @@ ReduceToPi::reducePiDfs(const Pin *drvr_pin, + pinCapacitance(node); y1 = dwn_cap; y2 = y3 = 0.0; - max_resistance = max(max_resistance, src_resistance); + max_resistance = std::max(max_resistance, src_resistance); visit(node); ParasiticResistorSeq &resistors = resistor_map_[node]; @@ -185,7 +180,7 @@ ReduceToPi::reducePiDfs(const Pin *drvr_pin, && resistor != from_res) { if (isVisited(onode)) { // Resistor loop. - debugPrint(debug_, "parasitic_reduce", 2, " loop detected thru resistor %zu", + debugPrint(debug_, "parasitic_reduce", 2, " loop detected thru resistor {}", parasitics_->id(resistor)); markLoopResistor(resistor); } @@ -208,7 +203,7 @@ ReduceToPi::reducePiDfs(const Pin *drvr_pin, setDownstreamCap(node, dwn_cap); leave(node); debugPrint(debug_, "parasitic_reduce", 3, - " node %s y1=%.3g y2=%.3g y3=%.3g cap=%.3g", + " node {} y1={:.3g} y2={:.3g} y3={:.3g} cap={:.3g}", parasitics_->name(node), y1, y2, y3, dwn_cap); } @@ -220,14 +215,15 @@ ReduceToPi::pinCapacitance(ParasiticNode *node) if (pin) { Port *port = network_->port(pin); LibertyPort *lib_port = network_->libertyPort(port); + const Sdc *sdc = scene_->sdc(); if (lib_port) { if (!includes_pin_caps_) { - pin_cap = sdc_->pinCapacitance(pin, rf_, corner_, min_max_); - pin_caps_one_value_ &= lib_port->capacitanceIsOneValue(); + pin_cap = sdc->pinCapacitance(pin, rf_, scene_, min_max_); + pin_caps_one_value_ &= lib_port->capacitanceIsOneValue(); } } else if (network_->isTopLevelPort(pin)) - pin_cap = sdc_->portExtCap(port, rf_, corner_, min_max_); + pin_cap = sdc->portExtCap(port, rf_, min_max_); } return pin_cap; } @@ -241,7 +237,7 @@ ReduceToPi::visit(ParasiticNode *node) bool ReduceToPi::isVisited(ParasiticNode *node) { - return visited_nodes_.hasKey(node); + return visited_nodes_.contains(node); } void @@ -253,7 +249,7 @@ ReduceToPi::leave(ParasiticNode *node) bool ReduceToPi::isLoopResistor(ParasiticResistor *resistor) { - return loop_resistors_.hasKey(resistor); + return loop_resistors_.contains(resistor); } void @@ -264,7 +260,7 @@ ReduceToPi::markLoopResistor(ParasiticResistor *resistor) void ReduceToPi::setDownstreamCap(ParasiticNode *node, - float cap) + float cap) { node_values_[node] = cap; } @@ -286,38 +282,35 @@ class ReduceToPiElmore : public ReduceToPi ParasiticNode *drvr_node, float coupling_cap_factor, const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap); + const Scene *scene, + const MinMax *min_max); void reduceElmoreDfs(const Pin *drvr_pin, - ParasiticNode *node, - ParasiticResistor *from_res, - double elmore, - Parasitic *pi_elmore); + ParasiticNode *node, + ParasiticResistor *from_res, + double elmore, + Parasitic *pi_elmore); }; Parasitic * reduceToPiElmore(const Parasitic *parasitic_network, - const Pin *drvr_pin, + const Pin *drvr_pin, const RiseFall *rf, float coupling_cap_factor, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap, - StaState *sta) + const Scene *scene, + const MinMax *min_max, + StaState *sta) { - Parasitics *parasitics = sta->parasitics(); + Parasitics *parasitics = scene->parasitics(min_max); ParasiticNode *drvr_node = parasitics->findParasiticNode(parasitic_network, drvr_pin); if (drvr_node) { - debugPrint(sta->debug(), "parasitic_reduce", 1, "Reduce driver %s %s %s", + debugPrint(sta->debug(), "parasitic_reduce", 1, "Reduce driver {} {} {}", sta->network()->pathName(drvr_pin), - rf->to_string().c_str(), - min_max->to_string().c_str()); + rf->shortName(), + min_max->to_string()); ReduceToPiElmore reducer(sta); return reducer.makePiElmore(parasitic_network, drvr_pin, drvr_node, - coupling_cap_factor, rf, corner, - min_max, ap); + coupling_cap_factor, rf, scene, min_max); } return nullptr; } @@ -329,21 +322,20 @@ ReduceToPiElmore::ReduceToPiElmore(StaState *sta) : Parasitic * ReduceToPiElmore::makePiElmore(const Parasitic *parasitic_network, - const Pin *drvr_pin, - ParasiticNode *drvr_node, - float coupling_cap_factor, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap) + const Pin *drvr_pin, + ParasiticNode *drvr_node, + float coupling_cap_factor, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) { float c2, rpi, c1; reduceToPi(parasitic_network, drvr_pin, drvr_node, coupling_cap_factor, - rf, corner, min_max, ap, c2, rpi, c1); - Parasitic *pi_elmore = parasitics_->makePiElmore(drvr_pin, rf, ap, - c2, rpi, c1); + rf, scene, min_max, c2, rpi, c1); + Parasitic *pi_elmore = parasitics_->makePiElmore(drvr_pin, rf, min_max, + c2, rpi, c1); parasitics_->setIsReducedParasiticNetwork(pi_elmore, true); - reduceElmoreDfs(drvr_pin, drvr_node, 0, 0.0, pi_elmore); + reduceElmoreDfs(drvr_pin, drvr_node, nullptr, 0.0, pi_elmore); return pi_elmore; } @@ -351,15 +343,15 @@ ReduceToPiElmore::makePiElmore(const Parasitic *parasitic_network, // set by reducePiDfs. void ReduceToPiElmore::reduceElmoreDfs(const Pin *drvr_pin, - ParasiticNode *node, - ParasiticResistor *from_res, - double elmore, - Parasitic *pi_elmore) + ParasiticNode *node, + ParasiticResistor *from_res, + double elmore, + Parasitic *pi_elmore) { const Pin *pin = parasitics_->pin(node); if (from_res && pin) { if (network_->isLoad(pin)) { - debugPrint(debug_, "parasitic_reduce", 2, " Load %s elmore=%.3g", + debugPrint(debug_, "parasitic_reduce", 2, " Load {} elmore={:.3g}", network_->pathName(pin), elmore); parasitics_->setElmore(pi_elmore, pin, elmore); @@ -386,54 +378,52 @@ class ReduceToPiPoleResidue2 : public ReduceToPi { public: ReduceToPiPoleResidue2(StaState *sta); - ~ReduceToPiPoleResidue2(); + ~ReduceToPiPoleResidue2() override; void findPolesResidues(const Parasitic *parasitic_network, Parasitic *pi_pole_residue, - const Pin *drvr_pin, - ParasiticNode *drvr_node); + const Pin *drvr_pin, + ParasiticNode *drvr_node); Parasitic *makePiPoleResidue2(const Parasitic *parasitic_network, const Pin *drvr_pin, ParasiticNode *drvr_node, float coupling_cap_factor, const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap); + const Scene *scene, + const MinMax *min_max); private: void findMoments(const Pin *drvr_pin, - ParasiticNode *drvr_node, - int moment_count); + ParasiticNode *drvr_node, + int moment_count); void findMoments(const Pin *drvr_pin, - ParasiticNode *node, - double from_volt, - ParasiticResistor *from_res, - int moment_index); + ParasiticNode *node, + double from_volt, + ParasiticResistor *from_res, + int moment_index); double findBranchCurrents(const Pin *drvr_pin, - ParasiticNode *node, - ParasiticResistor *from_res, - int moment_index); + ParasiticNode *node, + ParasiticResistor *from_res, + int moment_index); double moment(ParasiticNode *node, - int moment_index); + int moment_index); void setMoment(ParasiticNode *node, - double moment, - int moment_index); + double moment, + int moment_index); double current(ParasiticResistor *res); void setCurrent(ParasiticResistor *res, - double i); + double i); void findPolesResidues(Parasitic *pi_pole_residue, - const Pin *drvr_pin, - const Pin *load_pin, - ParasiticNode *load_node); + const Pin *drvr_pin, + const Pin *load_pin, + ParasiticNode *load_node); // Resistor/capacitor currents. ResistorCurrentMap currents_; - ParasiticNodeValueMap *moments_; + ParasiticNodeValueMap *moments_ {nullptr}; }; ReduceToPiPoleResidue2::ReduceToPiPoleResidue2(StaState *sta) : - ReduceToPi(sta), - moments_(nullptr) + ReduceToPi(sta) { } @@ -449,45 +439,43 @@ ReduceToPiPoleResidue2::ReduceToPiPoleResidue2(StaState *sta) : // Design Automation Conference, 1996, pg 611-616. Parasitic * reduceToPiPoleResidue2(const Parasitic *parasitic_network, - const Pin *drvr_pin, + const Pin *drvr_pin, const RiseFall *rf, - float coupling_cap_factor, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap, - StaState *sta) + float coupling_cap_factor, + const Scene *scene, + const MinMax *min_max, + StaState *sta) { - Parasitics *parasitics = sta->parasitics(); + Parasitics *parasitics = scene->parasitics(min_max); ParasiticNode *drvr_node = parasitics->findParasiticNode(parasitic_network, drvr_pin); if (drvr_node) { - debugPrint(sta->debug(), "parasitic_reduce", 1, "Reduce driver %s", + debugPrint(sta->debug(), "parasitic_reduce", 1, "Reduce driver {}", sta->network()->pathName(drvr_pin)); ReduceToPiPoleResidue2 reducer(sta); return reducer.makePiPoleResidue2(parasitic_network, drvr_pin, drvr_node, coupling_cap_factor, rf, - corner, min_max, ap); + scene, min_max); } return nullptr; } Parasitic * ReduceToPiPoleResidue2::makePiPoleResidue2(const Parasitic *parasitic_network, - const Pin *drvr_pin, - ParasiticNode *drvr_node, - float coupling_cap_factor, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap) + const Pin *drvr_pin, + ParasiticNode *drvr_node, + float coupling_cap_factor, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) { float c2, rpi, c1; reduceToPi(parasitic_network, drvr_pin, drvr_node, - coupling_cap_factor, rf, corner, min_max, ap, - c2, rpi, c1); + coupling_cap_factor, rf, scene, min_max, + c2, rpi, c1); Parasitic *pi_pole_residue = parasitics_->makePiPoleResidue(drvr_pin, - rf, ap, - c2, rpi, c1); + rf, min_max, + c2, rpi, c1); parasitics_->setIsReducedParasiticNetwork(pi_pole_residue, true); findPolesResidues(parasitic_network, pi_pole_residue, drvr_pin, drvr_node); return pi_pole_residue; @@ -501,8 +489,8 @@ ReduceToPiPoleResidue2::~ReduceToPiPoleResidue2() void ReduceToPiPoleResidue2::findPolesResidues(const Parasitic *parasitic_network, Parasitic *pi_pole_residue, - const Pin *drvr_pin, - ParasiticNode *drvr_node) + const Pin *drvr_pin, + ParasiticNode *drvr_node) { moments_ = new ParasiticNodeValueMap[4]; findMoments(drvr_pin, drvr_node, 4); @@ -514,7 +502,7 @@ ReduceToPiPoleResidue2::findPolesResidues(const Parasitic *parasitic_network, ParasiticNode *load_node = parasitics_->findParasiticNode(parasitic_network, pin); if (load_node) { - findPolesResidues(pi_pole_residue, drvr_pin, pin, load_node); + findPolesResidues(pi_pole_residue, drvr_pin, pin, load_node); } } } @@ -523,8 +511,8 @@ ReduceToPiPoleResidue2::findPolesResidues(const Parasitic *parasitic_network, void ReduceToPiPoleResidue2::findMoments(const Pin *drvr_pin, - ParasiticNode *drvr_node, - int moment_count) + ParasiticNode *drvr_node, + int moment_count) { // Driver model thevenin resistance. double rd = 0.0; @@ -532,18 +520,18 @@ ReduceToPiPoleResidue2::findMoments(const Pin *drvr_pin, // current thru the resistors. Thus, there is no point in doing a // pass to find the zero'th moments. for (int moment_index = 1; moment_index < moment_count; moment_index++) { - double rd_i = findBranchCurrents(drvr_pin, drvr_node, 0, moment_index); + double rd_i = findBranchCurrents(drvr_pin, drvr_node, nullptr, moment_index); double rd_volt = rd_i * rd; setMoment(drvr_node, 0.0, moment_index); - findMoments(drvr_pin, drvr_node, -rd_volt, 0, moment_index); + findMoments(drvr_pin, drvr_node, -rd_volt, nullptr, moment_index); } } double ReduceToPiPoleResidue2::findBranchCurrents(const Pin *drvr_pin, - ParasiticNode *node, - ParasiticResistor *from_res, - int moment_index) + ParasiticNode *node, + ParasiticResistor *from_res, + int moment_index) { visit(node); double branch_i = 0.0; @@ -570,17 +558,17 @@ ReduceToPiPoleResidue2::findBranchCurrents(const Pin *drvr_pin, leave(node); if (from_res) { setCurrent(from_res, branch_i); - debugPrint(debug_, "parasitic_reduce", 3, " res i=%.3g", branch_i); + debugPrint(debug_, "parasitic_reduce", 3, " res i={:.3g}", branch_i); } return branch_i; } void ReduceToPiPoleResidue2::findMoments(const Pin *drvr_pin, - ParasiticNode *node, - double from_volt, - ParasiticResistor *from_res, - int moment_index) + ParasiticNode *node, + double from_volt, + ParasiticResistor *from_res, + int moment_index) { visit(node); ParasiticResistorSeq &resistors = resistor_map_[node]; @@ -595,7 +583,7 @@ ReduceToPiPoleResidue2::findMoments(const Pin *drvr_pin, double r_volt = r * current(resistor); double onode_volt = from_volt - r_volt; setMoment(onode, onode_volt, moment_index); - debugPrint(debug_, "parasitic_reduce", 3, " moment %s %d %.3g", + debugPrint(debug_, "parasitic_reduce", 3, " moment {} {} {:.3g}", parasitics_->name(onode), moment_index, onode_volt); @@ -607,7 +595,7 @@ ReduceToPiPoleResidue2::findMoments(const Pin *drvr_pin, double ReduceToPiPoleResidue2::moment(ParasiticNode *node, - int moment_index) + int moment_index) { // Zero'th moments are all 1. if (moment_index == 0) @@ -620,8 +608,8 @@ ReduceToPiPoleResidue2::moment(ParasiticNode *node, void ReduceToPiPoleResidue2::setMoment(ParasiticNode *node, - double moment, - int moment_index) + double moment, + int moment_index) { // Zero'th moments are all 1. if (moment_index > 0) { @@ -638,16 +626,16 @@ ReduceToPiPoleResidue2::current(ParasiticResistor *res) void ReduceToPiPoleResidue2::setCurrent(ParasiticResistor *res, - double i) + double i) { currents_[res] = i; } void ReduceToPiPoleResidue2::findPolesResidues(Parasitic *pi_pole_residue, - const Pin *, - const Pin *load_pin, - ParasiticNode *load_node) + const Pin *, + const Pin *load_pin, + ParasiticNode *load_node) { double m1 = moment(load_node, 1); double m2 = moment(load_node, 2); @@ -661,7 +649,7 @@ ReduceToPiPoleResidue2::findPolesResidues(Parasitic *pi_pole_residue, || m1 / m2 == m2 / m3) { double p1 = -1.0 / m1; double k1 = 1.0; - debugPrint(debug_, "parasitic_reduce", 3, " load %s p1=%.3g k1=%.3g", + debugPrint(debug_, "parasitic_reduce", 3, " load {} p1={:.3g} k1={:.3g}", network_->pathName(load_pin), p1, k1); ComplexFloatSeq *poles = new ComplexFloatSeq(1); ComplexFloatSeq *residues = new ComplexFloatSeq(1); @@ -681,7 +669,7 @@ ReduceToPiPoleResidue2::findPolesResidues(Parasitic *pi_pole_residue, k1 = k; } debugPrint(debug_, "parasitic_reduce", 3, - " load %s p1=%.3g p2=%.3g k1=%.3g k2=%.3g", + " load {} p1={:.3g} p2={:.3g} k1={:.3g} k2={:.3g}", network_->pathName(load_pin), p1, p2, k1, k2); ComplexFloatSeq *poles = new ComplexFloatSeq(2); @@ -694,4 +682,4 @@ ReduceToPiPoleResidue2::findPolesResidues(Parasitic *pi_pole_residue, } } -} // namespace +} // namespace sta diff --git a/parasitics/ReduceParasitics.hh b/parasitics/ReduceParasitics.hh index 84a51e640..8268d8573 100644 --- a/parasitics/ReduceParasitics.hh +++ b/parasitics/ReduceParasitics.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ namespace sta { -class Corner; +class Scene; class Parasitic; class ParasiticAnalysisPt; class StaState; @@ -37,24 +37,22 @@ class StaState; // Reduce parasitic network to pi elmore model for drvr_pin. Parasitic * reduceToPiElmore(const Parasitic *parasitic_network, - const Pin *drvr_pin, + const Pin *drvr_pin, const RiseFall *rf, - float coupling_cap_factor, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap, - StaState *sta); + float coupling_cap_factor, + const Scene *scene, + const MinMax *min_max, + StaState *sta); // Reduce parasitic network to pi and 2nd order pole/residue models // for drvr_pin. Parasitic * reduceToPiPoleResidue2(const Parasitic *parasitic_network, - const Pin *drvr_pin, + const Pin *drvr_pin, const RiseFall *rf, - float coupling_cap_factor, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap, - StaState *sta); + float coupling_cap_factor, + const Scene *scene, + const MinMax *min_max, + StaState *sta); -} // namespace +} // namespace sta diff --git a/parasitics/ReportParasiticAnnotation.cc b/parasitics/ReportParasiticAnnotation.cc index d27ac0f40..f9651e577 100644 --- a/parasitics/ReportParasiticAnnotation.cc +++ b/parasitics/ReportParasiticAnnotation.cc @@ -1,48 +1,49 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "ReportParasiticAnnotation.hh" -#include "Report.hh" +#include "ArcDelayCalc.hh" +#include "ContainerHelpers.hh" +#include "Graph.hh" #include "Network.hh" #include "NetworkCmp.hh" -#include "PortDirection.hh" -#include "Graph.hh" -#include "Corner.hh" #include "Parasitics.hh" -#include "DcalcAnalysisPt.hh" -#include "ArcDelayCalc.hh" +#include "PortDirection.hh" +#include "Report.hh" +#include "Scene.hh" namespace sta { class ReportParasiticAnnotation : public StaState { public: - ReportParasiticAnnotation(bool report_unannotated, - const Corner *corner, + ReportParasiticAnnotation(Parasitics *parasitics, + bool report_unannotated, + const Scene *scene, StaState *sta); - void report(); + void reportAnnotation(); private: void reportAnnotationCounts(); @@ -50,36 +51,39 @@ class ReportParasiticAnnotation : public StaState void findCounts(Instance *inst); void findCounts(Net *net); + Parasitics *parasitics_; bool report_unannotated_; - const Corner *corner_; + const Scene *scene_; const MinMax *min_max_; - const ParasiticAnalysisPt *parasitic_ap_; PinSeq unannotated_; PinSeq partially_annotated_; }; void -reportParasiticAnnotation(bool report_unannotated, - const Corner *corner, +reportParasiticAnnotation(Parasitics *parasitics, + bool report_unannotated, + const Scene *scene, StaState *sta) { - ReportParasiticAnnotation report_annotation(report_unannotated, corner, sta); - report_annotation.report(); + ReportParasiticAnnotation report_annotation(parasitics, report_unannotated, scene, + sta); + report_annotation.reportAnnotation(); } -ReportParasiticAnnotation::ReportParasiticAnnotation(bool report_unannotated, - const Corner *corner, +ReportParasiticAnnotation::ReportParasiticAnnotation(Parasitics *parasitics, + bool report_unannotated, + const Scene *scene, StaState *sta) : StaState(sta), + parasitics_(parasitics), report_unannotated_(report_unannotated), - corner_(corner), - min_max_(MinMax::max()), - parasitic_ap_(corner_->findParasiticAnalysisPt(min_max_)) + scene_(scene), + min_max_(MinMax::max()) { } void -ReportParasiticAnnotation::report() +ReportParasiticAnnotation::reportAnnotation() { findCounts(); reportAnnotationCounts(); @@ -88,25 +92,26 @@ ReportParasiticAnnotation::report() void ReportParasiticAnnotation::reportAnnotationCounts() { - report_->reportLine("Found %zu unannotated drivers.", unannotated_.size()); + report_->report("Found {} unannotated drivers.", unannotated_.size()); if (report_unannotated_) { sort(unannotated_, PinPathNameLess(network_)); for (const Pin *drvr_pin : unannotated_) - report_->reportLine(" %s", network_->pathName(drvr_pin)); + report_->report(" {}", network_->pathName(drvr_pin)); } - report_->reportLine("Found %zu partially unannotated drivers.", - partially_annotated_.size()); + report_->report("Found {} partially unannotated drivers.", + partially_annotated_.size()); if (report_unannotated_) { sort(partially_annotated_, PinPathNameLess(network_)); for (const Pin *drvr_pin : partially_annotated_) { - report_->reportLine(" %s", network_->pathName(drvr_pin)); + report_->report(" {}", network_->pathName(drvr_pin)); - Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap_); + Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin); if (parasitic) { - PinSet unannotated_loads = parasitics_->unannotatedLoads(parasitic, drvr_pin); + PinSet unannotated_loads = + parasitics_->unannotatedLoads(parasitic, drvr_pin); for (const Pin *load_pin : unannotated_loads) - report_->reportLine(" %s", network_->pathName(load_pin)); + report_->report(" {}", network_->pathName(load_pin)); } } } @@ -115,26 +120,25 @@ ReportParasiticAnnotation::reportAnnotationCounts() void ReportParasiticAnnotation::findCounts() { - DcalcAnalysisPt *dcalc_ap = corner_->findDcalcAnalysisPt(min_max_); VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); Pin *pin = vertex->pin(); PortDirection *dir = network_->direction(pin); - if (vertex->isDriver(network_) - && !dir->isInternal()) { - Parasitic *parasitic = parasitics_->findParasiticNetwork(pin, parasitic_ap_); + if (vertex->isDriver(network_) && !dir->isInternal()) { + Parasitic *parasitic = parasitics_->findParasiticNetwork(pin); if (parasitic == nullptr) - parasitic = arc_delay_calc_->findParasitic(pin, RiseFall::rise(), dcalc_ap); + parasitic = + arc_delay_calc_->findParasitic(pin, RiseFall::rise(), scene_, min_max_); if (parasitic) { PinSet unannotated_loads = parasitics_->unannotatedLoads(parasitic, pin); - if (unannotated_loads.size() > 0) + if (!unannotated_loads.empty()) partially_annotated_.push_back(pin); } - else + else unannotated_.push_back(pin); } } } -} // namespace +} // namespace sta diff --git a/parasitics/ReportParasiticAnnotation.hh b/parasitics/ReportParasiticAnnotation.hh index eb446c05e..fab788bfb 100644 --- a/parasitics/ReportParasiticAnnotation.hh +++ b/parasitics/ReportParasiticAnnotation.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,12 +26,14 @@ namespace sta { +class Parasitics; class StaState; -class Corner; +class Scene; void -reportParasiticAnnotation(bool report_unannotated, - const Corner *corner, +reportParasiticAnnotation(Parasitics *parasitics, + bool report_unannotated, + const Scene *scene, StaState *sta); -} // namespace +} // namespace sta diff --git a/parasitics/SpefLex.ll b/parasitics/SpefLex.ll index bf7bbdde5..0cb0dda48 100644 --- a/parasitics/SpefLex.ll +++ b/parasitics/SpefLex.ll @@ -73,11 +73,11 @@ FLOAT {DECIMAL}|{FRACTION}|{EXP} HCHAR "."|"/"|"|"|":" PREFIX_BUS_DELIM "["|"{"|"("|"<" SUFFIX_BUS_DELIM "]"|"}"|")"|">" -SPECIAL_CHAR "!"|"#"|"$"|"%"|"&"|"`"|"("|")"|"*"|"+"|","|"-"|"."|"/"|":"|";"|"<"|"="|">"|"?"|"@"|"["|"\\"|"]"|"^"|"'"|"{"|"|"|"}"|"~" +SPECIAL_CHAR "#"|"$"|"%"|"&"|"`"|"("|")"|"*"|"+"|","|"-"|"."|"/"|":"|";"|"<"|"="|">"|"?"|"@"|"["|"\\"|"]"|"^"|"'"|"{"|"|"|"}"|"~" ESCAPED_CHAR_SET {SPECIAL_CHAR}|\" ESCAPED_CHAR \\{ESCAPED_CHAR_SET} IDENT_ACHAR {ESCAPED_CHAR}|{ALPHA}|"_" -IDENT_CHAR {IDENT_ACHAR}|{DIGIT} +IDENT_CHAR {IDENT_ACHAR}|{DIGIT}|"!" ID {IDENT_ACHAR}{IDENT_CHAR}* BUS_SUB {DIGIT}|{ALPHA}|"_" BIT_IDENT {ID}({PREFIX_BUS_DELIM}{BUS_SUB}+{SUFFIX_BUS_DELIM})+ @@ -163,7 +163,7 @@ INDEX "*"{POS_INTEGER} "\"" { BEGIN INITIAL; - yylval->string = sta::stringCopy(token_.c_str()); + yylval->emplace(std::move(token_)); return token::QSTRING; } @@ -179,27 +179,27 @@ INDEX "*"{POS_INTEGER} {BLANK}*\n { loc->lines(); loc->step(); } {INTEGER} { - yylval->integer = atoi(yytext); + yylval->emplace(atoi(yytext)); return token::INTEGER; } {FLOAT} { - yylval->number = static_cast(atof(yytext)); + yylval->emplace(static_cast(atof(yytext))); return token::FLOAT; } {IDENT} { - yylval->string = reader_->translated(yytext); + yylval->emplace(reader_->translated(yytext)); return token::IDENT; } {PATH}|{NAME_PAIR} { - yylval->string = reader_->translated(yytext); + yylval->emplace(reader_->translated(yytext)); return token::NAME; } {INDEX} { - yylval->string = sta::stringCopy(yytext); + yylval->emplace(yytext); return token::INDEX; } diff --git a/parasitics/SpefNamespace.cc b/parasitics/SpefNamespace.cc index b9178a1f0..9c380a011 100644 --- a/parasitics/SpefNamespace.cc +++ b/parasitics/SpefNamespace.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,97 +26,92 @@ #include #include +#include namespace sta { -char * -spefToSta(const char *token, +std::string +spefToSta(std::string_view spef_name, char spef_divider, - char path_divider, + char path_divider, char path_escape) { const char spef_escape = '\\'; - char *trans_token = new char[strlen(token) + 1]; - char *t = trans_token; - - for (const char *s = token; *s ; s++) { - char ch = *s; + std::string sta_name; + for (size_t i = 0; i < spef_name.size(); i++) { + char ch = spef_name[i]; if (ch == spef_escape) { - char next_ch = s[1]; + char next_ch = spef_name[i + 1]; if (next_ch == spef_divider) { - // Translate spef escape to network escape. - *t++ = path_escape; - // Translate spef divider to network divider. - *t++ = path_divider; + // Translate spef escape to network escape. + sta_name += path_escape; + // Translate spef divider to network divider. + sta_name += path_divider; } else if (next_ch == '[' - || next_ch == ']' - || next_ch == spef_escape) { - // Translate spef escape to network escape. - *t++ = path_escape; - *t++ = next_ch; + || next_ch == ']' + || next_ch == spef_escape) { + // Translate spef escape to network escape. + sta_name += path_escape; + sta_name += next_ch; } else - // No need to escape other characters. - *t++ = next_ch; - s++; + // No need to escape other characters. + sta_name += next_ch; + i++; } else if (ch == spef_divider) // Translate spef divider to network divider. - *t++ = path_divider; + sta_name += path_divider; else // Just the normal noises. - *t++ = ch; + sta_name += ch; } - *t++ = '\0'; - return trans_token; + return sta_name; } -char * -staToSpef(const char *token, +std::string +staToSpef(std::string_view sta_name, char spef_divider, - char path_divider, + char path_divider, char path_escape) { const char spef_escape = '\\'; - char *trans_token = new char[strlen(token) + 1]; - char *t = trans_token; - - for (const char *s = token; *s ; s++) { - char ch = *s; + std::string spef_name; + for (size_t i = 0; i < sta_name.size(); i++) { + char ch = sta_name[i]; if (ch == path_escape) { - char next_ch = s[1]; + char next_ch = sta_name[i + 1]; if (next_ch == path_divider) { - // Translate network escape to spef escape. - *t++ = spef_escape; - // Translate network divider to spef divider. - *t++ = spef_divider; + // Translate network escape to spef escape. + spef_name += spef_escape; + // Translate network divider to spef divider. + spef_name += spef_divider; } else if (next_ch == '[' - || next_ch == ']') { - // Translate network escape to spef escape. - *t++ = spef_escape; - *t++ = next_ch; + || next_ch == ']') { + // Translate network escape to spef escape. + spef_name += spef_escape; + spef_name += next_ch; } else - // No need to escape other characters. - *t++ = next_ch; - s++; + // No need to escape other characters. + spef_name += next_ch; + i++; } else if (ch == path_divider) // Translate network divider to spef divider. - *t++ = spef_divider; + spef_name += spef_divider; else if (!(isdigit(ch) || isalpha(ch) || ch == '_')) { // Escape non-alphanum characters. - *t++ = spef_escape; - *t++ = ch; + spef_name += spef_escape; + spef_name += ch; } else // Just the normal noises. - *t++ = ch; + spef_name += ch; } - *t++ = '\0'; - return trans_token; + return spef_name; } -} // namespace +} // namespace sta diff --git a/parasitics/SpefNamespace.hh b/parasitics/SpefNamespace.hh index 15f47dede..6761b21e7 100644 --- a/parasitics/SpefNamespace.hh +++ b/parasitics/SpefNamespace.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,18 +24,22 @@ #pragma once +#include + namespace sta { // Translate from spf/spef namespace to sta namespace. -// Caller owns the result string. -char * -spefToSta(const char *token, char spef_divider, - char path_escape, char path_divider); -// Translate from sta namespace to spf/spef namespace. -// Caller owns the result string. -char * -staToSpef(const char *token, char spef_divider, - char path_divider, char path_escape); +std::string +spefToSta(std::string_view spef_name, + char spef_divider, + char path_divider, + char path_escape); +// Translate from sta namespace to spf/spef namespace. +std::string +staToSpef(std::string_view sta_name, + char spef_divider, + char path_divider, + char path_escape); -} // namespace +} // namespace sta diff --git a/parasitics/SpefParse.yy b/parasitics/SpefParse.yy index b8fbb0c29..fed603c07 100755 --- a/parasitics/SpefParse.yy +++ b/parasitics/SpefParse.yy @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -23,11 +23,11 @@ // This notice may not be removed or altered from any source distribution. %{ -#include +#include +#include #include "Report.hh" #include "StringUtil.hh" -#include "StringSeq.hh" #include "parasitics/SpefReaderPvt.hh" #include "parasitics/SpefScanner.hh" @@ -41,11 +41,27 @@ void sta::SpefParse::error(const location_type &loc, const std::string &msg) { - reader->report()->fileError(164,reader->filename(), - loc.begin.line,"%s",msg.c_str()); + reader->report()->fileError(1670, reader->filename(), loc.begin.line, "{}", msg); } %} +%code requires { +#include +#include "StringUtil.hh" + +namespace sta { +// Bison's C++ variant skeleton cannot use void as a semantic type. +struct SpefParseVoid {}; +class SpefReader; +class SpefScanner; +class Net; +class Pin; +class PortDirection; +class SpefRspfPi; +class SpefTriple; +} +} + %require "3.2" %skeleton "lalr1.cc" %debug @@ -56,19 +72,11 @@ sta::SpefParse::error(const location_type &loc, %parse-param { SpefScanner *scanner } %parse-param { SpefReader *reader } %define api.parser.class {SpefParse} +%define api.value.type variant -%union { - char ch; - char *string; - int integer; - float number; - sta::StringSeq *string_seq; - sta::PortDirection *port_dir; - sta::SpefRspfPi *pi; - sta::SpefTriple *triple; - sta::Pin *pin; - sta::Net *net; -} +%token INTEGER +%token FLOAT +%token QSTRING INDEX IDENT NAME %token SPEF DESIGN DATE VENDOR PROGRAM DESIGN_FLOW %token PVERSION DIVIDER DELIMITER @@ -79,48 +87,42 @@ sta::SpefParse::error(const location_type &loc, %token CONN CAP RES INDUC KW_P KW_I KW_N DRIVER CELL C2_R1_C1 LOADS %token RC KW_Q KW_K -%token INTEGER FLOAT QSTRING INDEX IDENT NAME +%type conf cap_id res_id induc_id cap_elem cap_elems +%type res_elem res_elems induc_elem induc_elems +%type pos_integer -%type INTEGER -%type FLOAT -%type QSTRING INDEX IDENT NAME +%type number pos_number threshold +%type real_component imaginary_component pole poles +%type residue residues complex_par_value cnumber -%type conf cap_id res_id induc_id cap_elem cap_elems -%type res_elem res_elems induc_elem induc_elems -%type pos_integer +%nterm name_map_entries name_map_entry net_names +%nterm net_name port_entries port_entry pport_entries pport_entry +%nterm pport_name pexternal_connection -%type number pos_number threshold -%type real_component imaginary_component pole poles -%type residue residues complex_par_value cnumber +%type name_or_index inst_name +%type mapped_item +%type physical_inst port_name pport +%type entity external_connection +%type cell_type +%type driver_cell pnet_ref +%type internal_pdspf_node +%type parasitic_node internal_parasitic_node -%type name_or_index net_name net_names inst_name -%type name_map_entries name_map_entry mapped_item -%type physical_inst port_name pport_name port_entry pport_entry -%type port_entries pport_entries pport -%type entity external_connection -%type cell_type -%type driver_cell pnet_ref -%type pexternal_connection internal_pdspf_node -%type parasitic_node internal_parasitic_node +%type hchar suffix_bus_delim prefix_bus_delim -%type hchar suffix_bus_delim prefix_bus_delim +%type qstrings +%type direction -%type qstrings -%type direction +%type par_value total_cap -%type par_value total_cap +%type pi_model -%type pi_model +%type pin_name driver_pair internal_connection -%type pin_name driver_pair internal_connection - -%type net +%type net %start file -%{ -%} - %% file: @@ -185,32 +187,26 @@ header_def: spef_version: SPEF QSTRING - { sta::stringDelete($2); } ; design_name: DESIGN QSTRING - { sta::stringDelete($2); } ; date: DATE QSTRING - { sta::stringDelete($2); } ; program_name: PROGRAM QSTRING - { sta::stringDelete($2); } ; program_version: PVERSION QSTRING - { sta::stringDelete($2); } ; vendor: VENDOR QSTRING - { sta::stringDelete($2); } ; design_flow: @@ -221,10 +217,12 @@ design_flow: qstrings: QSTRING { $$ = new sta::StringSeq; - $$->push_back($1); + $$->push_back(std::move($1)); } | qstrings QSTRING - { $$->push_back($2); } + { $$ = $1; + $$->push_back(std::move($2)); + } ; hierarchy_div_def: @@ -320,7 +318,7 @@ net_names: net_name: name_or_index - { sta::stringDelete($1); } + { $$ = sta::SpefParseVoid{}; } ; /****************************************************************/ @@ -343,14 +341,12 @@ port_entries: port_entry: port_name direction conn_attrs - { sta::stringDelete($1); } + { $$ = sta::SpefParseVoid{}; } ; direction: IDENT - { $$ = reader->portDirection($1); - sta::stringDelete($1); - } + { $$ = reader->portDirection($1); } ; port_name: @@ -372,15 +368,14 @@ pport_entries: pport_entry: pport_name IDENT conn_attrs + { $$ = sta::SpefParseVoid{}; } ; pport_name: name_or_index - { sta::stringDelete($1); } + { $$ = sta::SpefParseVoid{}; } | physical_inst ':' pport - { sta::stringDelete($1); - sta::stringDelete($3); - } + { $$ = sta::SpefParseVoid{}; } ; pport: @@ -439,7 +434,6 @@ threshold: driving_cell: KW_D cell_type - { sta::stringDelete($2); } ; cell_type: @@ -456,18 +450,8 @@ define_def: define_entry: DEFINE inst_name entity - { sta::stringDelete($2); - sta::stringDelete($3); - } | DEFINE inst_name inst_name entity - { sta::stringDelete($2); - sta::stringDelete($3); - sta::stringDelete($4); - } | PDEFINE physical_inst entity - { sta::stringDelete($2); - sta::stringDelete($3); - } ; entity: @@ -499,9 +483,7 @@ d_net: net: name_or_index - { $$ = reader->findNet($1); - sta::stringDelete($1); - } + { $$ = reader->findNet($1); } ; total_cap: @@ -536,11 +518,9 @@ conn_def: external_connection: name_or_index - { sta::stringDelete($1); } + { $$ = std::string(); } | physical_inst ':' pport - { sta::stringDelete($1); - sta::stringDelete($3); - } + { $$ = std::string(); } ; internal_connection: @@ -549,9 +529,7 @@ internal_connection: pin_name: name_or_index - { $$ = reader->findPin($1); - sta::stringDelete($1); - } + { $$ = reader->findPin($1); } ; internal_node_coords: @@ -565,7 +543,7 @@ internal_node_coord: internal_parasitic_node: name_or_index - { sta::stringDelete($1); } + { $$ = std::string(); } ; /****************************************************************/ @@ -579,6 +557,7 @@ cap_elems: /* empty */ { $$ = 0; } | cap_elems cap_elem + { $$ = $1; } ; cap_elem: @@ -607,6 +586,7 @@ res_elems: /* empty */ { $$ = 0; } | res_elems res_elem + { $$ = $1; } ; res_elem: @@ -629,6 +609,7 @@ induc_elems: /* empty */ { $$ = 0; } | induc_elems induc_elem + { $$ = $1; } ; induc_elem: @@ -656,9 +637,7 @@ driver_reducs: driver_reduc: driver_pair driver_cell pi_model - { reader->rspfDrvrBegin($1, $3); - sta::stringDelete($2); - } + { reader->rspfDrvrBegin($1, $3); } load_desc { reader->rspfDrvrFinish(); } ; @@ -707,6 +686,7 @@ pole_desc: poles: pole | poles pole + { $$ = $2; } ; pole: @@ -751,7 +731,6 @@ residue: d_pnet: D_PNET pnet_ref total_cap routing_conf pconn_sec cap_sec res_sec induc_sec END - { sta::stringDelete($2); } ; pnet_ref: @@ -787,10 +766,7 @@ internal_pnode_coord: internal_pdspf_node: name_or_index - { - sta::stringDelete($1); - $$ = 0; - } + { $$ = std::string(); } ; name_or_index: @@ -803,9 +779,7 @@ name_or_index: r_pnet: R_PNET pnet_ref total_cap routing_conf END - { sta::stringDelete($2); } | R_PNET pnet_ref total_cap routing_conf pdriver_reduc END - { sta::stringDelete($2); } ; pdriver_reduc: @@ -822,13 +796,14 @@ number: INTEGER { $$ = static_cast($1); } | FLOAT + { $$ = $1; } ; pos_integer: INTEGER { int value = $1; if (value < 0) - reader->warn(1525, "%d is not positive.", value); + reader->warn(1525, "{} is not positive.", value); $$ = value; } ; @@ -837,13 +812,13 @@ pos_number: INTEGER { float value = static_cast($1); if (value < 0) - reader->warn(1526, "%.4f is not positive.", value); + reader->warn(1526, "{:.4f} is not positive.", value); $$ = value; } | FLOAT { float value = static_cast($1); if (value < 0) - reader->warn(1527, "%.4f is not positive.", value); + reader->warn(1527, "{:.4f} is not positive.", value); $$ = value; } ; diff --git a/parasitics/SpefReader.cc b/parasitics/SpefReader.cc index e81d691fc..051416730 100644 --- a/parasitics/SpefReader.cc +++ b/parasitics/SpefReader.cc @@ -1,127 +1,106 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "SpefReader.hh" -#include "Zlib.hh" -#include "Stats.hh" -#include "Report.hh" +#include +#include +#include + +#include "ArcDelayCalc.hh" #include "Debug.hh" -#include "StringUtil.hh" -#include "Map.hh" -#include "Transition.hh" #include "Liberty.hh" #include "Network.hh" +#include "Parasitics.hh" #include "PortDirection.hh" +#include "Report.hh" +#include "Scene.hh" #include "Sdc.hh" -#include "Parasitics.hh" -#include "Corner.hh" -#include "ArcDelayCalc.hh" -#include "SpefReaderPvt.hh" #include "SpefNamespace.hh" +#include "SpefReaderPvt.hh" +#include "Stats.hh" +#include "StringUtil.hh" +#include "Transition.hh" +#include "Zlib.hh" #include "parasitics/SpefScanner.hh" namespace sta { -using std::string; - bool -readSpefFile(const char *filename, - Instance *instance, - ParasiticAnalysisPt *ap, - bool pin_cap_included, - bool keep_coupling_caps, - float coupling_cap_factor, - bool reduce, - const Corner *corner, - const MinMaxAll *min_max, +readSpefFile(std::string_view filename, + Instance *instance, + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + bool reduce, + const Scene *scene, + const MinMaxAll *min_max, + Parasitics *parasitics, StaState *sta) { - SpefReader reader(filename, instance, ap, - pin_cap_included, keep_coupling_caps, coupling_cap_factor, - reduce, corner, min_max, sta); + SpefReader reader(filename, instance, pin_cap_included, keep_coupling_caps, + coupling_cap_factor, reduce, scene, min_max, parasitics, sta); bool success = reader.read(); return success; } -SpefReader::SpefReader(const char *filename, - Instance *instance, - ParasiticAnalysisPt *ap, - bool pin_cap_included, - bool keep_coupling_caps, - float coupling_cap_factor, - bool reduce, - const Corner *corner, - const MinMaxAll *min_max, - StaState *sta) : +SpefReader::SpefReader(std::string_view filename, + Instance *instance, + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + bool reduce, + const Scene *scene, + const MinMaxAll *min_max, + Parasitics *parasitics, + StaState *sta) : StaState(sta), filename_(filename), instance_(instance), - ap_(ap), pin_cap_included_(pin_cap_included), keep_coupling_caps_(keep_coupling_caps), + coupling_cap_factor_(coupling_cap_factor), reduce_(reduce), - corner_(corner), + scene_(scene), min_max_(min_max), - // defaults - divider_('\0'), - delimiter_('\0'), - bus_brkt_left_('\0'), - bus_brkt_right_('\0'), - net_(nullptr), - triple_index_(0), - time_scale_(1.0), - cap_scale_(1.0), - res_scale_(1.0), - induct_scale_(1.0), - design_flow_(nullptr), - parasitic_(nullptr) -{ - ap->setCouplingCapFactor(coupling_cap_factor); -} - -SpefReader::~SpefReader() -{ - if (design_flow_) { - deleteContents(design_flow_); - delete design_flow_; - design_flow_ = nullptr; - } + parasitics_(parasitics) +{ + parasitics->setCouplingCapFactor(coupling_cap_factor); } bool SpefReader::read() { bool success; - gzstream::igzstream stream(filename_); + gzstream::igzstream stream(std::string(filename_).c_str()); if (stream.is_open()) { Stats stats(debug_, report_); SpefScanner scanner(&stream, filename_, this, report_); scanner_ = &scanner; SpefParse parser(&scanner, this); - //parser.set_debug_level(1); - // yyparse returns 0 on success. + // parser.set_debug_level(1); + // yyparse returns 0 on success. success = (parser.parse() == 0); stats.report("Read spef"); } @@ -146,25 +125,22 @@ void SpefReader::setBusBrackets(char left, char right) { - if (!((left == '[' && right == ']') - || (left == '{' && right == '}') - || (left == '(' && right == ')') - || (left == '<' && right == '>') - || (left == ':' && right == '\0') - || (left == '.' && right == '\0'))) + if (!((left == '[' && right == ']') || (left == '{' && right == '}') + || (left == '(' && right == ')') || (left == '<' && right == '>') + || (left == ':' && right == '\0') || (left == '.' && right == '\0'))) warn(1640, "illegal bus delimiters."); bus_brkt_left_ = left; bus_brkt_right_ = right; } Instance * -SpefReader::findInstanceRelative(const char *name) +SpefReader::findInstanceRelative(std::string_view name) { return sdc_network_->findInstanceRelative(instance_, name); } Net * -SpefReader::findNetRelative(const char *name) +SpefReader::findNetRelative(std::string_view name) { Net *net = network_->findNetRelative(instance_, name); // Relax spef escaping requirement because some commercial tools @@ -175,108 +151,101 @@ SpefReader::findNetRelative(const char *name) } Pin * -SpefReader::findPinRelative(const char *name) +SpefReader::findPinRelative(std::string_view name) { return network_->findPinRelative(instance_, name); } Pin * -SpefReader::findPortPinRelative(const char *name) +SpefReader::findPortPinRelative(std::string_view name) { return network_->findPin(instance_, name); } -char * -SpefReader::translated(const char *token) +std::string +SpefReader::translated(std::string_view spef_name) { - return spefToSta(token, divider_, network_->pathDivider(), - network_->pathEscape()); + return spefToSta(spef_name, divider_, network_->pathDivider(), + network_->pathEscape()); } -void -SpefReader::warn(int id, const char *fmt, ...) +int +SpefReader::warnLine() const { - va_list args; - va_start(args, fmt); - report_->vfileWarn(id, filename_, scanner_->line(), fmt, args); - va_end(args); + return scanner_->line(); } + void SpefReader::setTimeScale(float scale, - const char *units) + std::string_view units) { - if (stringEq(units, "NS")) + if (stringEqual(units, "NS")) time_scale_ = scale * 1E-9F; - else if (stringEq(units, "PS")) + else if (stringEqual(units, "PS")) time_scale_ = scale * 1E-12F; else - warn(1641, "unknown units %s.", units); - stringDelete(units); + warn(1641, "unknown units {}.", units); } void SpefReader::setCapScale(float scale, - const char *units) + std::string_view units) { - if (stringEq(units, "PF")) + if (stringEqual(units, "PF")) cap_scale_ = scale * 1E-12F; - else if (stringEq(units, "FF")) + else if (stringEqual(units, "FF")) cap_scale_ = scale * 1E-15F; else - warn(1642, "unknown units %s.", units); - stringDelete(units); + warn(1642, "unknown units {}.", units); } void SpefReader::setResScale(float scale, - const char *units) + std::string_view units) { - if (stringEq(units, "OHM")) + if (stringEqual(units, "OHM")) res_scale_ = scale; - else if (stringEq(units, "KOHM")) + else if (stringEqual(units, "KOHM")) res_scale_ = scale * 1E+3F; else - warn(1643, "unknown units %s.", units); - stringDelete(units); + warn(1643, "unknown units {}.", units); } void SpefReader::setInductScale(float scale, - const char *units) + std::string_view units) { - if (stringEq(units, "HENRY")) + if (stringEqual(units, "HENRY")) induct_scale_ = scale; - else if (stringEq(units, "MH")) + else if (stringEqual(units, "MH")) induct_scale_ = scale * 1E-3F; - else if (stringEq(units, "UH")) + else if (stringEqual(units, "UH")) induct_scale_ = scale * 1E-6F; else - warn(1644, "unknown units %s.", units); - stringDelete(units); + warn(1644, "unknown units {}.", units); } void -SpefReader::makeNameMapEntry(const char *index, - const char *name) +SpefReader::makeNameMapEntry(std::string_view index, + std::string_view name) { - int i = atoi(index + 1); + int i = std::stoi(std::string(index.substr(1))); name_map_[i] = name; - stringDelete(index); - stringDelete(name); } -const char * -SpefReader::nameMapLookup(const char *name) +std::string_view +SpefReader::nameMapLookup(std::string_view name) { - if (name && name[0] == '*') { - int index = atoi(name + 1); + if (!name.empty() && name[0] == '*') { + std::string index_str(name.substr(1)); + int index = std::stoi(index_str); const auto &itr = name_map_.find(index); if (itr != name_map_.end()) - return itr->second.c_str(); + return itr->second; else { - warn(1645, "no name map entry for %d.", index); - return nullptr; + warn(1645, "no name map entry for {}.", index); + return ""; } } else @@ -284,77 +253,78 @@ SpefReader::nameMapLookup(const char *name) } PortDirection * -SpefReader::portDirection(char *spef_dir) +SpefReader::portDirection(std::string_view spef_dir) { PortDirection *direction = PortDirection::unknown(); - if (stringEq(spef_dir, "I")) + if (spef_dir == "I") direction = PortDirection::input(); - else if (stringEq(spef_dir, "O")) + else if (spef_dir == "O") direction = PortDirection::output(); - else if (stringEq(spef_dir, "B")) + else if (spef_dir == "B") direction = PortDirection::bidirect(); else - warn(1646, "unknown port direction %s.", spef_dir); + warn(1646, "unknown port direction {}.", spef_dir); return direction; } void SpefReader::setDesignFlow(StringSeq *flow) { - design_flow_ = flow; + design_flow_ = *flow; + delete flow; } Pin * -SpefReader::findPin(char *name) +SpefReader::findPin(std::string_view name) { Pin *pin = nullptr; - if (name) { - char *delim = strrchr(name, delimiter_); - if (delim) { - *delim = '\0'; - const char *name1 = nameMapLookup(name); - if (name1) { - Instance *inst = findInstanceRelative(name1); - // Replace delimiter for error messages. - *delim = delimiter_; - const char *port_name = delim + 1; + if (!name.empty()) { + size_t delim = name.rfind(delimiter_); + if (delim != std::string::npos) { + std::string inst_name_mapped(name.substr(0, delim)); + std::string_view inst_name = nameMapLookup(inst_name_mapped); + if (!inst_name.empty()) { + Instance *inst = findInstanceRelative(inst_name); + std::string port_name(name.substr(delim + 1, std::string::npos)); if (inst) { pin = network_->findPin(inst, port_name); if (pin == nullptr) - warn(1647, "pin %s not found.", name1); + warn(1647, "pin {}{}{} not found.", + inst_name, delimiter_, port_name); } else - warn(1648, "instance %s not found.", name1); + warn(1648, "instance {}{}{} not found.", + inst_name, delimiter_, port_name); } } else { pin = findPortPinRelative(name); if (pin == nullptr) - warn(1649, "pin %s not found.", name); + warn(1649, "pin {} not found.", name); } } return pin; } Net * -SpefReader::findNet(const char *name) +SpefReader::findNet(std::string_view name) { Net *net = nullptr; - const char *name1 = nameMapLookup(name); - if (name1) { + std::string_view name1 = nameMapLookup(name); + if (!name1.empty()) { net = findNetRelative(name1); if (net == nullptr) - warn(1650, "net %s not found.", name1); + warn(1650, "net {} not found.", name1); } return net; } void SpefReader::rspfBegin(Net *net, - SpefTriple *total_cap) + SpefTriple *total_cap) { if (net) - parasitics_->deleteParasitics(net, ap_); + parasitics_->deleteParasitics(net); // Net total capacitance is ignored. delete total_cap; } @@ -366,14 +336,14 @@ SpefReader::rspfFinish() void SpefReader::rspfDrvrBegin(Pin *drvr_pin, - SpefRspfPi *pi) + SpefRspfPi *pi) { if (drvr_pin) { float c2 = pi->c2()->value(triple_index_) * cap_scale_; float rpi = pi->r1()->value(triple_index_) * res_scale_; float c1 = pi->c1()->value(triple_index_) * cap_scale_; // Only one parasitic, save it under rise transition. - parasitic_ = parasitics_->makePiElmore(drvr_pin, RiseFall::rise(), ap_, + parasitic_ = parasitics_->makePiElmore(drvr_pin, RiseFall::rise(), MinMax::max(), c2, rpi, c1); } delete pi; @@ -381,7 +351,7 @@ SpefReader::rspfDrvrBegin(Pin *drvr_pin, void SpefReader::rspfLoad(Pin *load_pin, - SpefTriple *rc) + SpefTriple *rc) { if (parasitic_ && load_pin) { float elmore = rc->value(triple_index_) * time_scale_; @@ -399,12 +369,12 @@ SpefReader::rspfDrvrFinish() // Net cap (total_cap) is ignored. void SpefReader::dspfBegin(Net *net, - SpefTriple *total_cap) + SpefTriple *total_cap) { if (net) { if (network_->isTopInstance(instance_)) { - parasitics_->deleteReducedParasitics(net, ap_); - parasitic_ = parasitics_->makeParasiticNetwork(net, pin_cap_included_, ap_); + parasitics_->deleteReducedParasitics(net); + parasitic_ = parasitics_->makeParasiticNetwork(net, pin_cap_included_); } else { Net *parasitic_owner = net; @@ -415,10 +385,10 @@ SpefReader::dspfBegin(Net *net, parasitic_owner = network_->net(hpin); } delete term_iter; - parasitic_ = parasitics_->findParasiticNetwork(parasitic_owner, ap_); + parasitic_ = parasitics_->findParasiticNetwork(parasitic_owner); if (parasitic_ == nullptr) - parasitic_ = parasitics_->makeParasiticNetwork(parasitic_owner, - pin_cap_included_, ap_); + parasitic_ = + parasitics_->makeParasiticNetwork(parasitic_owner, pin_cap_included_); } net_ = net; } @@ -433,87 +403,81 @@ void SpefReader::dspfFinish() { if (parasitic_ && reduce_) { - arc_delay_calc_->reduceParasitic(parasitic_, net_, corner_, min_max_); - parasitics_->deleteParasiticNetwork(net_, ap_); + arc_delay_calc_->reduceParasitic(parasitic_, net_, scene_, min_max_); + parasitics_->deleteParasiticNetwork(net_); } parasitic_ = nullptr; net_ = nullptr; } ParasiticNode * -SpefReader::findParasiticNode(char *name, - bool local_only) -{ - if (name && parasitic_) { - char *delim = strrchr(name, delimiter_); - if (delim) { - *delim = '\0'; - char *name2 = delim + 1; - const char *name1 = nameMapLookup(name); - if (name1) { +SpefReader::findParasiticNode(std::string_view name, + bool local_only) +{ + if (!name.empty() && parasitic_) { + size_t delim = name.rfind(delimiter_); + if (delim != std::string::npos) { + std::string name1_mapped(name.substr(0, delim)); + std::string name2(name.substr(delim + 1, std::string::npos)); + std::string_view name1 = nameMapLookup(name1_mapped); + if (!name1.empty()) { Instance *inst = findInstanceRelative(name1); if (inst) { // : Pin *pin = network_->findPin(inst, name2); if (pin) { - if (local_only - && !network_->isConnected(net_, pin)) - warn(1651, "%s not connected to net %s.", - name1, sdc_network_->pathName(net_)); + if (local_only && !network_->isConnected(net_, pin)) + warn(1651, "{} not connected to net {}.", name1, + sdc_network_->pathName(net_)); return parasitics_->ensureParasiticNode(parasitic_, pin, network_); } - else { - // Replace delimiter for error message. - *delim = delimiter_; - warn(1652, "pin %s not found.", name1); - } + else + warn(1652, "pin {}{}{} not found.", + name1, delimiter_, name2); } else { Net *net = findNet(name1); - // Replace delimiter for error messages. - *delim = delimiter_; if (net) { // : - const char *id_str = delim + 1; - if (isDigits(id_str)) { - int id = atoi(id_str); - if (local_only - && !network_->isConnected(net, net_)) - warn(1653, "%s not connected to net %s.", - name1, - network_->pathName(net_)); + if (isDigits(name2)) { + uint32_t id = std::stoi(name2); + if (local_only && !network_->isConnected(net, net_)) + warn(1653, "{}{}{} not connected to net {}.", + name1, delimiter_, name2, network_->pathName(net_)); return parasitics_->ensureParasiticNode(parasitic_, net, id, network_); } else - warn(1654, "node %s not a pin or net:number", name1); + warn(1654, "node {}{}{} not a pin or net:number", + name1, delimiter_, name2); } } } } else { // - const char *name1 = nameMapLookup(name); - if (name1) { + std::string_view name1 = nameMapLookup(name); + if (!name1.empty()) { Pin *pin = findPortPinRelative(name1); if (pin) { - if (local_only - && !network_->isConnected(net_, pin)) - warn(1655, "%s not connected to net %s.", name1, network_->pathName(net_)); + if (local_only && !network_->isConnected(net_, pin)) + warn(1655, "{} not connected to net {}.", name1, + network_->pathName(net_)); return parasitics_->ensureParasiticNode(parasitic_, pin, network_); } else - warn(1656, "pin %s not found.", name1); + warn(1656, "pin {} not found.", name1); } else - warn(1657, "pin %s not found.", name); + warn(1657, "pin {} not found.", name); } } return nullptr; } void -SpefReader::makeCapacitor(int, char *node_name, - SpefTriple *cap) +SpefReader::makeCapacitor(uint32_t, + std::string_view node_name, + SpefTriple *cap) { ParasiticNode *node = findParasiticNode(node_name, true); if (node) { @@ -521,14 +485,13 @@ SpefReader::makeCapacitor(int, char *node_name, parasitics_->incrCap(node, cap1); } delete cap; - stringDelete(node_name); } void -SpefReader::makeCapacitor(int id, - char *node_name1, - char *node_name2, - SpefTriple *cap) +SpefReader::makeCapacitor(uint32_t id, + std::string_view node_name1, + std::string_view node_name2, + SpefTriple *cap) { ParasiticNode *node1 = findParasiticNode(node_name1, false); ParasiticNode *node2 = findParasiticNode(node_name2, false); @@ -537,7 +500,7 @@ SpefReader::makeCapacitor(int id, if (keep_coupling_caps_) parasitics_->makeCapacitor(parasitic_, id, cap1, node1, node2); else { - float scaled_cap = cap1 * ap_->couplingCapFactor(); + float scaled_cap = cap1 * coupling_cap_factor_; if (node1 && parasitics_->net(node1, network_) == net_) parasitics_->incrCap(node1, scaled_cap); if (node2 && parasitics_->net(node2, network_) == net_) @@ -545,15 +508,13 @@ SpefReader::makeCapacitor(int id, } } delete cap; - stringDelete(node_name1); - stringDelete(node_name2); } void -SpefReader::makeResistor(int id, - char *node_name1, - char *node_name2, - SpefTriple *res) +SpefReader::makeResistor(uint32_t id, + std::string_view node_name1, + std::string_view node_name2, + SpefTriple *res) { ParasiticNode *node1 = findParasiticNode(node_name1, true); ParasiticNode *node2 = findParasiticNode(node_name2, true); @@ -562,15 +523,13 @@ SpefReader::makeResistor(int id, parasitics_->makeResistor(parasitic_, id, res1, node1, node2); } delete res; - stringDelete(node_name1); - stringDelete(node_name2); } //////////////////////////////////////////////////////////////// SpefRspfPi::SpefRspfPi(SpefTriple *c2, - SpefTriple *r1, - SpefTriple *c1) : + SpefTriple *r1, + SpefTriple *c1) : c2_(c2), r1_(r1), c1_(c1) @@ -593,8 +552,8 @@ SpefTriple::SpefTriple(float value) : } SpefTriple::SpefTriple(float value1, - float value2, - float value3) : + float value2, + float value3) : is_triple_(true) { values_[0] = value1; @@ -614,7 +573,7 @@ SpefTriple::value(int index) const //////////////////////////////////////////////////////////////// SpefScanner::SpefScanner(std::istream *stream, - const string &filename, + std::string_view filename, SpefReader *reader, Report *report) : yyFlexLexer(stream), @@ -625,9 +584,9 @@ SpefScanner::SpefScanner(std::istream *stream, } void -SpefScanner::error(const char *msg) +SpefScanner::error(std::string_view msg) { - report_->fileError(1867, filename_.c_str(), lineno(), "%s", msg); + report_->fileError(1658, filename_, lineno(), "{}", msg); } -} // namespace +} // namespace sta diff --git a/parasitics/SpefReader.hh b/parasitics/SpefReader.hh index 3e3913488..4708fc1c4 100644 --- a/parasitics/SpefReader.hh +++ b/parasitics/SpefReader.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,33 +24,35 @@ #pragma once -#include "Zlib.hh" +#include +#include + #include "MinMax.hh" #include "ParasiticsClass.hh" +#include "Zlib.hh" namespace sta { class ParasiticAnalysisPt; class Instance; -class Corner; +class Scene; class OperatingConditions; class StaState; // Read a file single value parasitics into analysis point ap. // In a Spef file with triplet values the first value is used. -// Constraint min/max cnst_min_max and operating condition op_cond -// are used for parasitic network reduction. +// Min/max and operating condition op_cond are used for parasitic network reduction. // Return true if successful. bool -readSpefFile(const char *filename, - Instance *instance, - ParasiticAnalysisPt *ap, - bool pin_cap_included, - bool keep_coupling_caps, - float coupling_cap_factor, - bool reduce, - const Corner *corner, - const MinMaxAll *min_max, - StaState *sta); +readSpefFile(std::string_view filename, + Instance *instance, + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + bool reduce, + const Scene *scene, + const MinMaxAll *min_max, + Parasitics *parasitics, + StaState *sta); -} // namespace +} // namespace sta diff --git a/parasitics/SpefReaderPvt.hh b/parasitics/SpefReaderPvt.hh index 033a34311..4b1b90ffe 100644 --- a/parasitics/SpefReaderPvt.hh +++ b/parasitics/SpefReaderPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,12 +25,14 @@ #pragma once #include +#include +#include -#include "Zlib.hh" -#include "StringSeq.hh" #include "NetworkClass.hh" #include "ParasiticsClass.hh" #include "StaState.hh" +#include "StringUtil.hh" +#include "Zlib.hh" namespace sta { @@ -38,109 +40,115 @@ class Report; class MinMaxAll; class SpefRspfPi; class SpefTriple; -class Corner; +class Scene; class SpefScanner; -typedef std::map SpefNameMap; +using SpefNameMap = std::map; class SpefReader : public StaState { public: - SpefReader(const char *filename, - Instance *instance, - ParasiticAnalysisPt *ap, - bool pin_cap_included, - bool keep_coupling_caps, - float coupling_cap_factor, - bool reduce, - const Corner *corner, - const MinMaxAll *min_max, + SpefReader(std::string_view filename, + Instance *instance, + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + bool reduce, + const Scene *scene, + const MinMaxAll *min_max, + Parasitics *parasitics, StaState *sta); - virtual ~SpefReader(); + ~SpefReader() override = default; bool read(); char divider() const { return divider_; } void setDivider(char divider); char delimiter() const { return delimiter_; } void setDelimiter(char delimiter); - const char *filename() const { return filename_; } + std::string_view filename() const { return filename_; } // Translate from spf/spef namespace to sta namespace. - char *translated(const char *token); - void warn(int id, - const char *fmt, - ...) - __attribute__((format (printf, 3, 4))); + std::string translated(std::string_view spef_name); void setBusBrackets(char left, - char right); + char right); void setTimeScale(float scale, - const char *units); + std::string_view units); void setCapScale(float scale, - const char *units); + std::string_view units); void setResScale(float scale, - const char *units); + std::string_view units); void setInductScale(float scale, - const char *units); - void makeNameMapEntry(const char *index, - const char *name); - const char *nameMapLookup(const char *index); + std::string_view units); + void makeNameMapEntry(std::string_view index, + std::string_view name); + std::string_view nameMapLookup(std::string_view name); void setDesignFlow(StringSeq *flow_keys); - Pin *findPin(char *name); - Net *findNet(const char *name); + Pin *findPin(std::string_view name); + Net *findNet(std::string_view name); void rspfBegin(Net *net, - SpefTriple *total_cap); + SpefTriple *total_cap); void rspfFinish(); void rspfDrvrBegin(Pin *drvr_pin, - SpefRspfPi *pi); + SpefRspfPi *pi); void rspfLoad(Pin *load_pin, - SpefTriple *rc); + SpefTriple *rc); void rspfDrvrFinish(); void dspfBegin(Net *net, - SpefTriple *total_cap); + SpefTriple *total_cap); void dspfFinish(); - void makeCapacitor(int id, - char *node_name, - SpefTriple *cap); - void makeCapacitor(int id, - char *node_name1, - char *node_name2, - SpefTriple *cap); - void makeResistor(int id, - char *node_name1, - char *node_name2, - SpefTriple *res); - PortDirection *portDirection(char *spef_dir); + void makeCapacitor(uint32_t id, + std::string_view node_name, + SpefTriple *cap); + void makeCapacitor(uint32_t id, + std::string_view node_name1, + std::string_view node_name2, + SpefTriple *cap); + void makeResistor(uint32_t id, + std::string_view node_name1, + std::string_view node_name2, + SpefTriple *res); + PortDirection *portDirection(std::string_view spef_dir); + int warnLine() const; + template + void warn(int id, + std::string_view fmt, + Args &&...args) + { + report_->fileWarn(id, filename_, warnLine(), fmt, + std::forward(args)...); + } private: - Pin *findPinRelative(const char *name); - Pin *findPortPinRelative(const char *name); - Net *findNetRelative(const char *name); - Instance *findInstanceRelative(const char *name); - ParasiticNode *findParasiticNode(char *name, + Pin *findPinRelative(std::string_view name); + Pin *findPortPinRelative(std::string_view name); + Net *findNetRelative(std::string_view name); + Instance *findInstanceRelative(std::string_view name); + ParasiticNode *findParasiticNode(std::string_view name, bool local_only); - const char *filename_; + std::string_view filename_; SpefScanner *scanner_; Instance *instance_; - const ParasiticAnalysisPt *ap_; bool pin_cap_included_; bool keep_coupling_caps_; + bool coupling_cap_factor_; bool reduce_; - const Corner *corner_; + const Scene *scene_; const MinMaxAll *min_max_; // Normally no need to keep device names. - char divider_; - char delimiter_; - char bus_brkt_left_; - char bus_brkt_right_; - Net *net_; - - int triple_index_; - float time_scale_; - float cap_scale_; - float res_scale_; - float induct_scale_; + char divider_{'\0'}; + char delimiter_{'\0'}; + char bus_brkt_left_{'\0'}; + char bus_brkt_right_{'\0'}; + Net *net_{nullptr}; + + int triple_index_{0}; + float time_scale_{1.0}; + float cap_scale_{1.0}; + float res_scale_{1.0}; + float induct_scale_{1.0}; SpefNameMap name_map_; - StringSeq *design_flow_; - Parasitic *parasitic_; + StringSeq design_flow_; + Parasitics *parasitics_; + Parasitic *parasitic_{nullptr}; }; class SpefTriple @@ -148,8 +156,8 @@ class SpefTriple public: SpefTriple(float value); SpefTriple(float value1, - float value2, - float value3); + float value2, + float value3); float value(int index) const; bool isTriple() const { return is_triple_; } @@ -173,6 +181,4 @@ private: SpefTriple *c1_; }; -extern SpefReader *spef_reader; - -} // namespace +} // namespace sta diff --git a/parasitics/SpefScanner.hh b/parasitics/SpefScanner.hh index eb59d16f3..d9c21b862 100644 --- a/parasitics/SpefScanner.hh +++ b/parasitics/SpefScanner.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,8 @@ #pragma once +#include + #include "SpefLocation.hh" #include "SpefParse.hh" @@ -41,27 +43,25 @@ class SpefScanner : public SpefFlexLexer { public: SpefScanner(std::istream *stream, - const std::string &filename, + std::string_view filename, SpefReader *reader, Report *report); - virtual ~SpefScanner() {} - - virtual int lex(SpefParse::semantic_type *const yylval, + virtual int lex(SpefParse::semantic_type *yylval, SpefParse::location_type *yylloc); // YY_DECL defined in SpefLex.ll // Method body created by flex in SpefLex.cc - void error(const char *msg); + void error(std::string_view msg); int line() const { return yylineno; } // Get rid of override virtual function warning. using FlexLexer::yylex; private: - std::string filename_; + std::string_view filename_; SpefReader *reader_; Report *report_; std::string token_; }; -} // namespace +} // namespace sta diff --git a/power/Power.cc b/power/Power.cc index 29021500d..c60439412 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -1,62 +1,78 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Power.hh" -#include // max -#include // abs +#include // max +#include // abs +#include +#include +#include +#include -#include "Sta.hh" -#include "cudd.h" -#include "Stats.hh" +#include "Bfs.hh" +#include "ClkNetwork.hh" +#include "Clock.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" +#include "Delay.hh" #include "EnumNameMap.hh" +#include "Error.hh" +#include "FuncExpr.hh" +#include "Graph.hh" +#include "GraphClass.hh" +#include "GraphDelayCalc.hh" #include "Hash.hh" -#include "MinMax.hh" -#include "Units.hh" -#include "Transition.hh" -#include "TimingRole.hh" -#include "Liberty.hh" #include "InternalPower.hh" #include "LeakagePower.hh" -#include "Sequential.hh" -#include "TimingArc.hh" -#include "FuncExpr.hh" -#include "PortDirection.hh" +#include "Liberty.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "Mode.hh" #include "Network.hh" -#include "Clock.hh" -#include "Sdc.hh" -#include "Graph.hh" -#include "DcalcAnalysisPt.hh" -#include "GraphDelayCalc.hh" -#include "Corner.hh" +#include "NetworkClass.hh" +#include "NetworkCmp.hh" #include "Path.hh" +#include "PortDirection.hh" +#include "PowerClass.hh" +#include "ReportPower.hh" +#include "Scene.hh" +#include "Sdc.hh" +#include "SdcClass.hh" +#include "Search.hh" +#include "SearchClass.hh" +#include "Sequential.hh" +#include "Sta.hh" +#include "Stats.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Transition.hh" +#include "Units.hh" +#include "VertexVisitor.hh" +#include "cudd.h" #include "search/Levelize.hh" #include "search/Sim.hh" -#include "Search.hh" -#include "Bfs.hh" -#include "ClkNetwork.hh" // Related liberty not supported: // library @@ -71,46 +87,32 @@ namespace sta { -using std::abs; -using std::max; -using std::min; -using std::isnormal; -using std::vector; -using std::map; - static bool isPositiveUnate(const LibertyCell *cell, - const LibertyPort *from, - const LibertyPort *to); - -static EnumNameMap pwr_activity_origin_map = - {{PwrActivityOrigin::global, "global"}, - {PwrActivityOrigin::input, "input"}, - {PwrActivityOrigin::user, "user"}, - {PwrActivityOrigin::vcd, "vcd"}, - {PwrActivityOrigin::saif, "saif"}, - {PwrActivityOrigin::propagated, "propagated"}, - {PwrActivityOrigin::clock, "clock"}, - {PwrActivityOrigin::constant, "constant"}, - {PwrActivityOrigin::defaulted, "defaulted"}, - {PwrActivityOrigin::unknown, "unknown"}}; + const LibertyPort *from, + const LibertyPort *to); + +static EnumNameMap pwr_activity_origin_map = { + {PwrActivityOrigin::global, "global"}, + {PwrActivityOrigin::input, "input"}, + {PwrActivityOrigin::user, "user"}, + {PwrActivityOrigin::vcd, "vcd"}, + {PwrActivityOrigin::saif, "saif"}, + {PwrActivityOrigin::propagated, "propagated"}, + {PwrActivityOrigin::clock, "clock"}, + {PwrActivityOrigin::constant, "constant"}, + {PwrActivityOrigin::unknown, "unknown"}}; Power::Power(StaState *sta) : StaState(sta), - global_activity_(), - input_activity_(), // default set in ensureActivities() - seq_activity_map_(100, SeqPinHash(network_), SeqPinEqual()), - activities_valid_(false), - bdd_(sta), - instance_powers_(InstanceIdLess(network_)), - instance_powers_valid_(false), - corner_(nullptr) + bdd_(sta) { } void Power::clear() { + scene_ = nullptr; global_activity_.init(); input_activity_.init(); user_activity_map_.clear(); @@ -118,7 +120,6 @@ Power::clear() activity_map_.clear(); activities_valid_ = false; instance_powers_.clear(); - corner_ = nullptr; } void @@ -130,12 +131,12 @@ Power::activitiesInvalid() void Power::setGlobalActivity(float density, - float duty) + float duty) { global_activity_.set(density, duty, PwrActivityOrigin::global); activitiesInvalid(); } - + void Power::unsetGlobalActivity() { @@ -145,7 +146,7 @@ Power::unsetGlobalActivity() void Power::setInputActivity(float density, - float duty) + float duty) { input_activity_.set(density, duty, PwrActivityOrigin::input); activitiesInvalid(); @@ -160,8 +161,8 @@ Power::unsetInputActivity() void Power::setInputPortActivity(const Port *input_port, - float density, - float duty) + float density, + float duty) { Instance *top_inst = network_->topInstance(); const Pin *pin = network_->findPin(top_inst, input_port); @@ -208,17 +209,15 @@ Power::userActivity(const Pin *pin) bool Power::hasUserActivity(const Pin *pin) { - return user_activity_map_.hasKey(pin); + return user_activity_map_.contains(pin); } void Power::setActivity(const Pin *pin, - PwrActivity &activity) + PwrActivity &activity) { - debugPrint(debug_, "power_activity", 3, "set %s %.2e %.2f %s", - network_->pathName(pin), - activity.density(), - activity.duty(), + debugPrint(debug_, "power_activity", 3, "set {} {:.2e} {:.2f} {}", + network_->pathName(pin), activity.density(), activity.duty(), pwr_activity_origin_map.find(activity.origin())); activity_map_[pin] = activity; } @@ -232,15 +231,15 @@ Power::activity(const Pin *pin) bool Power::hasActivity(const Pin *pin) { - return activity_map_.hasKey(pin); + return activity_map_.contains(pin); } // Sequential internal pins may not be in the netlist so their // activities are stored by instance/liberty_port pairs. void Power::setSeqActivity(const Instance *reg, - LibertyPort *output, - PwrActivity &activity) + LibertyPort *output, + PwrActivity &activity) { seq_activity_map_[SeqPin(reg, output)] = activity; activitiesInvalid(); @@ -248,14 +247,14 @@ Power::setSeqActivity(const Instance *reg, bool Power::hasSeqActivity(const Instance *reg, - LibertyPort *output) + LibertyPort *output) { - return seq_activity_map_.hasKey(SeqPin(reg, output)); + return seq_activity_map_.contains(SeqPin(reg, output)); } PwrActivity & Power::seqActivity(const Instance *reg, - LibertyPort *output) + LibertyPort *output) { return seq_activity_map_[SeqPin(reg, output)]; } @@ -268,28 +267,163 @@ SeqPinHash::SeqPinHash(const Network *network) : size_t SeqPinHash::operator()(const SeqPin &pin) const { - return hashSum(network_->id(pin.first), pin.second->id()); + const auto &[inst, port] = pin; + return hashSum(network_->id(inst), port->id()); } bool SeqPinEqual::operator()(const SeqPin &pin1, - const SeqPin &pin2) const + const SeqPin &pin2) const { - return pin1.first == pin2.first - && pin1.second == pin2.second; + const auto &[inst1, port1] = pin1; + const auto &[inst2, port2] = pin2; + return inst1 == inst2 && port1 == port2; } //////////////////////////////////////////////////////////////// void -Power::power(const Corner *corner, - // Return values. - PowerResult &total, - PowerResult &sequential, - PowerResult &combinational, +Power::reportDesign(const Scene *scene, + int digits) +{ + PowerResult total, sequential, combinational, clock, macro, pad; + power(scene, total, sequential, combinational, clock, macro, pad); + ReportPower report_power(this); + report_power.reportDesign(total, sequential, combinational, clock, macro, pad, + digits); +} + +void +Power::reportInsts(const InstanceSeq &insts, + const Scene *scene, + int digits) +{ + InstPowers inst_pwrs = sortInstsByPower(insts, scene); + ReportPower report_power(this); + report_power.reportInsts(inst_pwrs, digits); +} + +void +Power::reportHighestInsts(size_t count, + const Scene *scene, + int digits) +{ + InstPowers inst_pwrs = highestInstPowers(count, scene); + ReportPower report_power(this); + report_power.reportInsts(inst_pwrs, digits); +} + +void +Power::reportDesignJson(const Scene *scene, + int digits) +{ + PowerResult total, sequential, combinational, clock, macro, pad; + power(scene, total, sequential, combinational, clock, macro, pad); + + report_->report("{{"); + reportPowerRowJson("Sequential", sequential, digits, ","); + reportPowerRowJson("Combinational", combinational, digits, ","); + reportPowerRowJson("Clock", clock, digits, ","); + reportPowerRowJson("Macro", macro, digits, ","); + reportPowerRowJson("Pad", pad, digits, ","); + reportPowerRowJson("Total", total, digits, ""); + report_->report("}}"); +} + +void +Power::reportInstsJson(const InstanceSeq &insts, + const Scene *scene, + int digits) +{ + InstPowers inst_pwrs = sortInstsByPower(insts, scene); + + report_->report("["); + bool first = true; + for (const InstPower &inst_pwr : inst_pwrs) { + if (!first) { + report_->report(","); + } + first = false; + reportPowerInstJson(inst_pwr.first, inst_pwr.second, digits); + } + report_->report("]"); +} + +void +Power::reportPowerRowJson(std::string_view type, + const PowerResult &power, + int digits, + std::string_view eol) +{ + float internal = power.internal(); + float switching = power.switching(); + float leakage = power.leakage(); + float total = power.total(); + + report_->report(" \"{}\": {{", type); + report_->report(" \"internal\": {:.{}e},", internal, digits); + report_->report(" \"switching\": {:.{}e},", switching, digits); + report_->report(" \"leakage\": {:.{}e},", leakage, digits); + report_->report(" \"total\": {:.{}e}", total, digits); + std::string line = " }"; + if (!eol.empty()) + line += eol; + report_->reportLine(line); +} + +void +Power::reportPowerInstJson(const Instance *inst, + const PowerResult &power, + int digits) +{ + float internal = power.internal(); + float switching = power.switching(); + float leakage = power.leakage(); + float total = power.total(); + + report_->report("{{"); + report_->report(" \"name\": \"{}\",", network_->pathName(inst)); + report_->report(" \"internal\": {:.{}e},", internal, digits); + report_->report(" \"switching\": {:.{}e},", switching, digits); + report_->report(" \"leakage\": {:.{}e},", leakage, digits); + report_->report(" \"total\": {:.{}e}", total, digits); + report_->report("}}"); +} + +static bool +instPowerGreater(const InstPower &pwr1, + const InstPower &pwr2) +{ + return pwr1.second.total() > pwr2.second.total(); +} + +InstPowers +Power::sortInstsByPower(const InstanceSeq &insts, + const Scene *scene) +{ + // Collect instance powers. + InstPowers inst_pwrs; + for (const Instance *inst : insts) { + PowerResult inst_power = power(inst, scene); + inst_pwrs.emplace_back(inst, inst_power); + } + + // Sort by total power (descending) + sort(inst_pwrs, instPowerGreater); + return inst_pwrs; +} + +//////////////////////////////////////////////////////////////// + +void +Power::power(const Scene *scene, + // Return values. + PowerResult &total, + PowerResult &sequential, + PowerResult &combinational, PowerResult &clock, - PowerResult ¯o, - PowerResult &pad) + PowerResult ¯o, + PowerResult &pad) { total.clear(); sequential.clear(); @@ -298,36 +432,35 @@ Power::power(const Corner *corner, macro.clear(); pad.clear(); - ensureActivities(); - ensureInstPowers(corner); + ensureActivities(scene); + ensureInstPowers(); + ClkNetwork *clk_network = scene_->mode()->clkNetwork(); for (auto [inst, inst_power] : instance_powers_) { LibertyCell *cell = network_->libertyCell(inst); if (cell) { - if (cell->isMacro() - || cell->isMemory() - || cell->interfaceTiming()) - macro.incr(inst_power); + if (cell->isMacro() || cell->isMemory() || cell->interfaceTiming()) + macro.incr(inst_power); else if (cell->isPad()) - pad.incr(inst_power); - else if (inClockNetwork(inst)) - clock.incr(inst_power); + pad.incr(inst_power); + else if (inClockNetwork(inst, clk_network)) + clock.incr(inst_power); else if (cell->hasSequentials()) - sequential.incr(inst_power); + sequential.incr(inst_power); else - combinational.incr(inst_power); + combinational.incr(inst_power); total.incr(inst_power); } } } bool -Power::inClockNetwork(const Instance *inst) +Power::inClockNetwork(const Instance *inst, + const ClkNetwork *clk_network) { InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); - if (network_->direction(pin)->isAnyOutput() - && !clk_network_->isClock(pin)) { + if (network_->direction(pin)->isAnyOutput() && !clk_network->isClock(pin)) { delete pin_iter; return false; } @@ -338,13 +471,13 @@ Power::inClockNetwork(const Instance *inst) PowerResult Power::power(const Instance *inst, - const Corner *corner) + const Scene *scene) { - ensureActivities(); - ensureInstPowers(corner); + ensureActivities(scene); + ensureInstPowers(); if (network_->isHierarchical(inst)) { PowerResult result; - powerInside(inst, corner, result); + powerInside(inst, scene, result); return result; } else @@ -353,66 +486,86 @@ Power::power(const Instance *inst, void Power::powerInside(const Instance *hinst, - const Corner *corner, + const Scene *scene, PowerResult &result) { InstanceChildIterator *child_iter = network_->childIterator(hinst); while (child_iter->hasNext()) { Instance *child = child_iter->next(); if (network_->isHierarchical(child)) - powerInside(child, corner, result); + powerInside(child, scene, result); else result.incr(instance_powers_[child]); } delete child_iter; } -typedef std::pair InstPower; +//////////////////////////////////////////////////////////////// -InstanceSeq -Power::highestPowerInstances(size_t count, - const Corner *corner) +InstPowers +Power::highestInstPowers(size_t count, + const Scene *scene) { - vector inst_pwrs; + InstPowers inst_pwrs; LeafInstanceIterator *inst_iter = network_->leafInstanceIterator(); while (inst_iter->hasNext()) { Instance *inst = inst_iter->next(); - PowerResult pwr = power(inst, corner); - inst_pwrs.push_back({inst, pwr.total()}); + PowerResult pwr = power(inst, scene); + inst_pwrs.emplace_back(inst, pwr); } delete inst_iter; - sort(inst_pwrs.begin(), inst_pwrs.end(), [](InstPower &inst_pwr1, - InstPower &inst_pwr2) { - return inst_pwr1.second > inst_pwr2.second; - }); - - InstanceSeq insts; - for (size_t i = 0; i < count; i++) - insts.push_back(inst_pwrs[i].first); - return insts; + sort(inst_pwrs, instPowerGreater); + if (inst_pwrs.size() > count) + inst_pwrs.resize(count); + return inst_pwrs; } //////////////////////////////////////////////////////////////// -class ActivitySrchPred : public SearchPredNonLatch2 +class ActivitySrchPred : public SearchPred { public: - explicit ActivitySrchPred(const StaState *sta); - virtual bool searchThru(Edge *edge); + ActivitySrchPred(const StaState *sta); + bool searchFrom(const Vertex *from_vertex, + const Mode *mode) const override; + bool searchThru(Edge *edge, + const Mode *mode) const override; + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; }; ActivitySrchPred::ActivitySrchPred(const StaState *sta) : - SearchPredNonLatch2(sta) + SearchPred(sta) +{ +} + +bool +ActivitySrchPred::searchFrom(const Vertex *from_vertex, + const Mode *mode) const { + const Pin *from_pin = from_vertex->pin(); + const Sdc *sdc = mode->sdc(); + return !sdc->isDisabledConstraint(from_pin); } bool -ActivitySrchPred::searchThru(Edge *edge) +ActivitySrchPred::searchThru(Edge *edge, + const Mode *mode) const { + const Sdc *sdc = mode->sdc(); const TimingRole *role = edge->role(); - return SearchPredNonLatch2::searchThru(edge) - && role != TimingRole::regClkToQ(); + return !(edge->role()->isTimingCheck() || sdc->isDisabledConstraint(edge) + || sdc->isDisabledCondDefault(edge) || edge->isBidirectInstPath() + || edge->isDisabledLoop() || role == TimingRole::regClkToQ() + || role->isLatchDtoQ()); +} + +bool +ActivitySrchPred::searchTo(const Vertex *, + const Mode *) const +{ + return true; } //////////////////////////////////////////////////////////////// @@ -421,44 +574,50 @@ class PropActivityVisitor : public VertexVisitor, StaState { public: PropActivityVisitor(Power *power, - BfsFwdIterator *bfs); - virtual VertexVisitor *copy() const; - virtual void visit(Vertex *vertex); + const Mode *mode, + BfsFwdIterator *bfs); + VertexVisitor *copy() const override; + void visit(Vertex *vertex) override; InstanceSet &visitedRegs() { return visited_regs_; } void init(); float maxChange() const { return max_change_; } + const Pin *maxChangePin() const { return max_change_pin_; } private: bool setActivityCheck(const Pin *pin, PwrActivity &activity); - static constexpr float change_tolerance_ = .001; + static constexpr float change_tolerance_ = .01; InstanceSet visited_regs_; - float max_change_; - Power *power_; + float max_change_{0.0}; + const Pin *max_change_pin_{nullptr}; BfsFwdIterator *bfs_; + Power *power_; + const Mode *mode_; }; PropActivityVisitor::PropActivityVisitor(Power *power, - BfsFwdIterator *bfs) : + const Mode *mode, + BfsFwdIterator *bfs) : StaState(power), visited_regs_(network_), - max_change_(0.0), + bfs_(bfs), power_(power), - bfs_(bfs) + mode_(mode) { } VertexVisitor * PropActivityVisitor::copy() const { - return new PropActivityVisitor(power_, bfs_); + return new PropActivityVisitor(power_, mode_, bfs_); } void PropActivityVisitor::init() { max_change_ = 0.0; + max_change_pin_ = nullptr; } void @@ -466,8 +625,8 @@ PropActivityVisitor::visit(Vertex *vertex) { Pin *pin = vertex->pin(); Instance *inst = network_->instance(pin); - debugPrint(debug_, "power_activity", 3, "visit %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "power_activity", 3, "visit {}", + vertex->to_string(this)); bool changed = false; if (power_->hasUserActivity(pin)) { PwrActivity &activity = power_->userActivity(pin); @@ -477,22 +636,21 @@ PropActivityVisitor::visit(Vertex *vertex) if (network_->isLoad(pin)) { VertexInEdgeIterator edge_iter(vertex, graph_); if (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->isWire()) { - Vertex *from_vertex = edge->from(graph_); + Edge *edge = edge_iter.next(); + if (edge->isWire()) { + Vertex *from_vertex = edge->from(graph_); const Pin *from_pin = from_vertex->pin(); - PwrActivity &from_activity = power_->activity(from_pin); - PwrActivity to_activity(from_activity.density(), - from_activity.duty(), - PwrActivityOrigin::propagated); - changed = setActivityCheck(pin, to_activity); - } + PwrActivity &from_activity = power_->activity(from_pin); + PwrActivity to_activity(from_activity.density(), from_activity.duty(), + PwrActivityOrigin::propagated); + changed = setActivityCheck(pin, to_activity); + } } } if (network_->isDriver(pin)) { LibertyPort *port = network_->libertyPort(pin); if (port) { - FuncExpr *func = port->function(); + FuncExpr *func = port->function(); if (func == nullptr) { LibertyCell *test_cell = port->libertyCell()->testCell(); if (test_cell) { @@ -501,12 +659,12 @@ PropActivityVisitor::visit(Vertex *vertex) func = port->function(); } } - bool annotated = false; - if (func) { + bool annotated = false; + if (func) { PwrActivity activity = power_->evalActivity(func, inst); - changed = setActivityCheck(pin, activity); - annotated = true; - } + changed = setActivityCheck(pin, activity); + annotated = true; + } if (port->isClockGateOut()) { const Pin *enable, *clk, *gclk; power_->clockGatePins(inst, enable, clk, gclk); @@ -516,23 +674,21 @@ PropActivityVisitor::visit(Vertex *vertex) float p1 = activity1.duty(); float p2 = activity2.duty(); PwrActivity activity(activity1.density() * p2 + activity2.density() * p1, - p1 * p2, - PwrActivityOrigin::propagated); + p1 * p2, PwrActivityOrigin::propagated); changed = setActivityCheck(gclk, activity); - debugPrint(debug_, "power_activity", 3, "gated_clk %s %.2e %.2f", - network_->pathName(gclk), - activity.density(), + debugPrint(debug_, "power_activity", 3, "gated_clk {} {:.2e} {:.2f}", + network_->pathName(gclk), activity.density(), activity.duty()); annotated = true; } } - if (!annotated) { + if (!annotated) { PwrActivity default_act = power_->inputActivity(); PwrActivity activity(default_act.density(), default_act.duty(), - PwrActivityOrigin::defaulted); - changed = setActivityCheck(pin, activity); - } + PwrActivityOrigin::input); + changed = setActivityCheck(pin, activity); + } } } } @@ -541,28 +697,40 @@ PropActivityVisitor::visit(Vertex *vertex) if (cell) { LibertyCell *test_cell = cell->libertyCell()->testCell(); if (network_->isLoad(pin)) { - if (cell->hasSequentials() - || (test_cell - && test_cell->hasSequentials())) { - debugPrint(debug_, "power_activity", 3, "pending seq %s", - network_->pathName(inst)); - visited_regs_.insert(inst); - } - // Gated clock cells latch the enable so there is no EN->GCLK timing arc. - if (cell->isClockGate()) { - const Pin *enable, *clk, *gclk; - power_->clockGatePins(inst, enable, clk, gclk); - if (gclk) { - Vertex *gclk_vertex = graph_->pinDrvrVertex(gclk); - bfs_->enqueue(gclk_vertex); - } - } + if (cell->hasSequentials() || (test_cell && test_cell->hasSequentials())) { + debugPrint(debug_, "power_activity", 3, "pending seq {}", + network_->pathName(inst)); + visited_regs_.insert(inst); + } + // Gated clock cells latch the enable so there is no EN->GCLK timing arc. + if (cell->isClockGate()) { + const Pin *enable, *clk, *gclk; + power_->clockGatePins(inst, enable, clk, gclk); + if (gclk) { + Vertex *gclk_vertex = graph_->pinDrvrVertex(gclk); + bfs_->enqueue(gclk_vertex); + } + } } - bfs_->enqueueAdjacentVertices(vertex); + bfs_->enqueueAdjacentVertices(vertex, mode_); } } } +static float +percentChange(float value, + float prev) +{ + if (prev == 0.0) { + if (value == 0.0) + return 0.0; + else + return 1.0; + } + else + return std::abs(value - prev) / prev; +} + // Return true if the activity changed. bool PropActivityVisitor::setActivityCheck(const Pin *pin, @@ -573,20 +741,25 @@ PropActivityVisitor::setActivityCheck(const Pin *pin, if (activity.density() > max_density) activity.setDensity(max_density); PwrActivity &prev_activity = power_->activity(pin); - float density_delta = abs(activity.density() - prev_activity.density()); - float duty_delta = abs(activity.duty() - prev_activity.duty()); - if (density_delta > change_tolerance_ - || duty_delta > change_tolerance_ - || activity.origin() != prev_activity.origin()) { - max_change_ = max(max_change_, density_delta); - max_change_ = max(max_change_, duty_delta); - power_->setActivity(pin, activity); - return true; + float density_delta = percentChange(activity.density(), prev_activity.density()); + float duty_delta = percentChange(activity.duty(), prev_activity.duty()); + if (density_delta > max_change_) { + max_change_ = density_delta; + max_change_pin_ = pin; } - else - return false; + if (duty_delta > max_change_) { + max_change_ = duty_delta; + max_change_pin_ = pin; + } + bool changed = density_delta > change_tolerance_ || duty_delta > change_tolerance_ + || activity.origin() != prev_activity.origin(); + ; + power_->setActivity(pin, activity); + return changed; } +//////////////////////////////////////////////////////////////// + void Power::clockGatePins(const Instance *inst, // Return values. @@ -615,10 +788,10 @@ Power::clockGatePins(const Instance *inst, PwrActivity Power::evalActivity(FuncExpr *expr, - const Instance *inst) + const Instance *inst) { LibertyPort *func_port = expr->port(); - if (func_port && func_port->direction()->isInternal()) + if (func_port && func_port->direction()->isInternal()) return findSeqActivity(inst, func_port); else { DdNode *bdd = bdd_.funcBdd(expr); @@ -662,7 +835,7 @@ Power::evalBddDuty(DdNode *bdd, else if (bdd == Cudd_ReadLogicZero(bdd_.cuddMgr())) return 0.0; else - criticalError(1100, "unknown cudd constant"); + criticalError(2400, "unknown cudd constant"); } else { float duty0 = evalBddDuty(Cudd_E(bdd), inst); @@ -671,7 +844,7 @@ Power::evalBddDuty(DdNode *bdd, int var_index = Cudd_ReadPerm(bdd_.cuddMgr(), index); const LibertyPort *port = bdd_.varIndexPort(var_index); if (port->direction()->isInternal()) - return findSeqActivity(inst, const_cast(port)).duty(); + return findSeqActivity(inst, const_cast(port)).duty(); else { const Pin *pin = findLinkPin(inst, port); if (pin) { @@ -706,10 +879,8 @@ Power::evalBddActivity(DdNode *bdd, Cudd_RecursiveDeref(bdd_.cuddMgr(), diff); float var_density = var_activity.density() * diff_duty; density += var_density; - debugPrint(debug_, "power_activity", 3, "%s %.3e * %.3f = %.3e", - network_->pathName(pin), - var_activity.density(), - diff_duty, + debugPrint(debug_, "power_activity", 3, "{} {:.3e} * {:.3f} = {:.3e}", + network_->pathName(pin), var_activity.density(), diff_duty, var_density); } } @@ -719,9 +890,15 @@ Power::evalBddActivity(DdNode *bdd, //////////////////////////////////////////////////////////////// void -Power::ensureActivities() +Power::ensureActivities(const Scene *scene) { Stats stats(debug_, report_); + if (scene != scene_) { + scene_ = scene; + activities_valid_ = false; + instance_powers_.clear(); + } + if (!activities_valid_) { // No need to propagate activites if global activity is set. if (!global_activity_.isSet()) { @@ -732,16 +909,15 @@ Power::ensureActivities() // Initialize default input activity (after sdc is defined) // unless it has been set by command. if (input_activity_.origin() == PwrActivityOrigin::unknown) { - float min_period = clockMinPeriod(); - float density = 0.1 / (min_period != 0.0 - ? min_period - : units_->timeUnit()->scale()); + float min_period = clockMinPeriod(scene_->mode()->sdc()); + float density = + 0.1 / (min_period != 0.0 ? min_period : units_->timeUnit()->scale()); input_activity_.set(density, 0.5, PwrActivityOrigin::input); } ActivitySrchPred activity_srch_pred(this); BfsFwdIterator bfs(BfsIndex::other, &activity_srch_pred, this); seedActivities(bfs); - PropActivityVisitor visitor(this, &bfs); + PropActivityVisitor visitor(this, scene_->mode(), &bfs); // Propagate activities through combinational logic. bfs.visit(levelize_->maxLevel(), &visitor); // Propagate activiities through registers. @@ -749,15 +925,15 @@ Power::ensureActivities() int pass = 1; while (!regs.empty() && pass < max_activity_passes_) { visitor.init(); - for (const Instance *reg : regs) - // Propagate activiities across register D->Q. - seedRegOutputActivities(reg, bfs); - // Propagate register output activities through - // combinational logic. - bfs.visit(levelize_->maxLevel(), &visitor); + for (const Instance *reg : regs) + // Propagate activiities across register D->Q. + seedRegOutputActivities(reg, bfs); + // Propagate register output activities through + // combinational logic. + bfs.visit(levelize_->maxLevel(), &visitor); regs = std::move(visitor.visitedRegs()); - debugPrint(debug_, "power_activity", 1, "Pass %d change %.2f", - pass, visitor.maxChange()); + debugPrint(debug_, "power_activity", 1, "Pass {} change {:.2f} {}", pass, + visitor.maxChange(), network_->pathName(visitor.maxChangePin())); pass++; } } @@ -772,24 +948,24 @@ Power::seedActivities(BfsFwdIterator &bfs) for (Vertex *vertex : levelize_->roots()) { const Pin *pin = vertex->pin(); // Clock activities are baked in. - if (!sdc_->isLeafPinClock(pin) - && !network_->direction(pin)->isInternal()) { - debugPrint(debug_, "power_activity", 3, "seed %s", - vertex->to_string(this).c_str()); + if (!scene_->mode()->sdc()->isLeafPinClock(pin) + && !network_->direction(pin)->isInternal()) { + debugPrint(debug_, "power_activity", 3, "seed {}", + vertex->to_string(this)); if (hasUserActivity(pin)) - setActivity(pin, userActivity(pin)); + setActivity(pin, userActivity(pin)); else - // Default inputs without explicit activities to the input default. - setActivity(pin, input_activity_); + // Default inputs without explicit activities to the input default. + setActivity(pin, input_activity_); Vertex *vertex = graph_->pinDrvrVertex(pin); - bfs.enqueueAdjacentVertices(vertex); + bfs.enqueueAdjacentVertices(vertex, scene_->mode()); } } } void Power::seedRegOutputActivities(const Instance *inst, - BfsFwdIterator &bfs) + BfsFwdIterator &bfs) { LibertyCell *cell = network_->libertyCell(inst); const SequentialSeq &seqs = cell->sequentials(); @@ -810,9 +986,9 @@ Power::seedRegOutputActivities(const Instance *inst, const SequentialSeq &seqs, BfsFwdIterator &bfs) { - for (Sequential *seq : seqs) { - seedRegOutputActivities(inst, seq, seq->output(), false); - seedRegOutputActivities(inst, seq, seq->outputInv(), true); + for (const Sequential &seq : seqs) { + seedRegOutputActivities(inst, seq, seq.output(), false); + seedRegOutputActivities(inst, seq, seq.outputInv(), true); // Enqueue register output pins with functions that reference // the sequential internal pins (IQ, IQN). InstancePinIterator *pin_iter = network_->pinIterator(inst); @@ -824,12 +1000,10 @@ Power::seedRegOutputActivities(const Instance *inst, if (port) { FuncExpr *func = port->function(); Vertex *vertex = graph_->pinDrvrVertex(pin); - if (vertex - && func - && (func->port() == seq->output() - || func->port() == seq->outputInv())) { - debugPrint(debug_, "power_reg", 1, "enqueue reg output %s", - vertex->to_string(this).c_str()); + if (vertex && func + && (func->port() == seq.output() || func->port() == seq.outputInv())) { + debugPrint(debug_, "power_reg", 1, "enqueue reg output {}", + vertex->to_string(this)); bfs.enqueue(vertex); } } @@ -840,30 +1014,29 @@ Power::seedRegOutputActivities(const Instance *inst, void Power::seedRegOutputActivities(const Instance *reg, - Sequential *seq, - LibertyPort *output, - bool invert) + const Sequential &seq, + LibertyPort *output, + bool invert) { const Pin *out_pin = network_->findPin(reg, output); if (!hasUserActivity(out_pin)) { - PwrActivity in_activity = evalActivity(seq->data(), reg); + PwrActivity in_activity = evalActivity(seq.data(), reg); float in_density = in_activity.density(); float in_duty = in_activity.duty(); // Default propagates input density/duty thru reg/latch. float out_density = in_density; float out_duty = in_duty; - PwrActivity clk_activity = evalActivity(seq->clock(), reg); + PwrActivity clk_activity = evalActivity(seq.clock(), reg); float clk_density = clk_activity.density(); if (in_density > clk_density / 2) { - if (seq->isRegister()) + if (seq.isRegister()) out_density = 2 * in_duty * (1 - in_duty) * clk_density; - else if (seq->isLatch()) { - PwrActivity clk_activity = evalActivity(seq->clock(), reg); + else if (seq.isLatch()) { + PwrActivity clk_activity = evalActivity(seq.clock(), reg); float clk_duty = clk_activity.duty(); - FuncExpr *clk_func = seq->clock(); - bool clk_invert = clk_func - && clk_func->op() == FuncExpr::op_not - && clk_func->left()->op() == FuncExpr::op_port; + FuncExpr *clk_func = seq.clock(); + bool clk_invert = clk_func && clk_func->op() == FuncExpr::Op::not_ + && clk_func->left()->op() == FuncExpr::Op::port; if (clk_invert) out_density = in_density * (1 - clk_duty); else @@ -880,17 +1053,16 @@ Power::seedRegOutputActivities(const Instance *reg, //////////////////////////////////////////////////////////////// void -Power::ensureInstPowers(const Corner *corner) +Power::ensureInstPowers() { - if (!instance_powers_valid_ - || corner != corner_) { - findInstPowers(corner); + if (!instance_powers_valid_) { + findInstPowers(); instance_powers_valid_ = true; } } void -Power::findInstPowers(const Corner *corner) +Power::findInstPowers() { Stats stats(debug_, report_); LeafInstanceIterator *inst_iter = network_->leafInstanceIterator(); @@ -898,21 +1070,20 @@ Power::findInstPowers(const Corner *corner) Instance *inst = inst_iter->next(); LibertyCell *cell = network_->libertyCell(inst); if (cell) { - PowerResult inst_power = power(inst, cell, corner); + PowerResult inst_power = power(inst, cell, scene_); instance_powers_[inst] = inst_power; } } delete inst_iter; - corner_ = corner; stats.report("Find power"); } PowerResult Power::power(const Instance *inst, - LibertyCell *cell, - const Corner *corner) + LibertyCell *cell, + const Scene *scene) { - debugPrint(debug_, "power", 2, "find power %s", sdc_network_->pathName(inst)); + debugPrint(debug_, "power", 2, "find power {}", sdc_network_->pathName(inst)); PowerResult result; // Zero out inverter power if flag is enabled @@ -920,9 +1091,9 @@ Power::power(const Instance *inst, return result; } - findInternalPower(inst, cell, corner, result); - findSwitchingPower(inst, cell, corner, result); - findLeakagePower(inst, cell, corner, result); + findInternalPower(inst, cell, scene, result); + findSwitchingPower(inst, cell, scene, result); + findLeakagePower(inst, cell, scene, result); return result; } @@ -946,26 +1117,25 @@ Power::findInstClk(const Instance *inst) void Power::findInternalPower(const Instance *inst, LibertyCell *cell, - const Corner *corner, + const Scene *scene, // Return values. PowerResult &result) { - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { const Pin *to_pin = pin_iter->next(); LibertyPort *to_port = network_->libertyPort(to_pin); if (to_port) { float load_cap = to_port->direction()->isAnyOutput() - ? graph_delay_calc_->loadCap(to_pin, dcalc_ap) - : 0.0; + ? graph_delay_calc_->loadCap(to_pin, scene, MinMax::max()) + : 0.0; PwrActivity activity = findActivity(to_pin); if (to_port->direction()->isAnyOutput()) - findOutputInternalPower(to_port, inst, cell, activity, - load_cap, corner, result); + findOutputInternalPower(to_port, inst, cell, activity, load_cap, scene, + result); if (to_port->direction()->isAnyInput()) - findInputInternalPower(to_pin, to_port, inst, cell, activity, - load_cap, corner, result); + findInputInternalPower(to_pin, to_port, inst, cell, activity, load_cap, + scene, result); } } delete pin_iter; @@ -973,50 +1143,49 @@ Power::findInternalPower(const Instance *inst, void Power::findInputInternalPower(const Pin *pin, - LibertyPort *port, - const Instance *inst, - LibertyCell *cell, - PwrActivity &activity, - float load_cap, - const Corner *corner, - // Return values. - PowerResult &result) + LibertyPort *port, + const Instance *inst, + LibertyCell *cell, + PwrActivity &activity, + float load_cap, + const Scene *scene, + // Return values. + PowerResult &result) { const MinMax *min_max = MinMax::max(); - LibertyCell *corner_cell = cell->cornerCell(corner, min_max); - const LibertyPort *corner_port = port->cornerPort(corner, min_max); - if (corner_cell && corner_port) { - const InternalPowerSeq &internal_pwrs = corner_cell->internalPowers(corner_port); + LibertyCell *scene_cell = cell->sceneCell(scene, min_max); + const LibertyPort *scene_port = port->scenePort(scene, min_max); + if (scene_cell && scene_port) { + const InternalPowerPtrSeq &internal_pwrs = + scene_cell->internalPowers(scene_port); if (!internal_pwrs.empty()) { - debugPrint(debug_, "power", 2, "internal input %s/%s cap %s", - network_->pathName(inst), - port->name(), + debugPrint(debug_, "power", 2, "internal input {}/{} cap {}", + network_->pathName(inst), port->name(), units_->capacitanceUnit()->asString(load_cap)); debugPrint(debug_, "power", 2, " when act/ns duty energy power"); - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); - const Pvt *pvt = dcalc_ap->operatingConditions(); + const Pvt *pvt = scene->sdc()->operatingConditions(MinMax::max()); Vertex *vertex = graph_->pinLoadVertex(pin); float internal = 0.0; - for (InternalPower *pwr : internal_pwrs) { - const char *related_pg_pin = pwr->relatedPgPin(); + for (const InternalPower *pwr : internal_pwrs) { + LibertyPort *related_pg_pin = pwr->relatedPgPin(); float energy = 0.0; int rf_count = 0; for (const RiseFall *rf : RiseFall::range()) { - float slew = getSlew(vertex, rf, corner); - if (!delayInf(slew)) { + float slew = getSlew(vertex, rf, scene); + if (!delayInf(slew, this)) { float table_energy = pwr->power(rf, pvt, slew, load_cap); energy += table_energy; rf_count++; } } if (rf_count) - energy /= rf_count; // average non-inf energies - float duty = 1.0; // fallback default + energy /= rf_count; // average non-inf energies + float duty = 1.0; // fallback default FuncExpr *when = pwr->when(); if (when) { - const LibertyPort *out_corner_port = findExprOutPort(when); - if (out_corner_port) { - LibertyPort *out_port = findLinkPort(cell, out_corner_port); + const LibertyPort *out_scene_port = findExprOutPort(when); + if (out_scene_port) { + LibertyPort *out_port = findLinkPort(cell, out_scene_port); if (out_port) { FuncExpr *func = out_port->function(); if (func && func->hasPort(port)) @@ -1029,14 +1198,10 @@ Power::findInputInternalPower(const Pin *pin, duty = evalActivity(when, inst).duty(); } float port_internal = energy * duty * activity.density(); - debugPrint(debug_, "power", 2, " %3s %6s %.2f %.2f %9.2e %9.2e %s", - port->name(), - when ? when->to_string().c_str() : "", - activity.density() * 1e-9, - duty, - energy, - port_internal, - related_pg_pin ? related_pg_pin : "no pg_pin"); + debugPrint(debug_, "power", 2, " {} {} {:.2f} {:.2f} {:9.2e} {:9.2e} {}", + port->name(), when ? when->to_string() : "", + activity.density() * 1e-9, duty, energy, port_internal, + related_pg_pin ? related_pg_pin->name() : "no pg_pin"); internal += port_internal; } result.incrInternal(internal); @@ -1048,14 +1213,17 @@ Power::findInputInternalPower(const Pin *pin, float Power::getSlew(Vertex *vertex, const RiseFall *rf, - const Corner *corner) + const Scene *scene) { - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); + const MinMax *min_max = MinMax::max(); const Pin *pin = vertex->pin(); - if (clk_network_->isIdealClock(pin)) - return clk_network_->idealClkSlew(pin, rf, MinMax::max()); - else - return delayAsFloat(graph_->slew(vertex, rf, dcalc_ap->index())); + const ClkNetwork *clk_network = scene->mode()->clkNetwork(); + if (clk_network->isIdealClock(pin)) + return clk_network->idealClkSlew(pin, rf, min_max); + else { + DcalcAPIndex slew_index = scene->dcalcAnalysisPtIndex(min_max); + return delayAsFloat(graph_->slew(vertex, rf, slew_index)); + } } float @@ -1065,16 +1233,18 @@ Power::getMinRfSlew(const Pin *pin) graph_->pinVertices(pin, vertex, bidir_vertex); if (vertex) { const MinMax *min_max = MinMax::min(); - Slew mm_slew = min_max->initValue(); - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - DcalcAPIndex ap_index = dcalc_ap->index(); + float mm_slew = min_max->initValue(); + for (DcalcAPIndex ap_index = 0; + ap_index < dcalcAnalysisPtCount(); + ap_index++) { const Slew &slew1 = graph_->slew(vertex, RiseFall::rise(), ap_index); const Slew &slew2 = graph_->slew(vertex, RiseFall::fall(), ap_index); - Slew slew = delayAsFloat(slew1 + slew2) / 2.0; - if (delayGreater(slew, mm_slew, min_max, this)) - mm_slew = slew; + float slew_avg = (delayAsFloat(slew1, min_max, this) + + delayAsFloat(slew2, min_max, this)) / 2.0; + if (delayGreater(slew_avg, mm_slew, min_max, this)) + mm_slew = slew_avg; } - return delayAsFloat(mm_slew); + return mm_slew; } return 0.0; } @@ -1084,63 +1254,62 @@ Power::findExprOutPort(FuncExpr *expr) { LibertyPort *port; switch (expr->op()) { - case FuncExpr::op_port: - port = expr->port(); - if (port && port->direction()->isAnyOutput()) - return expr->port(); - return nullptr; - case FuncExpr::op_not: - port = findExprOutPort(expr->left()); - if (port) - return port; - return nullptr; - case FuncExpr::op_or: - case FuncExpr::op_and: - case FuncExpr::op_xor: - port = findExprOutPort(expr->left()); - if (port) - return port; - port = findExprOutPort(expr->right()); - if (port) - return port; - return nullptr; - case FuncExpr::op_one: - case FuncExpr::op_zero: - return nullptr; + case FuncExpr::Op::port: + port = expr->port(); + if (port && port->direction()->isAnyOutput()) + return expr->port(); + return nullptr; + case FuncExpr::Op::not_: + port = findExprOutPort(expr->left()); + if (port) + return port; + return nullptr; + case FuncExpr::Op::or_: + case FuncExpr::Op::and_: + case FuncExpr::Op::xor_: + port = findExprOutPort(expr->left()); + if (port) + return port; + port = findExprOutPort(expr->right()); + if (port) + return port; + return nullptr; + case FuncExpr::Op::one: + case FuncExpr::Op::zero: + return nullptr; } return nullptr; } void Power::findOutputInternalPower(const LibertyPort *to_port, - const Instance *inst, - LibertyCell *cell, - PwrActivity &to_activity, - float load_cap, - const Corner *corner, - // Return values. - PowerResult &result) -{ - debugPrint(debug_, "power", 2, "internal output %s/%s cap %s", - network_->pathName(inst), - to_port->name(), + const Instance *inst, + LibertyCell *cell, + PwrActivity &to_activity, + float load_cap, + const Scene *scene, + // Return values. + PowerResult &result) +{ + debugPrint(debug_, "power", 2, "internal output {}/{} cap {}", + network_->pathName(inst), to_port->name(), units_->capacitanceUnit()->asString(load_cap)); - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); - const Pvt *pvt = dcalc_ap->operatingConditions(); - LibertyCell *corner_cell = cell->cornerCell(dcalc_ap); - const LibertyPort *to_corner_port = to_port->cornerPort(dcalc_ap); + const MinMax *min_max = MinMax::max(); + const Pvt *pvt = scene->sdc()->operatingConditions(min_max); + LibertyCell *scene_cell = cell->sceneCell(scene, min_max); + const LibertyPort *to_scene_port = to_port->scenePort(scene, min_max); FuncExpr *func = to_port->function(); - map pg_duty_sum; + std::map pg_duty_sum; int numArcs = 0; - for (InternalPower *pwr : corner_cell->internalPowers(to_corner_port)) { + for (const InternalPower *pwr : scene_cell->internalPowers(to_scene_port)) { numArcs += 1; - const LibertyPort *from_corner_port = pwr->relatedPort(); - if (from_corner_port) { - const Pin *from_pin = findLinkPin(inst, from_corner_port); + const LibertyPort *from_scene_port = pwr->relatedPort(); + if (from_scene_port) { + const Pin *from_pin = findLinkPin(inst, from_scene_port); float from_density = findActivity(from_pin).density(); float duty = findInputDuty(inst, func, pwr); - const char *related_pg_pin = pwr->relatedPgPin(); + LibertyPort *related_pg_pin = pwr->relatedPgPin(); // Note related_pg_pin may be null. pg_duty_sum[related_pg_pin] += from_density * duty; } @@ -1152,19 +1321,19 @@ Power::findOutputInternalPower(const LibertyPort *to_port, " when act/ns duty wgt energy power"); float internal = 0.0; float out_internal = 0.0; - for (InternalPower *pwr : corner_cell->internalPowers(to_corner_port)) { + for (const InternalPower *pwr : scene_cell->internalPowers(to_scene_port)) { FuncExpr *when = pwr->when(); - const char *related_pg_pin = pwr->relatedPgPin(); + LibertyPort *related_pg_pin = pwr->relatedPgPin(); float duty = findInputDuty(inst, func, pwr); Vertex *from_vertex = nullptr; bool positive_unate = true; - const LibertyPort *from_corner_port = pwr->relatedPort(); + const LibertyPort *from_scene_port = pwr->relatedPort(); const Pin *from_pin = nullptr; - if (from_corner_port) { - positive_unate = isPositiveUnate(corner_cell, from_corner_port, to_corner_port); - from_pin = findLinkPin(inst, from_corner_port); + if (from_scene_port) { + positive_unate = isPositiveUnate(scene_cell, from_scene_port, to_scene_port); + from_pin = findLinkPin(inst, from_scene_port); if (from_pin) - from_vertex = graph_->pinLoadVertex(from_pin); + from_vertex = graph_->pinLoadVertex(from_pin); } float energy = 0.0; int rf_count = 0; @@ -1172,29 +1341,29 @@ Power::findOutputInternalPower(const LibertyPort *to_port, // Use unateness to find from_rf. const RiseFall *from_rf = positive_unate ? to_rf : to_rf->opposite(); float slew = from_vertex - ? getSlew(from_vertex, from_rf, corner) + ? getSlew(from_vertex, from_rf, scene) : 0.0; - if (!delayInf(slew)) { + if (!delayInf(slew, this)) { float table_energy = pwr->power(to_rf, pvt, slew, load_cap); energy += table_energy; rf_count++; } } if (rf_count) - energy /= rf_count; // average non-inf energies + energy /= rf_count; // average non-inf energies auto duty_sum_iter = pg_duty_sum.find(related_pg_pin); float weight = 0.0; if (duty_sum_iter != pg_duty_sum.end()) { float duty_sum = duty_sum_iter->second; if (duty_sum != 0.0 && from_pin) { float from_density = findActivity(from_pin).density(); - weight = from_density * duty / duty_sum; + weight = from_density * duty / duty_sum; } } float port_internal = weight * energy * to_activity.density(); float avg_arc_internal = energy * to_activity.density(); - debugPrint(debug_, "power", 2, "%3s -> %-3s %6s %.3f %.3f %.3f %9.2e %9.2e %s", - from_corner_port ? from_corner_port->name() : "-" , + debugPrint(debug_, "power", 2, "{:>3} -> {:<3} {:>6} {:.3f} {:.3f} {:.3f} {:9.2e} {:9.2e} {}", + from_scene_port ? from_scene_port->name() : "-" , to_port->name(), when ? when->to_string().c_str() : "", to_activity.density() * 1e-9, @@ -1202,7 +1371,7 @@ Power::findOutputInternalPower(const LibertyPort *to_port, weight, energy, port_internal, - related_pg_pin ? related_pg_pin : "no pg_pin"); + related_pg_pin ? related_pg_pin->name() : "no pg_pin"); internal += port_internal; out_internal += avg_arc_internal; } @@ -1216,58 +1385,56 @@ Power::findOutputInternalPower(const LibertyPort *to_port, float Power::findInputDuty(const Instance *inst, FuncExpr *func, - InternalPower *pwr) - + const InternalPower *pwr) { - const LibertyPort *from_corner_port = pwr->relatedPort(); - if (from_corner_port) { - LibertyPort *from_port = findLinkPort(network_->libertyCell(inst), - from_corner_port); + const LibertyPort *from_scene_port = pwr->relatedPort(); + if (from_scene_port) { + LibertyPort *from_port = + findLinkPort(network_->libertyCell(inst), from_scene_port); const Pin *from_pin = network_->findPin(inst, from_port); if (from_pin) { FuncExpr *when = pwr->when(); Vertex *from_vertex = graph_->pinLoadVertex(from_pin); if (func && func->hasPort(from_port)) { - float duty = evalDiffDuty(func, from_port, inst); - return duty; + float duty = evalDiffDuty(func, from_port, inst); + return duty; } else if (when) - return evalActivity(when, inst).duty(); - else if (search_->isClock(from_vertex)) - return 0.5; + return evalActivity(when, inst).duty(); + else if (scene_->mode()->clkNetwork()->isClock(from_vertex->pin())) + return 0.5; return 0.5; } } return 0.0; } -// Hack to find cell port that corresponds to corner_port. +// Hack to find cell port that corresponds to scene_port. LibertyPort * Power::findLinkPort(const LibertyCell *cell, - const LibertyPort *corner_port) + const LibertyPort *scene_port) { - return cell->findLibertyPort(corner_port->name()); + return cell->findLibertyPort(scene_port->name()); } Pin * Power::findLinkPin(const Instance *inst, - const LibertyPort *corner_port) + const LibertyPort *scene_port) { const LibertyCell *cell = network_->libertyCell(inst); - LibertyPort *port = findLinkPort(cell, corner_port); + LibertyPort *port = findLinkPort(cell, scene_port); return network_->findPin(inst, port); } static bool isPositiveUnate(const LibertyCell *cell, - const LibertyPort *from, - const LibertyPort *to) + const LibertyPort *from, + const LibertyPort *to) { const TimingArcSetSeq &arc_sets = cell->timingArcSets(from, to); if (!arc_sets.empty()) { TimingSense sense = arc_sets[0]->sense(); - return sense == TimingSense::positive_unate - || sense == TimingSense::non_unate; + return sense == TimingSense::positive_unate || sense == TimingSense::non_unate; } // default return true; @@ -1278,25 +1445,25 @@ isPositiveUnate(const LibertyCell *cell, void Power::findSwitchingPower(const Instance *inst, LibertyCell *cell, - const Corner *corner, + const Scene *scene, // Return values. PowerResult &result) { - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); - LibertyCell *corner_cell = cell->cornerCell(dcalc_ap); + LibertyCell *scene_cell = cell->sceneCell(scene, MinMax::max()); InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { const Pin *to_pin = pin_iter->next(); const LibertyPort *to_port = network_->libertyPort(to_pin); if (to_port) { float load_cap = to_port->direction()->isAnyOutput() - ? graph_delay_calc_->loadCap(to_pin, dcalc_ap) - : 0.0; + ? graph_delay_calc_->loadCap(to_pin, scene, MinMax::max()) + : 0.0; PwrActivity activity = findActivity(to_pin); if (to_port->direction()->isAnyOutput()) { - float volt = portVoltage(corner_cell, to_port, dcalc_ap); + float volt = portVoltage(scene_cell, to_port, scene, MinMax::max()); float switching = .5 * load_cap * volt * volt * activity.density(); - debugPrint(debug_, "power", 2, "switching %s/%s activity = %.2e volt = %.2f %.3e", + debugPrint(debug_, "power", 2, + "switching {}/{} activity = {:.2e} volt = {:.2f} {:.3e}", cell->name(), to_port->name(), activity.density(), @@ -1311,56 +1478,43 @@ Power::findSwitchingPower(const Instance *inst, //////////////////////////////////////////////////////////////// - // Leakage totals for one power/gnd pin. class LeakageSummary { public: - LeakageSummary(); - - bool cond_exists; - float cond_leakage; - float cond_duty_sum; - bool cond_true_exists; - float cond_true_leakage; - bool uncond_exists; - float uncond_leakage; + bool cond_exists{false}; + float cond_leakage{0.0}; + float cond_duty_sum{0.0}; + bool cond_true_exists{false}; + float cond_true_leakage{0.0}; + bool uncond_exists{false}; + float uncond_leakage{0.0}; }; -LeakageSummary::LeakageSummary() : - cond_exists(false), - cond_leakage(0.0), - cond_duty_sum(0.0), - cond_true_exists(false), - cond_true_leakage(0.0), - uncond_exists(false), - uncond_leakage(0.0) -{ -} - void Power::findLeakagePower(const Instance *inst, - LibertyCell *cell, - const Corner *corner, - // Return values. - PowerResult &result) -{ - LibertyCell *corner_cell = cell->cornerCell(corner, MinMax::max()); - std::map leakage_summaries; - for (LeakagePower *leak : *corner_cell->leakagePowers()) { - LibertyPort *pg_port = leak->relatedPgPort(); - if (pg_port == nullptr - || pg_port->pwrGndType() == PwrGndType::primary_power) { + LibertyCell *cell, + const Scene *scene, + // Return values. + PowerResult &result) +{ + LibertyCell *scene_cell = cell->sceneCell(scene, MinMax::max()); + std::map leakage_summaries; + Sim *sim = scene->mode()->sim(); + for (const LeakagePower &pwr : scene_cell->leakagePowers()) { + LibertyPort *pg_port = pwr.relatedPgPort(); + if (pg_port == nullptr || pg_port->pwrGndType() == PwrGndType::primary_power) { + std::string pg_name = pg_port ? pg_port->name() : "?"; LeakageSummary &sum = leakage_summaries[pg_port]; - float leakage = leak->power(); - FuncExpr *when = leak->when(); + float leakage = pwr.power(); + FuncExpr *when = pwr.when(); if (when) { - LogicValue when_value = sim_->evalExpr(when, inst); + LogicValue when_value = sim->evalExpr(when, inst); if (when_value == LogicValue::one) { - debugPrint(debug_, "power", 2, "leakage %s/%s %s=1 %.3e", + debugPrint(debug_, "power", 2, "leakage {}/{} {}=1 {:.3e}", cell->name(), - pg_port->name(), - when->to_string().c_str(), + pg_name, + when->to_string(), leakage); sum.cond_true_leakage = leakage; sum.cond_true_exists = true; @@ -1368,12 +1522,11 @@ Power::findLeakagePower(const Instance *inst, else { PwrActivity cond_activity = evalActivity(when, inst); float cond_duty = cond_activity.duty(); - debugPrint(debug_, "power", 2, "leakage %s %s %s %.3e * %.2f", + debugPrint(debug_, "power", 2, "leakage {} {} {} {:.3e} * {:.2f}", cell->name(), - pg_port->name(), - when->to_string().c_str(), - leakage, - cond_duty); + pg_name, + when->to_string(), + leakage, cond_duty); // Leakage power average weighted by duty. sum.cond_leakage += leakage * cond_duty; if (leakage > 0.0) @@ -1382,9 +1535,9 @@ Power::findLeakagePower(const Instance *inst, } } else { - debugPrint(debug_, "power", 2, "leakage %s %s -- %.3e", + debugPrint(debug_, "power", 2, "leakage {} {} -- {:.3e}", cell->name(), - pg_port->name(), + pg_name, leakage); sum.uncond_leakage = leakage; sum.uncond_exists = true; @@ -1411,9 +1564,9 @@ Power::findLeakagePower(const Instance *inst, // Ignore unconditional leakage unless there are no conditional leakage groups. else if (sum.uncond_exists) leakage = sum.uncond_leakage; - debugPrint(debug_, "power", 2, "leakage %s/%s %.3e", + debugPrint(debug_, "power", 2, "leakage {}/{} {:.3e}", cell->name(), - pg_port->name(), + pg_port ? pg_port->name() : "?", leakage); result.incrLeakage(leakage); } @@ -1424,36 +1577,34 @@ Power::findLeakagePower(const Instance *inst, // External. PwrActivity -Power::pinActivity(const Pin *pin) +Power::pinActivity(const Pin *pin, + const Scene *scene) { - ensureActivities(); + ensureActivities(scene); return findActivity(pin); } PwrActivity Power::findActivity(const Pin *pin) { + const Mode *mode = scene_->mode(); Vertex *vertex = graph_->pinLoadVertex(pin); - if (vertex && vertex->isConstant()) - return PwrActivity(0.0, 0.0, PwrActivityOrigin::constant); - else if (vertex && search_->isClock(vertex)) { - if (activity_map_.hasKey(pin)) { - PwrActivity &activity = activity_map_[pin]; - if (activity.origin() != PwrActivityOrigin::unknown) - return activity; - } + if (vertex && mode->clkNetwork()->isClock(pin)) { + PwrActivity *activity = findKeyValuePtr(activity_map_, pin); + if (activity && activity->origin() != PwrActivityOrigin::unknown) + return *activity; const Clock *clk = findClk(pin); float duty = clockDuty(clk); return PwrActivity(2.0 / clk->period(), duty, PwrActivityOrigin::clock); } else if (global_activity_.isSet()) return global_activity_; - else if (activity_map_.hasKey(pin)) { - PwrActivity &activity = activity_map_[pin]; - if (activity.origin() != PwrActivityOrigin::unknown) - return activity; + else { + PwrActivity *activity = findKeyValuePtr(activity_map_, pin); + if (activity && activity->origin() != PwrActivityOrigin::unknown) + return *activity; } - if (user_activity_map_.hasKey(pin)) + if (user_activity_map_.count(pin)) return user_activity_map_[pin]; return PwrActivity(0.0, 0.0, PwrActivityOrigin::unknown); } @@ -1464,14 +1615,14 @@ Power::clockDuty(const Clock *clk) if (clk->isGenerated()) { const Clock *master = clk->masterClk(); if (master == nullptr) - return 0.5; // punt + return 0.5; // punt else return clockDuty(master); } else { - const FloatSeq *waveform = clk->waveform(); - float rise_time = (*waveform)[0]; - float fall_time = (*waveform)[1]; + const FloatSeq &waveform = clk->waveform(); + float rise_time = waveform[0]; + float fall_time = waveform[1]; float duty = (fall_time - rise_time) / clk->period(); return duty; } @@ -1479,7 +1630,7 @@ Power::clockDuty(const Clock *clk) PwrActivity Power::findSeqActivity(const Instance *inst, - LibertyPort *port) + LibertyPort *port) { if (global_activity_.isSet()) return global_activity_; @@ -1492,31 +1643,30 @@ Power::findSeqActivity(const Instance *inst, float Power::portVoltage(LibertyCell *cell, - const LibertyPort *port, - const DcalcAnalysisPt *dcalc_ap) + const LibertyPort *port, + const Scene *scene, + const MinMax *min_max) { - return pgNameVoltage(cell, port->relatedPowerPin(), dcalc_ap); + return pgPortVoltage(cell, port->relatedPowerPort(), scene, min_max); } float -Power::pgNameVoltage(LibertyCell *cell, - const char *pg_port_name, - const DcalcAnalysisPt *dcalc_ap) -{ - if (pg_port_name) { - LibertyPort *pg_port = cell->findLibertyPort(pg_port_name); - if (pg_port) { - const char *volt_name = pg_port->voltageName(); - LibertyLibrary *library = cell->libertyLibrary(); - float voltage; - bool exists; - library->supplyVoltage(volt_name, voltage, exists); - if (exists) - return voltage; - } +Power::pgPortVoltage(LibertyCell *cell, + const LibertyPort *pg_port, + const Scene *scene, + const MinMax *min_max) +{ + if (pg_port) { + const std::string &volt_name = pg_port->voltageName(); + LibertyLibrary *library = cell->libertyLibrary(); + float voltage; + bool exists; + library->supplyVoltage(volt_name, voltage, exists); + if (exists) + return voltage; } - const Pvt *pvt = dcalc_ap->operatingConditions(); + Pvt *pvt = scene->sdc()->operatingConditions(min_max); if (pvt == nullptr) pvt = cell->libertyLibrary()->defaultOperatingConditions(); if (pvt) @@ -1535,10 +1685,8 @@ Power::findClk(const Pin *to_pin) while (path_iter.hasNext()) { Path *path = path_iter.next(); const Clock *path_clk = path->clock(this); - if (path_clk - && (clk == nullptr - || path_clk->period() < clk->period())) - clk = path_clk; + if (path_clk && (clk == nullptr || path_clk->period() < clk->period())) + clk = path_clk; } } return clk; @@ -1553,45 +1701,43 @@ Power::reportActivityAnnotation(bool report_unannotated, size_t vcd_count = 0; size_t saif_count = 0; size_t input_count = 0; - for (auto const& [pin, activity] : user_activity_map_) { + for (auto const &[pin, activity] : user_activity_map_) { PwrActivityOrigin origin = activity.origin(); switch (origin) { - case PwrActivityOrigin::vcd: - vcd_count++; - break; - case PwrActivityOrigin::saif: - saif_count++; - break; - case PwrActivityOrigin::user: - input_count++; - break; - default: - break; + case PwrActivityOrigin::vcd: + vcd_count++; + break; + case PwrActivityOrigin::saif: + saif_count++; + break; + case PwrActivityOrigin::user: + input_count++; + break; + default: + break; } } if (vcd_count > 0) - report_->reportLine("vcd %5zu", vcd_count); + report_->report("vcd {:>5}", vcd_count); if (saif_count > 0) - report_->reportLine("saif %5zu", saif_count); + report_->report("saif {:>5}", saif_count); if (input_count > 0) - report_->reportLine("input %5zu", input_count); + report_->report("input {:>5}", input_count); size_t pin_count = pinCount(); size_t unannotated_count = pin_count - vcd_count - saif_count - input_count; - report_->reportLine("unannotated %5zu", unannotated_count); + report_->report("unannotated {:>5}", unannotated_count); if (report_annotated) { PinSeq annotated_pins; - for (auto const& [pin, activity] : user_activity_map_) + for (auto const &[pin, activity] : user_activity_map_) annotated_pins.push_back(pin); - sort(annotated_pins.begin(), annotated_pins.end(), PinPathNameLess(sdc_network_)); - report_->reportLine("Annotated pins:"); + sort(annotated_pins, PinPathNameLess(sdc_network_)); + report_->report("Annotated pins:"); for (const Pin *pin : annotated_pins) { const PwrActivity &activity = user_activity_map_[pin]; PwrActivityOrigin origin = activity.origin(); - const char *origin_name = pwr_activity_origin_map.find(origin); - report_->reportLine("%5s %s", - origin_name, - sdc_network_->pathName(pin)); + const std::string &origin_name = pwr_activity_origin_map.find(origin); + report_->report("{:>5} {}", origin_name, sdc_network_->pathName(pin)); } } if (report_unannotated) { @@ -1604,11 +1750,10 @@ Power::reportActivityAnnotation(bool report_unannotated, } delete inst_iter; - sort(unannotated_pins.begin(), unannotated_pins.end(), - PinPathNameLess(sdc_network_)); - report_->reportLine("Unannotated pins:"); + sort(unannotated_pins, PinPathNameLess(sdc_network_)); + report_->report("Unannotated pins:"); for (const Pin *pin : unannotated_pins) { - report_->reportLine(" %s", sdc_network_->pathName(pin)); + report_->report(" {}", sdc_network_->pathName(pin)); } } } @@ -1622,9 +1767,9 @@ Power::findUnannotatedPins(const Instance *inst, const Pin *pin = pin_iter->next(); LibertyPort *liberty_port = sdc_network_->libertyPort(pin); if (!network_->direction(pin)->isInternal() - && !network_->direction(pin)->isPowerGround() - && !(liberty_port && liberty_port->isPwrGnd()) - && user_activity_map_.find(pin) == user_activity_map_.end()) + && !network_->direction(pin)->isPowerGround() + && !(liberty_port && liberty_port->isPwrGnd()) + && !user_activity_map_.contains(pin)) unannotated_pins.push_back(pin); } delete pin_iter; @@ -1643,8 +1788,8 @@ Power::pinCount() const Pin *pin = pin_iter->next(); LibertyPort *liberty_port = sdc_network_->libertyPort(pin); if (!network_->direction(pin)->isInternal() - && !network_->direction(pin)->isPowerGround() - && !(liberty_port && liberty_port->isPwrGnd())) + && !network_->direction(pin)->isPowerGround() + && !(liberty_port && liberty_port->isPwrGnd())) count++; } delete pin_iter; @@ -1662,13 +1807,13 @@ Power::pinCount() } float -Power::clockMinPeriod() +Power::clockMinPeriod(const Sdc *sdc) { - ClockSeq *clks = sdc_->clocks(); - if (clks && !clks->empty()) { + const ClockSeq &clks = sdc->clocks(); + if (!clks.empty()) { float min_period = INF; - for (const Clock *clk : *clks) - min_period = min(min_period, clk->period()); + for (const Clock *clk : clks) + min_period = std::min(min_period, clk->period()); return min_period; } else @@ -1680,22 +1825,13 @@ Power::powerInvalid() { activities_valid_ = false; instance_powers_.clear(); - corner_ = nullptr; + scene_ = nullptr; } //////////////////////////////////////////////////////////////// -PowerResult::PowerResult() : - internal_(0.0), - inputinternal_(0.0), - outputinternal_(0.0), - switching_(0.0), - leakage_(0.0) -{ -} - void -PowerResult::clear() +PowerResult::clear() { internal_ = 0.0; inputinternal_ = 0.0; @@ -1752,8 +1888,8 @@ PowerResult::incr(PowerResult &result) //////////////////////////////////////////////////////////////// PwrActivity::PwrActivity(float density, - float duty, - PwrActivityOrigin origin) : + float duty, + PwrActivityOrigin origin) : density_(density), duty_(duty), origin_(origin) @@ -1761,13 +1897,6 @@ PwrActivity::PwrActivity(float density, check(); } -PwrActivity::PwrActivity() : - density_(0.0), - duty_(0.0), - origin_(PwrActivityOrigin::unknown) -{ -} - void PwrActivity::setDensity(float density) { @@ -1796,8 +1925,8 @@ PwrActivity::init() void PwrActivity::set(float density, - float duty, - PwrActivityOrigin origin) + float duty, + PwrActivityOrigin origin) { density_ = density; duty_ = duty; @@ -1811,7 +1940,7 @@ PwrActivity::check() // Densities can get very small from multiplying probabilities // through deep chains of logic. Clip them to prevent floating // point anomalies. - if (abs(density_) < min_density) + if (std::abs(density_) < min_density) density_ = 0.0; } @@ -1821,10 +1950,10 @@ PwrActivity::isSet() const return origin_ != PwrActivityOrigin::unknown; } -const char * +const std::string & PwrActivity::originName() const { return pwr_activity_origin_map.find(origin_); } -} // namespace +} // namespace sta diff --git a/power/Power.hh b/power/Power.hh index 69440abd7..aeaf3308e 100644 --- a/power/Power.hh +++ b/power/Power.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,15 +24,16 @@ #pragma once +#include +#include #include -#include "StaConfig.hh" // CUDD -#include "UnorderedMap.hh" +#include "Bdd.hh" #include "Network.hh" -#include "SdcClass.hh" #include "PowerClass.hh" +#include "SdcClass.hh" +#include "StaConfig.hh" // CUDD #include "StaState.hh" -#include "Bdd.hh" struct DdNode; struct DdManager; @@ -40,13 +41,13 @@ struct DdManager; namespace sta { class Sta; -class Corner; -class DcalcAnalysisPt; +class Scene; class PropActivityVisitor; class BfsFwdIterator; class Vertex; +class ClkNetwork; -typedef std::pair SeqPin; +using SeqPin = std::pair; class SeqPinHash { @@ -65,9 +66,9 @@ public: const SeqPin &pin2) const; }; -typedef UnorderedMap PwrActivityMap; -typedef UnorderedMap PwrSeqActivityMap; +using PwrActivityMap = std::unordered_map; +using PwrSeqActivityMap = std::unordered_map; // The Power class has access to Sta components directly for // convenience but also requires access to the Sta class member functions. @@ -77,7 +78,25 @@ public: Power(StaState *sta); void clear(); void activitiesInvalid(); - void power(const Corner *corner, + void reportDesign(const Scene *scene, + int digits); + void reportInsts(const InstanceSeq &insts, + const Scene *scene, + int digits); + void reportHighestInsts(size_t count, + const Scene *scene, + int digits); + void reportDesignJson(const Scene *scene, + int digits); + void reportInstsJson(const InstanceSeq &insts, + const Scene *scene, + int digits); + InstPowers highestInstPowers(size_t count, + const Scene *scene); + InstPowers sortInstsByPower(const InstanceSeq &insts, + const Scene *scene); + + void power(const Scene *scene, // Return values. PowerResult &total, PowerResult &sequential, @@ -86,38 +105,40 @@ public: PowerResult ¯o, PowerResult &pad); PowerResult power(const Instance *inst, - const Corner *corner); - void setGlobalActivity(float activity, + const Scene *scene); + + void setGlobalActivity(float density, float duty); void unsetGlobalActivity(); - void setInputActivity(float activity, + void setInputActivity(float density, float duty); void unsetInputActivity(); void setInputPortActivity(const Port *input_port, - float activity, + float density, float duty); void unsetInputPortActivity(const Port *input_port); - PwrActivity pinActivity(const Pin *pin); + PwrActivity pinActivity(const Pin *pin, + const Scene *scene); void setUserActivity(const Pin *pin, - float activity, + float density, float duty, PwrActivityOrigin origin); void unsetUserActivity(const Pin *pin); void reportActivityAnnotation(bool report_unannotated, bool report_annotated); + float clockMinPeriod(const Sdc *sdc); float clockMinPeriod(); - InstanceSeq highestPowerInstances(size_t count, - const Corner *corner); void powerInvalid(); PwrActivity inputActivity() const { return input_activity_; } protected: PwrActivity &activity(const Pin *pin); - bool inClockNetwork(const Instance *inst); + bool inClockNetwork(const Instance *inst, + const ClkNetwork *clk_network); void powerInside(const Instance *hinst, - const Corner *corner, + const Scene *scene, PowerResult &result); - void ensureActivities(); + void ensureActivities(const Scene *scene); bool hasUserActivity(const Pin *pin); PwrActivity &userActivity(const Pin *pin); void setSeqActivity(const Instance *reg, @@ -131,15 +152,22 @@ protected: void setActivity(const Pin *pin, PwrActivity &activity); PwrActivity findActivity(const Pin *pin); + void reportPowerRowJson(std::string_view type, + const PowerResult &power, + int digits, + std::string_view eol); + void reportPowerInstJson(const Instance *inst, + const PowerResult &power, + int digits); - void ensureInstPowers(const Corner *corner); - void findInstPowers(const Corner *corner); + void ensureInstPowers(); + void findInstPowers(); PowerResult power(const Instance *inst, LibertyCell *cell, - const Corner *corner); + const Scene *scene); void findInternalPower(const Instance *inst, LibertyCell *cell, - const Corner *corner, + const Scene *scene, // Return values. PowerResult &result); void findInputInternalPower(const Pin *to_pin, @@ -148,7 +176,7 @@ protected: LibertyCell *cell, PwrActivity &to_activity, float load_cap, - const Corner *corner, + const Scene *scene, // Return values. PowerResult &result); void findOutputInternalPower(const LibertyPort *to_port, @@ -156,22 +184,22 @@ protected: LibertyCell *cell, PwrActivity &to_activity, float load_cap, - const Corner *corner, + const Scene *scene, // Return values. PowerResult &result); void findLeakagePower(const Instance *inst, LibertyCell *cell, - const Corner *corner, + const Scene *scene, // Return values. PowerResult &result); void findSwitchingPower(const Instance *inst, LibertyCell *cell, - const Corner *corner, + const Scene *scene, // Return values. PowerResult &result); float getSlew(Vertex *vertex, const RiseFall *rf, - const Corner *corner); + const Scene *scene); float getMinRfSlew(const Pin *pin); const Clock *findInstClk(const Instance *inst); const Clock *findClk(const Pin *to_pin); @@ -180,13 +208,15 @@ protected: LibertyPort *port); float portVoltage(LibertyCell *cell, const LibertyPort *port, - const DcalcAnalysisPt *dcalc_ap); - float pgNameVoltage(LibertyCell *cell, - const char *pg_port_name, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); + float pgPortVoltage(LibertyCell *cell, + const LibertyPort *pg_port, + const Scene *scene, + const MinMax *min_max); void seedActivities(BfsFwdIterator &bfs); void seedRegOutputActivities(const Instance *reg, - Sequential *seq, + const Sequential &seq, LibertyPort *output, bool invert); void seedRegOutputActivities(const Instance *inst, @@ -204,14 +234,14 @@ protected: LibertyPort *findExprOutPort(FuncExpr *expr); float findInputDuty(const Instance *inst, FuncExpr *func, - InternalPower *pwr); + const InternalPower *pwr); float evalDiffDuty(FuncExpr *expr, LibertyPort *from_port, const Instance *inst); LibertyPort *findLinkPort(const LibertyCell *cell, - const LibertyPort *corner_port); + const LibertyPort *scene_port); Pin *findLinkPin(const Instance *inst, - const LibertyPort *corner_port); + const LibertyPort *scene_port); void clockGatePins(const Instance *inst, // Return values. const Pin *&enable, @@ -226,6 +256,7 @@ protected: size_t pinCount(); private: + const Scene *scene_{nullptr}; // Port/pin activities set by set_pin_activity. // set_pin_activity -global PwrActivity global_activity_; @@ -235,16 +266,18 @@ private: PwrActivityMap user_activity_map_; // Propagated activities. PwrActivityMap activity_map_; - PwrSeqActivityMap seq_activity_map_; - bool activities_valid_; + PwrSeqActivityMap seq_activity_map_{100, + SeqPinHash(network_), + SeqPinEqual()}; + bool activities_valid_{false}; Bdd bdd_; - std::map instance_powers_; - bool instance_powers_valid_; - const Corner *corner_; + std::map instance_powers_{ + InstanceIdLess(network_)}; + bool instance_powers_valid_{false}; - static constexpr int max_activity_passes_ = 100; + static constexpr int max_activity_passes_ = 50; friend class PropActivityVisitor; }; -} // namespace +} // namespace sta diff --git a/power/Power.i b/power/Power.i index 6a492cee6..db7272308 100644 --- a/power/Power.i +++ b/power/Power.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,16 +22,16 @@ // // This notice may not be removed or altered from any source distribution. -%module power - %include "stdint.i" %{ -#include "Sta.hh" -#include "Sdc.hh" #include "power/Power.hh" -#include "power/VcdReader.hh" + +#include "Mode.hh" +#include "Sdc.hh" +#include "Sta.hh" #include "power/SaifReader.hh" +#include "power/VcdReader.hh" using namespace sta; @@ -39,9 +39,54 @@ using namespace sta; %inline %{ +void +report_power_design(const Scene *scene, + int digits) +{ + Sta *sta = Sta::sta(); + sta->reportPowerDesign(scene, digits); +} + +void +report_power_insts(const InstanceSeq insts, + const Scene *scene, + int digits) +{ + Sta *sta = Sta::sta(); + sta->reportPowerInsts(insts, scene, digits); +} + +void +report_power_highest_insts(size_t count, + const Scene *scene, + int digits) +{ + Sta *sta = Sta::sta(); + sta->reportPowerHighestInsts(count, scene, digits); +} + +void +report_power_design_json(const Scene *scene, + int digits) +{ + Sta *sta = Sta::sta(); + sta->reportPowerDesignJson(scene, digits); +} + +void +report_power_insts_json(const InstanceSeq insts, + const Scene *scene, + int digits) +{ + Sta *sta = Sta::sta(); + sta->reportPowerInstsJson(insts, scene, digits); +} + +//////////////////////////////////////////////////////////////// + static void pushPowerResultFloats(PowerResult &power, - FloatSeq &powers) + FloatSeq &powers) { powers.push_back(power.internal()); powers.push_back(power.switching()); @@ -56,11 +101,12 @@ pushInternalPowerComponents(PowerResult &power, powers.push_back(power.outputinternal()); } +// Use lassign to retrieve power values. FloatSeq -design_power(const Corner *corner) +design_power(const Scene *scene) { PowerResult total, sequential, combinational, clock, macro, pad; - Sta::sta()->power(corner, total, sequential, combinational, clock, macro, pad); + Sta::sta()->power(scene, total, sequential, combinational, clock, macro, pad); FloatSeq powers; pushPowerResultFloats(total, powers); pushPowerResultFloats(sequential, powers); @@ -72,10 +118,10 @@ design_power(const Corner *corner) } FloatSeq -internal_power_components(const Corner *corner) +internal_power_components(const Scene *scene) { PowerResult total, sequential, combinational, clock, macro, pad; - Sta::sta()->power(corner, total, sequential, combinational, clock, macro, pad); + Sta::sta()->power(scene, total, sequential, combinational, clock, macro, pad); FloatSeq powers; pushInternalPowerComponents(total, powers); return powers; @@ -83,10 +129,10 @@ internal_power_components(const Corner *corner) FloatSeq instance_power(Instance *inst, - const Corner *corner) + const Scene *scene) { Sta *sta = Sta::sta(); - PowerResult power = sta->power(inst, corner); + PowerResult power = sta->power(inst, scene); FloatSeq powers; powers.push_back(power.internal()); powers.push_back(power.switching()); @@ -97,7 +143,7 @@ instance_power(Instance *inst, void set_power_global_activity(float activity, - float duty) + float duty) { Power *power = Sta::sta()->power(); power->setGlobalActivity(activity, duty); @@ -112,7 +158,7 @@ unset_power_global_activity() void set_power_input_activity(float activity, - float duty) + float duty) { Power *power = Sta::sta()->power(); return power->setInputActivity(activity, duty); @@ -127,8 +173,8 @@ unset_power_input_activity() void set_power_input_port_activity(const Port *input_port, - float activity, - float duty) + float activity, + float duty) { Power *power = Sta::sta()->power(); return power->setInputPortActivity(input_port, activity, duty); @@ -143,8 +189,8 @@ unset_power_input_port_activity(const Port *input_port) void set_power_pin_activity(const Pin *pin, - float activity, - float duty) + float activity, + float duty) { Power *power = Sta::sta()->power(); return power->setUserActivity(pin, activity, duty, PwrActivityOrigin::user); @@ -158,18 +204,15 @@ unset_power_pin_activity(const Pin *pin) } float -clock_min_period() -{ - Power *power = Sta::sta()->power(); - return power->clockMinPeriod(); -} - -InstanceSeq -highest_power_instances(size_t count, - const Corner *corner) +clock_min_period(const char *mode_name) { - Power *power = Sta::sta()->power(); - return power->highestPowerInstances(count, corner); + Sta *sta = Sta::sta(); + Power *power = sta->power(); + const Mode *mode = sta->findMode(mode_name); + if (mode) + return power->clockMinPeriod(mode->sdc()); + else + return 0.0; } //////////////////////////////////////////////////////////////// @@ -177,12 +220,13 @@ highest_power_instances(size_t count, void read_vcd_file(const char *filename, const char *scope, + const char *mode_name, int64_t start_time, int64_t end_time) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); - readVcdActivities(filename, scope, start_time, end_time, sta); + readVcdActivities(filename, scope, mode_name, start_time, end_time, sta); } //////////////////////////////////////////////////////////////// diff --git a/power/Power.tcl b/power/Power.tcl index c0b6b141e..c414ab8d4 100644 --- a/power/Power.tcl +++ b/power/Power.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -33,7 +33,7 @@ namespace eval sta { define_cmd_args "report_power" \ { [-instances instances]\ [-highest_power_instances count]\ - [-corner corner]\ + [-scene scene]\ [-digits digits]\ [-format format]\ [> filename] [>> filename] } @@ -42,7 +42,8 @@ proc_redirect report_power { global sta_report_default_digits parse_key_args "report_power" args \ - keys {-instances -highest_power_instances -corner -digits -format} flags {} + keys {-instances -highest_power_instances -corner -scene -format -digits}\ + flags {} check_argc_eq0 "report_power" $args @@ -55,7 +56,7 @@ proc_redirect report_power { } else { set digits $sta_report_default_digits } - set corner [parse_corner keys] + set scene [parse_scene keys] if { [info exists keys(-format)] } { set format $keys(-format) @@ -69,24 +70,24 @@ proc_redirect report_power { if { [info exists keys(-instances)] } { set insts [get_instances_error "-instances" $keys(-instances)] if { $format == "json" } { - report_power_insts_json $insts $corner $digits + report_power_insts_json $insts $scene $digits } else { - report_power_insts $insts $corner $digits + report_power_insts $insts $scene $digits } } elseif { [info exists keys(-highest_power_instances)] } { set count $keys(-highest_power_instances) check_positive_integer "-highest_power_instances" $count - set insts [highest_power_instances $count $corner] + set insts [highest_power_instances $count $scene] if { $format == "json" } { - report_power_insts_json $insts $corner $digits + report_power_insts_json $insts $scene $digits } else { - report_power_insts $insts $corner $digits + report_power_insts $insts $scene $digits } } else { if { $format == "json" } { - report_power_design_json $corner $digits + report_power_design_json $scene $digits } else { - report_power_design $corner $digits + report_power_design $scene $digits } } } @@ -95,11 +96,11 @@ define_cmd_args "report_internal_power_components" { [> filename] [>> filename] proc_redirect report_internal_power_components { global sta_report_default_digits # Set the default corner - set corner [cmd_corner] + set scene [cmd_scene] if { ![liberty_libraries_exist] } { sta_error 304 "No liberty libraries have been read." } - set power_result [internal_power_components $corner] + set power_result [internal_power_components $scene] report_line $power_result } @@ -375,11 +376,11 @@ proc report_power_inst { inst power_result field_width digits {report_format "te ################################################################ define_cmd_args "set_power_activity" { [-global]\ - [-input]\ - [-input_ports ports]\ - [-pins pins]\ - [-activity activity | -density density]\ - [-duty duty]\ + [-input]\ + [-input_ports ports]\ + [-pins pins]\ + [-activity activity | -density density]\ + [-duty duty]\ [-clock clock]} proc set_power_activity { args } { @@ -405,7 +406,7 @@ proc set_power_activity { args } { sta_error 307 "-activity requires a clock to be defined" } } - set density [expr $activity / [clock_min_period]] + set density [expr $activity / [clock_min_period [cmd_mode_name]]] } if { [info exists keys(-density)] } { @@ -435,7 +436,7 @@ proc set_power_activity { args } { set ports [get_ports_error "input_ports" $keys(-input_ports)] foreach port $ports { if { [get_property $port "direction"] == "input" || [get_property $port "direction"] == "in" } { - if { [is_clock_src [sta::get_port_pin $port]] } { + if { [is_clock_src [get_port_pin $port]] } { sta_warn 310 "activity cannot be set on clock ports." } else { set_power_input_port_activity $port $density $duty @@ -476,7 +477,7 @@ proc unset_power_activity { args } { set ports [get_ports_error "input_ports" $keys(-input_ports)] foreach port $ports { if { [get_property $port "direction"] == "input" } { - if { [is_clock_src [sta::get_port_pin $port]] } { + if { [is_clock_src [get_port_pin $port]] } { sta_warn 303 "activity cannot be set on clock ports." } else { unset_power_input_port_activity $port @@ -513,11 +514,11 @@ proc read_power_activities { args } { ################################################################ -define_cmd_args "read_vcd" { [-scope scope] [-start_time start_time] [-end_time end_time] filename } +define_cmd_args "read_vcd" { [-scope scope] [-mode mode_name] [-start_time start_time] [-end_time end_time] filename } proc read_vcd { args } { parse_key_args "read_vcd" args \ - keys {-scope -start_time -end_time} flags {} + keys {-scope -mode_name -start_time -end_time} flags {} check_argc_eq1 "read_vcd" $args set filename [file nativename [lindex $args 0]] @@ -535,16 +536,18 @@ proc read_vcd { args } { if { [info exists keys(-end_time)] } { set end_time $keys(-end_time) } - read_vcd_file $filename $scope $start_time $end_time + set mode_name [cmd_mode_name] + if { [info exists keys(-mode)] } { + set mode_name $keys(-mode) + } + read_vcd_file $filename $scope $mode_name $start_time $end_time } - ################################################################ define_cmd_args "read_saif" { [-scope scope] filename } proc read_saif { args } { parse_key_args "read_saif" args keys {-scope} flags {} - check_argc_eq1 "read_saif" $args set filename [file nativename [lindex $args 0]] set scope "" @@ -571,9 +574,9 @@ proc_redirect report_activity_annotation { ################################################################ proc power_find_nan { } { - set corner [cmd_corner] + set scene [cmd_scene] foreach inst [network_leaf_instances] { - set power_result [instance_power $inst $corner] + set power_result [instance_power $inst $scene] lassign $power_result internal switching leakage total if { [is_nan $internal] || [is_nan $switching] || [is_nan $leakage] } { report_line "[get_full_name $inst] $internal $switching $leakage" @@ -582,5 +585,9 @@ proc power_find_nan { } { } } +proc is_nan { str } { + return [string match "*NaN" $str] +} + # sta namespace end. } diff --git a/power/ReportPower.cc b/power/ReportPower.cc new file mode 100644 index 000000000..16de910ae --- /dev/null +++ b/power/ReportPower.cc @@ -0,0 +1,229 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#include "ReportPower.hh" + +#include +#include + +#include "Format.hh" +#include "Network.hh" +#include "Report.hh" + +namespace sta { + +ReportPower::ReportPower(StaState *sta) : + StaState(sta) +{ +} + +void +ReportPower::reportDesign(PowerResult &total, + PowerResult &sequential, + PowerResult &combinational, + PowerResult &clock, + PowerResult ¯o, + PowerResult &pad, + int digits) +{ + float design_internal = total.internal(); + float design_switching = total.switching(); + float design_leakage = total.leakage(); + float design_total = total.total(); + + int field_width = std::max(digits + 6, 10); + + reportTitle5("Group", "Internal", "Switching", "Leakage", "Total", field_width); + reportTitle5Units(" ", "Power", "Power", "Power", "Power", "(Watts)", + field_width); + reportTitleDashes5(field_width); + + reportRow("Sequential", sequential, design_total, field_width, digits); + reportRow("Combinational", combinational, design_total, field_width, digits); + reportRow("Clock", clock, design_total, field_width, digits); + reportRow("Macro", macro, design_total, field_width, digits); + reportRow("Pad", pad, design_total, field_width, digits); + + reportTitleDashes5(field_width); + + // Report total row using the totals PowerResult + reportRow("Total", total, design_total, field_width, digits); + + // Report percentage line + std::string percent_line = sta::format("{:<20}", ""); + percent_line += powerColPercent(design_internal, design_total, field_width); + percent_line += powerColPercent(design_switching, design_total, field_width); + percent_line += powerColPercent(design_leakage, design_total, field_width); + report_->reportLine(percent_line); +} + +void +ReportPower::reportInsts(const InstPowers &inst_pwrs, + int digits) +{ + int field_width = std::max(digits + 6, 10); + + reportTitle4("Internal", "Switching", "Leakage", "Total", field_width); + reportTitle4Units("Power", "Power", "Power", "Power", "(Watts)", field_width); + reportTitleDashes4(field_width); + + for (const InstPower &inst_pwr : inst_pwrs) { + reportInst(inst_pwr.first, inst_pwr.second, field_width, digits); + } +} + +void +ReportPower::reportInst(const Instance *inst, + const PowerResult &power, + int field_width, + int digits) +{ + float internal = power.internal(); + float switching = power.switching(); + float leakage = power.leakage(); + float total = power.total(); + + std::string line = powerCol(internal, field_width, digits); + line += powerCol(switching, field_width, digits); + line += powerCol(leakage, field_width, digits); + line += powerCol(total, field_width, digits); + line += " "; + line += network_->pathName(inst); + report_->reportLine(line); +} + +std::string +ReportPower::powerCol(float pwr, + int field_width, + int digits) +{ + if (std::isnan(pwr)) + return sta::format(" {:>{}}", "NaN", field_width); + else + return sta::format(" {:{}.{}e}", pwr, field_width, digits); +} + +std::string +ReportPower::powerColPercent(float col_total, + float total, + int field_width) +{ + float percent = 0.0; + if (total != 0.0 && !std::isnan(total)) { + percent = col_total / total * 100.0; + } + return sta::format("{:{}.1f}%", percent, field_width); +} + +void +ReportPower::reportTitle5(std::string_view title1, + std::string_view title2, + std::string_view title3, + std::string_view title4, + std::string_view title5, + int field_width) +{ + report_->report("{:<20} {:>{}} {:>{}} {:>{}} {:>{}}", title1, title2, field_width, + title3, field_width, title4, field_width, title5, field_width); +} + +void +ReportPower::reportTitle5Units(std::string_view title1, + std::string_view title2, + std::string_view title3, + std::string_view title4, + std::string_view title5, + std::string_view units, + int field_width) +{ + report_->report("{:<20} {:>{}} {:>{}} {:>{}} {:>{}} {}", title1, title2, + field_width, title3, field_width, title4, field_width, title5, + field_width, units); +} + +void +ReportPower::reportTitleDashes5(int field_width) +{ + int count = 20 + (field_width + 1) * 4; + std::string dashes(count, '-'); + report_->reportLine(dashes); +} + +void +ReportPower::reportRow(std::string_view type, + const PowerResult &power, + float design_total, + int field_width, + int digits) +{ + float internal = power.internal(); + float switching = power.switching(); + float leakage = power.leakage(); + float total = power.total(); + + float percent = 0.0; + if (design_total != 0.0 && !std::isnan(design_total)) + percent = total / design_total * 100.0; + + std::string line = sta::format("{:<20}", std::string(type)); + line += powerCol(internal, field_width, digits); + line += powerCol(switching, field_width, digits); + line += powerCol(leakage, field_width, digits); + line += powerCol(total, field_width, digits); + line += sta::format(" {:5.1f}%", percent); + report_->reportLine(line); +} + +void +ReportPower::reportTitle4(std::string_view title1, + std::string_view title2, + std::string_view title3, + std::string_view title4, + int field_width) +{ + report_->report(" {:>{}} {:>{}} {:>{}} {:>{}}", title1, field_width, title2, + field_width, title3, field_width, title4, field_width); +} + +void +ReportPower::reportTitle4Units(std::string_view title1, + std::string_view title2, + std::string_view title3, + std::string_view title4, + std::string_view units, + int field_width) +{ + report_->report(" {:>{}} {:>{}} {:>{}} {:>{}} {}", title1, field_width, title2, + field_width, title3, field_width, title4, field_width, units); +} + +void +ReportPower::reportTitleDashes4(int field_width) +{ + int count = (field_width + 1) * 4; + std::string dashes(count, '-'); + report_->reportLine(dashes); +} + +} // namespace sta diff --git a/power/ReportPower.hh b/power/ReportPower.hh new file mode 100644 index 000000000..105a9a00a --- /dev/null +++ b/power/ReportPower.hh @@ -0,0 +1,93 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include + +#include "NetworkClass.hh" +#include "PowerClass.hh" +#include "StaState.hh" + +namespace sta { + +class ReportPower : public StaState +{ +public: + ReportPower(StaState *sta); + void reportDesign(PowerResult &total, + PowerResult &sequential, + PowerResult &combinational, + PowerResult &clock, + PowerResult ¯o, + PowerResult &pad, + int digits); + void reportInsts(const InstPowers &inst_pwrs, + int digits); + +private: + std::string powerCol(float pwr, + int field_width, + int digits); + std::string powerColPercent(float col_total, + float total, + int field_width); + void reportTitle5(std::string_view title1, + std::string_view title2, + std::string_view title3, + std::string_view title4, + std::string_view title5, + int field_width); + void reportTitle5Units(std::string_view title1, + std::string_view title2, + std::string_view title3, + std::string_view title4, + std::string_view title5, + std::string_view units, + int field_width); + void reportTitleDashes5(int field_width); + void reportRow(std::string_view type, + const PowerResult &power, + float design_total, + int field_width, + int digits); + void reportTitle4(std::string_view title1, + std::string_view title2, + std::string_view title3, + std::string_view title4, + int field_width); + void reportTitle4Units(std::string_view title1, + std::string_view title2, + std::string_view title3, + std::string_view title4, + std::string_view units, + int field_width); + void reportTitleDashes4(int field_width); + void reportInst(const Instance *inst, + const PowerResult &power, + int field_width, + int digits); +}; + +} // namespace sta diff --git a/power/SaifLex.ll b/power/SaifLex.ll index e602983b2..7d53c2a0b 100644 --- a/power/SaifLex.ll +++ b/power/SaifLex.ll @@ -24,9 +24,10 @@ // This notice may not be removed or altered from any source distribution. #include +#include +#include #include "util/FlexDisableRegister.hh" -#include "StringUtil.hh" #include "power/SaifReaderPvt.hh" #include "SaifParse.hh" #include "power/SaifScanner.hh" @@ -85,7 +86,7 @@ EOL \r?\n "\"" { BEGIN INITIAL; - yylval->string = sta::stringCopy(token_.c_str()); + yylval->emplace(std::move(token_)); return token::QSTRING; } @@ -101,7 +102,7 @@ EOL \r?\n "//"[^\n]*{EOL} { loc->lines(); loc->step(); } [0-9]+ { - yylval->uint = atoll(yytext); + yylval->emplace(atoll(yytext)); return token::UINT; } @@ -132,7 +133,7 @@ TC { return token::TC; } IG { return token::IG; } {ID} { - yylval->string = sta::stringCopy(yytext); + yylval->emplace(yytext); return token::ID; } diff --git a/power/SaifParse.yy b/power/SaifParse.yy index 0cae9d637..a447a0cb6 100644 --- a/power/SaifParse.yy +++ b/power/SaifParse.yy @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,9 +24,11 @@ %{ #include +#include +#include +#include #include "Report.hh" -#include "StringUtil.hh" #include "power/SaifReaderPvt.hh" #include "power/SaifScanner.hh" @@ -42,7 +44,7 @@ void sta::SaifParse::error(const location_type &loc, const std::string &msg) { - reader->report()->fileError(169,reader->filename(),loc.begin.line,"%s",msg.c_str()); + reader->report()->fileError(169,reader->filename(),loc.begin.line,{}, msg); } %} @@ -56,29 +58,26 @@ sta::SaifParse::error(const location_type &loc, %parse-param { SaifScanner *scanner } %parse-param { SaifReader *reader } %define api.parser.class {SaifParse} +%define api.value.type variant + +%code requires { +#include "power/SaifReaderPvt.hh" +} // expected shift/reduce conflicts %expect 2 -%union { - char character; - const char *string; - uint64_t uint; - sta::SaifState state; - sta::SaifStateDurations state_durations; -} - %token SAIFILE SAIFVERSION DIRECTION DESIGN DATE VENDOR PROGRAM_NAME VERSION %token DIVIDER TIMESCALE DURATION %token INSTANCE NET PORT %token T0 T1 TX TZ TB TC IG -%token QSTRING ID FNUMBER DNUMBER UINT +%token FNUMBER DNUMBER +%token QSTRING ID +%token UINT -%type UINT -%type QSTRING ID -%type hchar -%type state -%type state_durations +%type hchar +%type state +%type state_durations %start file @@ -97,16 +96,16 @@ header: ; header_stmt: - '(' SAIFVERSION QSTRING ')' { sta::stringDelete($3); } -| '(' DIRECTION QSTRING ')' { sta::stringDelete($3); } -| '(' DESIGN QSTRING ')' { sta::stringDelete($3); } -| '(' DESIGN ')' { } -| '(' DATE QSTRING ')' { sta::stringDelete($3); } -| '(' VENDOR QSTRING ')' { sta::stringDelete($3); } -| '(' PROGRAM_NAME QSTRING ')' { sta::stringDelete($3); } -| '(' VERSION QSTRING ')' { sta::stringDelete($3); } + '(' SAIFVERSION QSTRING ')' +| '(' DIRECTION QSTRING ')' +| '(' DESIGN QSTRING ')' +| '(' DESIGN ')' +| '(' DATE QSTRING ')' +| '(' VENDOR QSTRING ')' +| '(' PROGRAM_NAME QSTRING ')' +| '(' VERSION QSTRING ')' | '(' DIVIDER hchar ')' { reader->setDivider($3); } -| '(' TIMESCALE UINT ID ')' { reader->setTimescale($3, $4); } +| '(' TIMESCALE UINT ID ')' { reader->setTimescale($3, std::move($4)); } | '(' DURATION UINT ')' { reader->setDuration($3); } ; @@ -119,11 +118,11 @@ hchar: instance: '(' INSTANCE ID - { reader->instancePush($3); } + { reader->instancePush(std::move($3)); } instance_contents ')' { reader->instancePop(); } | '(' INSTANCE QSTRING ID - { reader->instancePush($3); } + { reader->instancePush(std::move($3)); } instance_contents ')' { reader->instancePop(); } ; @@ -147,7 +146,7 @@ nets: net: '(' ID state_durations ')' - { reader->setNetDurations($2, $3); } + { reader->setNetDurations(std::move($2), $3); } ; ports: @@ -163,7 +162,7 @@ state_durations: '(' state UINT ')' { $$[static_cast($2)] = $3; } | state_durations '(' state UINT ')' - { $$[static_cast($3)] = $4; } + { $$ = $1; $$[static_cast($3)] = $4; } ; state: diff --git a/power/SaifReader.cc b/power/SaifReader.cc index ecd14277b..b05b6c121 100644 --- a/power/SaifReader.cc +++ b/power/SaifReader.cc @@ -1,50 +1,49 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "power/SaifReader.hh" #include #include +#include +#include -#include "Error.hh" #include "Debug.hh" -#include "Stats.hh" -#include "Report.hh" +#include "Error.hh" +#include "Liberty.hh" #include "Network.hh" #include "PortDirection.hh" -#include "Liberty.hh" -#include "Sdc.hh" #include "Power.hh" +#include "Report.hh" +#include "Sdc.hh" +#include "Sta.hh" +#include "Stats.hh" #include "power/SaifReaderPvt.hh" #include "power/SaifScanner.hh" -#include "Sta.hh" namespace sta { -using std::string; -using std::min; - bool readSaif(const char *filename, const char *scope, @@ -61,11 +60,6 @@ SaifReader::SaifReader(const char *filename, StaState(sta), filename_(filename), scope_(scope), - divider_('/'), - escape_('\\'), - timescale_(1.0E-9F), // default units of ns - duration_(0.0), - in_scope_level_(0), power_(sta->power()) { } @@ -80,7 +74,7 @@ SaifReader::read() SaifParse parser(&scanner, this); // yyparse returns 0 on success. bool success = (parser.parse() == 0); - report_->reportLine("Annotated %zu pin activities.", annotated_pins_.size()); + report_->report("Annotated {} pin activities.", annotated_pins_.size()); return success; } else @@ -95,25 +89,22 @@ SaifReader::setDivider(char divider) void SaifReader::setTimescale(uint64_t multiplier, - const char *units) + std::string &&units) { - if (multiplier == 1 - || multiplier == 10 - || multiplier == 100) { - if (stringEq(units, "us")) + if (multiplier == 1 || multiplier == 10 || multiplier == 100) { + if (stringEqual(units, "us")) timescale_ = multiplier * 1E-6; - else if (stringEq(units, "ns")) + else if (stringEqual(units, "ns")) timescale_ = multiplier * 1E-9; - else if (stringEq(units, "ps")) + else if (stringEqual(units, "ps")) timescale_ = multiplier * 1E-12; - else if (stringEq(units, "fs")) + else if (stringEqual(units, "fs")) timescale_ = multiplier * 1E-15; else - report_->error(180, "SAIF TIMESCALE units not us, ns, or ps."); + report_->error(1861, "SAIF TIMESCALE units not us, ns, or ps."); } else - report_->error(181, "SAIF TIMESCALE multiplier not 1, 10, or 100."); - stringDelete(units); + report_->error(1862, "SAIF TIMESCALE multiplier not 1, 10, or 100."); } void @@ -123,30 +114,31 @@ SaifReader::setDuration(uint64_t duration) } void -SaifReader::instancePush(const char *instance_name) +SaifReader::instancePush(std::string &&instance_name) { if (in_scope_level_ == 0) { // Check for a match to the annotation scope. - saif_scope_.push_back(instance_name); + saif_scope_.push_back(std::move(instance_name)); - string saif_scope; + std::string saif_scope; bool first = true; - for (string &inst : saif_scope_) { + for (std::string &inst : saif_scope_) { if (!first) saif_scope += sdc_network_->pathDivider(); saif_scope += inst; first = false; } - if (stringEq(saif_scope.c_str(), scope_)) + if (saif_scope == scope_) in_scope_level_ = saif_scope_.size(); } else { // Inside annotation scope. Instance *parent = path_.empty() ? sdc_network_->topInstance() : path_.back(); - Instance *child = sdc_network_->findChild(parent, instance_name); + Instance *child = parent + ? sdc_network_->findChild(parent, instance_name) + : nullptr; path_.push_back(child); } - stringDelete(instance_name); } void @@ -161,57 +153,48 @@ SaifReader::instancePop() } void -SaifReader::setNetDurations(const char *net_name, - SaifStateDurations &durations) +SaifReader::setNetDurations(std::string &&net_name, + const SaifStateDurations &durations) { if (in_scope_level_ > 0) { Instance *parent = path_.empty() ? sdc_network_->topInstance() : path_.back(); if (parent) { - string unescaped_name = unescaped(net_name); - const Pin *pin = sdc_network_->findPin(parent, unescaped_name.c_str()); + std::string unescaped_name = unescaped(net_name); + const Pin *pin = sdc_network_->findPin(parent, unescaped_name); LibertyPort *liberty_port = pin ? sdc_network_->libertyPort(pin) : nullptr; - if (pin - && !sdc_network_->isHierarchical(pin) + if (pin && !sdc_network_->isHierarchical(pin) && !sdc_network_->direction(pin)->isInternal() - && !(liberty_port && liberty_port->isPwrGnd())) { + && !(liberty_port && liberty_port->isPwrGnd())) { double t1 = durations[static_cast(SaifState::T1)]; float duty = t1 / duration_; double tc = durations[static_cast(SaifState::TC)]; float density = tc / (duration_ * timescale_); debugPrint(debug_, "read_saif", 2, - "%s duty %.0f / %" PRIu64 " = %.2f tc %.0f density %.2f", - sdc_network_->pathName(pin), - t1, - duration_, - duty, - tc, - density); + "{} duty {:.0f} / {} = {:.2f} tc {:.0f} density {:.2f}", + sdc_network_->pathName(pin), t1, duration_, duty, tc, density); power_->setUserActivity(pin, density, duty, PwrActivityOrigin::saif); annotated_pins_.insert(pin); } } } - stringDelete(net_name); } -string -SaifReader::unescaped(const char *token) +std::string +SaifReader::unescaped(const std::string &token) { - string unescaped; - for (const char *t = token; *t; t++) { - char ch = *t; + std::string unescaped; + for (char ch : token) { if (ch != escape_) - // Just the normal noises. unescaped += ch; } - debugPrint(debug_, "saif_name", 1, "token %s -> %s", token, unescaped.c_str()); + debugPrint(debug_, "saif_name", 1, "token {} -> {}", token, unescaped); return unescaped; } //////////////////////////////////////////////////////////////// SaifScanner::SaifScanner(std::istream *stream, - const string &filename, + const std::string &filename, SaifReader *reader, Report *report) : yyFlexLexer(stream), @@ -224,7 +207,7 @@ SaifScanner::SaifScanner(std::istream *stream, void SaifScanner::error(const char *msg) { - report_->fileError(1868, filename_.c_str(), lineno(), "%s", msg); + report_->fileError(1860, filename_, lineno(), "{}", msg); } -} // namespace +} // namespace sta diff --git a/power/SaifReader.hh b/power/SaifReader.hh index 7fa8b9884..8f408fd9b 100644 --- a/power/SaifReader.hh +++ b/power/SaifReader.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -33,4 +33,4 @@ readSaif(const char *filename, const char *scope, Sta *sta); -} // namespace +} // namespace sta diff --git a/power/SaifReaderPvt.hh b/power/SaifReaderPvt.hh index b5a57bdb6..243e14dc6 100644 --- a/power/SaifReaderPvt.hh +++ b/power/SaifReaderPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,15 +24,15 @@ #pragma once +#include #include -#include -#include #include -#include +#include +#include -#include "Zlib.hh" #include "NetworkClass.hh" #include "StaState.hh" +#include "Zlib.hh" // Header for SaifReader.cc to communicate with SaifLex.cc, SaifParse.cc @@ -46,7 +46,7 @@ class SaifScanner; enum class SaifState { T0, T1, TX, TZ, TB, TC, IG }; -typedef std::array(SaifState::IG)+1> SaifStateDurations; +using SaifStateDurations = std::array(SaifState::IG)+1>; class SaifReader : public StaState { @@ -58,30 +58,30 @@ public: void setDivider(char divider); void setTimescale(uint64_t multiplier, - const char *units); + std::string &&units); void setDuration(uint64_t duration); - void instancePush(const char *instance_name); + void instancePush(std::string &&instance_name); void instancePop(); - void setNetDurations(const char *net_name, - SaifStateDurations &durations); + void setNetDurations(std::string &&net_name, + const SaifStateDurations &durations); const char *filename() { return filename_; } private: - std::string unescaped(const char *token); + std::string unescaped(const std::string &token); const char *filename_; const char *scope_; // Divider delimited scope to begin annotation. - char divider_; - char escape_; - double timescale_; - int64_t duration_; + char divider_ = '/'; + char escape_ = '\\'; + double timescale_ = 1.0E-9; // default units of ns + int64_t duration_ = 0; std::vector saif_scope_; // Scope during parsing. - size_t in_scope_level_; + size_t in_scope_level_ = 0; std::vector path_; // Path within scope. std::set annotated_pins_; Power *power_; }; -} // namespace +} // namespace sta diff --git a/power/SaifScanner.hh b/power/SaifScanner.hh index 2e7e6dcb8..9ca6867f9 100644 --- a/power/SaifScanner.hh +++ b/power/SaifScanner.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -36,6 +36,7 @@ namespace sta { class Report; +class SaifReader; class SaifScanner : public SaifFlexLexer { @@ -44,9 +45,7 @@ public: const std::string &filename, SaifReader *reader, Report *report); - virtual ~SaifScanner() {} - - virtual int lex(SaifParse::semantic_type *const yylval, + virtual int lex(SaifParse::semantic_type *yylval, SaifParse::location_type *yylloc); // YY_DECL defined in SaifLex.ll // Method body created by flex in SaifLex.cc @@ -63,4 +62,4 @@ private: std::string token_; }; -} // namespace +} // namespace sta diff --git a/power/VcdParse.cc b/power/VcdParse.cc index ebfdbf668..c58a41ea5 100644 --- a/power/VcdParse.cc +++ b/power/VcdParse.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "VcdParse.hh" @@ -28,17 +28,13 @@ #include #include -#include "Stats.hh" -#include "Report.hh" -#include "Error.hh" #include "EnumNameMap.hh" +#include "Error.hh" +#include "Report.hh" +#include "Stats.hh" namespace sta { -using std::vector; -using std::string; -using std::isspace; - // Very imprecise syntax definition // https://en.wikipedia.org/wiki/Value_change_dump#Structure.2FSyntax // Much better syntax definition @@ -94,14 +90,12 @@ VcdParse::read(const char *filename, else if (token[0] == '#') { try { time_ = stoll(token.substr(1)); - } - catch (std::invalid_argument &error) { - report_->fileError(805, filename_, file_line_, "invalid time %s", - token.substr(1).c_str()); - } - catch (std::out_of_range &error) { - report_->fileError(806, filename_, file_line_, "time out of range %s", - token.substr(1).c_str()); + } catch (std::invalid_argument &error) { + report_->fileError(805, filename_, file_line_, "invalid time {}", + token.substr(1)); + } catch (std::out_of_range &error) { + report_->fileError(806, filename_, file_line_, "time out of range {}", + token.substr(1)); } // Set time min to start time if it is not set at beginning if (start_time == -1) { @@ -125,13 +119,6 @@ VcdParse::read(const char *filename, VcdParse::VcdParse(Report *report, Debug *debug) : - reader_(nullptr), - file_line_(0), - stmt_line_(0), - time_(0), - prev_time_(0), - start_time_(-1), - end_time_(-1), report_(report), debug_(debug) { @@ -140,7 +127,7 @@ VcdParse::VcdParse(Report *report, void VcdParse::parseTimescale() { - vector tokens = readStmtTokens(); + std::vector tokens = readStmtTokens(); if (tokens.size() == 1) { size_t last; double time_scale = std::stod(tokens[0], &last); @@ -155,7 +142,7 @@ VcdParse::parseTimescale() } void -VcdParse::setTimeUnit(const string &time_unit, +VcdParse::setTimeUnit(std::string_view time_unit, double time_scale) { double time_unit_scale = 1.0; @@ -170,41 +157,38 @@ VcdParse::setTimeUnit(const string &time_unit, reader_->setTimeUnit(time_unit, time_unit_scale, time_scale); } -static EnumNameMap vcd_var_type_map = - {{VcdVarType::wire, "wire"}, - {VcdVarType::reg, "reg"}, - {VcdVarType::parameter, "parameter"}, - {VcdVarType::integer, "integer"}, - {VcdVarType::real, "real"}, - {VcdVarType::supply0, "supply0"}, - {VcdVarType::supply1, "supply1"}, - {VcdVarType::time, "time"}, - {VcdVarType::tri, "tri"}, - {VcdVarType::triand, "triand"}, - {VcdVarType::trior, "trior"}, - {VcdVarType::trireg, "trireg"}, - {VcdVarType::tri0, "tri0"}, - {VcdVarType::tri1, "tri1"}, - {VcdVarType::wand, "wand"}, - {VcdVarType::wor, "wor"} - }; +static EnumNameMap vcd_var_type_map = { + {VcdVarType::wire, "wire"}, + {VcdVarType::reg, "reg"}, + {VcdVarType::parameter, "parameter"}, + {VcdVarType::integer, "integer"}, + {VcdVarType::real, "real"}, + {VcdVarType::supply0, "supply0"}, + {VcdVarType::supply1, "supply1"}, + {VcdVarType::time, "time"}, + {VcdVarType::tri, "tri"}, + {VcdVarType::triand, "triand"}, + {VcdVarType::trior, "trior"}, + {VcdVarType::trireg, "trireg"}, + {VcdVarType::tri0, "tri0"}, + {VcdVarType::tri1, "tri1"}, + {VcdVarType::wand, "wand"}, + {VcdVarType::wor, "wor"}}; void VcdParse::parseVar() { - vector tokens = readStmtTokens(); - if (tokens.size() == 4 - || tokens.size() == 5) { - string type_name = tokens[0]; + std::vector tokens = readStmtTokens(); + if (tokens.size() == 4 || tokens.size() == 5) { + std::string &type_name = tokens[0]; VcdVarType type = vcd_var_type_map.find(type_name, VcdVarType::unknown); if (type == VcdVarType::unknown) - report_->fileWarn(1370, filename_, file_line_, - "Unknown variable type %s.", - type_name.c_str()); + report_->fileWarn(809, filename_, file_line_, "Unknown variable type {}.", + type_name); else { - size_t width = stoi(tokens[1]); - string &id = tokens[2]; - string name = tokens[3]; + size_t width = std::stoi(tokens[1]); + std::string &id = tokens[2]; + std::string &name = tokens[3]; // iverilog separates bus base name from bit range. if (tokens.size() == 5) { // Preserve space after esacaped name. @@ -212,7 +196,6 @@ VcdParse::parseVar() name += ' '; name += tokens[4]; } - reader_->makeVar(scope_, name, type, width, id); } } @@ -223,8 +206,8 @@ VcdParse::parseVar() void VcdParse::parseScope() { - vector tokens = readStmtTokens(); - string &scope = tokens[1]; + std::vector tokens = readStmtTokens(); + std::string &scope = tokens[1]; scope_.push_back(scope); } @@ -238,36 +221,31 @@ VcdParse::parseUpscope() void VcdParse::parseVarValues() { - string token = getToken(); + std::string token = getToken(); while (!token.empty()) { char char0 = toupper(token[0]); if (char0 == '#' && token.size() > 1) { - VcdTime time = stoll(token.substr(1)); + VcdTime time = std::stoll(token.substr(1)); prev_time_ = time_; time_ = time; if (time_ > prev_time_) reader_->varMinDeltaTime(time_ - prev_time_); } - else if (char0 == '0' - || char0 == '1' - || char0 == 'X' - || char0 == 'U' + else if (char0 == '0' || char0 == '1' || char0 == 'X' || char0 == 'U' || char0 == 'Z') { - string id = token.substr(1); + std::string id = token.substr(1); if (!reader_->varIdValid(id)) - report_->fileError(805, filename_, file_line_, - "unknown variable %s", id.c_str()); + report_->fileError(808, filename_, file_line_, "unknown variable {}", id); reader_->varAppendValue(id, time_, char0); } else if (char0 == 'B') { - string bus_value = token.substr(1); - string id = getToken(); + std::string bus_value = token.substr(1); + std::string id = getToken(); if (!reader_->varIdValid(id)) - report_->fileError(807, filename_, file_line_, - "unknown variable %s", id.c_str()); + report_->fileError(807, filename_, file_line_, "unknown variable {}", id); else { // Reverse the bus value to match the bit order in the VCD file. - std::reverse(bus_value.begin(), bus_value.end()); + std::ranges::reverse(bus_value); reader_->varAppendBusValue(id, time_, bus_value); } } @@ -282,12 +260,12 @@ VcdParse::parseVarValues() } } -string +std::string VcdParse::readStmtString() { stmt_line_ = file_line_; - string line; - string token = getToken(); + std::string line; + std::string token = getToken(); while (!token.empty() && token != "$end") { if (!line.empty()) line += " "; @@ -297,12 +275,12 @@ VcdParse::readStmtString() return line; } -vector +std::vector VcdParse::readStmtTokens() { stmt_line_ = file_line_; - vector tokens; - string token = getToken(); + std::vector tokens; + std::string token = getToken(); while (!token.empty() && token != "$end") { tokens.push_back(token); token = getToken(); @@ -310,18 +288,18 @@ VcdParse::readStmtTokens() return tokens; } -string +std::string VcdParse::getToken() { - string token; + std::string token; int ch = gzgetc(stream_); // skip whitespace - while (ch != EOF && isspace(ch)) { + while (ch != EOF && std::isspace(ch)) { if (ch == '\n') file_line_++; ch = gzgetc(stream_); } - while (ch != EOF && !isspace(ch)) { + while (ch != EOF && !std::isspace(ch)) { token.push_back(ch); ch = gzgetc(stream_); } @@ -352,4 +330,4 @@ VcdValue::setValue(VcdTime time, value_ = value; } -} // namespace +} // namespace sta diff --git a/power/VcdParse.hh b/power/VcdParse.hh index 8a2fc62f8..1d7c9aa0d 100644 --- a/power/VcdParse.hh +++ b/power/VcdParse.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -28,13 +28,13 @@ #include #include -#include "Zlib.hh" #include "StaState.hh" +#include "Zlib.hh" namespace sta { -typedef int64_t VcdTime; -typedef std::vector VcdScope; +using VcdTime = int64_t; +using VcdScope = std::vector; enum class VcdVarType { wire, @@ -70,7 +70,7 @@ public: private: void parseTimescale(); - void setTimeUnit(const std::string &time_unit, + void setTimeUnit(std::string_view time_unit, double time_scale); void parseVar(); void parseScope(); @@ -80,19 +80,20 @@ private: std::string readStmtString(); std::vector readStmtTokens(); - VcdReader *reader_; + VcdReader *reader_ = nullptr; gzFile stream_; std::string token_; const char *filename_; - int file_line_; - int stmt_line_; + int file_line_ = 0; + int stmt_line_ = 0; - VcdTime time_; - VcdTime prev_time_; + VcdTime time_ = 0; + VcdTime prev_time_ = 0; // Arguments to VcdParse - VcdTime start_time_; - VcdTime end_time_; + VcdTime start_time_ = -1; + VcdTime end_time_ = -1; + VcdScope scope_; Report *report_; @@ -103,28 +104,28 @@ private: class VcdReader { public: - virtual ~VcdReader() {} - virtual void setDate(const std::string &date) = 0; - virtual void setComment(const std::string &comment) = 0; - virtual void setVersion(const std::string &version) = 0; - virtual void setTimeUnit(const std::string &time_unit, + virtual ~VcdReader() = default; + virtual void setDate(std::string_view date) = 0; + virtual void setComment(std::string_view comment) = 0; + virtual void setVersion(std::string_view version) = 0; + virtual void setTimeUnit(std::string_view time_unit, double time_unit_scale, double time_scale) = 0; virtual void setTimeMin(VcdTime time) = 0; virtual void setTimeMax(VcdTime time) = 0; virtual void varMinDeltaTime(VcdTime min_delta_time) = 0; - virtual bool varIdValid(const std::string &id) = 0; + virtual bool varIdValid(std::string_view id) = 0; virtual void makeVar(const VcdScope &scope, - const std::string &name, + std::string_view name, VcdVarType type, size_t width, - const std::string &id) = 0; + std::string_view id) = 0; virtual void varAppendValue(const std::string &id, VcdTime time, char value) = 0; virtual void varAppendBusValue(const std::string &id, VcdTime time, - const std::string &bus_value) = 0; + std::string_view bus_value) = 0; }; class VcdValue @@ -145,4 +146,4 @@ private: uint64_t bus_value_; }; -} // namespace +} // namespace sta diff --git a/power/VcdReader.cc b/power/VcdReader.cc index 6753c61d1..98297ea66 100644 --- a/power/VcdReader.cc +++ b/power/VcdReader.cc @@ -1,58 +1,53 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "VcdReader.hh" -#include +#include +#include #include +#include -#include "VcdParse.hh" #include "Debug.hh" -#include "Network.hh" #include "Liberty.hh" -#include "PortDirection.hh" -#include "VerilogNamespace.hh" +#include "Mode.hh" +#include "Network.hh" #include "ParseBus.hh" -#include "Sdc.hh" +#include "PortDirection.hh" #include "Power.hh" +#include "Sdc.hh" #include "Sta.hh" +#include "VcdParse.hh" +#include "VerilogNamespace.hh" namespace sta { -using std::string; -using std::abs; -using std::min; -using std::to_string; -using std::vector; -using std::unordered_map; - // Transition count and high time for duty cycle for a group of pins // for one bit of vcd ID. class VcdCount { public: - VcdCount(); double transitionCount() const { return transition_count_; } VcdTime highTime(VcdTime time_max) const; void incrCounts(VcdTime time, @@ -65,10 +60,10 @@ class VcdCount private: VcdTime clippedIntervalStart() const; PinSeq pins_; - VcdTime prev_time_; - char prev_value_; - VcdTime high_time_; - double transition_count_; + VcdTime prev_time_ = -1; + char prev_value_ = '\0'; + VcdTime high_time_ = 0; + double transition_count_ = 0; static VcdTime filter_start_; static VcdTime filter_end_; @@ -78,14 +73,6 @@ class VcdCount VcdTime VcdCount::filter_start_ = -1; VcdTime VcdCount::filter_end_ = -1; -VcdCount::VcdCount() : - prev_time_(-1), - prev_value_('\0'), - high_time_(0), - transition_count_(0) -{ -} - void VcdCount::addPin(const Pin *pin) { @@ -123,12 +110,10 @@ VcdCount::incrCounts(VcdTime time, high_time_ += time - interval_start; } if (value != prev_value_) - transition_count_ += (value == 'X' - || value == 'Z' - || prev_value_ == 'X' - || prev_value_ == 'Z') - ? .5 - : 1.0; + transition_count_ += + (value == 'X' || value == 'Z' || prev_value_ == 'X' || prev_value_ == 'Z') + ? .5 + : 1.0; } // Update state for transitions before or within the window. // This prevents values after window boundaries corrupting high time. @@ -155,15 +140,15 @@ VcdCount::highTime(VcdTime time_max) const //////////////////////////////////////////////////////////////// // VcdCount[bit] -typedef vector VcdCounts; +using VcdCounts = std::vector; // ID -> VcdCount[bit] -typedef unordered_map VcdIdCountsMap; +using VcdIdCountsMap = std::unordered_map; class VcdCountReader : public VcdReader { public: - VcdCountReader(const char *scope, - Network *sdc_network, + VcdCountReader(std::string_view scope, + const Network *sdc_network, Report *report, Debug *debug); VcdTime timeMax() const { return time_max_; } @@ -172,61 +157,59 @@ class VcdCountReader : public VcdReader double timeScale() const { return time_scale_; } // VcdParse callbacks. - void setDate(const string &) override {} - void setComment(const string &) override {} - void setVersion(const string &) override {} - void setTimeUnit(const string &time_unit, + void setDate(std::string_view ) override {} + void setComment(std::string_view ) override {} + void setVersion(std::string_view ) override {} + void setTimeUnit(std::string_view time_unit, double time_unit_scale, double time_scale) override; void setTimeMin(VcdTime time) override; void setTimeMax(VcdTime time) override; void varMinDeltaTime(VcdTime) override {} - bool varIdValid(const string &id) override; + bool varIdValid(std::string_view id) override; void makeVar(const VcdScope &scope, - const string &name, + std::string_view name, VcdVarType type, size_t width, - const string &id) override; - void varAppendValue(const string &id, + std::string_view id) override; + void varAppendValue(const std::string &id, VcdTime time, char value) override; - void varAppendBusValue(const string &id, + void varAppendBusValue(const std::string &id, VcdTime time, - const string &bus_value) override; + std::string_view bus_value) override; private: - void addVarPin(const string &pin_name, - const string &id, + void addVarPin(std::string_view pin_name, + std::string_view id, size_t width, size_t bit_idx); - const char *scope_; - Network *sdc_network_; - Report *report_; - Debug *debug_; + const std::string scope_; - double time_scale_; - VcdTime time_min_; - VcdTime time_max_; + double time_scale_ = 1.0; + VcdTime time_min_ = 0; + VcdTime time_max_ = 0; VcdIdCountsMap vcd_count_map_; + + const Network *sdc_network_; + Report *report_; + Debug *debug_; }; -VcdCountReader::VcdCountReader(const char *scope, - Network *sdc_network, +VcdCountReader::VcdCountReader(std::string_view scope, + const Network *sdc_network, Report *report, Debug *debug) : scope_(scope), sdc_network_(sdc_network), report_(report), - debug_(debug), - time_scale_(1.0), - time_min_(0), - time_max_(0) + debug_(debug) { } void -VcdCountReader::setTimeUnit(const string &, +VcdCountReader::setTimeUnit(std::string_view , double time_unit_scale, double time_scale) { @@ -236,66 +219,64 @@ VcdCountReader::setTimeUnit(const string &, void VcdCountReader::setTimeMin(VcdTime time) { - debugPrint(debug_, "read_vcd", 1, "setTimeMin called with time %" PRIu64, time); + debugPrint(debug_, "read_vcd", 1, "setTimeMin called with time {}", time); time_min_ = time; } void VcdCountReader::setTimeMax(VcdTime time) { - debugPrint(debug_, "read_vcd", 1, "setTimeMax called with time %" PRIu64, time); + debugPrint(debug_, "read_vcd", 1, "setTimeMax called with time {}", time); time_max_ = time; } bool -VcdCountReader::varIdValid(const string &) +VcdCountReader::varIdValid(std::string_view ) { return true; } void VcdCountReader::makeVar(const VcdScope &scope, - const string &name, + std::string_view name, VcdVarType type, size_t width, - const string &id) + std::string_view id) { - if (type == VcdVarType::wire - || type == VcdVarType::reg) { - string path_name; + if (type == VcdVarType::wire || type == VcdVarType::reg) { + std::string path_name; bool first = true; - for (const string &context : scope) { + for (std::string_view context : scope) { if (!first) path_name += '/'; path_name += context; first = false; } - size_t scope_length = strlen(scope_); + size_t scope_length = scope_.size(); // string::starts_with in c++20 - if (scope_length == 0 - || path_name.substr(0, scope_length) == scope_) { + if (scope_length == 0 || path_name.substr(0, scope_length) == scope_) { path_name += '/'; path_name += name; // Strip the scope from the name. - string var_scoped = path_name.substr(scope_length + 1); + std::string var_scoped = path_name.substr(scope_length + 1); if (width == 1) { - string pin_name = netVerilogToSta(&var_scoped); + std::string pin_name = netVerilogToSta(var_scoped); addVarPin(pin_name, id, width, 0); } else { bool is_bus, is_range, subscript_wild; - string bus_name; + std::string bus_name; int from, to; - parseBusName(var_scoped.c_str(), '[', ']', '\\', - is_bus, is_range, bus_name, from, to, subscript_wild); + parseBusName(var_scoped, '[', ']', '\\', is_bus, is_range, bus_name, + from, to, subscript_wild); if (is_bus) { - string sta_bus_name = netVerilogToSta(&bus_name); + std::string sta_bus_name = netVerilogToSta(bus_name); int bit_idx = 0; if (to < from) { for (int bus_bit = to; bus_bit <= from; bus_bit++) { - string pin_name = sta_bus_name; + std::string pin_name = sta_bus_name; pin_name += '['; - pin_name += to_string(bus_bit); + pin_name += std::to_string(bus_bit); pin_name += ']'; addVarPin(pin_name, id, width, bit_idx); bit_idx++; @@ -303,9 +284,9 @@ VcdCountReader::makeVar(const VcdScope &scope, } else { for (int bus_bit = to; bus_bit >= from; bus_bit--) { - string pin_name = sta_bus_name; + std::string pin_name = sta_bus_name; pin_name += '['; - pin_name += to_string(bus_bit); + pin_name += std::to_string(bus_bit); pin_name += ']'; addVarPin(pin_name, id, width, bit_idx); bit_idx++; @@ -313,36 +294,33 @@ VcdCountReader::makeVar(const VcdScope &scope, } } else - report_->warn(1451, "problem parsing bus %s.", var_scoped.c_str()); + report_->warn(1451, "problem parsing bus {}.", var_scoped); } } } } void -VcdCountReader::addVarPin(const string &pin_name, - const string &id, +VcdCountReader::addVarPin(std::string_view pin_name, + std::string_view id, size_t width, size_t bit_idx) { - const Pin *pin = sdc_network_->findPin(pin_name.c_str()); + const Pin *pin = sdc_network_->findPin(pin_name); LibertyPort *liberty_port = pin ? sdc_network_->libertyPort(pin) : nullptr; - if (pin - && !sdc_network_->isHierarchical(pin) + if (pin && !sdc_network_->isHierarchical(pin) && !sdc_network_->direction(pin)->isInternal() && !sdc_network_->direction(pin)->isPowerGround() && !(liberty_port && liberty_port->isPwrGnd())) { - VcdCounts &vcd_counts = vcd_count_map_[id]; + VcdCounts &vcd_counts = vcd_count_map_[std::string(id)]; vcd_counts.resize(width); vcd_counts[bit_idx].addPin(pin); - debugPrint(debug_, "read_vcd", 2, "id %s pin %s", - id.c_str(), - pin_name.c_str()); + debugPrint(debug_, "read_vcd", 2, "id {} pin {}", id, pin_name); } } void -VcdCountReader::varAppendValue(const string &id, +VcdCountReader::varAppendValue(const std::string &id, VcdTime time, char value) { @@ -350,27 +328,23 @@ VcdCountReader::varAppendValue(const string &id, if (itr != vcd_count_map_.end()) { VcdCounts &vcd_counts = itr->second; if (debug_->check("read_vcd", 3)) { - for (size_t bit_idx = 0; bit_idx < vcd_counts.size(); bit_idx++) { - VcdCount &vcd_count = vcd_counts[bit_idx]; + for (auto &vcd_count : vcd_counts) { for (const Pin *pin : vcd_count.pins()) { - debugPrint(debug_, "read_vcd", 3, "%s time %" PRIu64 " value %c", - sdc_network_->pathName(pin), - time, - value); + debugPrint(debug_, "read_vcd", 3, "{} time {} value {}", + sdc_network_->pathName(pin), time, value); } } } - for (size_t bit_idx = 0; bit_idx < vcd_counts.size(); bit_idx++) { - VcdCount &vcd_count = vcd_counts[bit_idx]; + for (auto &vcd_count : vcd_counts) { vcd_count.incrCounts(time, value); } } } void -VcdCountReader::varAppendBusValue(const string &id, +VcdCountReader::varAppendBusValue(const std::string &id, VcdTime time, - const string &bus_value) + std::string_view bus_value) { const auto &itr = vcd_count_map_.find(id); if (itr != vcd_count_map_.end()) { @@ -379,7 +353,7 @@ VcdCountReader::varAppendBusValue(const string &id, char bit_value; if (bus_value.size() == 1) bit_value = bus_value[0]; - else if (bit_idx < bus_value.size()) + else if (bit_idx < bus_value.size()) bit_value = bus_value[bit_idx]; else bit_value = '0'; @@ -387,10 +361,8 @@ VcdCountReader::varAppendBusValue(const string &id, vcd_count.incrCounts(time, bit_value); if (debug_->check("read_vcd", 3)) { for (const Pin *pin : vcd_count.pins()) { - debugPrint(debug_, "read_vcd", 3, "%s time %" PRIu64 " value %c", - sdc_network_->pathName(pin), - time, - bit_value); + debugPrint(debug_, "read_vcd", 3, "{} time {} value {}", + sdc_network_->pathName(pin), time, bit_value); } } } @@ -402,10 +374,11 @@ VcdCountReader::varAppendBusValue(const string &id, class ReadVcdActivities : public StaState { public: - ReadVcdActivities(const char *filename, - const char *scope, + ReadVcdActivities(std::string_view filename, + std::string_view scope, int64_t start_time, int64_t end_time, + const Sdc *sdc, Sta *sta); void readActivities(); @@ -414,40 +387,50 @@ class ReadVcdActivities : public StaState void checkClkPeriod(const Pin *pin, double transition_count); - const char *filename_; + const std::string filename_; int64_t start_time_; int64_t end_time_; + + std::set annotated_pins_; VcdCountReader vcd_reader_; VcdParse vcd_parse_; - + const Sdc *sdc_; Power *power_; - std::set annotated_pins_; static constexpr double sim_clk_period_tolerance_ = .1; }; void -readVcdActivities(const char *filename, - const char *scope, +readVcdActivities(std::string_view filename, + std::string_view scope, + std::string_view mode_name, int64_t start_time, int64_t end_time, Sta *sta) { - ReadVcdActivities reader(filename, scope, start_time, end_time, sta); + const Mode *mode = sta->findMode(mode_name); + const Sdc *sdc = mode->sdc(); + ReadVcdActivities reader(filename, scope, start_time, end_time, sdc, sta); reader.readActivities(); } -ReadVcdActivities::ReadVcdActivities(const char *filename, - const char *scope, +ReadVcdActivities::ReadVcdActivities(std::string_view filename, + std::string_view scope, int64_t start_time, int64_t end_time, + const Sdc *sdc, Sta *sta) : StaState(sta), filename_(filename), start_time_(start_time), end_time_(end_time), - vcd_reader_(scope, sdc_network_, report_, debug_), - vcd_parse_(report_, debug_), + vcd_reader_(scope, + sdc_network_, + report_, + debug_), + vcd_parse_(report_, + debug_), + sdc_(sdc), power_(sta->power()) { } @@ -455,19 +438,19 @@ ReadVcdActivities::ReadVcdActivities(const char *filename, void ReadVcdActivities::readActivities() { - ClockSeq *clks = sdc_->clocks(); - if (clks->empty()) + const ClockSeq &clks = sdc_->clocks(); + if (clks.empty()) report_->error(820, "No clocks have been defined."); // Set the time window filter once globally VcdCount::setFilter(start_time_, end_time_); - vcd_parse_.read(filename_, &vcd_reader_, start_time_, end_time_); + vcd_parse_.read(filename_.c_str(), &vcd_reader_, start_time_, end_time_); if (vcd_reader_.timeMax() > 0) setActivities(); else report_->warn(1450, "VCD max time is zero."); - report_->reportLine("Annotated %zu pin activities.", annotated_pins_.size()); + report_->report("Annotated {} pin activities.", annotated_pins_.size()); } void @@ -477,7 +460,7 @@ ReadVcdActivities::setActivities() VcdTime time_max = vcd_reader_.timeMax(); VcdTime time_delta = time_max - time_min; double time_scale = vcd_reader_.timeScale(); - for (auto& [id, vcd_counts] : vcd_reader_.countMap()) { + for (auto &[id, vcd_counts] : vcd_reader_.countMap()) { for (const VcdCount &vcd_count : vcd_counts) { double transition_count = vcd_count.transitionCount(); VcdTime high_time = vcd_count.highTime(time_max); @@ -486,11 +469,8 @@ ReadVcdActivities::setActivities() if (debug_->check("read_vcd", 1)) { for (const Pin *pin : vcd_count.pins()) { debugPrint(debug_, "read_vcd", 1, - "%s transitions %.1f activity %.2f duty %.2f", - sdc_network_->pathName(pin), - transition_count, - density, - duty); + "{} transitions {:.1f} activity {:.2f} duty {:.2f}", + sdc_network_->pathName(pin), transition_count, density, duty); } } for (const Pin *pin : vcd_count.pins()) { @@ -512,23 +492,24 @@ ReadVcdActivities::checkClkPeriod(const Pin *pin, VcdTime time_max = vcd_reader_.timeMax(); VcdTime time_min = vcd_reader_.timeMin(); double time_scale = vcd_reader_.timeScale(); - double sim_period = (time_max - time_min) * time_scale / (transition_count / 2.0); + double sim_period = + (time_max - time_min) * time_scale / (transition_count / 2.0); for (Clock *clk : *clks) { if (transition_count == 0) - report_->warn(1452, "clock %s pin %s has no vcd transitions.", - clk->name(), + report_->warn(1453, "clock {} pin {} has no vcd transitions.", clk->name(), sdc_network_->pathName(pin)); else { double clk_period = clk->period(); - if (abs((clk_period - sim_period) / clk_period) > sim_clk_period_tolerance_) + if (std::abs((clk_period - sim_period) / clk_period) + > sim_clk_period_tolerance_) // Warn if sim clock period differs from SDC by more than 10%. - report_->warn(1453, "clock %s vcd period %s differs from SDC clock period %s", - clk->name(), - delayAsString(sim_period, this), + report_->warn(1452, + "clock {} vcd period {} differs from SDC clock period {}", + clk->name(), delayAsString(sim_period, this), delayAsString(clk_period, this)); } } } } -} // namespace +} // namespace sta diff --git a/power/VcdReader.hh b/power/VcdReader.hh index 523823e6c..a683f9645 100644 --- a/power/VcdReader.hh +++ b/power/VcdReader.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,16 +25,18 @@ #pragma once #include +#include namespace sta { class Sta; void -readVcdActivities(const char *filename, - const char *scope, +readVcdActivities(std::string_view filename, + std::string_view scope, + std::string_view mode_name, int64_t start_time, int64_t end_time, Sta *sta); -} // namespace +} // namespace sta diff --git a/sdc/Clock.cc b/sdc/Clock.cc index 379444189..29eabb9db 100644 --- a/sdc/Clock.cc +++ b/sdc/Clock.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,60 +25,44 @@ #include "Clock.hh" #include +#include +#include "ContainerHelpers.hh" #include "Error.hh" -#include "StringUtil.hh" +#include "Format.hh" +#include "Graph.hh" #include "MinMax.hh" -#include "Transition.hh" -#include "TimingRole.hh" #include "Network.hh" -#include "Graph.hh" #include "Sdc.hh" #include "Sta.hh" +#include "StringUtil.hh" +#include "TimingRole.hh" +#include "Transition.hh" namespace sta { -Clock::Clock(const char *name, - int index, +Clock::Clock(std::string_view name, + int index, const Network *network) : - name_(stringCopy(name)), + name_(name), pins_(network), - add_to_pins_(false), leaf_pins_(network), - period_(0.0), - waveform_(nullptr), - waveform_valid_(false), - index_(index), - clk_edges_(nullptr), - is_propagated_(false), - uncertainties_(nullptr), - is_generated_(false), - src_pin_(nullptr), - master_clk_(nullptr), - master_clk_infered_(false), - divide_by_(0), - multiply_by_(0), - duty_cycle_(0), - invert_(false), - combinational_(false), - edges_(nullptr), - edge_shifts_(nullptr) + index_(index) { makeClkEdges(); } void -Clock::initClk(PinSet *pins, - bool add_to_pins, - float period, - FloatSeq *waveform, - const char *comment, - const Network *network) +Clock::initClk(const PinSet &pins, + bool add_to_pins, + float period, + const FloatSeq &waveform, + std::string_view comment, + const Network *network) { is_generated_ = false; setPins(pins, network); add_to_pins_ = add_to_pins; - delete waveform_; waveform_ = waveform; waveform_valid_ = true; period_ = period; @@ -93,12 +77,10 @@ Clock::isVirtual() const } void -Clock::setPins(PinSet *pins, - const Network *network) +Clock::setPins(const PinSet &pins, + const Network *network) { - if (pins) - pins_ = *pins; - delete pins; + pins_ = pins; makeLeafPins(network); } @@ -106,11 +88,8 @@ void Clock::makeLeafPins(const Network *network) { leaf_pins_.clear(); - PinSet::Iterator pin_iter(pins_); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); + for (const Pin *pin : pins_) findLeafDriverPins(pin, network, &leaf_pins_); - } } void @@ -123,24 +102,15 @@ Clock::setMasterClk(Clock *master) void Clock::makeClkEdges() { - clk_edges_ = new ClockEdge*[RiseFall::index_count]; - for (auto rf : RiseFall::range()) { + for (const RiseFall *rf : RiseFall::range()) { clk_edges_[rf->index()] = new ClockEdge(this, rf); } } Clock::~Clock() { - stringDelete(name_); - if (clk_edges_) { - delete clk_edges_[RiseFall::riseIndex()]; - delete clk_edges_[RiseFall::fallIndex()]; - delete [] clk_edges_; - } - delete waveform_; - delete edges_; - delete edge_shifts_; - delete uncertainties_; + for (size_t rf_index : RiseFall::rangeIndex()) + delete clk_edges_[rf_index]; } void @@ -172,16 +142,16 @@ Clock::setClkEdgeTimes() void Clock::setClkEdgeTime(const RiseFall *rf) { - float time = (rf == RiseFall::rise()) ? (*waveform_)[0]:(*waveform_)[1]; + float time = waveform_[rf->index()]; clk_edges_[rf->index()]->setTime(time); } const Pin * Clock::defaultPin() const { - PinSet::ConstIterator pin_iter(leaf_pins_); - if (pin_iter.hasNext()) - return pin_iter.next(); + auto itr = leaf_pins_.begin(); + if (itr != leaf_pins_.end()) + return *itr; else return nullptr; } @@ -200,17 +170,17 @@ Clock::setIsPropagated(bool propagated) void Clock::slew(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &slew, - bool &exists) const + const MinMax *min_max, + // Return values. + float &slew, + bool &exists) const { slews_.value(rf, min_max, slew, exists); } float Clock::slew(const RiseFall *rf, - const MinMax *min_max) const + const MinMax *min_max) const { float slew; bool exists; @@ -222,16 +192,16 @@ Clock::slew(const RiseFall *rf, void Clock::setSlew(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew) + const MinMaxAll *min_max, + float slew) { slews_.setValue(rf, min_max, slew); } void Clock::setSlew(const RiseFall *rf, - const MinMax *min_max, - float slew) + const MinMax *min_max, + float slew) { slews_.setValue(rf, min_max, slew); } @@ -244,66 +214,51 @@ Clock::removeSlew() void Clock::setSlewLimit(const RiseFallBoth *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - float slew) + PathClkOrData clk_data, + const MinMax *min_max, + float slew) { - slew_limits_[int(clk_data)].setValue(rf, min_max, slew); + slew_limits_[static_cast(clk_data)].setValue(rf, min_max, slew); } void Clock::slewLimit(const RiseFall *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - // Return values. - float &slew, - bool &exists) const + PathClkOrData clk_data, + const MinMax *min_max, + // Return values. + float &slew, + bool &exists) const { - slew_limits_[int(clk_data)].value(rf, min_max, slew, exists); + slew_limits_[static_cast(clk_data)].value(rf, min_max, slew, exists); } void Clock::uncertainty(const SetupHold *setup_hold, - // Return values. - float &uncertainty, - bool &exists) const + // Return values. + float &uncertainty, + bool &exists) const { - if (uncertainties_) - uncertainties_->value(setup_hold, uncertainty, exists); - else { - uncertainty = 0.0F; - exists = false; - } + uncertainties_.value(setup_hold, uncertainty, exists); } void Clock::setUncertainty(const SetupHoldAll *setup_hold, - float uncertainty) + float uncertainty) { - if (uncertainties_ == nullptr) - uncertainties_ = new ClockUncertainties; - uncertainties_->setValue(setup_hold, uncertainty); + uncertainties_.setValue(setup_hold, uncertainty); } void Clock::setUncertainty(const SetupHold *setup_hold, - float uncertainty) + float uncertainty) { - if (uncertainties_ == nullptr) - uncertainties_ = new ClockUncertainties; - uncertainties_->setValue(setup_hold, uncertainty); + uncertainties_.setValue(setup_hold, uncertainty); } void Clock::removeUncertainty(const SetupHoldAll *setup_hold) { - if (uncertainties_) { - uncertainties_->removeValue(setup_hold); - if (uncertainties_->empty()) { - delete uncertainties_; - uncertainties_ = nullptr; - } - } + uncertainties_.removeValue(setup_hold); } void @@ -315,20 +270,20 @@ Clock::waveformInvalid() //////////////////////////////////////////////////////////////// void -Clock::initGeneratedClk(PinSet *pins, - bool add_to_pins, - Pin *src_pin, - Clock *master_clk, - int divide_by, - int multiply_by, - float duty_cycle, - bool invert, - bool combinational, - IntSeq *edges, - FloatSeq *edge_shifts, - bool is_propagated, - const char *comment, - const Network *network) +Clock::initGeneratedClk(const PinSet &pins, + bool add_to_pins, + Pin *src_pin, + Clock *master_clk, + int divide_by, + int multiply_by, + float duty_cycle, + bool invert, + bool combinational, + const IntSeq &edges, + const FloatSeq &edge_shifts, + bool is_propagated, + std::string_view comment, + const Network *network) { is_generated_ = true; setPins(pins, network); @@ -345,20 +300,7 @@ Clock::initGeneratedClk(PinSet *pins, is_propagated_ = is_propagated; setComment(comment); - delete edges_; - if (edges - && edges->empty()) { - delete edges; - edges = nullptr; - } edges_ = edges; - - delete edge_shifts_; - if (edge_shifts - && edge_shifts->empty()) { - delete edge_shifts; - edge_shifts = nullptr; - } edge_shifts_ = edge_shifts; } @@ -388,40 +330,36 @@ Clock::isGeneratedWithPropagatedMaster() const void Clock::generate(const Clock *src_clk) { - if (waveform_ == nullptr) - waveform_ = new FloatSeq; - else - waveform_->clear(); - + waveform_.clear(); if (divide_by_ == 1.0) { period_ = src_clk->period(); - const FloatSeq *src_wave = src_clk->waveform(); - waveform_->push_back((*src_wave)[0]); - waveform_->push_back((*src_wave)[1]); + const FloatSeq &src_wave = src_clk->waveform(); + waveform_.push_back(src_wave[0]); + waveform_.push_back(src_wave[1]); } else if (divide_by_ > 1) { if (isPowerOfTwo(divide_by_)) { period_ = src_clk->period() * divide_by_; - const FloatSeq *src_wave = src_clk->waveform(); - float rise = (*src_wave)[0]; - waveform_->push_back(rise); - waveform_->push_back(rise + period_ / 2); + const FloatSeq &src_wave = src_clk->waveform(); + float rise = src_wave[0]; + waveform_.push_back(rise); + waveform_.push_back(rise + period_ / 2); } else generateScaledClk(src_clk, static_cast(divide_by_)); } else if (multiply_by_ >= 1) generateScaledClk(src_clk, 1.0F / multiply_by_); - else if (edges_) + else if (!edges_.empty()) generateEdgesClk(src_clk); if (invert_) { - float first_time = (*waveform_)[0]; + float first_time = waveform_[0]; float offset = (first_time >= period_) ? period_ : 0.0F; - size_t edge_count = waveform_->size(); + size_t edge_count = waveform_.size(); for (size_t i = 0; i < edge_count - 1; i++) - (*waveform_)[i] = (*waveform_)[i + 1] - offset; - (*waveform_)[edge_count - 1] = first_time - offset + period_; + waveform_[i] = waveform_[i + 1] - offset; + waveform_[edge_count - 1] = first_time - offset + period_; } setClkEdgeTimes(); waveform_valid_ = true; @@ -429,28 +367,25 @@ Clock::generate(const Clock *src_clk) void Clock::generateScaledClk(const Clock *src_clk, - float scale) + float scale) { period_ = src_clk->period() * scale; if (duty_cycle_ != 0.0) { - float rise = (*src_clk->waveform())[0] * scale; - waveform_->push_back(rise); - waveform_->push_back(rise + period_ * duty_cycle_ / 100.0F); + float rise = src_clk->waveform()[0] * scale; + waveform_.push_back(rise); + waveform_.push_back(rise + period_ * duty_cycle_ / 100.0F); } else { - FloatSeq::ConstIterator wave_iter(src_clk->waveform()); - while (wave_iter.hasNext()) { - float time = wave_iter.next(); - waveform_->push_back(time * scale); - } + for (float time : src_clk->waveform()) + waveform_.push_back(time * scale); } } void Clock::generateEdgesClk(const Clock *src_clk) { - // Edges must be an odd number greater than or equal to 3. - size_t num_edges = edges_->size(); + // Edges must be an odd number >= 3 (Silimate: warn rather than error for != 3). + const size_t num_edges = edges_.size(); if (num_edges < 3) { Sta::sta()->report()->warn(244, "clock %s edges size must be at least 3.", name_); @@ -462,43 +397,27 @@ Clock::generateEdgesClk(const Clock *src_clk) return; } - // Retrieve source clock waveform and shift information. - const FloatSeq *src_wave = src_clk->waveform(); - size_t src_size = src_wave->size(); - float src_period = src_clk->period(); - bool has_shifts = edge_shifts_ && edge_shifts_->size() >= 3; - - // Edge shifts size must be equal to the edges size. - if (has_shifts && edge_shifts_->size() != num_edges) { - Sta::sta()->report()->warn(244, - "clock %s edge shifts size must be equal to edges size.", name_); - has_shifts = false; - } + const FloatSeq &src_wave = src_clk->waveform(); + const int src_size = static_cast(src_wave.size()); + const float src_period = src_clk->period(); float first_edge_time = 0.0; float last_edge_time = 0.0; for (size_t i = 0; i < num_edges; i++) { - int edge_idx = (*edges_)[i] - 1; // Convert to 0-based - float edge_time = (*src_wave)[edge_idx % src_size] - + (edge_idx / src_size) * src_period; + const int edge_1 = edges_[i] - 1; + div_t edge_div = std::div(edge_1, src_size); + float edge_time = src_wave[edge_div.rem] + + static_cast(edge_div.quot) * src_period; + if (!edge_shifts_.empty() && i < edge_shifts_.size()) + edge_time += edge_shifts_[i]; - // Apply corresponding shift - if (has_shifts) { - edge_time += (*edge_shifts_)[i]; - } - - // First edge determines first rising edge of the new clock. if (i == 0) first_edge_time = edge_time; - - // Do not add last edge to the waveform if (i < num_edges - 1) - waveform_->push_back(edge_time); + waveform_.push_back(edge_time); else last_edge_time = edge_time; } - - // The period is the time between the first and last edges. period_ = last_edge_time - first_edge_time; } @@ -512,31 +431,29 @@ const RiseFall * Clock::masterClkEdgeTr(const RiseFall *rf) const { int edge_index = (rf == RiseFall::rise()) ? 0 : 1; - if (edges_ == nullptr || static_cast(edges_->size()) <= edge_index) + if (static_cast(edges_.size()) <= edge_index) return rf; - return ((*edges_)[edge_index] - 1) % 2 + return (edges_[edge_index] - 1) % 2 ? RiseFall::fall() : RiseFall::rise(); } void Clock::srcPinVertices(VertexSet &src_vertices, - const Network *network, - Graph *graph) + const Network *network, + Graph *graph) { if (network->isHierarchical(src_pin_)) { // Use the clocks on a non-hierarchical pin on the same net. PinSet leaf_pins(network); findLeafDriverPins(src_pin_, network, &leaf_pins); - PinSet::Iterator pin_iter(leaf_pins); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); + for (const Pin *pin : leaf_pins) { Vertex *vertex, *bidirect_drvr_vertex; graph->pinVertices(pin, vertex, bidirect_drvr_vertex); if (vertex) - src_vertices.insert(vertex); + src_vertices.insert(vertex); if (bidirect_drvr_vertex) - src_vertices.insert(bidirect_drvr_vertex); + src_vertices.insert(bidirect_drvr_vertex); } } else { @@ -551,26 +468,20 @@ Clock::isDivideByOneCombinational() const return combinational_ && divide_by_ == 1 && multiply_by_ == 0 - && edge_shifts_ == 0; + && edge_shifts_.empty(); } //////////////////////////////////////////////////////////////// ClockEdge::ClockEdge(Clock *clock, - const RiseFall *rf) : + const RiseFall *rf) : clock_(clock), rf_(rf), - name_(stringPrint("%s %s", clock_->name(), rf_->to_string().c_str())), - time_(0.0), + name_(sta::format("{} {}", clock_->name(), rf_->shortName())), index_(clock_->index() * RiseFall::index_count + rf_->index()) { } -ClockEdge::~ClockEdge() -{ - stringDelete(name_); -} - void ClockEdge::setTime(float time) { @@ -619,7 +530,7 @@ clkCmp(const Clock *clk1, int clkEdgeCmp(const ClockEdge *clk_edge1, - const ClockEdge *clk_edge2) + const ClockEdge *clk_edge2) { if (clk_edge1 == nullptr && clk_edge2) return -1; @@ -641,7 +552,7 @@ clkEdgeCmp(const ClockEdge *clk_edge1, bool clkEdgeLess(const ClockEdge *clk_edge1, - const ClockEdge *clk_edge2) + const ClockEdge *clk_edge2) { return clkEdgeCmp(clk_edge1, clk_edge2) < 0; } @@ -649,7 +560,7 @@ clkEdgeLess(const ClockEdge *clk_edge1, //////////////////////////////////////////////////////////////// InterClockUncertainty::InterClockUncertainty(const Clock *src, - const Clock *target) : + const Clock *target) : src_(src), target_(target) { @@ -664,20 +575,20 @@ InterClockUncertainty::empty() const void InterClockUncertainty::uncertainty(const RiseFall *src_rf, - const RiseFall *tgt_rf, - const SetupHold *setup_hold, - float &uncertainty, - bool &exists) const + const RiseFall *tgt_rf, + const SetupHold *setup_hold, + float &uncertainty, + bool &exists) const { uncertainties_[src_rf->index()].value(tgt_rf, setup_hold, - uncertainty, exists); + uncertainty, exists); } void InterClockUncertainty::setUncertainty(const RiseFallBoth *src_rf, - const RiseFallBoth *tgt_rf, - const SetupHoldAll *setup_hold, - float uncertainty) + const RiseFallBoth *tgt_rf, + const SetupHoldAll *setup_hold, + float uncertainty) { for (auto src_rf_index : src_rf->rangeIndex()) uncertainties_[src_rf_index].setValue(tgt_rf, setup_hold, uncertainty); @@ -685,8 +596,8 @@ InterClockUncertainty::setUncertainty(const RiseFallBoth *src_rf, void InterClockUncertainty::removeUncertainty(const RiseFallBoth *src_rf, - const RiseFallBoth *tgt_rf, - const SetupHoldAll *setup_hold) + const RiseFallBoth *tgt_rf, + const SetupHoldAll *setup_hold) { for (auto src_rf_index : src_rf->rangeIndex()) uncertainties_[src_rf_index].removeValue(tgt_rf, setup_hold); @@ -700,23 +611,15 @@ InterClockUncertainty::uncertainties(const RiseFall *src_rf) const bool InterClockUncertaintyLess::operator()(const InterClockUncertainty *inter1, - const InterClockUncertainty *inter2)const + const InterClockUncertainty *inter2)const { return inter1->src()->index() < inter2->src()->index() || (inter1->src() == inter2->src() - && inter1->target()->index() < inter2->target()->index()); + && inter1->target()->index() < inter2->target()->index()); } //////////////////////////////////////////////////////////////// -bool -ClockNameLess::operator()(const Clock *clk1, - const Clock *clk2) -{ - return stringLess(clk1->name(), clk2->name()); -} - - bool ClockIndexLess::operator()(const Clock *clk1, const Clock *clk2) const @@ -749,26 +652,7 @@ int compare(const ClockSet *set1, const ClockSet *set2) { - size_t size1 = set1 ? set1->size() : 0; - size_t size2 = set2 ? set2->size() : 0; - if (size1 == size2) { - ClockSet::ConstIterator iter1(set1); - ClockSet::ConstIterator iter2(set2); - while (iter1.hasNext() && iter2.hasNext()) { - Clock *clk1 = iter1.next(); - Clock *clk2 = iter2.next(); - int id1 = clk1->index(); - int id2 = clk2->index(); - if (id1 < id2) - return -1; - else if (id1 > id2) - return 1; - } - // Sets are equal. - return 0; - } - else - return (size1 > size2) ? 1 : -1; + return sta::compare(set1, set2, ClockIndexLess()); } -} // namespace +} // namespace sta diff --git a/sdc/ClockGatingCheck.cc b/sdc/ClockGatingCheck.cc index 1e890baf2..a8cb57635 100644 --- a/sdc/ClockGatingCheck.cc +++ b/sdc/ClockGatingCheck.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,15 +26,10 @@ namespace sta { -ClockGatingCheck::ClockGatingCheck() : - active_value_(LogicValue::unknown) -{ -} - void ClockGatingCheck::setActiveValue(LogicValue value) { active_value_ = value; } -} // namespace +} // namespace sta diff --git a/sdc/ClockGroups.cc b/sdc/ClockGroups.cc index 850611e56..279ae3c2e 100644 --- a/sdc/ClockGroups.cc +++ b/sdc/ClockGroups.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,18 +24,19 @@ #include "ClockGroups.hh" +#include "ContainerHelpers.hh" #include "StringUtil.hh" namespace sta { -ClockGroups::ClockGroups(const char *name, - bool logically_exclusive, - bool physically_exclusive, - bool asynchronous, - bool allow_paths, - const char *comment) : +ClockGroups::ClockGroups(std::string_view name, + bool logically_exclusive, + bool physically_exclusive, + bool asynchronous, + bool allow_paths, + std::string_view comment) : SdcCmdComment(comment), - name_(stringCopy(name)), + name_(name), logically_exclusive_(logically_exclusive), physically_exclusive_(physically_exclusive), asynchronous_(asynchronous), @@ -45,8 +46,7 @@ ClockGroups::ClockGroups(const char *name, ClockGroups::~ClockGroups() { - stringDelete(name_); - groups_.deleteContentsClear(); + deleteContents(groups_); } void @@ -70,4 +70,4 @@ ClockGroups::removeClock(Clock *clk) } } -} // namespace +} // namespace sta diff --git a/sdc/ClockInsertion.cc b/sdc/ClockInsertion.cc index 4c2455182..91a6c00a9 100644 --- a/sdc/ClockInsertion.cc +++ b/sdc/ClockInsertion.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -27,7 +27,7 @@ namespace sta { ClockInsertion::ClockInsertion(const Clock *clk, - const Pin *pin) : + const Pin *pin) : clk_(clk), pin_(pin) { @@ -35,9 +35,9 @@ ClockInsertion::ClockInsertion(const Clock *clk, void ClockInsertion::setDelay(const RiseFallBoth *rf, - const MinMaxAll *min_max, - const EarlyLateAll *early_late, - float delay) + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay) { for (auto el_index : early_late->rangeIndex()) delays_[el_index].setValue(rf, min_max, delay); @@ -45,8 +45,8 @@ ClockInsertion::setDelay(const RiseFallBoth *rf, float ClockInsertion::delay(const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late) + const MinMax *min_max, + const EarlyLate *early_late) { float insertion; bool exists; @@ -59,11 +59,11 @@ ClockInsertion::delay(const RiseFall *rf, void ClockInsertion::delay(const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late, - // Return values. - float &insertion, - bool &exists) + const MinMax *min_max, + const EarlyLate *early_late, + // Return values. + float &insertion, + bool &exists) { delays_[early_late->index()].value(rf, min_max, insertion, exists); @@ -73,9 +73,9 @@ ClockInsertion::delay(const RiseFall *rf, void ClockInsertion::setDelay(const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late, - float delay) + const MinMax *min_max, + const EarlyLate *early_late, + float delay) { delays_[early_late->index()].setValue(rf, min_max, delay); } @@ -93,4 +93,4 @@ ClockInsertion::delays(const EarlyLate *early_late) return &delays_[early_late->index()]; } -} // namespace +} // namespace sta diff --git a/sdc/ClockLatency.cc b/sdc/ClockLatency.cc index 22fe82ba9..9ef63d265 100644 --- a/sdc/ClockLatency.cc +++ b/sdc/ClockLatency.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -27,7 +27,7 @@ namespace sta { ClockLatency::ClockLatency(const Clock *clk, - const Pin *pin) : + const Pin *pin) : clk_(clk), pin_(pin) { @@ -35,15 +35,15 @@ ClockLatency::ClockLatency(const Clock *clk, void ClockLatency::setDelay(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float delay) + const MinMaxAll *min_max, + float delay) { delays_.setValue(rf, min_max, delay); } float ClockLatency::delay(const RiseFall *rf, - const MinMax *min_max) + const MinMax *min_max) { float latency; bool exists; @@ -56,10 +56,10 @@ ClockLatency::delay(const RiseFall *rf, void ClockLatency::delay(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &latency, - bool &exists) + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) { delays_.value(rf, min_max, latency, exists); @@ -69,8 +69,8 @@ ClockLatency::delay(const RiseFall *rf, void ClockLatency::setDelay(const RiseFall *rf, - const MinMax *min_max, - float delay) + const MinMax *min_max, + float delay) { delays_.setValue(rf, min_max, delay); } @@ -87,4 +87,4 @@ ClockLatency::delays() return &delays_; } -} // namespace +} // namespace sta diff --git a/sdc/CycleAccting.cc b/sdc/CycleAccting.cc index b5f53d51b..eb07d0b2a 100644 --- a/sdc/CycleAccting.cc +++ b/sdc/CycleAccting.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,15 +24,16 @@ #include "CycleAccting.hh" -#include // ceil #include // max +#include // ceil +#include "Clock.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" #include "Fuzzy.hh" -#include "Units.hh" -#include "TimingRole.hh" -#include "Clock.hh" #include "Sdc.hh" +#include "TimingRole.hh" +#include "Units.hh" namespace sta { @@ -49,7 +50,7 @@ CycleAcctings::~CycleAcctings() void CycleAcctings::clear() { - cycle_acctings_.deleteContentsClear(); + deleteContents(cycle_acctings_); } // Determine cycle accounting "on demand". @@ -60,7 +61,7 @@ CycleAcctings::cycleAccting(const ClockEdge *src, if (src == nullptr) src = tgt; CycleAccting probe(src, tgt); - CycleAccting *acct = cycle_acctings_.findKey(&probe); + CycleAccting *acct = findKey(cycle_acctings_, &probe); if (acct == nullptr) { acct = new CycleAccting(src, tgt); if (src == sdc_->defaultArrivalClockEdge()) @@ -78,21 +79,21 @@ CycleAcctings::reportClkToClkMaxCycleWarnings(Report *report) // Find cycle acctings that exceed max cycle count. Eliminate // duplicate warnings between different src/tgt clk edges. ClockPairSet clk_warnings; - for (Clock *src_clk : *sdc_->clocks()) { + for (Clock *src_clk : sdc_->clocks()) { for (const RiseFall *src_rf : RiseFall::range()) { ClockEdge *src = src_clk->edge(src_rf); - for (Clock *tgt_clk : *sdc_->clocks()) { + for (Clock *tgt_clk : sdc_->clocks()) { for (const RiseFall *tgt_rf : RiseFall::range()) { ClockEdge *tgt = tgt_clk->edge(tgt_rf); CycleAccting probe(src, tgt); - CycleAccting *acct = cycle_acctings_.findKey(&probe); + CycleAccting *acct = findKey(cycle_acctings_, &probe); if (acct && acct->maxCyclesExceeded()) { // Canonicalize the warning wrt src/tgt. ClockPair clk_pair1(src_clk, tgt_clk); ClockPair clk_pair2(tgt_clk, src_clk); - if (!clk_warnings.hasKey(clk_pair1) - && !clk_warnings.hasKey(clk_pair2)) { - report->warn(1010, "No common period was found between clocks %s and %s.", + if (!clk_warnings.contains(clk_pair1) + && !clk_warnings.contains(clk_pair2)) { + report->warn(1010, "No common period was found between clocks {} and {}.", src_clk->name(), tgt_clk->name()); clk_warnings.insert(clk_pair1); @@ -107,10 +108,9 @@ CycleAcctings::reportClkToClkMaxCycleWarnings(Report *report) //////////////////////////////////////////////////////////////// CycleAccting::CycleAccting(const ClockEdge *src, - const ClockEdge *tgt) : + const ClockEdge *tgt) : src_(src), - tgt_(tgt), - max_cycles_exceeded_(false) + tgt_(tgt) { for (int i = 0; i <= TimingRole::index_max; i++) { delay_[i] = MinMax::min()->initValue(); @@ -125,7 +125,7 @@ CycleAccting::findDelays(StaState *sta) { Debug *debug = sta->debug(); const Unit *time_unit = sta->units()->timeUnit(); - debugPrint(debug, "cycle_acct", 1, "%s -> %s", + debugPrint(debug, "cycle_acct", 1, "{} -> {}", src_->name(), tgt_->name()); const int setup_index = TimingRole::setup()->index(); @@ -157,127 +157,127 @@ CycleAccting::findDelays(StaState *sta) double tgt_time = tgt_cycle_start + tgt_->time(); double tgt_opp_time = tgt_cycle_start + tgt_opp_time1; for (src_cycle = firstCycle(src_); - ; - src_cycle++) { - double src_cycle_start = src_cycle * src_period; - double src_time = src_cycle_start + src_->time(); - - // Make sure both setup and hold required are determined. - if (tgt_past_src && src_past_tgt - // Synchronicity achieved. - && fuzzyEqual(src_cycle_start, tgt_cycle_start)) { - debugPrint(debug, "cycle_acct", 1, " setup = %s, required = %s", + ; + src_cycle++) { + double src_cycle_start = src_cycle * src_period; + double src_time = src_cycle_start + src_->time(); + + // Make sure both setup and hold required are determined. + if (tgt_past_src && src_past_tgt + // Synchronicity achieved. + && fuzzyEqual(src_cycle_start, tgt_cycle_start)) { + debugPrint(debug, "cycle_acct", 1, " setup = {}, required = {}", time_unit->asString(delay_[setup_index]), time_unit->asString(required_[setup_index])); - debugPrint(debug, "cycle_acct", 1, " hold = %s, required = %s", + debugPrint(debug, "cycle_acct", 1, " hold = {}, required = {}", time_unit->asString(delay_[hold_index]), time_unit->asString(required_[hold_index])); - debugPrint(debug, "cycle_acct", 1, - " converged at src cycles = %d tgt cycles = %d", + debugPrint(debug, "cycle_acct", 1, + " converged at src cycles = {} tgt cycles = {}", src_cycle, tgt_cycle); - return; - } + return; + } - if (fuzzyGreater(src_cycle_start, tgt_cycle_start + tgt_period) - && src_past_tgt) - break; - debugPrint(debug, "cycle_acct", 2, " %s src cycle %d %s + %s = %s", + if (fuzzyGreater(src_cycle_start, tgt_cycle_start + tgt_period) + && src_past_tgt) + break; + debugPrint(debug, "cycle_acct", 2, " {} src cycle {} {} + {} = {}", src_->name(), src_cycle, time_unit->asString(src_cycle_start), time_unit->asString(src_->time()), time_unit->asString(src_time)); - debugPrint(debug, "cycle_acct", 2, " %s tgt cycle %d %s + %s = %s", + debugPrint(debug, "cycle_acct", 2, " {} tgt cycle {} {} + {} = {}", tgt_->name(), tgt_cycle, time_unit->asString(tgt_cycle_start), time_unit->asString(tgt_->time()), time_unit->asString(tgt_time)); - // For setup checks, target has to be AFTER source. - if (fuzzyGreater(tgt_time, src_time)) { - tgt_past_src = true; - double delay = tgt_time - src_time; - if (fuzzyLess(delay, delay_[setup_index])) { - double required = tgt_time - src_cycle_start; - setSetupAccting(src_cycle, tgt_cycle, delay, required); - debugPrint(debug, "cycle_acct", 2, - " setup min delay = %s, required = %s", + // For setup checks, target has to be AFTER source. + if (fuzzyGreater(tgt_time, src_time)) { + tgt_past_src = true; + double delay = tgt_time - src_time; + if (fuzzyLess(delay, delay_[setup_index])) { + double required = tgt_time - src_cycle_start; + setSetupAccting(src_cycle, tgt_cycle, delay, required); + debugPrint(debug, "cycle_acct", 2, + " setup min delay = {}, required = {}", time_unit->asString(delay_[setup_index]), time_unit->asString(required_[setup_index])); - } - } - - // Data check setup checks are zero cycle. - if (fuzzyLessEqual(tgt_time, src_time)) { - double setup_delay = src_time - tgt_time; - if (fuzzyLess(setup_delay, delay_[data_check_setup_index])) { - double setup_required = tgt_time - src_cycle_start; - setAccting(TimingRole::dataCheckSetup(), src_cycle, tgt_cycle, - setup_delay, setup_required); - double hold_required = tgt_time - (src_cycle_start + src_period); - double hold_delay = (src_period + src_time) - tgt_time; - setAccting(TimingRole::dataCheckHold(), - src_cycle + 1, tgt_cycle, hold_delay, hold_required); - } - } - - // Latch setup cycle accting for the enable is the data clk edge - // closest to the disable (opposite) edge. - if (fuzzyGreater(tgt_opp_time, src_time)) { - double delay = tgt_opp_time - src_time; - if (fuzzyLess(delay, delay_[latch_setup_index])) { - double latch_tgt_time = tgt_time; - int latch_tgt_cycle = tgt_cycle; - // Enable time is the edge before the disable. - if (tgt_time > tgt_opp_time) { - latch_tgt_time -= tgt_period; - latch_tgt_cycle--; - } - double required = latch_tgt_time - src_cycle_start; - setAccting(TimingRole::latchSetup(), - src_cycle, latch_tgt_cycle, delay, required); - debugPrint(debug, "cycle_acct", 2, - " latch setup min delay = %s, required = %s", + } + } + + // Data check setup checks are zero cycle. + if (fuzzyLessEqual(tgt_time, src_time)) { + double setup_delay = src_time - tgt_time; + if (fuzzyLess(setup_delay, delay_[data_check_setup_index])) { + double setup_required = tgt_time - src_cycle_start; + setAccting(TimingRole::dataCheckSetup(), src_cycle, tgt_cycle, + setup_delay, setup_required); + double hold_required = tgt_time - (src_cycle_start + src_period); + double hold_delay = (src_period + src_time) - tgt_time; + setAccting(TimingRole::dataCheckHold(), + src_cycle + 1, tgt_cycle, hold_delay, hold_required); + } + } + + // Latch setup cycle accting for the enable is the data clk edge + // closest to the disable (opposite) edge. + if (fuzzyGreater(tgt_opp_time, src_time)) { + double delay = tgt_opp_time - src_time; + if (fuzzyLess(delay, delay_[latch_setup_index])) { + double latch_tgt_time = tgt_time; + int latch_tgt_cycle = tgt_cycle; + // Enable time is the edge before the disable. + if (tgt_time > tgt_opp_time) { + latch_tgt_time -= tgt_period; + latch_tgt_cycle--; + } + double required = latch_tgt_time - src_cycle_start; + setAccting(TimingRole::latchSetup(), + src_cycle, latch_tgt_cycle, delay, required); + debugPrint(debug, "cycle_acct", 2, + " latch setup min delay = {}, required = {}", time_unit->asString(delay_[latch_setup_index]), time_unit->asString(required_[latch_setup_index])); - } - } - - // For hold checks, target has to be BEFORE source. - if (fuzzyLessEqual(tgt_time, src_time)) { - double delay = src_time - tgt_time; - src_past_tgt = true; - if (fuzzyLess(delay, delay_[hold_index])) { - double required = tgt_time - src_cycle_start; - setHoldAccting(src_cycle, tgt_cycle, delay, required); - debugPrint(debug, "cycle_acct", 2, - " hold min delay = %s, required = %s", + } + } + + // For hold checks, target has to be BEFORE source. + if (fuzzyLessEqual(tgt_time, src_time)) { + double delay = src_time - tgt_time; + src_past_tgt = true; + if (fuzzyLess(delay, delay_[hold_index])) { + double required = tgt_time - src_cycle_start; + setHoldAccting(src_cycle, tgt_cycle, delay, required); + debugPrint(debug, "cycle_acct", 2, + " hold min delay = {}, required = {}", time_unit->asString(delay_[hold_index]), time_unit->asString(required_[hold_index])); - } - } - - // Gated clock hold checks are in the same cycle as the - // setup check. - if (fuzzyLessEqual(tgt_opp_time, src_time)) { - double delay = src_time - tgt_time; - if (fuzzyLess(delay, delay_[gclk_hold_index])) { - double required = tgt_time - src_cycle_start; - setAccting(TimingRole::gatedClockHold(), - src_cycle, tgt_cycle, delay, required); - debugPrint(debug, "cycle_acct", 2, - " gated clk hold min delay = %s, required = %s", + } + } + + // Gated clock hold checks are in the same cycle as the + // setup check. + if (fuzzyLessEqual(tgt_opp_time, src_time)) { + double delay = src_time - tgt_time; + if (fuzzyLess(delay, delay_[gclk_hold_index])) { + double required = tgt_time - src_cycle_start; + setAccting(TimingRole::gatedClockHold(), + src_cycle, tgt_cycle, delay, required); + debugPrint(debug, "cycle_acct", 2, + " gated clk hold min delay = {}, required = {}", time_unit->asString(delay_[gclk_hold_index]), time_unit->asString(required_[gclk_hold_index])); - } - } + } + } } tgt_cycle++; } max_cycles_exceeded_ = true; debugPrint(debug, "cycle_acct", 1, - " max cycles exceeded after %d src cycles, %d tgt_cycles", + " max cycles exceeded after {} src cycles, {} tgt_cycles", src_cycle, tgt_cycle); } else if (tgt_period > 0.0) @@ -297,9 +297,9 @@ CycleAccting::firstCycle(const ClockEdge *clk_edge) const void CycleAccting::setSetupAccting(int src_cycle, - int tgt_cycle, - float delay, - float req) + int tgt_cycle, + float delay, + float req) { setAccting(TimingRole::setup(), src_cycle, tgt_cycle, delay, req); setAccting(TimingRole::outputSetup(), src_cycle, tgt_cycle, delay, req); @@ -310,9 +310,9 @@ CycleAccting::setSetupAccting(int src_cycle, void CycleAccting::setHoldAccting(int src_cycle, - int tgt_cycle, - float delay, - float req) + int tgt_cycle, + float delay, + float req) { setAccting(TimingRole::hold(), src_cycle, tgt_cycle, delay, req); setAccting(TimingRole::outputHold(), src_cycle, tgt_cycle, delay, req); @@ -323,10 +323,10 @@ CycleAccting::setHoldAccting(int src_cycle, void CycleAccting::setAccting(const TimingRole *role, - int src_cycle, - int tgt_cycle, - float delay, - float req) + int src_cycle, + int tgt_cycle, + float delay, + float req) { int index = role->index(); src_cycle_[index] = src_cycle; @@ -353,9 +353,9 @@ CycleAccting::findDefaultArrivalSrcDelays() void CycleAccting::setDefaultSetupAccting(int src_cycle, - int tgt_cycle, - float delay, - float req) + int tgt_cycle, + float delay, + float req) { setSetupAccting(src_cycle, tgt_cycle, delay, req); setAccting(TimingRole::latchSetup(), src_cycle, tgt_cycle, delay, req); @@ -364,9 +364,9 @@ CycleAccting::setDefaultSetupAccting(int src_cycle, void CycleAccting::setDefaultHoldAccting(int src_cycle, - int tgt_cycle, - float delay, - float req) + int tgt_cycle, + float delay, + float req) { setHoldAccting(src_cycle, tgt_cycle, delay, req); setAccting(TimingRole::dataCheckHold(), src_cycle, tgt_cycle, delay, req); @@ -406,14 +406,14 @@ CycleAccting::targetTimeOffset(const TimingRole *check_role) bool CycleAcctingLess::operator()(const CycleAccting *acct1, - const CycleAccting *acct2) const + const CycleAccting *acct2) const { int src_index1 = acct1->src()->index(); int src_index2 = acct2->src()->index(); return src_index1 < src_index2 || (src_index1 == src_index2 - && acct1->target()->index() < acct2->target()->index()); + && acct1->target()->index() < acct2->target()->index()); } size_t @@ -424,10 +424,10 @@ CycleAcctingHash::operator()(const CycleAccting *acct) const bool CycleAcctingEqual::operator()(const CycleAccting *acct1, - const CycleAccting *acct2) const + const CycleAccting *acct2) const { return acct1->src() == acct2->src() && acct1->target() == acct2->target(); } -} // namespace +} // namespace sta diff --git a/sdc/DataCheck.cc b/sdc/DataCheck.cc index 15a839ba2..a538c6a05 100644 --- a/sdc/DataCheck.cc +++ b/sdc/DataCheck.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -30,8 +30,8 @@ namespace sta { DataCheck::DataCheck(Pin *from, - Pin *to, - Clock *clk) : + Pin *to, + Clock *clk) : from_(from), to_(to), clk_(clk) @@ -40,21 +40,21 @@ DataCheck::DataCheck(Pin *from, void DataCheck::margin(const RiseFall *from_rf, - const RiseFall *to_rf, - const SetupHold *setup_hold, - // Return values. - float &margin, - bool &exists) const + const RiseFall *to_rf, + const SetupHold *setup_hold, + // Return values. + float &margin, + bool &exists) const { - return margins_[from_rf->index()].value(to_rf, setup_hold, - margin, exists); + margins_[from_rf->index()].value(to_rf, setup_hold, + margin, exists); } void DataCheck::setMargin(const RiseFallBoth *from_rf, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold, - float margin) + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold, + float margin) { for (auto from_rf_index : from_rf->rangeIndex()) margins_[from_rf_index].setValue(to_rf, setup_hold, margin); @@ -62,8 +62,8 @@ DataCheck::setMargin(const RiseFallBoth *from_rf, void DataCheck::removeMargin(const RiseFallBoth *from_rf, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold) + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold) { for (int from_rf_index : from_rf->rangeIndex()) margins_[from_rf_index].removeValue(to_rf, setup_hold); @@ -81,9 +81,9 @@ DataCheck::empty() const void DataCheck::marginIsOneValue(const SetupHold *setup_hold, - // Return values. - float &value, - bool &one_value) const + // Return values. + float &value, + bool &one_value) const { float value1, value2; if (margins_[RiseFall::riseIndex()].isOneValue(setup_hold, value1) @@ -105,7 +105,7 @@ DataCheckLess::DataCheckLess(const Network *network) : bool DataCheckLess::operator()(const DataCheck *check1, - const DataCheck *check2) const + const DataCheck *check2) const { const Pin *from1 = check1->from(); const Pin *from2 = check2->from(); @@ -115,9 +115,9 @@ DataCheckLess::operator()(const DataCheck *check1, const Clock *clk2 = check2->clk(); return network_->id(from1) < network_->id(from2) || (from1 == from2 - && (network_->id(to1) < network_->id(to2) - || (to1 == to2 - && clkCmp(clk1, clk2) < 0))); + && (network_->id(to1) < network_->id(to2) + || (to1 == to2 + && clkCmp(clk1, clk2) < 0))); } -} // namespace +} // namespace sta diff --git a/sdc/DeratingFactors.cc b/sdc/DeratingFactors.cc index 34609412d..8b13bf2c1 100644 --- a/sdc/DeratingFactors.cc +++ b/sdc/DeratingFactors.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,16 +26,16 @@ namespace sta { -inline int +inline size_t index(TimingDerateType type) { - return int(type); + return static_cast(type); } -inline int +inline size_t index(TimingDerateCellType type) { - return int(type); + return static_cast(type); } DeratingFactors::DeratingFactors() @@ -45,35 +45,35 @@ DeratingFactors::DeratingFactors() void DeratingFactors::setFactor(PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float factor) + const RiseFallBoth *rf, + const EarlyLate *early_late, + float factor) { - for (auto rf1 : rf->range()) - factors_[int(clk_data)].setValue(rf1, early_late, factor); + for (const RiseFall *rf1 : rf->range()) + factors_[static_cast(clk_data)].setValue(rf1, early_late, factor); } void DeratingFactors::factor(PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late, - float &factor, - bool &exists) const + const RiseFall *rf, + const EarlyLate *early_late, + float &factor, + bool &exists) const { - factors_[int(clk_data)].value(rf, early_late, factor, exists); + factors_[static_cast(clk_data)].value(rf, early_late, factor, exists); } void DeratingFactors::clear() { - for (int clk_data = 0; clk_data < path_clk_or_data_count;clk_data++) - factors_[int(clk_data)].clear(); + for (RiseFallMinMax &factors : factors_) + factors.clear(); } void DeratingFactors::isOneValue(const EarlyLate *early_late, - bool &is_one_value, - float &value) const + bool &is_one_value, + float &value) const { bool is_one_value0, is_one_value1; float value0, value1; @@ -87,11 +87,11 @@ DeratingFactors::isOneValue(const EarlyLate *early_late, void DeratingFactors::isOneValue(PathClkOrData clk_data, - const EarlyLate *early_late, - bool &is_one_value, - float &value) const + const EarlyLate *early_late, + bool &is_one_value, + float &value) const { - is_one_value = factors_[int(clk_data)].isOneValue(early_late, value); + is_one_value = factors_[static_cast(clk_data)].isOneValue(early_late, value); } bool @@ -110,32 +110,32 @@ DeratingFactorsGlobal::DeratingFactorsGlobal() void DeratingFactorsGlobal::setFactor(TimingDerateType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float factor) + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float factor) { factors_[index(type)].setFactor(clk_data, rf, early_late, factor); } void DeratingFactorsGlobal::factor(TimingDerateType type, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late, - float &factor, - bool &exists) const + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late, + float &factor, + bool &exists) const { factors_[index(type)].factor(clk_data, rf, early_late, factor, exists); } void DeratingFactorsGlobal::factor(TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late, - float &factor, - bool &exists) const + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late, + float &factor, + bool &exists) const { factors_[index(type)].factor(clk_data, rf, early_late, factor, exists); } @@ -143,8 +143,8 @@ DeratingFactorsGlobal::factor(TimingDerateCellType type, void DeratingFactorsGlobal::clear() { - for (int type = 0; type < timing_derate_type_count; type++) - factors_[type].clear(); + for (DeratingFactors &factors : factors_) + factors.clear(); } DeratingFactors * @@ -162,21 +162,21 @@ DeratingFactorsCell::DeratingFactorsCell() void DeratingFactorsCell::setFactor(TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float factor) + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float factor) { factors_[index(type)].setFactor(clk_data, rf, early_late, factor); } void DeratingFactorsCell::factor(TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late, - float &factor, - bool &exists) const + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late, + float &factor, + bool &exists) const { factors_[index(type)].factor(clk_data, rf, early_late, factor, exists); } @@ -184,8 +184,8 @@ DeratingFactorsCell::factor(TimingDerateCellType type, void DeratingFactorsCell::clear() { - for (int type = 0; type < timing_derate_cell_type_count; type++) - factors_[type].clear(); + for (DeratingFactors &factors : factors_) + factors.clear(); } DeratingFactors * @@ -196,8 +196,8 @@ DeratingFactorsCell::factors(TimingDerateCellType type) void DeratingFactorsCell::isOneValue(const EarlyLate *early_late, - bool &is_one_value, - float &value) const + bool &is_one_value, + float &value) const { bool is_one_value1, is_one_value2; float value1, value2; @@ -211,10 +211,4 @@ DeratingFactorsCell::isOneValue(const EarlyLate *early_late, value = value1; } -//////////////////////////////////////////////////////////////// - -DeratingFactorsNet::DeratingFactorsNet() -{ -} - -} // namespace +} // namespace sta diff --git a/sdc/DisabledPorts.cc b/sdc/DisabledPorts.cc index 20f2cc36e..311a2439b 100644 --- a/sdc/DisabledPorts.cc +++ b/sdc/DisabledPorts.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,21 +26,13 @@ #include -#include "StringUtil.hh" -#include "TimingRole.hh" #include "Liberty.hh" #include "Network.hh" +#include "StringUtil.hh" +#include "TimingRole.hh" namespace sta { -DisabledPorts::DisabledPorts() : - all_(false), - from_(nullptr), - to_(nullptr), - from_to_(nullptr) -{ -} - DisabledPorts::~DisabledPorts() { delete from_; @@ -96,39 +88,33 @@ DisabledPorts::setDisabledFromTo(LibertyPort *from, { if (from_to_ == nullptr) from_to_ = new LibertyPortPairSet; - LibertyPortPair pair(from, to); - from_to_->insert(pair); + from_to_->insert({from, to}); } void DisabledPorts::removeDisabledFromTo(LibertyPort *from, LibertyPort *to) { - if (from_to_) { - LibertyPortPair from_to(from, to); - from_to_->erase(from_to); - } + if (from_to_) + from_to_->erase({from, to}); } bool DisabledPorts::isDisabled(LibertyPort *from, LibertyPort *to, - const TimingRole *role) + const TimingRole *role) { - LibertyPortPair from_to(from, to); // set_disable_timing instance does not disable timing checks. return (all_ && !role->isTimingCheck()) - || (from_ && from_->hasKey(from)) - || (to_ && to_->hasKey(to)) - || (from_to_ && from_to_->hasKey(from_to)); + || (from_ && from_->contains(from)) + || (to_ && to_->contains(to)) + || (from_to_ && from_to_->contains({from, to})); } //////////////////////////////////////////////////////////////// DisabledCellPorts::DisabledCellPorts(LibertyCell *cell) : - DisabledPorts(), - cell_(cell), - arc_sets_(nullptr) + cell_(cell) { } @@ -157,31 +143,25 @@ bool DisabledCellPorts::isDisabled(TimingArcSet *arc_set) const { return arc_sets_ - && arc_sets_->hasKey(arc_set); + && arc_sets_->contains(arc_set); } class DisabledCellPortsLess { public: - DisabledCellPortsLess(); bool operator()(const DisabledCellPorts *disable1, - const DisabledCellPorts *disable2); + const DisabledCellPorts *disable2); }; -DisabledCellPortsLess::DisabledCellPortsLess() -{ -} - bool DisabledCellPortsLess::operator()(const DisabledCellPorts *disable1, - const DisabledCellPorts *disable2) + const DisabledCellPorts *disable2) { - return stringLess(disable1->cell()->name(), - disable2->cell()->name()); + return disable1->cell()->name() < disable2->cell()->name(); } DisabledCellPortsSeq -sortByName(DisabledCellPortsMap *cell_map) +sortByName(const DisabledCellPortsMap *cell_map) { DisabledCellPortsSeq disables; for (auto cell_disable : *cell_map) { @@ -195,7 +175,6 @@ sortByName(DisabledCellPortsMap *cell_map) //////////////////////////////////////////////////////////////// DisabledInstancePorts::DisabledInstancePorts(Instance *inst) : - DisabledPorts(), inst_(inst) { } @@ -203,9 +182,9 @@ DisabledInstancePorts::DisabledInstancePorts(Instance *inst) : class DisabledInstPortsLess { public: - explicit DisabledInstPortsLess(const Network *network); + DisabledInstPortsLess(const Network *network); bool operator()(const DisabledInstancePorts *disable1, - const DisabledInstancePorts *disable2); + const DisabledInstancePorts *disable2); private: const Network *network_; @@ -220,8 +199,8 @@ bool DisabledInstPortsLess::operator()(const DisabledInstancePorts *disable1, const DisabledInstancePorts *disable2) { - return stringLess(network_->pathName(disable1->instance()), - network_->pathName(disable2->instance())); + return network_->pathName(disable1->instance()) < + network_->pathName(disable2->instance()); } DisabledInstancePortsSeq @@ -243,19 +222,19 @@ class LibertyPortPairNameLess { public: bool operator()(const LibertyPortPair &pair1, - const LibertyPortPair &pair2); + const LibertyPortPair &pair2); }; bool LibertyPortPairNameLess::operator()(const LibertyPortPair &pair1, - const LibertyPortPair &pair2) + const LibertyPortPair &pair2) { - const char *from1 = pair1.first->name(); - const char *from2 = pair2.first->name(); - const char *to1 = pair1.second->name(); - const char *to2 = pair2.second->name(); - return stringLess(from1, from2) - || (stringEq(from1, from2) && stringLess(to1, to2)); + const std::string &from1 = pair1.first->name(); + const std::string &from2 = pair2.first->name(); + const std::string &to1 = pair1.second->name(); + const std::string &to2 = pair2.second->name(); + return from1 < from2 + || (from1 == from2 && to1 < to2); } LibertyPortPairSeq @@ -268,4 +247,4 @@ sortByName(const LibertyPortPairSet *set) return pairs; } -} +} // namespace sta diff --git a/sdc/ExceptionPath.cc b/sdc/ExceptionPath.cc index 163dcf653..151c98ad2 100644 --- a/sdc/ExceptionPath.cc +++ b/sdc/ExceptionPath.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,35 +26,35 @@ #include +#include "Clock.hh" +#include "ContainerHelpers.hh" +#include "Format.hh" #include "MinMax.hh" -#include "TimingRole.hh" -#include "Units.hh" -#include "Transition.hh" -#include "PortDirection.hh" #include "Network.hh" #include "NetworkCmp.hh" -#include "Clock.hh" +#include "PortDirection.hh" +#include "TimingRole.hh" +#include "Transition.hh" +#include "Units.hh" namespace sta { -using std::string; - static bool thrusIntersectPts(ExceptionThruSeq *thrus1, - ExceptionThruSeq *thrus2, + ExceptionThruSeq *thrus2, const Network *network); static void insertPinPairsThruHierPin(const Pin *hpin, - const Network *network, - PinPairSet *pairs); + const Network *network, + PinPairSet *pairs); static void insertPinPairsThruNet(const Net *net, - const Network *network, - PinPairSet *pairs); + const Network *network, + PinPairSet *pairs); static void deletePinPairsThruHierPin(const Pin *hpin, - const Network *network, - PinPairSet *pairs); + const Network *network, + PinPairSet *pairs); //////////////////////////////////////////////////////////////// @@ -66,20 +66,20 @@ EmptyExpceptionPt::what() const noexcept void checkFromThrusTo(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to) + ExceptionThruSeq *thrus, + ExceptionTo *to) { bool found_empty = ((from && !from->hasObjects()) - || (to - && (!to->hasObjects() - && to->transition() - == RiseFallBoth::riseFall() - && (to->endTransition() - == RiseFallBoth::riseFall())))); + || (to + && (!to->hasObjects() + && to->transition() + == RiseFallBoth::riseFall() + && (to->endTransition() + == RiseFallBoth::riseFall())))); if (thrus) { for (ExceptionThru *thru : *thrus) { if (!thru->hasObjects()) - found_empty = true; + found_empty = true; } } if (found_empty) @@ -87,20 +87,19 @@ checkFromThrusTo(ExceptionFrom *from, } ExceptionPath::ExceptionPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool own_pts, - int priority, - const char *comment) : + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool own_pts, + int priority, + std::string_view comment) : SdcCmdComment(comment), from_(from), thrus_(thrus), to_(to), min_max_(min_max), own_pts_(own_pts), - priority_(priority), - id_(0) + priority_(priority) { makeStates(); } @@ -111,7 +110,7 @@ ExceptionPath::~ExceptionPath() delete from_; delete to_; if (thrus_) { - thrus_->deleteContents(); + deleteContents(*thrus_); delete thrus_; } } @@ -122,17 +121,10 @@ ExceptionPath::~ExceptionPath() } } -const char * -ExceptionPath::asString(const Network *network) const +std::string +ExceptionPath::to_string(const Network *network) const { - const char *from_thru_to = fromThruToString(network); - const char *type = typeString(); - size_t length = strlen(type) + strlen(from_thru_to) + 1; - char *result = makeTmpString(length); - char *r = result; - stringAppend(r, type); - stringAppend(r, from_thru_to); - return result; + return sta::format("{}{}", typeString(), fromThruToString(network)); } void @@ -156,7 +148,7 @@ ExceptionPath::firstPt() bool ExceptionPath::matchesFirstPt(const RiseFall *to_rf, - const MinMax *min_max) + const MinMax *min_max) { ExceptionPt *first_pt = firstPt(); return first_pt->transition()->matches(to_rf) @@ -165,7 +157,7 @@ ExceptionPath::matchesFirstPt(const RiseFall *to_rf, bool ExceptionPath::matches(const MinMax *min_max, - bool) const + bool) const { return min_max_->matches(min_max); } @@ -190,8 +182,8 @@ ExceptionPath::setPriority(int priority) // priority over an exception without this type of qualifier. int ExceptionPath::fromThruToPriority(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to) + ExceptionThruSeq *thrus, + ExceptionTo *to) { int priority = 0; if (from && (from->hasPins() || from->hasInstances())) @@ -232,7 +224,7 @@ ExceptionPath::hash(ExceptionPt *missing_pt) const bool ExceptionPath::mergeable(ExceptionPath *exception) const { - return stringEqualIf(comment_, exception->comment()); + return comment_ == exception->comment(); } bool @@ -244,44 +236,47 @@ ExceptionPath::mergeablePts(ExceptionPath *exception) const bool ExceptionPath::mergeablePts(ExceptionPath *exception2, - ExceptionPt *missing_pt2, - ExceptionPt *&missing_pt) const + ExceptionPt *missing_pt2, + ExceptionPt *&missing_pt) const { missing_pt = nullptr; ExceptionFrom *from2 = exception2->from(); if ((from_ && from2 && !(from_->transition() == from2->transition() - && (from2 == missing_pt2 - || from_->equal(from2)))) + && (from2 == missing_pt2 + || from_->equal(from2)))) || (from_ && from2 == nullptr) || (from_ == nullptr && from2)) return false; if (from2 == missing_pt2) missing_pt = from_; - ExceptionThruSeq::Iterator thru_iter(thrus_); - ExceptionThruSeq::Iterator thru_iter2(exception2->thrus()); - while (thru_iter.hasNext() - && thru_iter2.hasNext()) { - ExceptionThru *thru = thru_iter.next(); - ExceptionThru *thru2 = thru_iter2.next(); - if (!(thru->transition() == thru2->transition() - && (thru2 == missing_pt2 - || thru->equal(thru)))) + ExceptionThruSeq *thrus2 = exception2->thrus(); + if (thrus_ && thrus2) { + ExceptionThruSeq::iterator thru_iter1 = thrus_->begin(); + ExceptionThruSeq::iterator thru_iter2 = thrus2->begin(); + while (thru_iter1 != thrus_->end() + && thru_iter2 != thrus2->end()) { + ExceptionThru *thru1 = *thru_iter1++; + ExceptionThru *thru2 = *thru_iter2++; + if (!(thru1->transition() == thru2->transition() + && (thru2 == missing_pt2 + || thru1->equal(thru2)))) + return false; + if (thru2 == missing_pt2) + missing_pt = thru1; + } + if (thru_iter1 != thrus_->end() + || thru_iter2 != thrus2->end()) return false; - if (thru2 == missing_pt2) - missing_pt = thru; } - if (thru_iter.hasNext() - || thru_iter2.hasNext()) - return false; ExceptionTo *to2 = exception2->to(); if ((to_ && to2 && !(to_->transition() == to2->transition() - && to_->endTransition() == to2->endTransition() - && (to2 == missing_pt2 - || to_->equal(to2)))) + && to_->endTransition() == to2->endTransition() + && (to2 == missing_pt2 + || to_->equal(to2)))) || (to_ && to2 == nullptr) || (to_ == nullptr && to2)) return false; @@ -300,33 +295,36 @@ ExceptionPath::intersectsPts(ExceptionPath *exception, if (((from_ == nullptr && from2 == nullptr) || (from_ && from2 && from_->intersectsPts(from2, network))) && ((thrus_ == nullptr && thrus2 == nullptr) - || (thrus_ && thrus2 && thrus_->size() == thrus2->size())) + || (thrus_ && thrus2 && thrus_->size() == thrus2->size())) && ((to_ == nullptr && to2 == nullptr) - || (to_ && to2 && to_->intersectsPts(to2, network)))) { - ExceptionThruSeq::Iterator thrus_iter1(thrus_); - ExceptionThruSeq::Iterator thrus_iter2(thrus2); - while (thrus_iter1.hasNext() && thrus_iter2.hasNext()) { - ExceptionThru *thru1 = thrus_iter1.next(); - ExceptionThru *thru2 = thrus_iter2.next(); - if (!thru1->intersectsPts(thru2, network)) - return false; + || (to_ && to2 && to_->intersectsPts(to2, network)))) { + if (thrus_ && thrus2) { + ExceptionThruSeq::iterator thru_iter1 = thrus_->begin(); + ExceptionThruSeq::iterator thru_iter2 = thrus2->begin(); + while (thru_iter1 != thrus_->end() + && thru_iter2 != thrus2->end()) { + ExceptionThru *thru1 = *thru_iter1++; + ExceptionThru *thru2 = *thru_iter2++; + if (!thru1->intersectsPts(thru2, network)) + return false; + } } return true; } return false; } -const char * +std::string ExceptionPath::fromThruToString(const Network *network) const { - string str; + std::string str; if (min_max_ != MinMaxAll::all()) { str += " -"; str += min_max_->to_string(); } if (from_) - str += from_->asString(network); + str += from_->to_string(network); if (thrus_) { str += " -thru"; @@ -336,7 +334,7 @@ ExceptionPath::fromThruToString(const Network *network) const if (!first_thru) str += " &&"; str += " {"; - str += thru->asString(network); + str += thru->to_string(network); str += "}"; first_thru = false; } @@ -344,11 +342,9 @@ ExceptionPath::fromThruToString(const Network *network) const } if (to_) - str += to_->asString(network); + str += to_->to_string(network); - char *result = makeTmpString(str.size() + 1); - strcpy(result, str.c_str()); - return result; + return str; } ExceptionState * @@ -390,9 +386,9 @@ ExceptionPath::makeStates() bool ExceptionPath::resetMatch(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, const Network *network) { // Only the reset expception points need to match. @@ -400,58 +396,59 @@ ExceptionPath::resetMatch(ExceptionFrom *from, // exceptions that match the -from even if they are more specific. // -from return ((from && from_ - && thrus == nullptr - && to == nullptr - && from_->intersectsPts(from, network)) - // -thru - || (from == nullptr - && thrus && thrus_ - && to == nullptr - && thrusIntersectPts(thrus_, thrus, network)) - // -to - || (from == nullptr - && thrus == nullptr - && to && to_ - && to_->intersectsPts(to, network)) - // -from -thru - || (from && from_ - && thrus && thrus_ - && to == nullptr - && from_->intersectsPts(from, network) - && thrusIntersectPts(thrus_, thrus, network)) - // -from -to - || (from && from_ - && thrus == nullptr - && to && to_ - && from_->intersectsPts(from, network) - && to_->intersectsPts(to, network)) - // -thru -to - || (from == nullptr - && thrus && thrus_ - && to && to_ - && thrusIntersectPts(thrus_, thrus, network) - && to_->intersectsPts(to, network)) - // -from -thru -to - || (from && from_ - && thrus && thrus_ - && to && to_ - && from_->intersectsPts(from, network) - && thrusIntersectPts(thrus_, thrus, network) - && to_->intersectsPts(to, network))) + && thrus == nullptr + && to == nullptr + && from_->intersectsPts(from, network)) + // -thru + || (from == nullptr + && thrus && thrus_ + && to == nullptr + && thrusIntersectPts(thrus_, thrus, network)) + // -to + || (from == nullptr + && thrus == nullptr + && to && to_ + && to_->intersectsPts(to, network)) + // -from -thru + || (from && from_ + && thrus && thrus_ + && to == nullptr + && from_->intersectsPts(from, network) + && thrusIntersectPts(thrus_, thrus, network)) + // -from -to + || (from && from_ + && thrus == nullptr + && to && to_ + && from_->intersectsPts(from, network) + && to_->intersectsPts(to, network)) + // -thru -to + || (from == nullptr + && thrus && thrus_ + && to && to_ + && thrusIntersectPts(thrus_, thrus, network) + && to_->intersectsPts(to, network)) + // -from -thru -to + || (from && from_ + && thrus && thrus_ + && to && to_ + && from_->intersectsPts(from, network) + && thrusIntersectPts(thrus_, thrus, network) + && to_->intersectsPts(to, network))) && (min_max == MinMaxAll::all() - || min_max_ == min_max); + || min_max_ == min_max); } static bool thrusIntersectPts(ExceptionThruSeq *thrus1, - ExceptionThruSeq *thrus2, + ExceptionThruSeq *thrus2, const Network *network) { - ExceptionThruSeq::Iterator thrus_iter1(thrus1); - ExceptionThruSeq::Iterator thrus_iter2(thrus2); - while (thrus_iter1.hasNext() && thrus_iter2.hasNext()) { - ExceptionThru *thru1 = thrus_iter1.next(); - ExceptionThru *thru2 = thrus_iter2.next(); + ExceptionThruSeq::iterator thru_iter1 = thrus1->begin(); + ExceptionThruSeq::iterator thru_iter2 = thrus2->begin(); + while (thru_iter1 != thrus1->end() + && thru_iter2 != thrus2->end()) { + ExceptionThru *thru1 = *thru_iter1++; + ExceptionThru *thru2 = *thru_iter2++; if (!thru1->intersectsPts(thru2, network)) return false; } @@ -475,17 +472,17 @@ ExceptionPath::deleteInstance(const Instance *inst, //////////////////////////////////////////////////////////////// PathDelay::PathDelay(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMax *min_max, - bool ignore_clk_latency, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMax *min_max, + bool ignore_clk_latency, bool break_path, - float delay, - bool own_pts, - const char *comment) : + float delay, + bool own_pts, + std::string_view comment) : ExceptionPath(from, thrus, to, min_max->asMinMaxAll(), own_pts, - pathDelayPriority() + fromThruToPriority(from, thrus, to), - comment), + pathDelayPriority() + fromThruToPriority(from, thrus, to), + comment), ignore_clk_latency_(ignore_clk_latency), break_path_(break_path), delay_(delay) @@ -494,12 +491,12 @@ PathDelay::PathDelay(ExceptionFrom *from, ExceptionPath * PathDelay::clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts) + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) { return new PathDelay(from, thrus, to, min_max_->asMinMax(), - ignore_clk_latency_, break_path_, delay_, + ignore_clk_latency_, break_path_, delay_, own_pts, comment_); } @@ -518,17 +515,15 @@ PathDelay::tighterThan(ExceptionPath *exception) const return delay_ < exception->delay(); } -const char * -PathDelay::asString(const Network *network) const +std::string +PathDelay::to_string(const Network *network) const { - const char *from_thru_to = fromThruToString(network); - const char *result = stringPrintTmp("PathDelay %.3fns%s", - delay_ * 1E+9F, - from_thru_to); - return result; + return sta::format("PathDelay {:.3f}ns{}", + delay_ * 1E+9F, + fromThruToString(network)); } -const char * +std::string_view PathDelay::typeString() const { return "Path"; @@ -565,33 +560,33 @@ PathDelay::overrides(ExceptionPath *exception) const //////////////////////////////////////////////////////////////// FalsePath::FalsePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool own_pts, - const char *comment) : + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool own_pts, + std::string_view comment) : ExceptionPath(from, thrus, to, min_max, own_pts, - falsePathPriority() + fromThruToPriority(from, thrus, to), - comment) + falsePathPriority() + fromThruToPriority(from, thrus, to), + comment) { } FalsePath::FalsePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool own_pts, - int priority, - const char *comment) : + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool own_pts, + int priority, + std::string_view comment) : ExceptionPath(from, thrus, to, min_max, own_pts, priority, comment) { } ExceptionPath * FalsePath::clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts) + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) { return new FalsePath(from, thrus, to, min_max_, own_pts, comment_); } @@ -608,7 +603,7 @@ FalsePath::tighterThan(ExceptionPath *) const return false; } -const char * +std::string_view FalsePath::typeString() const { return "False"; @@ -631,14 +626,13 @@ FalsePath::overrides(ExceptionPath *exception) const //////////////////////////////////////////////////////////////// LoopPath::LoopPath(ExceptionThruSeq *thrus, - bool own_pts) : + bool own_pts) : FalsePath(nullptr, thrus, nullptr, MinMaxAll::all(), own_pts, - falsePathPriority() + fromThruToPriority(nullptr, thrus, nullptr), - nullptr) + falsePathPriority() + fromThruToPriority(nullptr, thrus, nullptr), "") { } -const char * +std::string_view LoopPath::typeString() const { return "Loop"; @@ -653,16 +647,16 @@ LoopPath::mergeable(ExceptionPath *) const //////////////////////////////////////////////////////////////// MultiCyclePath::MultiCyclePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool use_end_clk, - int path_multiplier, - bool own_pts, - const char *comment) : + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, + bool own_pts, + std::string_view comment) : ExceptionPath(from, thrus, to, min_max, own_pts, - multiCyclePathPriority() + fromThruToPriority(from, thrus, to), - comment), + multiCyclePathPriority() + fromThruToPriority(from, thrus, to), + comment), use_end_clk_(use_end_clk), path_multiplier_(path_multiplier) { @@ -670,12 +664,12 @@ MultiCyclePath::MultiCyclePath(ExceptionFrom *from, ExceptionPath * MultiCyclePath::clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts) + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) { return new MultiCyclePath(from, thrus, to, min_max_, use_end_clk_, - path_multiplier_, own_pts, comment_); + path_multiplier_, own_pts, comment_); } int @@ -714,7 +708,7 @@ MultiCyclePath::tighterThan(ExceptionPath *exception) const bool MultiCyclePath::matches(const MinMax *min_max, - bool exactly) const + bool exactly) const { return min_max_->matches(min_max) // set_multicycle_path -setup determines hold check accounting, @@ -722,18 +716,16 @@ MultiCyclePath::matches(const MinMax *min_max, || (!exactly && min_max == MinMax::min()); } -const char * -MultiCyclePath::asString(const Network *network) const +std::string +MultiCyclePath::to_string(const Network *network) const { - const char *from_thru_to = fromThruToString(network); - const char *result = stringPrintTmp("Multicycle %s %d%s", - (use_end_clk_) ? "-end" : "-start", - path_multiplier_, - from_thru_to); - return result; + return sta::format("Multicycle {} {}{}", + (use_end_clk_) ? "-end" : "-start", + path_multiplier_, + fromThruToString(network)); } -const char * +std::string_view MultiCyclePath::typeString() const { return "Multicycle"; @@ -758,16 +750,15 @@ MultiCyclePath::overrides(ExceptionPath *exception) const //////////////////////////////////////////////////////////////// FilterPath::FilterPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts) : + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) : ExceptionPath(from, thrus, to, MinMaxAll::all(), own_pts, - filterPathPriority() + fromThruToPriority(from, thrus, to), - nullptr) + filterPathPriority() + fromThruToPriority(from, thrus, to), "") { } -const char * +std::string_view FilterPath::typeString() const { return "Filter"; @@ -775,9 +766,9 @@ FilterPath::typeString() const ExceptionPath * FilterPath::clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts) + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) { return new FilterPath(from, thrus, to, own_pts); } @@ -810,9 +801,9 @@ FilterPath::overrides(ExceptionPath *) const bool FilterPath::resetMatch(ExceptionFrom *, - ExceptionThruSeq *, - ExceptionTo *, - const MinMaxAll *, + ExceptionThruSeq *, + ExceptionTo *, + const MinMaxAll *, const Network *) { return false; @@ -820,27 +811,22 @@ FilterPath::resetMatch(ExceptionFrom *, //////////////////////////////////////////////////////////////// -GroupPath::GroupPath(const char *name, - bool is_default, - ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts, - const char *comment) : +GroupPath::GroupPath(std::string_view name, + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts, + std::string_view comment) : ExceptionPath(from, thrus, to, MinMaxAll::all(), own_pts, - groupPathPriority() + fromThruToPriority(from, thrus, to), - comment), - name_(stringCopy(name)), + groupPathPriority() + fromThruToPriority(from, thrus, to), + comment), + name_(name), is_default_(is_default) { } -GroupPath::~GroupPath() -{ - stringDelete(name_); -} - -const char * +std::string_view GroupPath::typeString() const { return "Group"; @@ -848,12 +834,12 @@ GroupPath::typeString() const ExceptionPath * GroupPath::clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts) + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) { return new GroupPath(name_, is_default_, from, thrus, to, own_pts, - comment_); + comment_); } int @@ -871,7 +857,7 @@ GroupPath::tighterThan(ExceptionPath *) const bool GroupPath::mergeable(ExceptionPath *exception) const { - return stringEqIf(name_, exception->name()) + return name_ == exception->name() && ExceptionPath::mergeable(exception) && overrides(exception); } @@ -881,18 +867,17 @@ GroupPath::overrides(ExceptionPath *exception) const { return exception->isGroupPath() && is_default_ == exception->isDefault() - && stringEqIf(name_, exception->name()); + && name_ == exception->name(); } //////////////////////////////////////////////////////////////// -const int ExceptionPt::as_string_max_objects_ = 20; +const int ExceptionPt::to_string_max_objects_ = 20; ExceptionPt::ExceptionPt(const RiseFallBoth *rf, - bool own_pts) : + bool own_pts) : rf_(rf), - own_pts_(own_pts), - hash_(0) + own_pts_(own_pts) { } @@ -905,10 +890,10 @@ ExceptionPt::hash() const } ExceptionFromTo::ExceptionFromTo(PinSet *pins, - ClockSet *clks, - InstanceSet *insts, - const RiseFallBoth *rf, - bool own_pts, + ClockSet *clks, + InstanceSet *insts, + const RiseFallBoth *rf, + bool own_pts, const Network *network) : ExceptionPt(rf, own_pts), pins_(pins), @@ -931,7 +916,7 @@ ExceptionFromTo::ExceptionFromTo(PinSet *pins, delete insts_; insts_ = nullptr; } - findHash(network); + ExceptionFromTo::findHash(network); } ExceptionFromTo::~ExceptionFromTo() @@ -979,8 +964,8 @@ ExceptionFromTo::allPins(const Network *network) for (const Instance *inst : *insts_) { InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - pins.insert(pin); + const Pin *pin = pin_iter->next(); + pins.insert(pin); } delete pin_iter; } @@ -1015,30 +1000,33 @@ ExceptionFromTo::findHash(const Network *network) bool ExceptionFromTo::equal(ExceptionFromTo *from_to) const { - return PinSet::equal(from_to->pins_, pins_) - && ClockSet::equal(from_to->clks_, clks_) - && InstanceSet::equal(from_to->insts_, insts_) + return ((from_to->pins_ == nullptr && pins_ == nullptr) + || (from_to->pins_ && pins_ && *from_to->pins_ == *pins_)) + && ((from_to->clks_ == nullptr && clks_ == nullptr) + || (from_to->clks_ && clks_ && *from_to->clks_ == *clks_)) + && ((from_to->insts_ == nullptr && insts_ == nullptr) + || (from_to->insts_ && insts_ && *from_to->insts_ == *insts_)) && from_to->transition() == rf_; } int ExceptionFromTo::compare(ExceptionPt *pt2, - const Network *network) const + const Network *network) const { int priority_cmp = typePriority() - pt2->typePriority(); if (priority_cmp == 0) { - int pin_cmp = PinSet::compare(pins_, pt2->pins(), network); + int pin_cmp = sta::compare(pins_, pt2->pins(), network); if (pin_cmp == 0) { int clk_cmp = sta::compare(clks_, pt2->clks()); if (clk_cmp == 0) { - int inst_cmp = InstanceSet::compare(insts_, pt2->instances(), network); - if (inst_cmp == 0) - return rf_->index() - pt2->transition()->index(); - else - return inst_cmp; + int inst_cmp = sta::compare(insts_, pt2->instances(), network); + if (inst_cmp == 0) + return rf_->index() - pt2->transition()->index(); + else + return inst_cmp; } else - return clk_cmp; + return clk_cmp; } else return pin_cmp; @@ -1092,7 +1080,7 @@ ExceptionFromTo::addPin(const Pin *pin, { if (pins_ == nullptr) pins_ = new PinSet(network); - if (!pins_->hasKey(pin)) { + if (!pins_->contains(pin)) { pins_->insert(pin); // Incrementally update hash. hash_ += network->id(pin) * hash_pin; @@ -1104,7 +1092,7 @@ ExceptionFromTo::addClock(Clock *clk) { if (clks_ == nullptr) clks_ = new ClockSet; - if (!clks_->hasKey(clk)) { + if (!clks_->contains(clk)) { clks_->insert(clk); // Incrementally update hash. hash_ += clk->index() * hash_clk; @@ -1117,7 +1105,7 @@ ExceptionFromTo::addInstance(const Instance *inst, { if (insts_ == nullptr) insts_ = new InstanceSet(network); - if (!insts_->hasKey(inst)) { + if (!insts_->contains(inst)) { insts_->insert(inst); // Incrementally update hash. hash_ += network->id(inst) * hash_inst; @@ -1172,10 +1160,10 @@ ExceptionFromTo::deletePinBefore(const Pin *pin, deletePin(pin, network); } -const char * -ExceptionFromTo::asString(const Network *network) const +std::string +ExceptionFromTo::to_string(const Network *network) const { - string str; + std::string str; str += " "; str += cmdKeyword(); str += " {"; @@ -1186,11 +1174,11 @@ ExceptionFromTo::asString(const Network *network) const PinSeq pins = sortByPathName(pins_, network); for (const Pin *pin : pins) { if (!first) - str += ", "; + str += ", "; str += network->pathName(pin); first = false; obj_count++; - if (obj_count > as_string_max_objects_) + if (obj_count > to_string_max_objects_) break; } } @@ -1198,11 +1186,11 @@ ExceptionFromTo::asString(const Network *network) const ClockSeq clks = sortByName(clks_); for (Clock *clk : clks) { if (!first) - str += ", "; + str += ", "; str += clk->name(); first = false; obj_count++; - if (obj_count > as_string_max_objects_) + if (obj_count > to_string_max_objects_) break; } } @@ -1210,22 +1198,20 @@ ExceptionFromTo::asString(const Network *network) const InstanceSeq insts = sortByPathName(insts_, network); for (const Instance *inst : insts) { if (!first) - str += ", "; + str += ", "; str += network->pathName(inst); first = false; obj_count++; - if (obj_count > as_string_max_objects_) + if (obj_count > to_string_max_objects_) break; } } - if (obj_count == as_string_max_objects_) + if (obj_count == to_string_max_objects_) str += ", ..."; str += "}"; - char *result = makeTmpString(str.size() + 1); - strcpy(result, str.c_str()); - return result; + return str; } size_t @@ -1244,10 +1230,10 @@ ExceptionFromTo::objectCount() const //////////////////////////////////////////////////////////////// ExceptionFrom::ExceptionFrom(PinSet *pins, - ClockSet *clks, - InstanceSet *insts, - const RiseFallBoth *rf, - bool own_pts, + ClockSet *clks, + InstanceSet *insts, + const RiseFallBoth *rf, + bool own_pts, const Network *network) : ExceptionFromTo(pins, clks, insts, rf, own_pts, network) { @@ -1275,14 +1261,35 @@ ExceptionFrom::clone(const Network *network) return new ExceptionFrom(pins, clks, insts, rf_, true, network); } +bool +clkSetIntersects(ClockSet *set1, + ClockSet *set2) +{ + if (set1 && set2) { + auto iter1 = set1->begin(); + auto end1 = set1->end(); + auto iter2 = set2->begin(); + auto end2 = set2->end(); + while (iter1 != end1 && iter2 != end2) { + if (ClockIndexLess()(*iter1, *iter2)) + iter1++; + else if (ClockIndexLess()(*iter2, *iter1)) + iter2++; + else + return true; + } + } + return false; +} + bool ExceptionFrom::intersectsPts(ExceptionFrom *from, const Network *network) const { return from->transition() == rf_ - && ((pins_ && PinSet::intersects(pins_, from->pins(), network)) - || (clks_ && ClockSet::intersects(clks_, from->clks(), ClockIndexLess())) - || (insts_ && InstanceSet::intersects(insts_, from->instances(), network))); + && ((pins_ && intersects(pins_, from->pins(), network)) + || (clks_ && clkSetIntersects(clks_, from->clks())) + || (insts_ && intersects(insts_, from->instances(), network))); } const char * @@ -1299,11 +1306,11 @@ ExceptionFrom::cmdKeyword() const //////////////////////////////////////////////////////////////// ExceptionTo::ExceptionTo(PinSet *pins, - ClockSet *clks, - InstanceSet *insts, - const RiseFallBoth *rf, - const RiseFallBoth *end_rf, - bool own_pts, + ClockSet *clks, + InstanceSet *insts, + const RiseFallBoth *rf, + const RiseFallBoth *end_rf, + bool own_pts, const Network *network) : ExceptionFromTo(pins, clks, insts, rf, own_pts, network), end_rf_(end_rf) @@ -1325,19 +1332,17 @@ ExceptionTo::clone(const Network *network) return new ExceptionTo(pins, clks, insts, rf_, end_rf_, true, network); } -const char * -ExceptionTo::asString(const Network *network) const +std::string +ExceptionTo::to_string(const Network *network) const { - string str; + std::string str; if (hasObjects()) - str += ExceptionFromTo::asString(network); + str += ExceptionFromTo::to_string(network); if (end_rf_ != RiseFallBoth::riseFall()) str += (end_rf_ == RiseFallBoth::rise()) ? " -rise" : " -fall"; - char *result = makeTmpString(str.size() + 1); - strcpy(result, str.c_str()); - return result; + return str; } bool @@ -1346,16 +1351,16 @@ ExceptionTo::intersectsPts(ExceptionTo *to, { return to->transition() == rf_ && to->endTransition() == end_rf_ - && ((pins_ && PinSet::intersects(pins_, to->pins(), network)) - || (clks_ && ClockSet::intersects(clks_, to->clks(), ClockIndexLess())) - || (insts_ && InstanceSet::intersects(insts_, to->instances(), network))); + && ((pins_ && intersects(pins_, to->pins(), network)) + || (clks_ && clkSetIntersects(clks_, to->clks())) + || (insts_ && intersects(insts_, to->instances(), network))); } bool ExceptionTo::matchesFilter(const Pin *pin, - const ClockEdge *clk_edge, - const RiseFall *end_rf, - const Network *network) const + const ClockEdge *clk_edge, + const RiseFall *end_rf, + const Network *network) const { // "report -to reg" matches clock pins. return matches(pin, clk_edge, end_rf, true, network); @@ -1363,9 +1368,9 @@ ExceptionTo::matchesFilter(const Pin *pin, bool ExceptionTo::matches(const Pin *pin, - const ClockEdge *clk_edge, - const RiseFall *end_rf, - const Network *network) const + const ClockEdge *clk_edge, + const RiseFall *end_rf, + const Network *network) const { // "exception -to reg" does not match reg clock pins. return matches(pin, clk_edge, end_rf, false, network); @@ -1373,71 +1378,71 @@ ExceptionTo::matches(const Pin *pin, bool ExceptionTo::matches(const Pin *pin, - const ClockEdge *clk_edge, - const RiseFall *end_rf, - bool inst_matches_reg_clk_pin, - const Network *network) const + const ClockEdge *clk_edge, + const RiseFall *end_rf, + bool inst_matches_reg_clk_pin, + const Network *network) const { return (pins_ - && pins_->hasKey(const_cast(pin)) - && rf_->matches(end_rf) - && end_rf_->matches(end_rf)) + && pins_->contains(const_cast(pin)) + && rf_->matches(end_rf) + && end_rf_->matches(end_rf)) || (clk_edge - && clks_ - && clks_->hasKey(const_cast(clk_edge->clock())) - && rf_->matches(clk_edge->transition()) - && end_rf_->matches(end_rf)) + && clks_ + && clks_->contains(clk_edge->clock()) + && rf_->matches(clk_edge->transition()) + && end_rf_->matches(end_rf)) || (insts_ - && (inst_matches_reg_clk_pin - || !network->isRegClkPin(pin)) - && insts_->hasKey(network->instance(pin)) - && (network->direction(pin)->isAnyInput() + && (inst_matches_reg_clk_pin + || !network->isRegClkPin(pin)) + && insts_->contains(network->instance(pin)) + && (network->direction(pin)->isAnyInput() || network->direction(pin)->isInternal()) - && rf_->matches(end_rf) - && end_rf_->matches(end_rf)) + && rf_->matches(end_rf) + && end_rf_->matches(end_rf)) || (pins_ == nullptr - && clks_ == nullptr - && insts_ == nullptr - && end_rf_->matches(end_rf)); + && clks_ == nullptr + && insts_ == nullptr + && end_rf_->matches(end_rf)); } bool ExceptionTo::matches(const Pin *pin, - const RiseFall *end_rf, - const Network *network) const + const RiseFall *end_rf, + const Network *network) const { return (pins_ - && pins_->hasKey(const_cast(pin)) - && rf_->matches(end_rf) - && end_rf_->matches(end_rf)) + && pins_->contains(const_cast(pin)) + && rf_->matches(end_rf) + && end_rf_->matches(end_rf)) || (insts_ - && insts_->hasKey(network->instance(pin)) - && (network->direction(pin)->isAnyInput() + && insts_->contains(network->instance(pin)) + && (network->direction(pin)->isAnyInput() || network->direction(pin)->isInternal()) - && rf_->matches(end_rf) - && end_rf_->matches(end_rf)); + && rf_->matches(end_rf) + && end_rf_->matches(end_rf)); } bool ExceptionTo::matches(const Pin *pin, - const RiseFall *end_rf) const + const RiseFall *end_rf) const { return (pins_ - && pins_->hasKey(const_cast(pin)) - && rf_->matches(end_rf) - && end_rf_->matches(end_rf)) + && pins_->contains(const_cast(pin)) + && rf_->matches(end_rf) + && end_rf_->matches(end_rf)) || (pins_ == nullptr - && clks_ == nullptr - && insts_ == nullptr - && end_rf_->matches(end_rf)); + && clks_ == nullptr + && insts_ == nullptr + && end_rf_->matches(end_rf)); } bool ExceptionTo::matches(const Clock *clk) const { return clks_ - && clks_->hasKey(const_cast(clk)); + && clks_->contains(const_cast(clk)); } const char * @@ -1453,7 +1458,7 @@ ExceptionTo::cmdKeyword() const int ExceptionTo::compare(ExceptionPt *pt2, - const Network *network) const + const Network *network) const { ExceptionTo *to2 = dynamic_cast(pt2); int cmp = ExceptionFromTo::compare(pt2, network); @@ -1466,14 +1471,13 @@ ExceptionTo::compare(ExceptionPt *pt2, //////////////////////////////////////////////////////////////// ExceptionThru::ExceptionThru(PinSet *pins, - NetSet *nets, - InstanceSet *insts, - const RiseFallBoth *rf, - bool own_pts, - const Network *network) : + NetSet *nets, + InstanceSet *insts, + const RiseFallBoth *rf, + bool own_pts, + const Network *network) : ExceptionPt(rf, own_pts), pins_(pins), - edges_(nullptr), nets_(nets), insts_(insts) { @@ -1521,7 +1525,7 @@ ExceptionThru::makePinEdges(const Network *network) // but before the pin has been deleted from the netlist. void ExceptionThru::deletePinEdges(const Pin *pin, - Network *network) + Network *network) { // Incrementally delete only edges through (hier) or from/to (leaf) the pin. if (edges_ && network->net(pin)) { @@ -1530,12 +1534,12 @@ ExceptionThru::deletePinEdges(const Pin *pin, // deletePinPairsThruHierPin. PinSet *drvrs = network->drivers(pin); if (drvrs) { - // Some edges originating at drvrs may not actually go through pin, so - // still must use deletePinPairsThruHierPin to identify specific edges. - if (edges_) { - for (const EdgePins &edge_pins : *edges_) { + // Some edges originating at drvrs may not actually go through pin, so + // still must use deletePinPairsThruHierPin to identify specific edges. + if (edges_) { + for (const EdgePins &edge_pins : *edges_) { const Pin *p_first = edge_pins.first; - if (drvrs->hasKey(p_first)) { + if (drvrs->contains(p_first)) { deletePinPairsThruHierPin(pin, network, edges_); break; } @@ -1545,13 +1549,13 @@ ExceptionThru::deletePinEdges(const Pin *pin, } else { // erase prevents range iteration. - EdgePinsSet::Iterator edge_iter(edges_); - while (edge_iter.hasNext()) { - const EdgePins &edge_pins = edge_iter.next(); + for (auto itr = edges_->begin(); itr != edges_->end(); /* no incr */) { + const EdgePins &edge_pins = *itr; if (edge_pins.first == pin - || edge_pins.second == pin) { - edges_->erase(edge_pins); - } + || edge_pins.second == pin) + itr = edges_->erase(itr); + else + itr++; } } } @@ -1559,7 +1563,7 @@ ExceptionThru::deletePinEdges(const Pin *pin, void ExceptionThru::makeHpinEdges(const Pin *pin, - const Network *network) + const Network *network) { if (edges_ == nullptr) edges_ = new EdgePinsSet(network); @@ -1580,7 +1584,7 @@ ExceptionThru::makeNetEdges(const Network *network) void ExceptionThru::makeNetEdges(const Net *net, - const Network *network) + const Network *network) { if (edges_ == nullptr) edges_ = new EdgePinsSet(network); @@ -1595,8 +1599,8 @@ ExceptionThru::makeInstEdges(const Network *network) if (network->isHierarchical(inst)) { InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - makeHpinEdges(pin, network); + Pin *pin = pin_iter->next(); + makeHpinEdges(pin, network); } delete pin_iter; } @@ -1605,7 +1609,7 @@ ExceptionThru::makeInstEdges(const Network *network) void ExceptionThru::makeInstEdges(Instance *inst, - Network *network) + Network *network) { if (network->isHierarchical(inst)) { InstancePinIterator *pin_iter = network->pinIterator(inst); @@ -1621,7 +1625,7 @@ ExceptionThru::makeInstEdges(Instance *inst, // but before the inst has been deleted from the netlist. void ExceptionThru::deleteInstEdges(Instance *inst, - Network *network) + Network *network) { // Incrementally delete edges through each hier pin. if (edges_) { @@ -1644,21 +1648,21 @@ ExceptionThru::~ExceptionThru() } } -const char * -ExceptionThru::asString(const Network *network) const +std::string +ExceptionThru::to_string(const Network *network) const { - string str; + std::string str; bool first = true; int obj_count = 0; if (pins_) { PinSeq pins = sortByPathName(pins_, network); for (const Pin *pin : pins) { if (!first) - str += ", "; + str += ", "; str += network->pathName(pin); first = false; obj_count++; - if (obj_count > as_string_max_objects_) + if (obj_count > to_string_max_objects_) break; } } @@ -1666,11 +1670,11 @@ ExceptionThru::asString(const Network *network) const NetSeq nets = sortByPathName(nets_, network); for (const Net *net : nets) { if (!first) - str += ", "; + str += ", "; str += network->pathName(net); first = false; obj_count++; - if (obj_count > as_string_max_objects_) + if (obj_count > to_string_max_objects_) break; } } @@ -1678,29 +1682,27 @@ ExceptionThru::asString(const Network *network) const InstanceSeq insts = sortByPathName(insts_, network); for (const Instance *inst : insts) { if (!first) - str += ", "; + str += ", "; str += network->pathName(inst); first = false; obj_count++; - if (obj_count > as_string_max_objects_) + if (obj_count > to_string_max_objects_) break; } } - if (obj_count == as_string_max_objects_) + if (obj_count == to_string_max_objects_) str += ", ..."; if (rf_ == RiseFallBoth::rise()) str += " rise"; else if (rf_ == RiseFallBoth::fall()) str += " fall"; - char *result = makeTmpString(str.size() + 1); - strcpy(result, str.c_str()); - return result; + return str; } ExceptionThruSeq * exceptionThrusClone(ExceptionThruSeq *thrus, - const Network *network) + const Network *network) { if (thrus) { ExceptionThruSeq *thrus_cpy = new ExceptionThruSeq; @@ -1744,7 +1746,7 @@ ExceptionThru::addPin(const Pin *pin, { if (pins_ == nullptr) pins_ = new PinSet(network); - if (!pins_->hasKey(pin)) { + if (!pins_->contains(pin)) { pins_->insert(pin); // Incrementally update hash. hash_ += network->id(pin) * hash_pin; @@ -1757,7 +1759,7 @@ ExceptionThru::addNet(const Net *net, { if (nets_ == nullptr) nets_ = new NetSet(network); - if (!nets_->hasKey(net)) { + if (!nets_->contains(net)) { nets_->insert(net); // Incrementally update hash. hash_ += network->id(net) * hash_net; @@ -1770,7 +1772,7 @@ ExceptionThru::addInstance(const Instance *inst, { if (insts_ == nullptr) insts_ = new InstanceSet(network); - if (!insts_->hasKey(inst)) { + if (!insts_->contains(inst)) { insts_->insert(inst); // Incrementally update hash. hash_ += network->id(inst) * hash_inst; @@ -1849,8 +1851,8 @@ ExceptionThru::allPins(const Network *network) for (const Instance *inst : *insts_) { InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - pins.insert(pin); + Pin *pin = pin_iter->next(); + pins.insert(pin); } delete pin_iter; } @@ -1859,8 +1861,8 @@ ExceptionThru::allPins(const Network *network) for (const Net *net : *nets_) { NetConnectedPinIterator *pin_iter = network->connectedPinIterator(net); while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - pins.insert(pin); + const Pin *pin = pin_iter->next(); + pins.insert(pin); } delete pin_iter; } @@ -1870,15 +1872,15 @@ ExceptionThru::allPins(const Network *network) bool ExceptionThru::matches(const Pin *from_pin, - const Pin *to_pin, - const RiseFall *to_rf, - const Network *network) + const Pin *to_pin, + const RiseFall *to_rf, + const Network *network) { EdgePins edge_pins(from_pin, to_pin); - return ((pins_ && to_pin && pins_->hasKey(to_pin)) - || (edges_ && from_pin && to_pin && edges_->hasKey(edge_pins)) - || (nets_ && to_pin && nets_->hasKey(network->net(to_pin))) - || (insts_ && to_pin && insts_->hasKey(network->instance(to_pin)))) + return ((pins_ && to_pin && pins_->contains(to_pin)) + || (edges_ && from_pin && to_pin && edges_->contains(edge_pins)) + || (nets_ && to_pin && nets_->contains(network->net(to_pin))) + || (insts_ && to_pin && insts_->contains(network->instance(to_pin)))) && rf_->matches(to_rf); } @@ -1911,30 +1913,33 @@ bool ExceptionThru::equal(ExceptionThru *thru) const { // Edges_ are derived from pins_ so matching pins is sufficient. - return PinSet::equal(thru->pins_, pins_) - && NetSet::equal(thru->nets_, nets_) - && InstanceSet::equal(thru->insts_, insts_) + return ((thru->pins_ == nullptr && pins_ == nullptr) + || (thru->pins_ && pins_ && *thru->pins_ == *pins_)) + && ((thru->nets_ == nullptr && nets_ == nullptr) + || (thru->nets_ && nets_ && *thru->nets_ == *nets_)) + && ((thru->insts_ == nullptr && insts_ == nullptr) + || (thru->insts_ && insts_ && *thru->insts_ == *insts_)) && rf_ == thru->rf_; } int ExceptionThru::compare(ExceptionPt *pt2, - const Network *network) const + const Network *network) const { int priority_cmp = typePriority() - pt2->typePriority(); if (priority_cmp == 0) { - int pin_cmp = PinSet::compare(pins_, pt2->pins(), network); + int pin_cmp = sta::compare(pins_, pt2->pins(), network); if (pin_cmp == 0) { - int net_cmp = NetSet::compare(nets_, pt2->nets(), network); + int net_cmp = sta::compare(nets_, pt2->nets(), network); if (net_cmp == 0) { - int inst_cmp = InstanceSet::compare(insts_, pt2->instances(), network); - if (inst_cmp == 0) - return rf_->index() - pt2->transition()->index(); - else - return inst_cmp; + int inst_cmp = sta::compare(insts_, pt2->instances(), network); + if (inst_cmp == 0) + return rf_->index() - pt2->transition()->index(); + else + return inst_cmp; } else - return net_cmp; + return net_cmp; } else return pin_cmp; @@ -1998,9 +2003,9 @@ ExceptionThru::intersectsPts(ExceptionThru *thru, const Network *network) const { return thru->transition() == rf_ - && ((pins_ && PinSet::intersects(pins_, thru->pins(), network)) - || (nets_ && NetSet::intersects(nets_, thru->nets(), network)) - || (insts_ && InstanceSet::intersects(insts_, thru->instances(), network))); + && ((pins_ && intersects(pins_, thru->pins(), network)) + || (nets_ && intersects(nets_, thru->nets(), network)) + || (insts_ && intersects(insts_, thru->instances(), network))); } size_t @@ -2018,7 +2023,7 @@ ExceptionThru::objectCount() const void ExceptionThru::connectPinAfter(PinSet *drvrs, - Network *network) + Network *network) { // - Tricky to detect exactly what needs to be updated. In theory, // at most, only edges starting/ending (pin is leaf) or spanning @@ -2042,7 +2047,7 @@ ExceptionThru::connectPinAfter(PinSet *drvrs, for (const Pin *thru_pin : *pins_) { if (network->isHierarchical(thru_pin)) { PinSet *thru_pin_drvrs = network->drivers(thru_pin); - if (PinSet::intersects(drvrs, thru_pin_drvrs, network)) + if (intersects(drvrs, thru_pin_drvrs, network)) makePinEdges(thru_pin, network); } } @@ -2054,7 +2059,7 @@ ExceptionThru::connectPinAfter(PinSet *drvrs, while (inst_pin_iter->hasNext()) { Pin *inst_pin = inst_pin_iter->next(); PinSet *inst_pin_drvrs = network->drivers(inst_pin); - if (PinSet::intersects(drvrs, inst_pin_drvrs, network)) + if (intersects(drvrs, inst_pin_drvrs, network)) makePinEdges(inst_pin, network); } delete inst_pin_iter; @@ -2064,7 +2069,7 @@ ExceptionThru::connectPinAfter(PinSet *drvrs, if (nets_) { for (const Net *net : *nets_) { PinSet *net_drvrs = network->drivers(net); - if (PinSet::intersects(drvrs, net_drvrs, network)) + if (intersects(drvrs, net_drvrs, network)) makeNetEdges(net, network); } } @@ -2073,7 +2078,7 @@ ExceptionThru::connectPinAfter(PinSet *drvrs, void ExceptionThru::makePinEdges(const Pin *pin, - const Network *network) + const Network *network) { if (network->isHierarchical(pin)) makeHpinEdges(pin, network); @@ -2091,19 +2096,17 @@ ExceptionThru::deletePinBefore(const Pin *pin, //////////////////////////////////////////////////////////////// ExceptionPtIterator::ExceptionPtIterator(const ExceptionPath *exception) : - exception_(exception), - from_done_(false), - to_done_(false) + exception_(exception) { if (exception->thrus()) - thru_iter_.init(exception->thrus()); + thru_iter_ = exception->thrus()->begin(); } bool ExceptionPtIterator::hasNext() { return (!from_done_ && exception_->from()) - || thru_iter_.hasNext() + || (exception_->thrus() && thru_iter_ != exception_->thrus()->end()) || (!to_done_ && exception_->to()); } @@ -2115,8 +2118,9 @@ ExceptionPtIterator::next() from_done_ = true; return exception_->from(); } - else if (thru_iter_.hasNext()) - return thru_iter_.next(); + else if (exception_->thrus() + && thru_iter_ != exception_->thrus()->end()) + return *thru_iter_++; else { to_done_ = true; return exception_->to(); @@ -2126,7 +2130,7 @@ ExceptionPtIterator::next() //////////////////////////////////////////////////////////////// ExpandedExceptionVisitor::ExpandedExceptionVisitor(ExceptionPath *exception, - const Network *network) : + const Network *network) : exception_(exception), network_(network) { @@ -2164,7 +2168,7 @@ ExpandedExceptionVisitor::visitExpansions() } } else - expandThrus(0); + expandThrus(nullptr); } void @@ -2182,8 +2186,8 @@ ExpandedExceptionVisitor::expandThrus(ExceptionFrom *expanded_from) void ExpandedExceptionVisitor::expandThru(ExceptionFrom *expanded_from, - size_t next_thru_idx, - ExceptionThruSeq *expanded_thrus) + size_t next_thru_idx, + ExceptionThruSeq *expanded_thrus) { ExceptionThruSeq *thrus = exception_->thrus(); if (next_thru_idx < thrus->size()) { @@ -2191,32 +2195,32 @@ ExpandedExceptionVisitor::expandThru(ExceptionFrom *expanded_from, const RiseFallBoth *rf = thru->transition(); if (thru->pins()) { for (const Pin *pin : *thru->pins()) { - PinSet pins(network_); - pins.insert(pin); - ExceptionThru expanded_thru(&pins, nullptr, nullptr, rf, false, network_); - expanded_thrus->push_back(&expanded_thru); - expandThru(expanded_from, next_thru_idx + 1, expanded_thrus); - expanded_thrus->pop_back(); + PinSet pins(network_); + pins.insert(pin); + ExceptionThru expanded_thru(&pins, nullptr, nullptr, rf, false, network_); + expanded_thrus->push_back(&expanded_thru); + expandThru(expanded_from, next_thru_idx + 1, expanded_thrus); + expanded_thrus->pop_back(); } } if (thru->nets()) { for (const Net *net : *thru->nets()) { - NetSet nets(network_); - nets.insert(net); - ExceptionThru expanded_thru(nullptr, &nets, nullptr, rf, false, network_); - expanded_thrus->push_back(&expanded_thru); - expandThru(expanded_from, next_thru_idx + 1, expanded_thrus); - expanded_thrus->pop_back(); + NetSet nets(network_); + nets.insert(net); + ExceptionThru expanded_thru(nullptr, &nets, nullptr, rf, false, network_); + expanded_thrus->push_back(&expanded_thru); + expandThru(expanded_from, next_thru_idx + 1, expanded_thrus); + expanded_thrus->pop_back(); } } if (thru->instances()) { for (const Instance *inst : *thru->instances()) { - InstanceSet insts(network_); - insts.insert(inst); - ExceptionThru expanded_thru(nullptr, nullptr, &insts, rf, false, network_); - expanded_thrus->push_back(&expanded_thru); - expandThru(expanded_from, next_thru_idx + 1, expanded_thrus); - expanded_thrus->pop_back(); + InstanceSet insts(network_); + insts.insert(inst); + ExceptionThru expanded_thru(nullptr, nullptr, &insts, rf, false, network_); + expanded_thrus->push_back(&expanded_thru); + expandThru(expanded_from, next_thru_idx + 1, expanded_thrus); + expanded_thrus->pop_back(); } } } @@ -2227,7 +2231,7 @@ ExpandedExceptionVisitor::expandThru(ExceptionFrom *expanded_from, void ExpandedExceptionVisitor::expandTo(ExceptionFrom *expanded_from, - ExceptionThruSeq *expanded_thrus) + ExceptionThruSeq *expanded_thrus) { ExceptionTo *to = exception_->to(); if (to) { @@ -2265,11 +2269,10 @@ ExpandedExceptionVisitor::expandTo(ExceptionFrom *expanded_from, //////////////////////////////////////////////////////////////// ExceptionState::ExceptionState(ExceptionPath *exception, - ExceptionThru *next_thru, - int index) : + ExceptionThru *next_thru, + int index) : exception_(exception), next_thru_(next_thru), - next_state_(nullptr), index_(index) { } @@ -2282,10 +2285,10 @@ ExceptionState::setNextState(ExceptionState *next_state) bool ExceptionState::matchesNextThru(const Pin *from_pin, - const Pin *to_pin, - const RiseFall *to_rf, - const MinMax *min_max, - const Network *network) const + const Pin *to_pin, + const RiseFall *to_rf, + const MinMax *min_max, + const Network *network) const { // Don't advance the state if the exception is complete (no next_thru_). return next_thru_ @@ -2306,22 +2309,33 @@ ExceptionState::hash() const return hashSum(exception_->hash(), index_); } -bool -exceptionStateLess(const ExceptionState *state1, - const ExceptionState *state2) -{ - const ExceptionPath *except1 = state1->exception(); - const ExceptionPath *except2 = state2->exception(); - return except1->id() < except2->id() - || (except1 == except2 - && state1->index() < state2->index()); +int +exceptionStateCmp(const ExceptionState *state1, + const ExceptionState *state2) +{ + size_t id1 = state1->exception()->id(); + size_t id2 = state2->exception()->id(); + if (id1 < id2) + return -1; + else if (id1 > id2) + return 1; + else { + size_t state_index1 = state1->index(); + size_t state_index2 = state2->index(); + if (state_index1 < state_index2) + return -1; + else if (state_index1 > state_index2) + return 1; + else + return 0; + } } bool ExceptionStateLess::operator()(const ExceptionState *state1, const ExceptionState *state2) const { - return exceptionStateLess(state1, state2); + return exceptionStateCmp(state1, state2) < 0; } //////////////////////////////////////////////////////////////// @@ -2345,7 +2359,7 @@ ExceptionPathLess::operator()(const ExceptionPath *except1, ExceptionPt *pt2 = pt_iter2.next(); int cmp = pt1->compare(pt2, network_); if (cmp != 0) - return cmp < 0; + return cmp < 0; } // Lesser has fewer exception pts. return !pt_iter1.hasNext() && pt_iter2.hasNext(); @@ -2360,18 +2374,17 @@ class InsertPinPairsThru : public HierPinThruVisitor { public: InsertPinPairsThru(PinPairSet *pairs, - const Network *network); + const Network *network); + void visit(const Pin *drvr, + const Pin *load) override; protected: - virtual void visit(const Pin *drvr, - const Pin *load); - PinPairSet *pairs_; const Network *network_; }; InsertPinPairsThru::InsertPinPairsThru(PinPairSet *pairs, - const Network *network) : + const Network *network) : HierPinThruVisitor(), pairs_(pairs), network_(network) @@ -2380,16 +2393,15 @@ InsertPinPairsThru::InsertPinPairsThru(PinPairSet *pairs, void InsertPinPairsThru::visit(const Pin *drvr, - const Pin *load) + const Pin *load) { - PinPair pair(drvr, load); - pairs_->insert(pair); + pairs_->insert({drvr, load}); } static void insertPinPairsThruHierPin(const Pin *hpin, - const Network *network, - PinPairSet *pairs) + const Network *network, + PinPairSet *pairs) { InsertPinPairsThru visitor(pairs, network); visitDrvrLoadsThruHierPin(hpin, network, &visitor); @@ -2397,8 +2409,8 @@ insertPinPairsThruHierPin(const Pin *hpin, static void insertPinPairsThruNet(const Net *net, - const Network *network, - PinPairSet *pairs) + const Network *network, + PinPairSet *pairs) { InsertPinPairsThru visitor(pairs, network); visitDrvrLoadsThruNet(net, network, &visitor); @@ -2408,18 +2420,17 @@ class DeletePinPairsThru : public HierPinThruVisitor { public: DeletePinPairsThru(PinPairSet *pairs, - const Network *network); + const Network *network); + void visit(const Pin *drvr, + const Pin *load) override; protected: - virtual void visit(const Pin *drvr, - const Pin *load); - PinPairSet *pairs_; const Network *network_; }; DeletePinPairsThru::DeletePinPairsThru(PinPairSet *pairs, - const Network *network) : + const Network *network) : HierPinThruVisitor(), pairs_(pairs), network_(network) @@ -2428,19 +2439,18 @@ DeletePinPairsThru::DeletePinPairsThru(PinPairSet *pairs, void DeletePinPairsThru::visit(const Pin *drvr, - const Pin *load) + const Pin *load) { - PinPair pair(drvr, load); - pairs_->erase(pair); + pairs_->erase({drvr, load}); } static void deletePinPairsThruHierPin(const Pin *hpin, - const Network *network, - PinPairSet *pairs) + const Network *network, + PinPairSet *pairs) { DeletePinPairsThru visitor(pairs, network); visitDrvrLoadsThruHierPin(hpin, network, &visitor); } -} // namespace +} // namespace sta diff --git a/sdc/FilterExpr.cc b/sdc/FilterExpr.cc deleted file mode 100644 index 44a0cfad1..000000000 --- a/sdc/FilterExpr.cc +++ /dev/null @@ -1,175 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#include "Error.hh" -#include "FilterExpr.hh" - -#include -#include -#include - -using namespace sta; - -FilterError::FilterError(std::string_view error) : - Exception() -{ - error_ = error; -} - -const char * -FilterError::what() const noexcept -{ - return error_.c_str(); -} - -FilterExpr::Token::Token(std::string text, FilterExpr::PredicateToken::Kind kind): text(text), kind(kind) {} - -FilterExpr::PredicateToken::PredicateToken(std::string property, std::string op, std::string arg): Token( - property + " " + op + " " + arg, - FilterExpr::PredicateToken::Kind::predicate -), property(property), op(op), arg(arg) {} - -FilterExpr::FilterExpr(std::string expression): raw_(expression) {} - -std::vector> FilterExpr::postfix(bool sta_boolean_props_as_int) { - auto infix = lex(sta_boolean_props_as_int); - return shuntingYard(infix); -} - -std::vector> FilterExpr::lex(bool sta_boolean_props_as_int) { - std::vector> token_regexes = { - {std::regex("^\\s+"), FilterExpr::Token::Kind::skip}, - {std::regex("^defined\\(([a-zA-Z_]+)\\)"), FilterExpr::Token::Kind::defined}, - {std::regex("^undefined\\(([a-zA-Z_]+)\\)"), FilterExpr::Token::Kind::undefined}, - {std::regex("^@?([a-zA-Z_]+) *((==|!=|=~|!~) *([0-9a-zA-Z_\\/$\\[\\]*?]+))?"), FilterExpr::Token::Kind::predicate}, - {std::regex("^(&&)"), FilterExpr::Token::Kind::op_and}, - {std::regex("^(\\|\\|)"), FilterExpr::Token::Kind::op_or}, - {std::regex("^(!)"), FilterExpr::Token::Kind::op_inv}, - {std::regex("^(\\()"), FilterExpr::Token::Kind::op_lparen}, - {std::regex("^(\\))"), FilterExpr::Token::Kind::op_rparen}, - }; - - std::vector> result; - const char* ptr = &raw_[0]; - bool match = false; - while (*ptr != '\0') { - match = false; - for (auto& [regex, kind]: token_regexes) { - std::cmatch token_match; - if (std::regex_search(ptr, token_match, regex)) { - if (kind == FilterExpr::Token::Kind::predicate) { - std::string property = token_match[1].str(); - - // The default operation on a predicate if an op and arg are - // omitted is == 1/== true. - std::string op = "=="; - std::string arg = (sta_boolean_props_as_int ? "1" : "true"); - - if (token_match[2].length() != 0) { - op = token_match[3].str(); - arg = token_match[4].str(); - } - result.push_back(std::make_shared(property, op, arg)); - } else if (kind == FilterExpr::Token::Kind::defined) { - result.push_back(std::make_shared(token_match[1].str(), kind)); - } else if (kind == FilterExpr::Token::Kind::undefined) { - result.push_back(std::make_shared(token_match[1].str(), kind)); - } else if (kind != FilterExpr::Token::Kind::skip) { - result.push_back(std::make_shared(std::string(ptr, token_match.length()), kind)); - } - ptr += token_match.length(); - match = true; - break; - }; - } - if (!match) { - throw FilterError(std::string("unexpected character starting at: '") + ptr + "'"); - } - } - return result; -} - -std::vector> FilterExpr::shuntingYard(std::vector>& infix) { - std::vector> output; - std::stack> operator_stack; - - for (auto &pToken: infix) { - switch (pToken->kind) { - case FilterExpr::Token::Kind::predicate: - output.push_back(pToken); - break; - case FilterExpr::Token::Kind::op_or: - [[fallthrough]]; - case FilterExpr::Token::Kind::op_and: - // The operators' enum values are ascending by precedence: - // inv > and > or - while (operator_stack.size() && operator_stack.top()->kind > pToken->kind) { - output.push_back(operator_stack.top()); - operator_stack.pop(); - } - operator_stack.push(pToken); - break; - case FilterExpr::Token::Kind::op_inv: - // Unary with highest precedence, no need for the while loop - operator_stack.push(pToken); - break; - case FilterExpr::Token::Kind::defined: - operator_stack.push(pToken); - break; - case FilterExpr::Token::Kind::undefined: - operator_stack.push(pToken); - break; - case FilterExpr::Token::Kind::op_lparen: - operator_stack.push(pToken); - break; - case FilterExpr::Token::Kind::op_rparen: - if (operator_stack.empty()) { - throw FilterError("extraneous ) in expression"); - } - while (operator_stack.size() && operator_stack.top()->kind != FilterExpr::Token::Kind::op_lparen) { - output.push_back(operator_stack.top()); - operator_stack.pop(); - if (operator_stack.empty()) { - throw FilterError("extraneous ) in expression"); - } - } - // guaranteed to be lparen at this point - operator_stack.pop(); - break; - default: - // unhandled/skip - break; - } - } - - while (operator_stack.size()) { - if (operator_stack.top()->kind == FilterExpr::Token::Kind::op_lparen) { - throw FilterError("unmatched ( in expression"); - } - output.push_back(operator_stack.top()); - operator_stack.pop(); - } - - return output; -} diff --git a/sdc/FilterObjects.cc b/sdc/FilterObjects.cc new file mode 100644 index 000000000..472d41b47 --- /dev/null +++ b/sdc/FilterObjects.cc @@ -0,0 +1,583 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#include "FilterObjects.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Clock.hh" +#include "ContainerHelpers.hh" +#include "Format.hh" +#include "GraphClass.hh" +#include "GraphCmp.hh" +#include "Liberty.hh" +#include "LibertyClass.hh" +#include "Network.hh" +#include "NetworkClass.hh" +#include "PathEnd.hh" +#include "PatternMatch.hh" +#include "Property.hh" +#include "SdcClass.hh" +#include "SearchClass.hh" +#include "Sta.hh" +#include "StringUtil.hh" + +namespace sta { + +class FilterExpr +{ +public: + struct Token + { + virtual ~Token() = default; + enum class Kind { + skip = 0, + predicate, + op_lparen, + op_rparen, + op_or, + op_and, + op_inv, + defined, + undefined + }; + + Token(std::string_view text, + Kind kind); + const std::string &text() const { return text_; } + Kind kind() const { return kind_; } + + std::string text_; + Kind kind_; + }; + + struct PredicateToken : public Token + { + PredicateToken(std::string_view property, + std::string_view op, + std::string_view arg); + ~PredicateToken() override = default; + const std::string &property() const { return property_; } + const std::string &op() const { return op_; } + const std::string &arg() const { return arg_; } + + std::string property_; + std::string op_; + std::string arg_; + }; + + FilterExpr(std::string_view expression, + Report *report); + + std::vector> postfix(); +private: + std::vector> lex(); + std::vector> shuntingYard(std::vector> &infix); + + std::string raw_; + Report *report_; +}; + +FilterExpr::Token::Token(std::string_view text, + Token::Kind kind) : + text_(text), + kind_(kind) +{ +} + +FilterExpr::PredicateToken::PredicateToken(std::string_view property, + std::string_view op, + std::string_view arg) : + Token(sta::format("{} {} {}", property, op, arg), + Token::Kind::predicate), + property_(property), + op_(op), + arg_(arg) +{ +} + +FilterExpr::FilterExpr(std::string_view expression, + Report *report) : + raw_(expression), + report_(report) +{ +} + +std::vector> +FilterExpr::postfix() +{ + auto infix = lex(); + return shuntingYard(infix); +} + +std::vector> +FilterExpr::lex() +{ + std::vector> token_regexes = { + {std::regex("^\\s+"), Token::Kind::skip}, + {std::regex("^defined\\(([a-zA-Z_]+)\\)"), Token::Kind::defined}, + {std::regex("^undefined\\(([a-zA-Z_]+)\\)"), Token::Kind::undefined}, + {std::regex("^@?([a-zA-Z_]+) *((==|!=|=~|!~) *([0-9a-zA-Z_\\/$\\[\\]*?.]+))?"), + Token::Kind::predicate}, + {std::regex("^(&&)"), Token::Kind::op_and}, + {std::regex("^(\\|\\|)"), Token::Kind::op_or}, + {std::regex("^(!)"), Token::Kind::op_inv}, + {std::regex("^(\\()"), Token::Kind::op_lparen}, + {std::regex("^(\\))"), Token::Kind::op_rparen}, + }; + + std::vector> result; + const char* ptr = raw_.data(); + bool match = false; + while (*ptr != '\0') { + match = false; + for (auto& [regex, kind]: token_regexes) { + std::cmatch token_match; + if (std::regex_search(ptr, token_match, regex)) { + if (kind == Token::Kind::predicate) { + std::string property = token_match[1].str(); + + // The default operation on a predicate if an op and arg are + // omitted is 'prop == 1'. + std::string op = "=="; + std::string arg = "1"; + + if (token_match[2].length() != 0) { + op = token_match[3].str(); + arg = token_match[4].str(); + } + result.push_back(std::make_unique(property, op, arg)); + } + else if (kind == Token::Kind::defined) + result.push_back(std::make_unique(token_match[1].str(), kind)); + else if (kind == Token::Kind::undefined) + result.push_back(std::make_unique(token_match[1].str(), kind)); + else if (kind != Token::Kind::skip) + result.push_back(std::make_unique(std::string(ptr,token_match.length()), + kind)); + ptr += token_match.length(); + match = true; + break; + }; + } + if (!match) + report_->error(2600, "-filter parsing failed at '{}'.", ptr); + } + return result; +} + +std::vector> +FilterExpr::shuntingYard(std::vector> &infix) +{ + std::vector> output; + std::stack> operator_stack; + + for (auto &token : infix) { + switch (token->kind()) { + case Token::Kind::predicate: + output.push_back(std::move(token)); + break; + case Token::Kind::op_or: + [[fallthrough]]; + case Token::Kind::op_and: + // The operators' enum values are ascending by precedence: + // inv > and > or + while (!operator_stack.empty() + && operator_stack.top()->kind() > token->kind()) { + output.push_back(std::move(operator_stack.top())); + operator_stack.pop(); + } + operator_stack.push(std::move(token)); + break; + case Token::Kind::op_inv: + // Unary with highest precedence, no need for the while loop. + operator_stack.push(std::move(token)); + break; + case Token::Kind::defined: + operator_stack.push(std::move(token)); + break; + case Token::Kind::undefined: + operator_stack.push(std::move(token)); + break; + case Token::Kind::op_lparen: + operator_stack.push(std::move(token)); + break; + case Token::Kind::op_rparen: + if (operator_stack.empty()) + report_->error(2601, "-filter extraneous )."); + while (!operator_stack.empty() + && operator_stack.top()->kind() != Token::Kind::op_lparen) { + output.push_back(std::move(operator_stack.top())); + operator_stack.pop(); + if (operator_stack.empty()) + report_->error(2602, "-filter extraneous )."); + } + // Guaranteed to be lparen at this point. + operator_stack.pop(); + break; + default: + // Unhandled/skip. + break; + } + } + + while (!operator_stack.empty()) { + if (operator_stack.top()->kind() == Token::Kind::op_lparen) + report_->error(2603, "-filter unmatched (."); + output.push_back(std::move(operator_stack.top())); + operator_stack.pop(); + } + + return output; +} + +//////////////////////////////////////////////////////////////// + +template static std::set +filterObjects(std::string_view property, + std::string_view op, + std::string_view pattern, + std::set &all, + Sta *sta) +{ + Properties &properties = sta->properties(); + Network *network = sta->network(); + auto filtered_objects = std::set(); + bool exact_match = (op == "=="); + bool pattern_match = (op == "=~"); + bool not_match = (op == "!="); + bool not_pattern_match = (op == "!~"); + for (T *object : all) { + PropertyValue value = properties.getProperty(object, property); + std::string prop = value.to_string(network); + + // normalize boolean props + if (value.type() == PropertyValue::Type::bool_) { + if (sta->booleanPropsAsInt()) { + if (stringEqual(pattern, "true")) { + pattern = "1"; + } else if (stringEqual(pattern, "false")) { + pattern = "0"; + } + } else { + if (stringEqual(pattern, "1")) { + pattern = "true"; + } else if (stringEqual(pattern, "0")) { + pattern = "false"; + } + } + } + if ((exact_match && prop == pattern) + || (not_match && prop != pattern) + || (pattern_match && patternMatch(pattern, prop)) + || (not_pattern_match && !patternMatch(pattern, prop))) + filtered_objects.insert(object); + } + return filtered_objects; +} + +template static std::vector +filterObjects(std::string_view filter_expression, + const std::vector *objects, + const std::function &object_less, + Sta *sta) +{ + Report *report = sta->report(); + Network *network = sta->network(); + Properties &properties = sta->properties(); + std::vector result; + if (objects) { + std::set all; + for (auto object: *objects) + all.insert(object); + FilterExpr filter(filter_expression, report); + auto postfix = filter.postfix(); + std::stack> eval_stack; + for (auto &token : postfix) { + if (token->kind() == FilterExpr::Token::Kind::op_or) { + if (eval_stack.size() < 2) + report->error(2604, "-filter logical OR requires at least two operands."); + auto arg0 = eval_stack.top(); + eval_stack.pop(); + auto arg1 = eval_stack.top(); + eval_stack.pop(); + auto union_result = std::set(); + std::set_union(arg0.cbegin(), arg0.cend(), arg1.cbegin(), arg1.cend(), + std::inserter(union_result, union_result.begin())); + eval_stack.push(union_result); + } + else if (token->kind() == FilterExpr::Token::Kind::op_and) { + if (eval_stack.size() < 2) { + report->error(2605, "-filter logical AND requires two operands."); + } + auto arg0 = eval_stack.top(); + eval_stack.pop(); + auto arg1 = eval_stack.top(); + eval_stack.pop(); + auto intersection_result = std::set(); + std::set_intersection(arg0.cbegin(), arg0.cend(), + arg1.cbegin(), arg1.cend(), + std::inserter(intersection_result, + intersection_result.begin())); + eval_stack.push(intersection_result); + } + else if (token->kind() == FilterExpr::Token::Kind::op_inv) { + if (eval_stack.size() < 1) { + report->error(2606, "-filter NOT missing operand."); + } + auto arg0 = eval_stack.top(); + eval_stack.pop(); + + auto difference_result = std::set(); + std::set_difference(all.cbegin(), all.cend(), + arg0.cbegin(), arg0.cend(), + std::inserter(difference_result, + difference_result.begin())); + eval_stack.push(difference_result); + } + else if (token->kind() == FilterExpr::Token::Kind::defined + || token->kind() == FilterExpr::Token::Kind::undefined) { + bool should_be_defined = + (token->kind() == FilterExpr::Token::Kind::defined); + auto result = std::set(); + for (auto object : all) { + PropertyValue value = properties.getProperty(object, token->text()); + bool is_defined = false; + switch (value.type()) { + case PropertyValue::Type::float_: + is_defined = value.floatValue() != 0; + break; + case PropertyValue::Type::bool_: + is_defined = value.boolValue(); + break; + case PropertyValue::Type::string: + case PropertyValue::Type::liberty_library: + case PropertyValue::Type::liberty_cell: + case PropertyValue::Type::liberty_port: + case PropertyValue::Type::library: + case PropertyValue::Type::cell: + case PropertyValue::Type::port: + case PropertyValue::Type::instance: + case PropertyValue::Type::pin: + case PropertyValue::Type::net: + case PropertyValue::Type::clk: + is_defined = !value.to_string(network).empty(); + break; + case PropertyValue::Type::none: + is_defined = false; + break; + case PropertyValue::Type::pins: + is_defined = !value.pins()->empty(); + break; + case PropertyValue::Type::clks: + is_defined = !value.clocks()->empty(); + break; + case PropertyValue::Type::paths: + is_defined = !value.paths()->empty(); + break; + case PropertyValue::Type::pwr_activity: + is_defined = value.pwrActivity().isSet(); + break; + } + if (is_defined == should_be_defined) { + result.insert(object); + } + } + eval_stack.push(result); + } + else if (token->kind() == FilterExpr::Token::Kind::predicate) { + auto *predicate_token = + static_cast(token.get()); + auto result = filterObjects(predicate_token->property(), + predicate_token->op(), + predicate_token->arg(), + all, sta); + eval_stack.push(result); + } + } + if (eval_stack.empty()) + report->error(2607, "-filter expression is empty."); + if (eval_stack.size() > 1) + // huh? + report->error(2608, "-filter expression evaluated to multiple sets."); + auto result_set = eval_stack.top(); + result.resize(result_set.size()); + std::copy(result_set.begin(), result_set.end(), result.begin()); + sort(result, [object_less] (T *obj1, T *obj2) { + return object_less(obj1, obj2); + }); + } + return result; +} + +PortSeq +filterPorts(std::string_view filter_expression, + PortSeq *ports, + Sta *sta) +{ + Network *network = sta->network(); + return filterObjects(filter_expression, ports, + [network] (const Port *port1, + const Port *port2) { + return network->name(port1) < network->name(port2); + }, sta); +} + +InstanceSeq +filterInstances(std::string_view filter_expression, + InstanceSeq *insts, + Sta *sta) +{ + Network *network = sta->network(); + return filterObjects(filter_expression, insts, + [network] (const Instance *inst1, + const Instance *inst2) { + return network->name(inst1) < network->name(inst2); + }, sta); +} + +PinSeq +filterPins(std::string_view filter_expression, + PinSeq *pins, + Sta *sta) +{ + Network *network = sta->network(); + return filterObjects(filter_expression, pins, + [network] (const Pin *pin1, + const Pin *pin2) { + return network->pathName(pin1) < network->pathName(pin2); + }, sta); +} + +NetSeq +filterNets(std::string_view filter_expression, + NetSeq *nets, + Sta *sta) +{ + Network *network = sta->network(); + return filterObjects(filter_expression, nets, + [network] (const Net *net1, + const Net *net2) { + return network->pathName(net1) < network->pathName(net2); + }, sta); +} + +ClockSeq +filterClocks(std::string_view filter_expression, + ClockSeq *clks, + Sta *sta) +{ + return filterObjects(filter_expression, clks, + [] (const Clock *clk1, + const Clock *clk2) { + return clk1->name() < clk2->name(); + }, sta); +} + +LibertyCellSeq +filterLibCells(std::string_view filter_expression, + LibertyCellSeq *cells, + Sta *sta) +{ + return filterObjects(filter_expression, cells, + [] (const LibertyCell *cell1, + const LibertyCell *cell2) { + return cell1->name() < cell2->name(); + }, sta); +} + +LibertyPortSeq +filterLibPins(std::string_view filter_expression, + LibertyPortSeq *ports, + Sta *sta) +{ + return filterObjects(filter_expression, ports, + [] (const LibertyPort *port1, + const LibertyPort *port2) { + return port1->name() < port2->name(); + }, sta); +} + +LibertyLibrarySeq +filterLibertyLibraries(std::string_view filter_expression, + LibertyLibrarySeq *libs, + Sta *sta) +{ + return filterObjects(filter_expression, libs, + [] (const LibertyLibrary *lib1, + const LibertyLibrary *lib2) { + return lib1->name() < lib2->name(); + }, sta); +} + +EdgeSeq +filterTimingArcs(std::string_view filter_expression, + EdgeSeq *edges, + Sta *sta) +{ + Network *network = sta->network(); + Graph *graph = sta->graph(); + EdgeLess edge_less(network, graph); + return filterObjects(filter_expression, edges, + [edge_less] (const Edge *edge1, + const Edge *edge2) { + return edge_less.operator()(edge1, edge2); + }, sta); +} + +PathEndSeq +filterPathEnds(std::string_view filter_expression, + PathEndSeq *ends, + Sta *sta) +{ + PathEndLess end_less(true, sta); + return filterObjects(filter_expression, ends, + [end_less] (const PathEnd *end1, + const PathEnd *end2) { + return end_less.operator()(end1, end2); + }, sta); +} + +StringSeq +filterExprToPostfix(std::string_view expr, + Report *report) +{ + FilterExpr filter(expr, report); + auto postfix = filter.postfix(); + StringSeq result; + for (auto &token : postfix) + result.push_back(token->text()); + return result; +} + +} // namespace sta diff --git a/sdc/InputDrive.cc b/sdc/InputDrive.cc index 845bb5cfc..8f93a7f8e 100644 --- a/sdc/InputDrive.cc +++ b/sdc/InputDrive.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,8 @@ #include "InputDrive.hh" +#include "SdcClass.hh" + namespace sta { InputDrive::InputDrive() @@ -46,25 +48,25 @@ InputDrive::~InputDrive() void InputDrive::setSlew(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew) + const MinMaxAll *min_max, + float slew) { slews_.setValue(rf, min_max, slew); } void InputDrive::setDriveResistance(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float res) + const MinMaxAll *min_max, + float res) { drive_resistances_.setValue(rf, min_max, res); } void InputDrive::driveResistance(const RiseFall *rf, - const MinMax *min_max, - float &res, - bool &exists) const + const MinMax *min_max, + float &res, + bool &exists) const { drive_resistances_.value(rf, min_max, res, exists); } @@ -88,27 +90,27 @@ InputDrive::driveResistanceMinMaxEqual(const RiseFall *rf) const void InputDrive::setDriveCell(const LibertyLibrary *library, - const LibertyCell *cell, - const LibertyPort *from_port, - float *from_slews, - const LibertyPort *to_port, - const RiseFallBoth *rf, - const MinMaxAll *min_max) + const LibertyCell *cell, + const LibertyPort *from_port, + const DriveCellSlews &from_slews, + const LibertyPort *to_port, + const RiseFallBoth *rf, + const MinMaxAll *min_max) { for (auto rf_index : rf->rangeIndex()) { for (auto mm_index : min_max->rangeIndex()) { InputDriveCell *drive = drive_cells_[rf_index][mm_index]; if (drive) { - drive->setLibrary(library); - drive->setCell(cell); - drive->setFromPort(from_port); - drive->setFromSlews(from_slews); - drive->setToPort(to_port); + drive->setLibrary(library); + drive->setCell(cell); + drive->setFromPort(from_port); + drive->setFromSlews(from_slews); + drive->setToPort(to_port); } else { - drive = new InputDriveCell(library, cell, from_port, - from_slews, to_port); - drive_cells_[rf_index][mm_index] = drive; + drive = new InputDriveCell(library, cell, from_port, + from_slews, to_port); + drive_cells_[rf_index][mm_index] = drive; } } } @@ -116,38 +118,39 @@ InputDrive::setDriveCell(const LibertyLibrary *library, void InputDrive::driveCell(const RiseFall *rf, - const MinMax *min_max, + const MinMax *min_max, // Return values. - const LibertyCell *&cell, - const LibertyPort *&from_port, - float *&from_slews, - const LibertyPort *&to_port) const + const LibertyCell *&cell, + const LibertyPort *&from_port, + const DriveCellSlews *&from_slews, + const LibertyPort *&to_port) const { InputDriveCell *drive = drive_cells_[rf->index()][min_max->index()]; if (drive) { cell = drive->cell(); from_port = drive->fromPort(); - from_slews = drive->fromSlews(); + from_slews = &drive->fromSlews(); to_port = drive->toPort(); } else { cell = nullptr; from_port = nullptr; - from_slews = nullptr; + static constexpr DriveCellSlews slews_zero{0.0, 0.0}; + from_slews = &slews_zero; to_port = nullptr; } } InputDriveCell * InputDrive::driveCell(const RiseFall *rf, - const MinMax *min_max) const + const MinMax *min_max) const { return drive_cells_[rf->index()][min_max->index()]; } bool InputDrive::hasDriveCell(const RiseFall *rf, - const MinMax *min_max) const + const MinMax *min_max) const { return drive_cells_[rf->index()][min_max->index()] != nullptr; } @@ -170,9 +173,9 @@ InputDrive::driveCellsEqual() const void InputDrive::slew(const RiseFall *rf, - const MinMax *min_max, - float &slew, - bool &exists) const + const MinMax *min_max, + float &slew, + bool &exists) const { slews_.value(rf, min_max, slew, exists); } @@ -180,16 +183,16 @@ InputDrive::slew(const RiseFall *rf, //////////////////////////////////////////////////////////////// InputDriveCell::InputDriveCell(const LibertyLibrary *library, - const LibertyCell *cell, - const LibertyPort *from_port, - float *from_slews, - const LibertyPort *to_port) : + const LibertyCell *cell, + const LibertyPort *from_port, + const DriveCellSlews &from_slews, + const LibertyPort *to_port) : library_(library), cell_(cell), from_port_(from_port), + from_slews_(from_slews), to_port_(to_port) { - setFromSlews(from_slews); } void @@ -217,10 +220,9 @@ InputDriveCell::setToPort(const LibertyPort *to_port) } void -InputDriveCell::setFromSlews(float *from_slews) +InputDriveCell::setFromSlews(const DriveCellSlews &from_slews) { - for (auto rf_index : RiseFall::rangeIndex()) - from_slews_[rf_index] = from_slews[rf_index]; + from_slews_ = from_slews; } bool @@ -235,4 +237,4 @@ InputDriveCell::equal(const InputDriveCell *drive) const && to_port_ == drive->to_port_; } -} // namespace +} // namespace sta diff --git a/sdc/PinPair.cc b/sdc/PinPair.cc index 6178a9dd3..7c90d92b2 100644 --- a/sdc/PinPair.cc +++ b/sdc/PinPair.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -35,7 +35,7 @@ PinPairLess::PinPairLess(const Network *network) : bool PinPairLess::operator()(const PinPair &pair1, - const PinPair &pair2) const + const PinPair &pair2) const { const Pin *pair1_pin1 = pair1.first; const Pin *pair1_pin2 = pair1.second; @@ -52,7 +52,7 @@ PinPairLess::operator()(const PinPair &pair1, bool PinPairEqual::operator()(const PinPair &pair1, - const PinPair &pair2) const + const PinPair &pair2) const { return pair1.first == pair2.first && pair1.second == pair2.second; @@ -73,8 +73,8 @@ PinPairHash::operator()(const PinPair &pair) const } PinPairSet::PinPairSet(const Network *network) : - Set(PinPairLess(network)) + std::set(PinPairLess(network)) { } -} // namespace +} // namespace sta diff --git a/sdc/PortDelay.cc b/sdc/PortDelay.cc index e23812162..94d3bbb20 100644 --- a/sdc/PortDelay.cc +++ b/sdc/PortDelay.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,20 +24,16 @@ #include "PortDelay.hh" -#include "Sdc.hh" #include "Network.hh" +#include "Sdc.hh" namespace sta { PortDelay::PortDelay(const Pin *pin, - const ClockEdge *clk_edge, + const ClockEdge *clk_edge, const Network *network) : pin_(pin), clk_edge_(clk_edge), - source_latency_included_(false), - network_latency_included_(false), - ref_pin_(nullptr), - delays_(), leaf_pins_(network) { } @@ -92,9 +88,9 @@ PortDelay::refTransition() const } InputDelay::InputDelay(const Pin *pin, - const ClockEdge *clk_edge, - int index, - const Network *network) : + const ClockEdge *clk_edge, + int index, + const Network *network) : PortDelay(pin, clk_edge, network), index_(index) { @@ -102,8 +98,8 @@ InputDelay::InputDelay(const Pin *pin, } OutputDelay::OutputDelay(const Pin *pin, - const ClockEdge *clk_edge, - const Network *network) : + const ClockEdge *clk_edge, + const Network *network) : PortDelay(pin, clk_edge, network) { if (network) @@ -132,4 +128,4 @@ PortDelayLess::operator() (const PortDelay *delay1, return clkEdgeLess(delay1->clkEdge(), delay2->clkEdge()); } -} // namespace +} // namespace sta diff --git a/sdc/PortExtCap.cc b/sdc/PortExtCap.cc index 5dfd2180e..9802628c9 100644 --- a/sdc/PortExtCap.cc +++ b/sdc/PortExtCap.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,62 +26,63 @@ namespace sta { -PortExtCap::PortExtCap(const Port *port) : - port_(port) -{ -} - void PortExtCap::pinCap(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &cap, - bool &exists) + const MinMax *min_max, + // Return values. + float &cap, + bool &exists) const { pin_cap_.value(rf, min_max, cap, exists); } void -PortExtCap::setPinCap(float cap, - const RiseFall *rf, - const MinMax *min_max) +PortExtCap::setPinCap(const Port *port, + float cap, + const RiseFall *rf, + const MinMax *min_max) { + port_ = port; pin_cap_.setValue(rf, min_max, cap); } void PortExtCap::wireCap(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &cap, - bool &exists) + const MinMax *min_max, + // Return values. + float &cap, + bool &exists) const { wire_cap_.value(rf, min_max, cap, exists); } void -PortExtCap::setWireCap(float cap, - const RiseFall *rf, - const MinMax *min_max) +PortExtCap::setWireCap(const Port *port, + float cap, + const RiseFall *rf, + const MinMax *min_max) { + port_ = port; wire_cap_.setValue(rf, min_max, cap); } void -PortExtCap::setFanout(int fanout, - const MinMax *min_max) +PortExtCap::setFanout(const Port *port, + int fanout, + const MinMax *min_max) { + port_ = port; fanout_.setValue(min_max, fanout); } void PortExtCap::fanout(const MinMax *min_max, - // Return values. - int &fanout, - bool &exists) + // Return values. + int &fanout, + bool &exists) const { fanout_.value(min_max, fanout, exists); } -} // namespace +} // namespace sta diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index e105e3c9c..6bf69fd1e 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,66 +25,75 @@ #include "Sdc.hh" #include +#include +#include +#include +#include +#include -#include "Stats.hh" -#include "Debug.hh" -#include "Mutex.hh" -#include "Report.hh" -#include "Variables.hh" -#include "PatternMatch.hh" -#include "MinMax.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" -#include "Liberty.hh" -#include "Transition.hh" -#include "PortDirection.hh" -#include "Network.hh" -#include "RiseFallMinMax.hh" #include "Clock.hh" -#include "ClockLatency.hh" +#include "ClockGatingCheck.hh" +#include "ClockGroups.hh" #include "ClockInsertion.hh" -#include "GeneratedClock.hh" +#include "ClockLatency.hh" +#include "ContainerHelpers.hh" #include "CycleAccting.hh" -#include "PortDelay.hh" -#include "ExceptionPath.hh" -#include "PortExtCap.hh" -#include "Sta.hh" -#include "DisabledPorts.hh" -#include "InputDrive.hh" #include "DataCheck.hh" -#include "ClockGatingCheck.hh" -#include "ClockGroups.hh" +#include "Debug.hh" #include "DeratingFactors.hh" +#include "DisabledPorts.hh" +#include "ExceptionPath.hh" +#include "Format.hh" +#include "GeneratedClock.hh" +#include "Graph.hh" #include "HpinDrvrLoad.hh" +#include "InputDrive.hh" +#include "Liberty.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "Mutex.hh" +#include "Network.hh" +#include "NetworkClass.hh" +#include "PatternMatch.hh" +#include "PinPair.hh" +#include "PortDelay.hh" +#include "PortDirection.hh" +#include "PortExtCap.hh" +#include "Report.hh" +#include "RiseFallMinMax.hh" +#include "Scene.hh" +#include "SdcClass.hh" +#include "Sta.hh" +#include "Stats.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Transition.hh" +#include "Variables.hh" #include "search/Levelize.hh" -#include "Corner.hh" -#include "Graph.hh" namespace sta { -using std::swap; - bool ClockPairLess::operator()(const ClockPair &pair1, - const ClockPair &pair2) const + const ClockPair &pair2) const { - int first1 = pair1.first->index(); - int second1 = pair1.second->index(); + const auto& [clk1_1, clk2_1] = pair1; + int first1 = clk1_1->index(); + int second1 = clk2_1->index(); if (first1 > second1) std::swap(first1, second1); - int first2 = pair2.first->index(); - int second2 = pair2.second->index(); + const auto& [clk1_2, clk2_2] = pair2; + int first2 = clk1_2->index(); + int second2 = clk2_2->index(); if (first2 > second2) std::swap(first2, second2); return (first1 < first2) || (first1 == first2 - && second1 < second2); + && second1 < second2); } -//////////////////////////////////////////////////////////////// - -typedef Vector ClockPairSeq; -typedef Set PvtSet; +using ClockPairSeq = std::vector; +using PvtSet = std::set; static ExceptionThruSeq * clone(ExceptionThruSeq *thrus, @@ -92,47 +101,46 @@ clone(ExceptionThruSeq *thrus, //////////////////////////////////////////////////////////////// -Sdc::Sdc(StaState *sta) : +Sdc::Sdc(Mode *mode, + StaState *sta) : StaState(sta), - derating_factors_(nullptr), - clk_index_(0), - clock_pin_map_(PinIdHash(network_)), - clock_leaf_pin_map_(PinIdHash(network_)), + mode_(mode), + clock_pin_map_(10, PinIdHash(network_)), + clock_leaf_pin_map_(10, PinIdHash(network_)), clk_hpin_disables_(network_), propagated_clk_pins_(network_), clk_latencies_(network_), + clk_latency_pins_(network_), + edge_clk_latency_map_(network_), clk_insertions_(network_), clk_sense_map_(network_), - clk_gating_check_(nullptr), cycle_acctings_(this), input_delay_pin_map_(PinIdLess(network_)), input_delay_ref_pin_map_(PinIdLess(network_)), input_delay_leaf_pin_map_(PinIdLess(network_)), input_delay_internal_pin_map_(PinIdLess(network_)), - input_delay_index_(0), output_delay_pin_map_(PinIdLess(network_)), output_delay_ref_pin_map_(PinIdLess(network_)), output_delay_leaf_pin_map_(PinIdLess(network_)), + port_ext_cap_map_(network_), + net_wire_cap_map_(network_), + drvr_pin_wire_cap_map_(network_), + disabled_pins_(network_), disabled_ports_(network_), disabled_wire_edges_(network_), disabled_clk_gating_checks_inst_(network_), disabled_clk_gating_checks_pin_(network_), - exception_id_(0), - have_thru_hpin_exceptions_(false), first_thru_edge_exceptions_(0, PinPairHash(network_), PinPairEqual()), path_delay_internal_from_(network_), path_delay_internal_from_break_(network_), path_delay_internal_to_(network_), path_delay_internal_to_break_(network_) { - sdc_ = this; initVariables(); - if (corners_) - makeCornersAfter(corners_); setWireload(nullptr, MinMaxAll::all()); setWireloadSelection(nullptr, MinMaxAll::all()); setOperatingConditions(nullptr, MinMaxAll::all()); @@ -142,11 +150,11 @@ Sdc::Sdc(StaState *sta) : void Sdc::makeDefaultArrivalClock() { - FloatSeq *waveform = new FloatSeq; - waveform->push_back(0.0); - waveform->push_back(0.0); + FloatSeq waveform; + waveform.push_back(0.0); + waveform.push_back(0.0); default_arrival_clk_ = new Clock("input port clock", clk_index_++, network_); - default_arrival_clk_->initClk(0, false, 0.0, waveform, nullptr, network_); + default_arrival_clk_->initClk(nullptr, false, 0.0, waveform, "", network_); } Sdc::~Sdc() @@ -159,7 +167,6 @@ Sdc::~Sdc() void Sdc::clear() { - removeLibertyAnnotations(); deleteConstraints(); propagated_clk_pins_.clear(); clocks_.clear(); @@ -167,7 +174,8 @@ Sdc::clear() clock_pin_map_.clear(); clock_leaf_pin_map_.clear(); clk_latencies_.clear(); - edge_clk_latency_.clear(); + clk_latency_pins_.clear(); + edge_clk_latency_map_.clear(); clk_insertions_.clear(); pin_clk_uncertainty_map_.clear(); @@ -251,53 +259,53 @@ Sdc::initVariables() void Sdc::deleteConstraints() { - clocks_.deleteContents(); + deleteContents(clocks_);; delete default_arrival_clk_; - clock_pin_map_.deleteContents(); - clock_leaf_pin_map_.deleteContents(); - clk_latencies_.deleteContents(); - clk_insertions_.deleteContents(); + deleteContents(clock_pin_map_); + deleteContents(clock_leaf_pin_map_); + deleteContents(clk_latencies_); + deleteContents(clk_insertions_); - clk_groups_name_map_.deleteContents(); + deleteContents(clk_groups_name_map_); clearClkGroupExclusions(); - pin_clk_uncertainty_map_.deleteContents(); - inter_clk_uncertainties_.deleteContents(); + deleteContents(pin_clk_uncertainty_map_); + deleteContents(inter_clk_uncertainties_); delete clk_gating_check_; clk_gating_check_ = nullptr; - clk_gating_check_map_.deleteContents(); - inst_clk_gating_check_map_.deleteContents(); - pin_clk_gating_check_map_.deleteContents(); - input_drive_map_.deleteContents(); - disabled_cell_ports_.deleteContents(); - disabled_inst_ports_.deleteContents(); - pin_min_pulse_width_map_.deleteContentsClear(); - inst_min_pulse_width_map_.deleteContentsClear(); - clk_min_pulse_width_map_.deleteContentsClear(); + deleteContents(clk_gating_check_map_); + deleteContents(inst_clk_gating_check_map_); + deleteContents(pin_clk_gating_check_map_); + deleteContents(input_drive_map_); + deleteContents(disabled_cell_ports_); + deleteContents(disabled_inst_ports_); + deleteContents(pin_min_pulse_width_map_); + deleteContents(inst_min_pulse_width_map_); + deleteContents(clk_min_pulse_width_map_); for (auto [pin, checks] : data_checks_from_map_) { - checks->deleteContents(); + deleteContents(*checks); delete checks; } - for (auto [pin, checks] : data_checks_to_map_) - delete checks; + deleteContents(data_checks_to_map_); - input_delays_.deleteContents(); - input_delay_pin_map_.deleteContents(); - input_delay_leaf_pin_map_.deleteContents(); - input_delay_ref_pin_map_.deleteContents(); - input_delay_internal_pin_map_.deleteContents(); + deleteContents(input_delays_); + deleteContents(input_delay_pin_map_); + deleteContents(input_delay_leaf_pin_map_); + deleteContents(input_delay_ref_pin_map_); + deleteContents(input_delay_internal_pin_map_); - output_delays_.deleteContents(); - output_delay_pin_map_.deleteContents(); - output_delay_ref_pin_map_.deleteContents(); - output_delay_leaf_pin_map_.deleteContents(); + deleteContents(output_delays_); + deleteContents(output_delay_pin_map_); + deleteContents(output_delay_ref_pin_map_); + deleteContents(output_delay_leaf_pin_map_); - clk_hpin_disables_.deleteContentsClear(); + deleteContents(clk_hpin_disables_); clk_hpin_disables_valid_ = false; clearCycleAcctings(); deleteExceptions(); + deleteFilter(); clearGroupPathMap(); deleteDeratingFactors(); @@ -306,66 +314,30 @@ Sdc::deleteConstraints() clk_sense_map_.clear(); for (int mm_index : MinMax::rangeIndex()) - instance_pvt_maps_[mm_index].deleteContentsClear(); + deleteContents(instance_pvt_maps_[mm_index]); } void -Sdc::removeNetLoadCaps() +Sdc::searchPreamble() { - if (!net_wire_cap_maps_.empty()) { - for (int corner_index = 0; corner_index < corners_->count(); corner_index++) { - net_wire_cap_maps_[corner_index].clear(); - drvr_pin_wire_cap_maps_[corner_index].clear(); - port_ext_cap_maps_[corner_index].deleteContentsClear(); - } - } + ensureClkHpinDisables(); + ensureClkGroupExclusions(); } void -Sdc::removeLibertyAnnotations() +Sdc::removeNetLoadCaps() { - for (auto [cell, disable] : disabled_cell_ports_) { - if (disable->all()) - cell->setIsDisabledConstraint(false); - - if (disable->from()) { - for (LibertyPort *from : *disable->from()) - from->setIsDisabledConstraint(false); - } - - if (disable->to()) { - for (LibertyPort *to : *disable->to()) - to->setIsDisabledConstraint(false); - } - - if (disable->timingArcSets()) { - for (TimingArcSet *arc_set : *disable->timingArcSets()) - arc_set->setIsDisabledConstraint(false); - } - - - if (disable->fromTo()) { - for (const LibertyPortPair &pair : *disable->fromTo()) { - const LibertyPort *from = pair.first; - const LibertyPort *to = pair.second; - for (TimingArcSet *arc_set : cell->timingArcSets(from, to)) - arc_set->setIsDisabledConstraint(false); - } - } - } - - for (LibertyPort *port : disabled_lib_ports_) - port->setIsDisabledConstraint(false); + net_wire_cap_map_.clear(); + drvr_pin_wire_cap_map_.clear(); + port_ext_cap_map_.clear(); } void Sdc::deleteNetBefore(const Net *net) { - for (int corner_index = 0; corner_index < corners_->count(); corner_index++) { - net_wire_cap_maps_[corner_index].erase(net); - for (const Pin *pin : *network_->drivers(net)) - drvr_pin_wire_cap_maps_[corner_index].erase(pin); - } + net_wire_cap_map_.erase(net); + for (const Pin *pin : *network_->drivers(net)) + drvr_pin_wire_cap_map_.erase(pin); } // see Sdc::isConstrained @@ -388,82 +360,73 @@ Sdc::deleteInstanceBefore(const Instance *inst) } void -Sdc::makeCornersBefore() +Sdc::makeSceneBefore() { removeNetLoadCaps(); } -void -Sdc::makeCornersAfter(Corners *corners) -{ - corners_ = corners; - port_ext_cap_maps_.resize(corners_->count(), PortExtCapMap(PortIdLess(network_))); - net_wire_cap_maps_.resize(corners_->count(), NetWireCapMap(NetIdLess(network_))); - drvr_pin_wire_cap_maps_.resize(corners_->count(), PinWireCapMap(PinIdLess(network_))); -} - //////////////////////////////////////////////////////////////// bool Sdc::isConstrained(const Pin *pin) const { Port *port = network_->isTopLevelPort(pin) ? network_->port(pin) : nullptr; - return clock_pin_map_.hasKey(pin) - || propagated_clk_pins_.hasKey(pin) + return clock_pin_map_.contains(pin) + || propagated_clk_pins_.contains(pin) || hasClockLatency(pin) || hasClockInsertion(pin) - || pin_clk_uncertainty_map_.hasKey(pin) - || pin_clk_gating_check_map_.hasKey(pin) - || data_checks_from_map_.hasKey(pin) - || data_checks_to_map_.hasKey(pin) - || input_delay_pin_map_.hasKey(pin) - || output_delay_pin_map_.hasKey(pin) - || pin_cap_limit_map_.hasKey(pin) - || disabled_pins_.hasKey(pin) - || disabled_ports_.hasKey(port) - || disabled_clk_gating_checks_pin_.hasKey(pin) - || first_from_pin_exceptions_.hasKey(pin) - || first_thru_pin_exceptions_.hasKey(pin) - || first_to_pin_exceptions_.hasKey(pin) - || input_drive_map_.hasKey(port) - || logic_value_map_.hasKey(pin) - || case_value_map_.hasKey(pin) - || pin_latch_borrow_limit_map_.hasKey(pin) - || pin_min_pulse_width_map_.hasKey(pin) - || (port && (port_slew_limit_map_.hasKey(port) - || port_cap_limit_map_.hasKey(port) - || port_fanout_limit_map_.hasKey(port) + || pin_clk_uncertainty_map_.contains(pin) + || pin_clk_gating_check_map_.contains(pin) + || data_checks_from_map_.contains(pin) + || data_checks_to_map_.contains(pin) + || input_delay_pin_map_.contains(pin) + || output_delay_pin_map_.contains(pin) + || pin_cap_limit_map_.contains(pin) + || disabled_pins_.contains(pin) + || disabled_ports_.contains(port) + || disabled_clk_gating_checks_pin_.contains(pin) + || first_from_pin_exceptions_.contains(pin) + || first_thru_pin_exceptions_.contains(pin) + || first_to_pin_exceptions_.contains(pin) + || input_drive_map_.contains(port) + || logic_value_map_.contains(pin) + || case_value_map_.contains(pin) + || pin_latch_borrow_limit_map_.contains(pin) + || pin_min_pulse_width_map_.contains(pin) + || (port && (port_slew_limit_map_.contains(port) + || port_cap_limit_map_.contains(port) + || port_fanout_limit_map_.contains(port) || hasPortExtCap(port))); } bool Sdc::isConstrained(const Instance *inst) const { - return instance_pvt_maps_[MinMax::minIndex()].hasKey(inst) - || instance_pvt_maps_[MinMax::maxIndex()].hasKey(inst) - || inst_derating_factors_.hasKey(inst) - || inst_clk_gating_check_map_.hasKey(inst) - || disabled_inst_ports_.hasKey(inst) - || first_from_inst_exceptions_.hasKey(inst) - || first_thru_inst_exceptions_.hasKey(inst) - || first_to_inst_exceptions_.hasKey(inst) - || inst_latch_borrow_limit_map_.hasKey(inst) - || inst_min_pulse_width_map_.hasKey(inst); + return instance_pvt_maps_[MinMax::minIndex()].contains(inst) + || instance_pvt_maps_[MinMax::maxIndex()].contains(inst) + || inst_derating_factors_.contains(inst) + || inst_clk_gating_check_map_.contains(inst) + || disabled_inst_ports_.contains(inst) + || first_from_inst_exceptions_.contains(inst) + || first_thru_inst_exceptions_.contains(inst) + || first_to_inst_exceptions_.contains(inst) + || inst_latch_borrow_limit_map_.contains(inst) + || inst_min_pulse_width_map_.contains(inst); } bool Sdc::isConstrained(const Net *net) const { - return net_derating_factors_.hasKey(net) + return net_derating_factors_.contains(net) || hasNetWireCap(net) - || net_res_map_.hasKey(net) - || first_thru_net_exceptions_.hasKey(net); + || net_res_map_.contains(net) + || first_thru_net_exceptions_.contains(net); } //////////////////////////////////////////////////////////////// PortSeq -Sdc::allInputs(bool no_clks) +Sdc::allInputs(bool no_clks) const { PortSeq ports; Instance *top_inst = network_->topInstance(); @@ -481,7 +444,7 @@ Sdc::allInputs(bool no_clks) } PortSeq -Sdc::allOutputs() +Sdc::allOutputs() const { PortSeq ports; Instance *top_inst = network_->topInstance(); @@ -499,7 +462,7 @@ Sdc::allOutputs() void Sdc::portMembers(const Port *port, - PortSeq &ports) + PortSeq &ports) const { if (network_->isBus(port)) { PortMemberIterator *member_iter = network_->memberIterator(port); @@ -521,7 +484,7 @@ Sdc::setAnalysisType(AnalysisType analysis_type) void Sdc::setOperatingConditions(OperatingConditions *op_cond, - const MinMaxAll *min_max) + const MinMaxAll *min_max) { for (auto mm_index : min_max->rangeIndex()) operating_conditions_[mm_index] = op_cond; @@ -529,7 +492,7 @@ Sdc::setOperatingConditions(OperatingConditions *op_cond, void Sdc::setOperatingConditions(OperatingConditions *op_cond, - const MinMax *min_max) + const MinMax *min_max) { int mm_index = min_max->index(); operating_conditions_[mm_index] = op_cond; @@ -544,16 +507,16 @@ Sdc::operatingConditions(const MinMax *min_max) const const Pvt * Sdc::pvt(const Instance *inst, - const MinMax *min_max) const + const MinMax *min_max) const { const InstancePvtMap &pvt_map = instance_pvt_maps_[min_max->index()]; - return pvt_map.findKey(inst); + return findKey(pvt_map, inst); } void Sdc::setPvt(const Instance *inst, const MinMaxAll *min_max, - const Pvt &pvt) + const Pvt &pvt) { for (auto mm_index : min_max->rangeIndex()) { InstancePvtMap &pvt_map = instance_pvt_maps_[mm_index]; @@ -565,7 +528,7 @@ void Sdc::voltage(const MinMax *min_max, // Return values. float &voltage, - bool &exists) + bool &exists) const { voltages_.value(min_max, voltage, exists); } @@ -575,11 +538,14 @@ Sdc::voltage(const Net *net, const MinMax *min_max, // Return values. float &voltage, - bool &exists) + bool &exists) const { exists = false; - if (net_voltage_map_.hasKey(net)) - net_voltage_map_[net].value(min_max, voltage, exists); + auto itr = net_voltage_map_.find(net); + if (itr != net_voltage_map_.end()) { + const MinMaxFloatValues &values = itr->second; + values.value(min_max, voltage, exists); + } } void @@ -601,10 +567,10 @@ Sdc::setVoltage(const Net *net, void Sdc::setTimingDerate(TimingDerateType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) { if (derating_factors_ == nullptr) derating_factors_ = new DeratingFactorsGlobal; @@ -613,14 +579,14 @@ Sdc::setTimingDerate(TimingDerateType type, void Sdc::setTimingDerate(const Net *net, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) { - DeratingFactorsNet *factors = net_derating_factors_.findKey(net); + DeratingFactors *factors = findKey(net_derating_factors_, net); if (factors == nullptr) { - factors = new DeratingFactorsNet; + factors = new DeratingFactors; net_derating_factors_[net] = factors; } factors->setFactor(clk_data, rf, early_late, derate); @@ -628,13 +594,13 @@ Sdc::setTimingDerate(const Net *net, void Sdc::setTimingDerate(const Instance *inst, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) { - DeratingFactorsCell *factors = inst_derating_factors_.findKey(inst); + DeratingFactorsCell *factors = findKey(inst_derating_factors_, inst); if (factors == nullptr) { factors = new DeratingFactorsCell; inst_derating_factors_[inst] = factors; @@ -644,13 +610,13 @@ Sdc::setTimingDerate(const Instance *inst, void Sdc::setTimingDerate(const LibertyCell *cell, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) { - DeratingFactorsCell *factors = cell_derating_factors_.findKey(cell); + DeratingFactorsCell *factors = findKey(cell_derating_factors_, cell); if (factors == nullptr) { factors = new DeratingFactorsCell; cell_derating_factors_[cell] = factors; @@ -660,13 +626,13 @@ Sdc::setTimingDerate(const LibertyCell *cell, float Sdc::timingDerateInstance(const Pin *pin, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late) const + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late) const { const Instance *inst = network_->instance(pin); - DeratingFactorsCell *factors = inst_derating_factors_.findKey(inst); + DeratingFactorsCell *factors = findKey(inst_derating_factors_, inst); if (factors) { float factor; bool exists; @@ -677,7 +643,7 @@ Sdc::timingDerateInstance(const Pin *pin, const LibertyCell *cell = network_->libertyCell(inst); if (cell) { - DeratingFactorsCell *factors = cell_derating_factors_.findKey(cell); + DeratingFactorsCell *factors = findKey(cell_derating_factors_, cell); float factor; bool exists; if (factors) { @@ -699,12 +665,12 @@ Sdc::timingDerateInstance(const Pin *pin, float Sdc::timingDerateNet(const Pin *pin, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late) const + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late) const { const Net *net = network_->net(pin); - DeratingFactorsNet *factors = net_derating_factors_.findKey(net); + DeratingFactors *factors = findKey(net_derating_factors_, net); if (factors) { float factor; bool exists; @@ -717,7 +683,7 @@ Sdc::timingDerateNet(const Pin *pin, float factor; bool exists; derating_factors_->factor(TimingDerateType::net_delay, clk_data, rf, - early_late, factor, exists); + early_late, factor, exists); if (exists) return factor; } @@ -734,18 +700,18 @@ void Sdc::swapDeratingFactors(Sdc *sdc1, Sdc *sdc2) { - swap(sdc1->derating_factors_, sdc2->derating_factors_); - swap(sdc1->net_derating_factors_, sdc2->net_derating_factors_); - swap(sdc1->inst_derating_factors_, sdc2->inst_derating_factors_); - swap(sdc1->cell_derating_factors_, sdc2->cell_derating_factors_); + std::swap(sdc1->derating_factors_, sdc2->derating_factors_); + std::swap(sdc1->net_derating_factors_, sdc2->net_derating_factors_); + std::swap(sdc1->inst_derating_factors_, sdc2->inst_derating_factors_); + std::swap(sdc1->cell_derating_factors_, sdc2->cell_derating_factors_); } void Sdc::deleteDeratingFactors() { - net_derating_factors_.deleteContents(); - inst_derating_factors_.deleteContents(); - cell_derating_factors_.deleteContents(); + deleteContents(net_derating_factors_); + deleteContents(inst_derating_factors_); + deleteContents(cell_derating_factors_); delete derating_factors_; derating_factors_ = nullptr; @@ -755,32 +721,32 @@ Sdc::deleteDeratingFactors() void Sdc::setDriveCell(const LibertyLibrary *library, - const LibertyCell *cell, - const Port *port, - const LibertyPort *from_port, - float *from_slews, - const LibertyPort *to_port, - const RiseFallBoth *rf, - const MinMaxAll *min_max) + const LibertyCell *cell, + const Port *port, + const LibertyPort *from_port, + const DriveCellSlews &from_slews, + const LibertyPort *to_port, + const RiseFallBoth *rf, + const MinMaxAll *min_max) { ensureInputDrive(port)->setDriveCell(library, cell, from_port, from_slews, - to_port, rf, min_max); + to_port, rf, min_max); } void Sdc::setInputSlew(const Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew) + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew) { ensureInputDrive(port)->setSlew(rf, min_max, slew); } void Sdc::setDriveResistance(const Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float res) + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float res) { ensureInputDrive(port)->setDriveResistance(rf, min_max, res); } @@ -788,7 +754,7 @@ Sdc::setDriveResistance(const Port *port, InputDrive * Sdc::ensureInputDrive(const Port *port) { - InputDrive *drive = input_drive_map_.findKey(port); + InputDrive *drive = findKey(input_drive_map_, port); if (drive == nullptr) { drive = new InputDrive; input_drive_map_[port] = drive; @@ -800,10 +766,10 @@ Sdc::ensureInputDrive(const Port *port) void Sdc::setSlewLimit(Clock *clk, - const RiseFallBoth *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - float slew) + const RiseFallBoth *rf, + PathClkOrData clk_data, + const MinMax *min_max, + float slew) { clk->setSlewLimit(rf, clk_data, min_max, slew); have_clk_slew_limits_ = true; @@ -816,33 +782,32 @@ Sdc::haveClkSlewLimits() const } void -Sdc::slewLimit(Clock *clk, +Sdc::slewLimit(const Clock *clk, const RiseFall *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - float &slew, - bool &exists) + PathClkOrData clk_data, + const MinMax *min_max, + float &slew, + bool &exists) const { clk->slewLimit(rf, clk_data, min_max, slew, exists); } void Sdc::slewLimit(Port *port, - const MinMax *min_max, - float &slew, - bool &exists) + const MinMax *min_max, + float &slew, + bool &exists) const { slew = INF; MinMaxFloatValues values; - port_slew_limit_map_.findKey(port, values, exists); - if (exists) - values.value(min_max, slew, exists); + findKeyValue(port_slew_limit_map_, port, values, exists); + values.value(min_max, slew, exists); } void Sdc::setSlewLimit(Port *port, - const MinMax *min_max, - float slew) + const MinMax *min_max, + float slew) { MinMaxFloatValues &values = port_slew_limit_map_[port]; values.setValue(min_max, slew); @@ -850,21 +815,21 @@ Sdc::setSlewLimit(Port *port, void Sdc::slewLimit(Cell *cell, - const MinMax *min_max, - float &slew, - bool &exists) + const MinMax *min_max, + float &slew, + bool &exists) const { slew = INF; MinMaxFloatValues values; - cell_slew_limit_map_.findKey(cell, values, exists); + findKeyValue(cell_slew_limit_map_, cell, values, exists); if (exists) values.value(min_max, slew, exists); } void Sdc::setSlewLimit(Cell *cell, - const MinMax *min_max, - float slew) + const MinMax *min_max, + float slew) { MinMaxFloatValues &values = cell_slew_limit_map_[cell]; values.setValue(min_max, slew); @@ -872,22 +837,22 @@ Sdc::setSlewLimit(Cell *cell, void Sdc::capacitanceLimit(Cell *cell, - const MinMax *min_max, - float &cap, - bool &exists) + const MinMax *min_max, + float &cap, + bool &exists) const { cap = 0.0; exists = false; MinMaxFloatValues values; - cell_cap_limit_map_.findKey(cell, values, exists); + findKeyValue(cell_cap_limit_map_, cell, values, exists); if (exists) values.value(min_max, cap, exists); } void Sdc::setCapacitanceLimit(Cell *cell, - const MinMax *min_max, - float cap) + const MinMax *min_max, + float cap) { MinMaxFloatValues &values = cell_cap_limit_map_[cell]; values.setValue(min_max, cap); @@ -895,22 +860,22 @@ Sdc::setCapacitanceLimit(Cell *cell, void Sdc::capacitanceLimit(Port *port, - const MinMax *min_max, - float &cap, - bool &exists) + const MinMax *min_max, + float &cap, + bool &exists) const { cap = 0.0; exists = false; MinMaxFloatValues values; - port_cap_limit_map_.findKey(port, values, exists); + findKeyValue(port_cap_limit_map_, port, values, exists); if (exists) values.value(min_max, cap, exists); } void Sdc::setCapacitanceLimit(Port *port, - const MinMax *min_max, - float cap) + const MinMax *min_max, + float cap) { MinMaxFloatValues &values = port_cap_limit_map_[port]; values.setValue(min_max, cap); @@ -918,22 +883,22 @@ Sdc::setCapacitanceLimit(Port *port, void Sdc::capacitanceLimit(Pin *pin, - const MinMax *min_max, - float &cap, - bool &exists) + const MinMax *min_max, + float &cap, + bool &exists) const { cap = 0.0; exists = false; MinMaxFloatValues values; - pin_cap_limit_map_.findKey(pin, values, exists); + findKeyValue(pin_cap_limit_map_, pin, values, exists); if (exists) values.value(min_max, cap, exists); } void Sdc::setCapacitanceLimit(Pin *pin, - const MinMax *min_max, - float cap) + const MinMax *min_max, + float cap) { MinMaxFloatValues &values = pin_cap_limit_map_[pin]; values.setValue(min_max, cap); @@ -941,21 +906,21 @@ Sdc::setCapacitanceLimit(Pin *pin, void Sdc::fanoutLimit(Cell *cell, - const MinMax *min_max, - float &fanout, - bool &exists) + const MinMax *min_max, + float &fanout, + bool &exists) const { fanout = min_max->initValue(); MinMaxFloatValues values; - cell_fanout_limit_map_.findKey(cell, values, exists); + findKeyValue(cell_fanout_limit_map_, cell, values, exists); if (exists) values.value(min_max, fanout, exists); } void Sdc::setFanoutLimit(Cell *cell, - const MinMax *min_max, - float fanout) + const MinMax *min_max, + float fanout) { MinMaxFloatValues &values = cell_fanout_limit_map_[cell]; values.setValue(min_max, fanout); @@ -963,21 +928,21 @@ Sdc::setFanoutLimit(Cell *cell, void Sdc::fanoutLimit(Port *port, - const MinMax *min_max, - float &fanout, - bool &exists) + const MinMax *min_max, + float &fanout, + bool &exists) const { fanout = 0.0; MinMaxFloatValues values; - port_fanout_limit_map_.findKey(port, values, exists); + findKeyValue(port_fanout_limit_map_, port, values, exists); if (exists) values.value(min_max, fanout, exists); } void Sdc::setFanoutLimit(Port *port, - const MinMax *min_max, - float fanout) + const MinMax *min_max, + float fanout) { MinMaxFloatValues &values = port_fanout_limit_map_[port]; values.setValue(min_max, fanout); @@ -1031,31 +996,31 @@ void Sdc::createLibertyGeneratedClocks(Clock *clk) { Sta *sta = Sta::sta(); // Invalidate and rebuild the clock network to include the newly created clock - sta->clkPinsInvalid(); - sta->ensureClkNetwork(); + sta->clkPinsInvalid(mode_); + sta->ensureClkNetwork(mode_); - if (sta->pins(clk)) { + if (sta->pins(clk, mode_)) { // All pins along the clock network - const PinSet clkNetworkPins = *sta->pins(clk); + const PinSet clkNetworkPins = *sta->pins(clk, mode_); // Get all generated clock pins from the network - const Map &generated_clock_pins_to_cells = + const std::map &generated_clock_pins_to_cells = network_->generatedClockPinsToCellMap(); // The keys of generated_clock_pins_to_cells_ // (master clock pins) will be searched in the current clock network for (const auto &entry : generated_clock_pins_to_cells) { - const char *pinName = entry.first; + const std::string &pinName = entry.first; LibertyCell *cell = entry.second; - // Search the current clock network for the pin and validate + // Search the current clock network for the pin and validate // that it is in the clock network - Pin *pin = network_->findPin(pinName); - if (pin && clkNetworkPins.hasKey(pin)) { + Pin *pin = network_->findPin(pinName.c_str()); + if (pin && clkNetworkPins.count(pin)) { - debugPrint(debug_, "libgenclk", 1, "Found generated clock pin %s " - "in liberty cell %s at path %s", + debugPrint(debug_, "libgenclk", 1, "Found generated clock pin {} " + "in liberty cell {} at path {}", pinName, cell->name(), network_->pathName(pin)); // Search liberty cell for the corresponding generated clock @@ -1063,64 +1028,70 @@ void Sdc::createLibertyGeneratedClocks(Clock *clk) { // Use the pin to find the instance and the path to the instance const Instance *inst = network_->instance(pin); - const char *instPath = network_->pathName(inst); + std::string instPath = network_->pathName(inst); // Compare the full {instance path/master pin} // and the full path to pin to validate the correct // liberty information is supplied - const char *comparePath = stringPrintTmp( - "%s/%s", instPath, + std::string comparePath = sta::format( + "{}/{}", instPath, generatedClock->masterPin() ); - if (strcmp(comparePath, network_->pathName(pin)) == 0) { + if (comparePath == network_->pathName(pin)) { // Hierarchical path of the generated clock pin // (name is with respect to source clock) - const char *generatedClockName = stringCopy(stringPrintTmp( - "%s/%s", instPath, + std::string generatedClockName = sta::format( + "{}/{}", instPath, generatedClock->clockPin() - )); + ); - debugPrint(debug_, "libgenclk", 1, "Creating generated clock %s " - "from clock %s in instance %s", + debugPrint(debug_, "libgenclk", 1, "Creating generated clock {} " + "from clock {} in instance {}", generatedClockName, clk->name(), instPath); // Find the output pin, for nested generated clocks Pin *clkOutPin = network_->findPin( - inst, + inst, generatedClock->clockPin() ); - PinSet *clkPins = nullptr; - if (clkOutPin) { - clkPins = new PinSet(); - clkPins->insert(clkOutPin); - } + PinSet clkPins; + if (clkOutPin) + clkPins.insert(clkOutPin); // Deep copy edges and edge_shifts since makeGeneratedClock // takes ownership, but GeneratedClock retains its own copies. - IntSeq *edgesCopy = nullptr; - if (generatedClock->edges()) { - edgesCopy = new IntSeq(*generatedClock->edges()); - } - FloatSeq *edgeShiftsCopy = nullptr; - if (generatedClock->edgeShifts()) { - edgeShiftsCopy = new FloatSeq(*generatedClock->edgeShifts()); - } + IntSeq edgesCopy; + if (generatedClock->edges()) + edgesCopy = *generatedClock->edges(); + FloatSeq edgeShiftsCopy; + if (generatedClock->edgeShifts()) + edgeShiftsCopy = *generatedClock->edgeShifts(); + + // When edges are specified they fully define the waveform. + // Pass divide_by=0, multiply_by=0 so Clock::generate uses + // the edges-based path rather than the scalar divide path. + int divide_by = edgesCopy.empty() ? generatedClock->dividedBy() : 0; + int multiply_by = edgesCopy.empty() ? generatedClock->multipliedBy() : 0; + // Prevent divide_by==1 (default) from shadowing multiply_by>1 + // in Clock::generate's branch logic. + if (divide_by == 1 && multiply_by > 1) + divide_by = 0; makeGeneratedClock( generatedClockName, - clkPins, + clkPins, true, const_cast(pin), clk, - generatedClock->dividedBy(), - generatedClock->multipliedBy(), + divide_by, + multiply_by, generatedClock->dutyCycle(), generatedClock->invert(), false, edgesCopy, edgeShiftsCopy, - nullptr); + ""); } } } @@ -1129,14 +1100,14 @@ void Sdc::createLibertyGeneratedClocks(Clock *clk) { } Clock * -Sdc::makeClock(const char *name, - PinSet *pins, - bool add_to_pins, - float period, - FloatSeq *waveform, - const char *comment) -{ - Clock *clk = clock_name_map_.findKey(name); +Sdc::makeClock(std::string_view name, + const PinSet &pins, + bool add_to_pins, + float period, + const FloatSeq &waveform, + std::string_view comment) +{ + Clock *clk = findStringKey(clock_name_map_, name); if (!add_to_pins) deletePinClocks(clk, pins); if (clk) @@ -1157,7 +1128,7 @@ Sdc::makeClock(const char *name, clkHpinDisablesInvalid(); if (network_->generatedClockPinsToCellMap().size() > 0) { debugPrint(debug_, "libgenclk", 1, "Creating liberty-defined generated clocks " - "for clock %s by searching %lu liberty-defined generated clock pins", + "for clock {} by searching {} liberty-defined generated clock pins", name, network_->generatedClockPinsToCellMap().size()); createLibertyGeneratedClocks(clk); } @@ -1165,21 +1136,21 @@ Sdc::makeClock(const char *name, } Clock * -Sdc::makeGeneratedClock(const char *name, - PinSet *pins, - bool add_to_pins, - Pin *src_pin, - Clock *master_clk, - int divide_by, - int multiply_by, - float duty_cycle, - bool invert, - bool combinational, - IntSeq *edges, - FloatSeq *edge_shifts, - const char *comment) -{ - Clock *clk = clock_name_map_.findKey(name); +Sdc::makeGeneratedClock(std::string_view name, + const PinSet &pins, + bool add_to_pins, + Pin *src_pin, + Clock *master_clk, + int divide_by, + int multiply_by, + float duty_cycle, + bool invert, + bool combinational, + const IntSeq &edges, + const FloatSeq &edge_shifts, + std::string_view comment) +{ + Clock *clk = findStringKey(clock_name_map_, name); if (!add_to_pins) deletePinClocks(clk, pins); if (clk) @@ -1190,11 +1161,11 @@ Sdc::makeGeneratedClock(const char *name, clock_name_map_[clk->name()] = clk; } clk->initGeneratedClk(pins, add_to_pins, src_pin, master_clk, - divide_by, multiply_by, duty_cycle, - invert, combinational, - edges, edge_shifts, + divide_by, multiply_by, duty_cycle, + invert, combinational, + edges, edge_shifts, variables_->propagateAllClocks(), - comment, network_); + comment, network_); makeClkPinMappings(clk); clearCycleAcctings(); invalidateGeneratedClks(); @@ -1222,32 +1193,30 @@ Sdc::invalidateGeneratedClks() const // is not the clock being defined and has no pins it is removed. void Sdc::deletePinClocks(Clock *defining_clk, - PinSet *pins) + const PinSet &pins) { // Find all the clocks defined on pins to avoid finding the clock's // vertex pins multiple times. ClockSet clks; - if (pins) { - for (const Pin *pin : *pins) { - ClockSet *pin_clks = clock_pin_map_.findKey(pin); - if (pin_clks) { - for (Clock *clk : *pin_clks) - clks.insert(clk); - } + for (const Pin *pin : pins) { + ClockSet *pin_clks = findKey(clock_pin_map_, pin); + if (pin_clks) { + for (Clock *clk : *pin_clks) + clks.insert(clk); } } for (Clock *clk : clks) { deleteClkPinMappings(clk); - for (const Pin *pin : *pins) + for (const Pin *pin : pins) clk->deletePin(pin); if (clk != defining_clk) { if (clk->pins().empty()) - removeClock(clk); + removeClock(clk); else { - clk->makeLeafPins(network_); - // One of the remaining clock pins may use a vertex pin that - // was deleted above. - makeClkPinMappings(clk); + clk->makeLeafPins(network_); + // One of the remaining clock pins may use a vertex pin that + // was deleted above. + makeClkPinMappings(clk); } } } @@ -1257,23 +1226,23 @@ void Sdc::deleteClkPinMappings(Clock *clk) { for (const Pin *pin : clk->pins()) { - ClockSet *pin_clks = clock_pin_map_.findKey(pin); + ClockSet *pin_clks = findKey(clock_pin_map_, pin); if (pin_clks) { pin_clks->erase(clk); if (pin_clks->empty()) { - clock_pin_map_.erase(pin); - delete pin_clks; + clock_pin_map_.erase(pin); + delete pin_clks; } } } for (const Pin *pin : clk->leafPins()) { - ClockSet *pin_clks = clock_leaf_pin_map_.findKey(pin); + ClockSet *pin_clks = findKey(clock_leaf_pin_map_, pin); if (pin_clks) { pin_clks->erase(clk); if (pin_clks->empty()) { - clock_leaf_pin_map_.erase(pin); - delete pin_clks; + clock_leaf_pin_map_.erase(pin); + delete pin_clks; } } } @@ -1283,19 +1252,19 @@ void Sdc::makeClkPinMappings(Clock *clk) { for (const Pin *pin : clk->pins()) { - ClockSet *pin_clks = clock_pin_map_.findKey(pin); + ClockSet *pin_clks = findKey(clock_pin_map_, pin); if (pin_clks == nullptr) { pin_clks = new ClockSet; - clock_pin_map_.insert(pin, pin_clks); + clock_pin_map_[pin] = pin_clks; } pin_clks->insert(clk); } for (const Pin *pin : clk->leafPins()) { - ClockSet *pin_clks = clock_leaf_pin_map_.findKey(pin); + ClockSet *pin_clks = findKey(clock_leaf_pin_map_, pin); if (pin_clks == nullptr) { pin_clks = new ClockSet; - clock_leaf_pin_map_.insert(pin, pin_clks); + clock_leaf_pin_map_[pin] = pin_clks; } pin_clks->insert(clk); } @@ -1317,7 +1286,7 @@ Sdc::removeClock(Clock *clk) clearCycleAcctings(); deleteClkPinMappings(clk); - clocks_.eraseObject(clk); + clocks_.erase(std::ranges::find(clocks_, clk)); clock_name_map_.erase(clk->name()); delete clk; } @@ -1328,16 +1297,16 @@ Sdc::deleteMasterClkRefs(Clock *clk) { for (auto gclk : clocks_) { if (gclk->isGenerated() - && gclk->masterClk() == clk) { + && gclk->masterClk() == clk) { gclk->setMasterClk(nullptr); } } } Clock * -Sdc::findClock(const char *name) const +Sdc::findClock(std::string_view name) const { - return clock_name_map_.findKey(name); + return findStringKey(clock_name_map_, name); } bool @@ -1361,7 +1330,7 @@ Sdc::isLeafPinNonGeneratedClock(const Pin *pin) const if (clks) { for (Clock *clk : *clks) { if (!clk->isGenerated()) - return true; + return true; } return false; } @@ -1372,13 +1341,13 @@ Sdc::isLeafPinNonGeneratedClock(const Pin *pin) const ClockSet * Sdc::findLeafPinClocks(const Pin *pin) const { - return clock_leaf_pin_map_.findKey(pin); + return findKey(clock_leaf_pin_map_, pin); } ClockSet * Sdc::findClocks(const Pin *pin) const { - return clock_pin_map_.findKey(pin); + return findKey(clock_pin_map_, pin); } ClockSeq @@ -1393,18 +1362,20 @@ Sdc::findClocksMatching(PatternMatch *pattern) const else { for (auto clk : clocks_) { if (pattern->match(clk->name())) - matches.push_back(clk); + matches.push_back(clk); } } return matches; } -void -Sdc::sortedClocks(ClockSeq &clks) +ClockSeq +Sdc::sortedClocks() const { - for (auto clk : clocks_) + ClockSeq clks; + for (Clock *clk : clocks_) clks.push_back(clk); - sort(clks, ClkNameLess()); + sort(clks, ClockNameLess()); + return clks; } ClockEdge * @@ -1419,8 +1390,8 @@ class ClkHpinDisable { public: ClkHpinDisable(const Clock *clk, - const Pin *from_pin, - const Pin *to_pin); + const Pin *from_pin, + const Pin *to_pin); const Clock *clk() const { return clk_; } const Pin *fromPin() const { return from_pin_; } const Pin *toPin() const { return to_pin_; } @@ -1432,8 +1403,8 @@ class ClkHpinDisable }; ClkHpinDisable::ClkHpinDisable(const Clock *clk, - const Pin *from_pin, - const Pin *to_pin) : + const Pin *from_pin, + const Pin *to_pin) : clk_(clk), from_pin_(from_pin), to_pin_(to_pin) @@ -1447,7 +1418,7 @@ ClkHpinDisableLess::ClkHpinDisableLess(const Network *network) : bool ClkHpinDisableLess::operator()(const ClkHpinDisable *disable1, - const ClkHpinDisable *disable2) const + const ClkHpinDisable *disable2) const { int clk_index1 = disable1->clk()->index(); int clk_index2 = disable2->clk()->index(); @@ -1466,13 +1437,13 @@ class FindClkHpinDisables : public HpinDrvrLoadVisitor { public: FindClkHpinDisables(Clock *clk, - const Network *network, - Sdc *sdc); + const Network *network, + Sdc *sdc); bool drvrLoadExists(const Pin *drvr, - const Pin *load); + const Pin *load); + void visit(HpinDrvrLoad *drvr_load) override; protected: - virtual void visit(HpinDrvrLoad *drvr_load); void makeClkHpinDisables(const Pin *clk_src, const Pin *drvr, const Pin *load); @@ -1484,9 +1455,8 @@ class FindClkHpinDisables : public HpinDrvrLoadVisitor }; FindClkHpinDisables::FindClkHpinDisables(Clock *clk, - const Network *network, - Sdc *sdc) : - HpinDrvrLoadVisitor(), + const Network *network, + Sdc *sdc) : clk_(clk), drvr_loads_(network), network_(network), @@ -1510,8 +1480,8 @@ FindClkHpinDisables::visit(HpinDrvrLoad *drvr_load) void FindClkHpinDisables::makeClkHpinDisables(const Pin *clk_src, - const Pin *drvr, - const Pin *load) + const Pin *drvr, + const Pin *load) { ClockSet *clks = sdc_->findClocks(clk_src); if (clks) { @@ -1527,36 +1497,35 @@ FindClkHpinDisables::makeClkHpinDisables(const Pin *clk_src, bool FindClkHpinDisables::drvrLoadExists(const Pin *drvr, - const Pin *load) + const Pin *load) { - PinPair probe(drvr, load); - return drvr_loads_.hasKey(probe); + return drvr_loads_.contains({drvr, load}); } void Sdc::ensureClkHpinDisables() { if (!clk_hpin_disables_valid_) { - clk_hpin_disables_.deleteContentsClear(); + deleteContents(clk_hpin_disables_); for (auto clk : clocks_) { for (const Pin *src : clk->pins()) { - if (network_->isHierarchical(src)) { - FindClkHpinDisables visitor1(clk, network_, this); - visitHpinDrvrLoads(src, network_, &visitor1); - PinSeq loads, drvrs; - PinSet visited_drvrs(network_); - FindNetDrvrLoads visitor2(nullptr, visited_drvrs, loads, drvrs, network_); - network_->visitConnectedPins(src, visitor2); - - // Disable fanouts from the src driver pins that do - // not go thru the hierarchical src pin. - for (const Pin *drvr : drvrs) { - for (const Pin *load : loads) { - if (!visitor1.drvrLoadExists(drvr, load)) - makeClkHpinDisable(clk, drvr, load); - } - } - } + if (network_->isHierarchical(src)) { + FindClkHpinDisables visitor1(clk, network_, this); + visitHpinDrvrLoads(src, network_, &visitor1); + PinSeq loads, drvrs; + PinSet visited_drvrs(network_); + FindNetDrvrLoads visitor2(nullptr, visited_drvrs, loads, drvrs, network_); + network_->visitConnectedPins(src, visitor2); + + // Disable fanouts from the src driver pins that do + // not go thru the hierarchical src pin. + for (const Pin *drvr : drvrs) { + for (const Pin *load : loads) { + if (!visitor1.drvrLoadExists(drvr, load)) + makeClkHpinDisable(clk, drvr, load); + } + } + } } } clk_hpin_disables_valid_ = true; @@ -1565,11 +1534,11 @@ Sdc::ensureClkHpinDisables() void Sdc::makeClkHpinDisable(const Clock *clk, - const Pin *drvr, - const Pin *load) + const Pin *drvr, + const Pin *load) { ClkHpinDisable probe(clk, drvr, load); - if (!clk_hpin_disables_.hasKey(&probe)) { + if (!clk_hpin_disables_.contains(&probe)) { ClkHpinDisable *disable = new ClkHpinDisable(clk, drvr, load); clk_hpin_disables_.insert(disable); } @@ -1587,12 +1556,12 @@ Sdc::clkHpinDisablesInvalid() // Check for disable by hierarchical clock pin between driver and load. bool Sdc::clkDisabledByHpinThru(const Clock *clk, - const Pin *from_pin, - const Pin *to_pin) + const Pin *from_pin, + const Pin *to_pin) const { - if (clk->leafPins().hasKey(from_pin)) { + if (clk->leafPins().contains(from_pin)) { ClkHpinDisable probe(clk, from_pin, to_pin); - return clk_hpin_disables_.hasKey(&probe); + return clk_hpin_disables_.contains(&probe); } else return false; @@ -1627,16 +1596,16 @@ Sdc::removePropagatedClock(Pin *pin) } bool -Sdc::isPropagatedClock(const Pin *pin) +Sdc::isPropagatedClock(const Pin *pin) const { - return propagated_clk_pins_.hasKey(pin); + return propagated_clk_pins_.contains(pin); } void Sdc::setClockSlew(Clock *clk, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew) + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew) { clk->setSlew(rf, min_max, slew); } @@ -1647,20 +1616,53 @@ Sdc::removeClockSlew(Clock *clk) clk->removeSlew(); } +class MakeClkLatencyEdge : public HierPinThruVisitor +{ +public: + MakeClkLatencyEdge(ClockLatency *latency, + EdgeClockLatencyMap &edge_clk_latency_map); + void visit(const Pin *drvr, + const Pin *load) override; + +private: + ClockLatency *latency_; + EdgeClockLatencyMap &edge_clk_latency_map_; +}; + +MakeClkLatencyEdge::MakeClkLatencyEdge(ClockLatency *latency, + EdgeClockLatencyMap &edge_clk_latency_map) : + latency_(latency), + edge_clk_latency_map_(edge_clk_latency_map) +{ +} + +void +MakeClkLatencyEdge::visit(const Pin *drvr, + const Pin *load) +{ + edge_clk_latency_map_[{drvr, load}] = latency_; +} + void Sdc::setClockLatency(Clock *clk, - Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float delay) + Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float delay) { ClockLatency probe(clk, pin); - ClockLatency *latency = clk_latencies_.findKey(&probe); + ClockLatency *latency = findKey(clk_latencies_, &probe); if (latency == nullptr) { latency = new ClockLatency(clk, pin); clk_latencies_.insert(latency); + if (pin && network_->isHierarchical(pin)) { + MakeClkLatencyEdge visitor(latency, edge_clk_latency_map_); + visitDrvrLoadsThruHierPin(pin, network_, &visitor); + } } latency->setDelay(rf, min_max, delay); + if (pin) + clk_latency_pins_.insert(pin); // set_clock_latency removes set_propagated_clock on the same object. if (clk && pin == nullptr) @@ -1669,12 +1671,41 @@ Sdc::setClockLatency(Clock *clk, removePropagatedClock(pin); } +ClockLatency * +Sdc::clockLatency(Edge *edge) const +{ + PinPair pins(edge->from(graph_)->pin(), edge->to(graph_)->pin()); + auto itr = edge_clk_latency_map_.find(pins); + if (itr == edge_clk_latency_map_.end()) + return nullptr; + else + return itr->second; +} + +void +Sdc::clockLatency(Edge *edge, + const RiseFall *rf, + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) const + +{ + ClockLatency *latencies = clockLatency(edge); + if (latencies) + latencies->delay(rf, min_max, latency, exists); + else { + latency = 0.0; + exists = false; + } +} + void Sdc::removeClockLatency(const Clock *clk, - const Pin *pin) + const Pin *pin) { ClockLatency probe(clk, pin); - ClockLatency *latency = clk_latencies_.findKey(&probe); + ClockLatency *latency = findKey(clk_latencies_, &probe); if (latency) deleteClockLatency(latency); } @@ -1704,30 +1735,29 @@ Sdc::deleteClockLatenciesReferencing(Clock *clk) bool Sdc::hasClockLatency(const Pin *pin) const { - ClockLatency probe(nullptr, pin); - return clk_latencies_.hasKey(&probe); + return clk_latency_pins_.contains(pin); } void Sdc::clockLatency(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &latency, - bool &exists) const + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) const { latency = 0.0; exists = false; if (pin && clk) { ClockLatency probe(clk, pin); - ClockLatency *latencies = clk_latencies_.findKey(&probe); + ClockLatency *latencies = findKey(clk_latencies_, &probe); if (latencies) latencies->delay(rf, min_max, latency, exists); } if (!exists) { ClockLatency probe(nullptr, pin); - ClockLatency *latencies = clk_latencies_.findKey(&probe); + ClockLatency *latencies = findKey(clk_latencies_, &probe); if (latencies) latencies->delay(rf, min_max, latency, exists); } @@ -1735,38 +1765,38 @@ Sdc::clockLatency(const Clock *clk, void Sdc::clockLatency(const Clock *clk, - const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &latency, - bool &exists) const + const RiseFall *rf, + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) const { latency = 0.0; exists = false; ClockLatency probe(clk, nullptr); - ClockLatency *latencies = clk_latencies_.findKey(&probe); + ClockLatency *latencies = findKey(clk_latencies_, &probe); if (latencies) latencies->delay(rf, min_max, latency, exists); } float Sdc::clockLatency(const Clock *clk, - const RiseFall *rf, - const MinMax *min_max) const + const RiseFall *rf, + const MinMax *min_max) const { float latency; bool exists; clockLatency(clk, rf, min_max, - latency, exists); + latency, exists); return latency; } void Sdc::setClockUncertainty(Pin *pin, - const SetupHoldAll *setup_hold, - float uncertainty) + const SetupHoldAll *setup_hold, + float uncertainty) { - ClockUncertainties *uncertainties = pin_clk_uncertainty_map_.findKey(pin); + ClockUncertainties *uncertainties = findKey(pin_clk_uncertainty_map_, pin); if (uncertainties == nullptr) { uncertainties = new ClockUncertainties; pin_clk_uncertainty_map_[pin] = uncertainties; @@ -1776,9 +1806,9 @@ Sdc::setClockUncertainty(Pin *pin, void Sdc::removeClockUncertainty(Pin *pin, - const SetupHoldAll *setup_hold) + const SetupHoldAll *setup_hold) { - ClockUncertainties *uncertainties = pin_clk_uncertainty_map_.findKey(pin); + ClockUncertainties *uncertainties = findKey(pin_clk_uncertainty_map_, pin); if (uncertainties) { uncertainties->removeValue(setup_hold); if (uncertainties->empty()) { @@ -1788,19 +1818,19 @@ Sdc::removeClockUncertainty(Pin *pin, } } -ClockUncertainties * -Sdc::clockUncertainties(const Pin *pin) +const ClockUncertainties * +Sdc::clockUncertainties(const Pin *pin) const { - return pin_clk_uncertainty_map_.findKey(pin); + return findKey(pin_clk_uncertainty_map_, pin); } void Sdc::clockUncertainty(const Pin *pin, - const SetupHold *setup_hold, - float &uncertainty, - bool &exists) + const SetupHold *setup_hold, + float &uncertainty, + bool &exists) { - ClockUncertainties *uncertainties = clockUncertainties(pin); + const ClockUncertainties *uncertainties = clockUncertainties(pin); if (uncertainties) uncertainties->value(setup_hold, uncertainty, exists); else { @@ -1811,19 +1841,19 @@ Sdc::clockUncertainty(const Pin *pin, void Sdc::clockUncertainty(const Clock *src_clk, - const RiseFall *src_rf, - const Clock *tgt_clk, - const RiseFall *tgt_rf, - const SetupHold *setup_hold, - float &uncertainty, - bool &exists) + const RiseFall *src_rf, + const Clock *tgt_clk, + const RiseFall *tgt_rf, + const SetupHold *setup_hold, + float &uncertainty, + bool &exists) const { InterClockUncertainty probe(src_clk, tgt_clk); InterClockUncertainty *uncertainties = - inter_clk_uncertainties_.findKey(&probe); + findKey(inter_clk_uncertainties_, &probe); if (uncertainties) uncertainties->uncertainty(src_rf, tgt_rf, setup_hold, - uncertainty, exists); + uncertainty, exists); else { uncertainty = 0.0; exists = false; @@ -1832,15 +1862,15 @@ Sdc::clockUncertainty(const Clock *src_clk, void Sdc::setClockUncertainty(Clock *from_clk, - const RiseFallBoth *from_rf, - Clock *to_clk, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold, - float uncertainty) + const RiseFallBoth *from_rf, + Clock *to_clk, + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold, + float uncertainty) { InterClockUncertainty probe(from_clk, to_clk); InterClockUncertainty *uncertainties = - inter_clk_uncertainties_.findKey(&probe); + findKey(inter_clk_uncertainties_, &probe); if (uncertainties == nullptr) { uncertainties = new InterClockUncertainty(from_clk, to_clk); inter_clk_uncertainties_.insert(uncertainties); @@ -1850,14 +1880,14 @@ Sdc::setClockUncertainty(Clock *from_clk, void Sdc::removeClockUncertainty(Clock *from_clk, - const RiseFallBoth *from_rf, - Clock *to_clk, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold) + const RiseFallBoth *from_rf, + Clock *to_clk, + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold) { InterClockUncertainty probe(from_clk, to_clk); InterClockUncertainty *uncertainties = - inter_clk_uncertainties_.findKey(&probe); + findKey(inter_clk_uncertainties_, &probe); if (uncertainties) { uncertainties->removeUncertainty(from_rf, to_rf, setup_hold); if (uncertainties->empty()) { @@ -1881,7 +1911,7 @@ Sdc::deleteInterClockUncertaintiesReferencing(Clock *clk) iter != inter_clk_uncertainties_.cend(); ) { InterClockUncertainty *uncertainties = *iter; if (uncertainties->src() == clk - || uncertainties->target() == clk) { + || uncertainties->target() == clk) { iter = inter_clk_uncertainties_.erase(iter); delete uncertainties; } @@ -1894,14 +1924,14 @@ Sdc::deleteInterClockUncertaintiesReferencing(Clock *clk) void Sdc::setClockInsertion(const Clock *clk, - const Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - const EarlyLateAll *early_late, - float delay) + const Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay) { ClockInsertion probe(clk, pin); - ClockInsertion *insertion = clk_insertions_.findKey(&probe); + ClockInsertion *insertion = findKey(clk_insertions_, &probe); if (insertion == nullptr) { insertion = new ClockInsertion(clk, pin); clk_insertions_.insert(insertion); @@ -1911,14 +1941,14 @@ Sdc::setClockInsertion(const Clock *clk, void Sdc::setClockInsertion(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late, - float delay) + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + const EarlyLate *early_late, + float delay) { ClockInsertion probe(clk, pin); - ClockInsertion *insertion = clk_insertions_.findKey(&probe); + ClockInsertion *insertion = findKey(clk_insertions_, &probe); if (insertion == nullptr) { insertion = new ClockInsertion(clk, pin); clk_insertions_.insert(insertion); @@ -1928,10 +1958,10 @@ Sdc::setClockInsertion(const Clock *clk, void Sdc::removeClockInsertion(const Clock *clk, - const Pin *pin) + const Pin *pin) { ClockInsertion probe(clk, pin); - ClockInsertion *insertion = clk_insertions_.findKey(&probe); + ClockInsertion *insertion = findKey(clk_insertions_, &probe); if (insertion != nullptr) deleteClockInsertion(insertion); } @@ -1940,7 +1970,7 @@ void Sdc::swapClockInsertions(Sdc *sdc1, Sdc *sdc2) { - swap(sdc1->clk_insertions_, sdc2->clk_insertions_); + std::swap(sdc1->clk_insertions_, sdc2->clk_insertions_); } void @@ -1967,9 +1997,9 @@ Sdc::deleteClockInsertionsReferencing(Clock *clk) float Sdc::clockInsertion(const Clock *clk, - const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late) const + const RiseFall *rf, + const MinMax *min_max, + const EarlyLate *early_late) const { float insertion; bool exists; @@ -1981,31 +2011,31 @@ bool Sdc::hasClockInsertion(const Pin *pin) const { ClockInsertion probe(nullptr, pin); - return clk_insertions_.hasKey(&probe); + return clk_insertions_.contains(&probe); } void Sdc::clockInsertion(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late, - // Return values. - float &insertion, - bool &exists) const + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + const EarlyLate *early_late, + // Return values. + float &insertion, + bool &exists) const { ClockInsertion *insert = nullptr; if (clk && pin) { ClockInsertion probe(clk, pin); - insert = clk_insertions_.findKey(&probe); + insert = findKey(clk_insertions_, &probe); } if (insert == nullptr && pin) { ClockInsertion probe(nullptr, pin); - insert = clk_insertions_.findKey(&probe); + insert = findKey(clk_insertions_, &probe); } if (insert == nullptr && clk) { ClockInsertion probe(clk, nullptr); - insert = clk_insertions_.findKey(&probe); + insert = findKey(clk_insertions_, &probe); } if (insert) @@ -2063,47 +2093,46 @@ ClockInsertionkLess::operator()(const ClockInsertion *insert1, //////////////////////////////////////////////////////////////// ClockGroups * -Sdc::makeClockGroups(const char *name, - bool logically_exclusive, - bool physically_exclusive, - bool asynchronous, - bool allow_paths, - const char *comment) -{ - char *gen_name = nullptr; - if (name == nullptr - || name[0] == '\0') - name = gen_name = makeClockGroupsName(); +Sdc::makeClockGroups(std::string_view name, + bool logically_exclusive, + bool physically_exclusive, + bool asynchronous, + bool allow_paths, + std::string_view comment) +{ + std::string group_name; + if (name.empty()) + group_name = makeClockGroupsName(); else { - ClockGroups *groups = clk_groups_name_map_.findKey(name); + group_name = name; + ClockGroups *groups = findStringKey(clk_groups_name_map_, group_name); if (groups) removeClockGroups(groups); } - ClockGroups *groups = new ClockGroups(name, logically_exclusive, - physically_exclusive, - asynchronous, allow_paths, comment); + ClockGroups *groups = new ClockGroups(group_name, logically_exclusive, + physically_exclusive, + asynchronous, allow_paths, + comment); clk_groups_name_map_[groups->name()] = groups; - stringDelete(gen_name); return groups; } // Generate a name for the clock group. -char * +std::string Sdc::makeClockGroupsName() { - char *name = nullptr; + std::string name; int i = 0; do { i++; - stringDelete(name); - name = stringPrint("group%d", i); - } while (clk_groups_name_map_.hasKey(name)); + name = sta::format("group{}", i); + } while (clk_groups_name_map_.contains(name)); return name; } void Sdc::makeClockGroup(ClockGroups *clk_groups, - ClockSet *clks) + ClockSet *clks) { clk_groups->makeClockGroup(clks); } @@ -2112,7 +2141,7 @@ void Sdc::ensureClkGroupExclusions() { if (clk_group_exclusions_.empty()) { - for (const auto [name, clk_groups] : clk_groups_name_map_) + for (const auto &[name, clk_groups] : clk_groups_name_map_) makeClkGroupExclusions(clk_groups); } } @@ -2121,7 +2150,7 @@ void Sdc::makeClkGroupExclusions(ClockGroups *clk_groups) { if (!(clk_groups->asynchronous() - && clk_groups->allowPaths())) { + && clk_groups->allowPaths())) { ClockGroupSet *groups = clk_groups->groups(); if (groups->size() == 1) makeClkGroupExclusions1(groups); @@ -2135,13 +2164,12 @@ Sdc::makeClkGroupExclusions(ClockGroups *clk_groups) void Sdc::makeClkGroupExclusions1(ClockGroupSet *groups) { - ClockGroupSet::Iterator group_iter1(groups); - ClockGroup *group1 = group_iter1.next(); - for (auto clk1 : *group1) { + ClockGroup *group1 = *groups->begin(); + for (Clock *clk1 : *group1) { for (Clock *clk2 : clocks_) { if (clk2 != clk1 - && !group1->hasKey(clk2)) - clk_group_exclusions_.insert(ClockPair(clk1, clk2)); + && !group1->contains(clk2)) + clk_group_exclusions_.insert(ClockPair(clk1, clk2)); } } makeClkGroupSame(group1); @@ -2153,14 +2181,14 @@ Sdc::makeClkGroupExclusions(ClockGroupSet *groups) for (auto group1 : *groups) { for (auto group2 : *groups) { if (group1 != group2) { - for (auto clk1 : *group1) { - for (auto clk2 : *group2) { - // ClockPair is symmetric so only add one clk1/clk2 pair. - if (clk1->index() < clk2->index()) { - clk_group_exclusions_.insert(ClockPair(clk1, clk2)); - } - } - } + for (auto clk1 : *group1) { + for (auto clk2 : *group2) { + // ClockPair is symmetric so only add one clk1/clk2 pair. + if (clk1->index() < clk2->index()) { + clk_group_exclusions_.insert(ClockPair(clk1, clk2)); + } + } + } } } makeClkGroupSame(group1); @@ -2173,9 +2201,9 @@ Sdc::makeClkGroupSame(ClockGroup *group) for (auto clk1 : *group) { for (auto clk2 : *group) { if (clk1->index() <= clk2->index()) { - ClockPair clk_pair(clk1, clk2); - if (!clk_group_same_.hasKey(clk_pair)) - clk_group_same_.insert(clk_pair); + ClockPair clk_pair(clk1, clk2); + if (!clk_group_same_.contains(clk_pair)) + clk_group_same_.insert(clk_pair); } } } @@ -2190,11 +2218,11 @@ Sdc::clearClkGroupExclusions() bool Sdc::sameClockGroup(const Clock *clk1, - const Clock *clk2) + const Clock *clk2) const { if (clk1 && clk2) { ClockPair clk_pair(clk1, clk2); - bool excluded = clk_group_exclusions_.hasKey(clk_pair); + bool excluded = clk_group_exclusions_.contains(clk_pair); return !excluded; } else @@ -2203,66 +2231,70 @@ Sdc::sameClockGroup(const Clock *clk1, bool Sdc::sameClockGroupExplicit(const Clock *clk1, - const Clock *clk2) + const Clock *clk2) { ClockPair clk_pair(clk1, clk2); - return clk_group_same_.hasKey(clk_pair); + return clk_group_same_.contains(clk_pair); } void -Sdc::removeClockGroups(const char *name) +Sdc::removeClockGroups(const std::string &name) { - ClockGroups *clk_groups = clk_groups_name_map_.findKey(name); + ClockGroups *clk_groups = findKey(clk_groups_name_map_, name); if (clk_groups) removeClockGroups(clk_groups); } void -Sdc::removeClockGroupsLogicallyExclusive(const char *name) +Sdc::removeClockGroupsLogicallyExclusive() { - if (name) { - ClockGroups *groups = clk_groups_name_map_.findKey(name); - if (groups && groups->logicallyExclusive()) + + for (const auto &[name, groups] : clk_groups_name_map_) { + if (groups->logicallyExclusive()) removeClockGroups(groups); } - else { - for (const auto [name, groups] : clk_groups_name_map_) { - if (groups->logicallyExclusive()) - removeClockGroups(groups); - } - } } void -Sdc::removeClockGroupsPhysicallyExclusive(const char *name) +Sdc::removeClockGroupsLogicallyExclusive(const std::string &name) +{ + ClockGroups *groups = findKey(clk_groups_name_map_, name); + if (groups && groups->logicallyExclusive()) + removeClockGroups(groups); +} + +void +Sdc::removeClockGroupsPhysicallyExclusive() { - if (name) { - ClockGroups *groups = clk_groups_name_map_.findKey(name); - if (groups && groups->physicallyExclusive()) + for (const auto &[name, groups] : clk_groups_name_map_) { + if (groups->physicallyExclusive()) removeClockGroups(groups); } - else { - for (const auto [name, groups] : clk_groups_name_map_) { - if (groups->physicallyExclusive()) - removeClockGroups(groups); - } - } } void -Sdc::removeClockGroupsAsynchronous(const char *name) +Sdc::removeClockGroupsPhysicallyExclusive(const std::string &name) { - if (name) { - ClockGroups *groups = clk_groups_name_map_.findKey(name); - if (groups && groups->asynchronous()) + ClockGroups *groups = findKey(clk_groups_name_map_, name); + if (groups && groups->physicallyExclusive()) + removeClockGroups(groups); +} + +void +Sdc::removeClockGroupsAsynchronous() +{ + for (const auto &[name, groups] : clk_groups_name_map_) { + if (groups->asynchronous()) removeClockGroups(groups); } - else { - for (const auto [name, groups] : clk_groups_name_map_) { - if (groups->asynchronous()) - removeClockGroups(groups); - } - } +} + +void +Sdc::removeClockGroupsAsynchronous(const std::string &name) +{ + ClockGroups *groups = findKey(clk_groups_name_map_, name); + if (groups && groups->asynchronous()) + removeClockGroups(groups); } void @@ -2278,7 +2310,7 @@ Sdc::removeClockGroups(ClockGroups *groups) void Sdc::clockGroupsDeleteClkRefs(Clock *clk) { - for (const auto [name, groups] : clk_groups_name_map_) + for (const auto &[name, groups] : clk_groups_name_map_) groups->removeClock(clk); clearClkGroupExclusions(); } @@ -2287,8 +2319,8 @@ Sdc::clockGroupsDeleteClkRefs(Clock *clk) void Sdc::setClockSense(PinSet *pins, - ClockSet *clks, - ClockSense sense) + ClockSet *clks, + ClockSense sense) { if (clks && clks->empty()) { delete clks; @@ -2297,7 +2329,7 @@ Sdc::setClockSense(PinSet *pins, for (const Pin *pin : *pins) { if (clks) { for (const Clock *clk : *clks) - setClockSense(pin, clk, sense); + setClockSense(pin, clk, sense); } else setClockSense(pin, nullptr, sense); @@ -2312,7 +2344,7 @@ Sdc::setClockSense(const Pin *pin, ClockSense sense) { PinClockPair probe(pin, clk); - if (clk_sense_map_.hasKey(probe)) + if (clk_sense_map_.contains(probe)) clk_sense_map_[probe] = sense; else { PinClockPair pin_clk(pin, clk); @@ -2322,15 +2354,15 @@ Sdc::setClockSense(const Pin *pin, bool Sdc::clkStopPropagation(const Pin *pin, - const Clock *clk) const + const Clock *clk) const { PinClockPair pin_clk(pin, clk); ClockSense sense; bool exists; - clk_sense_map_.findKey(pin_clk, sense, exists); + findKeyValue(clk_sense_map_, pin_clk, sense, exists); if (!exists) { PinClockPair pin_clk1(pin, nullptr); - clk_sense_map_.findKey(pin_clk1, sense, exists); + findKeyValue(clk_sense_map_, pin_clk1, sense, exists); } return exists && sense == ClockSense::stop; @@ -2338,32 +2370,32 @@ Sdc::clkStopPropagation(const Pin *pin, bool Sdc::clkStopSense(const Pin *to_pin, - const Clock *clk, - const RiseFall *from_rf, - const RiseFall *to_rf) const + const Clock *clk, + const RiseFall *from_rf, + const RiseFall *to_rf) const { PinClockPair pin_clk(to_pin, clk); ClockSense sense; bool exists; - clk_sense_map_.findKey(pin_clk, sense, exists); + findKeyValue(clk_sense_map_, pin_clk, sense, exists); if (!exists) { PinClockPair pin(to_pin, nullptr); - clk_sense_map_.findKey(pin, sense, exists); + findKeyValue(clk_sense_map_, pin, sense, exists); } return exists && (sense == ClockSense::stop - || (sense == ClockSense::positive - && from_rf != to_rf) - || (sense == ClockSense::negative - && from_rf == to_rf)); + || (sense == ClockSense::positive + && from_rf != to_rf) + || (sense == ClockSense::negative + && from_rf == to_rf)); } bool Sdc::clkStopPropagation(const Clock *clk, - const Pin *from_pin, - const RiseFall *from_rf, - const Pin *to_pin, - const RiseFall *to_rf) const + const Pin *from_pin, + const RiseFall *from_rf, + const Pin *to_pin, + const RiseFall *to_rf) const { return clkStopPropagation(from_pin, clk) || clkStopSense(to_pin, clk, from_rf, to_rf); @@ -2376,25 +2408,23 @@ PinClockPairLess::PinClockPairLess(const Network *network) : bool PinClockPairLess::operator()(const PinClockPair &pin_clk1, - const PinClockPair &pin_clk2) const + const PinClockPair &pin_clk2) const { - const Pin *pin1 = pin_clk1.first; - const Pin *pin2 = pin_clk2.first; - const Clock *clk1 = pin_clk1.second; - const Clock *clk2 = pin_clk2.second; + const auto& [pin1, clk1] = pin_clk1; + const auto& [pin2, clk2] = pin_clk2; return pin1 < pin2 || (pin1 == pin2 - && ((clk1 == nullptr && clk2) - || (clk1 && clk2 - && clk1->index() < clk2->index()))); + && ((clk1 == nullptr && clk2) + || (clk1 && clk2 + && clk1->index() < clk2->index()))); } //////////////////////////////////////////////////////////////// void Sdc::setClockGatingCheck(const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin) + const SetupHold *setup_hold, + float margin) { if (clk_gating_check_ == nullptr) clk_gating_check_ = new ClockGatingCheck; @@ -2403,11 +2433,11 @@ Sdc::setClockGatingCheck(const RiseFallBoth *rf, void Sdc::setClockGatingCheck(Clock *clk, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin) + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin) { - ClockGatingCheck *check = clk_gating_check_map_.findKey(clk); + ClockGatingCheck *check = findKey(clk_gating_check_map_, clk); if (check == nullptr) { check = new ClockGatingCheck(); clk_gating_check_map_[clk] = check; @@ -2417,12 +2447,12 @@ Sdc::setClockGatingCheck(Clock *clk, void Sdc::setClockGatingCheck(Instance *inst, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value) + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value) { - ClockGatingCheck *check = inst_clk_gating_check_map_.findKey(inst); + ClockGatingCheck *check = findKey(inst_clk_gating_check_map_, inst); if (check == nullptr) { check = new ClockGatingCheck(); inst_clk_gating_check_map_[inst] = check; @@ -2433,12 +2463,12 @@ Sdc::setClockGatingCheck(Instance *inst, void Sdc::setClockGatingCheck(const Pin *pin, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value) + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value) { - ClockGatingCheck *check = pin_clk_gating_check_map_.findKey(pin); + ClockGatingCheck *check = findKey(pin_clk_gating_check_map_, pin); if (check == nullptr) { check = new ClockGatingCheck(); pin_clk_gating_check_map_[pin] = check; @@ -2449,11 +2479,11 @@ Sdc::setClockGatingCheck(const Pin *pin, void Sdc::clockGatingMarginEnablePin(const Pin *enable_pin, - const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, float &margin) + const RiseFall *enable_rf, + const SetupHold *setup_hold, + bool &exists, float &margin) const { - ClockGatingCheck *check = pin_clk_gating_check_map_.findKey(enable_pin); + ClockGatingCheck *check = findKey(pin_clk_gating_check_map_, enable_pin); if (check) check->margins()->value(enable_rf, setup_hold, margin, exists); else @@ -2462,12 +2492,12 @@ Sdc::clockGatingMarginEnablePin(const Pin *enable_pin, void Sdc::clockGatingMarginInstance(Instance *inst, - const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, - float &margin) + const RiseFall *enable_rf, + const SetupHold *setup_hold, + bool &exists, + float &margin) const { - ClockGatingCheck *check = inst_clk_gating_check_map_.findKey(inst); + ClockGatingCheck *check = findKey(inst_clk_gating_check_map_, inst); if (check) check->margins()->value(enable_rf, setup_hold, margin, exists); else @@ -2476,12 +2506,12 @@ Sdc::clockGatingMarginInstance(Instance *inst, void Sdc::clockGatingMarginClkPin(const Pin *clk_pin, - const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, - float &margin) + const RiseFall *enable_rf, + const SetupHold *setup_hold, + bool &exists, + float &margin) const { - ClockGatingCheck *check = pin_clk_gating_check_map_.findKey(clk_pin); + ClockGatingCheck *check = findKey(pin_clk_gating_check_map_, clk_pin); if (check) check->margins()->value(enable_rf, setup_hold, margin, exists); else @@ -2490,12 +2520,12 @@ Sdc::clockGatingMarginClkPin(const Pin *clk_pin, void Sdc::clockGatingMarginClk(const Clock *clk, - const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, - float &margin) + const RiseFall *enable_rf, + const SetupHold *setup_hold, + bool &exists, + float &margin) const { - ClockGatingCheck *check = clk_gating_check_map_.findKey(clk); + ClockGatingCheck *check = findKey(clk_gating_check_map_, clk); if (check) check->margins()->value(enable_rf, setup_hold, margin, exists); else @@ -2504,9 +2534,9 @@ Sdc::clockGatingMarginClk(const Clock *clk, void Sdc::clockGatingMargin(const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, - float &margin) + const SetupHold *setup_hold, + bool &exists, + float &margin) const { if (clk_gating_check_) clk_gating_check_->margins()->value(enable_rf, setup_hold, margin, exists); @@ -2516,17 +2546,17 @@ Sdc::clockGatingMargin(const RiseFall *enable_rf, LogicValue Sdc::clockGatingActiveValue(const Pin *clk_pin, - const Pin *enable_pin) + const Pin *enable_pin) const { ClockGatingCheck *check; - check = pin_clk_gating_check_map_.findKey(enable_pin); + check = findKey(pin_clk_gating_check_map_, enable_pin); if (check) return check->activeValue(); Instance *inst = network_->instance(enable_pin); - check = inst_clk_gating_check_map_.findKey(inst); + check = findKey(inst_clk_gating_check_map_, inst); if (check) return check->activeValue(); - check = pin_clk_gating_check_map_.findKey(clk_pin); + check = findKey(pin_clk_gating_check_map_, clk_pin); if (check) return check->activeValue(); return LogicValue::unknown; @@ -2537,7 +2567,7 @@ Sdc::clockGatingActiveValue(const Pin *clk_pin, // Determine cycle accounting "on demand". CycleAccting * Sdc::cycleAccting(const ClockEdge *src, - const ClockEdge *tgt) + const ClockEdge *tgt) { LockGuard lock(cycle_acctings_lock_); return cycle_acctings_.cycleAccting(src, tgt); @@ -2559,29 +2589,29 @@ Sdc::clearCycleAcctings() void Sdc::setDataCheck(Pin *from, - const RiseFallBoth *from_rf, - Pin *to, - const RiseFallBoth *to_rf, - Clock *clk, - const SetupHoldAll *setup_hold, - float margin) + const RiseFallBoth *from_rf, + Pin *to, + const RiseFallBoth *to_rf, + Clock *clk, + const SetupHoldAll *setup_hold, + float margin) { DataCheck *check = nullptr; - DataCheckSet *checks = data_checks_from_map_.findKey(from); + DataCheckSet *checks = findKey(data_checks_from_map_, from); if (checks == nullptr) { checks = new DataCheckSet(DataCheckLess(network_)); data_checks_from_map_[from] = checks; } else { DataCheck probe(from, to, clk); - check = checks->findKey(&probe); + check = findKey(*checks, &probe); } if (check == nullptr) check = new DataCheck(from, to, clk); check->setMargin(from_rf, to_rf, setup_hold, margin); checks->insert(check); - checks = data_checks_to_map_.findKey(to); + checks = findKey(data_checks_to_map_, to); if (checks == nullptr) { checks = new DataCheckSet(DataCheckLess(network_)); data_checks_to_map_[to] = checks; @@ -2591,24 +2621,24 @@ Sdc::setDataCheck(Pin *from, void Sdc::removeDataCheck(Pin *from, - const RiseFallBoth *from_rf, - Pin *to, - const RiseFallBoth *to_rf, - Clock *clk, - const SetupHoldAll *setup_hold) + const RiseFallBoth *from_rf, + Pin *to, + const RiseFallBoth *to_rf, + Clock *clk, + const SetupHoldAll *setup_hold) { DataCheck probe(from, to, clk); - DataCheckSet *checks = data_checks_from_map_.findKey(from); + DataCheckSet *checks = findKey(data_checks_from_map_, from); if (checks) { - DataCheck *check = checks->findKey(&probe); + DataCheck *check = findKey(*checks, &probe); if (check) { check->removeMargin(from_rf, to_rf, setup_hold); if (check->empty()) { - checks->erase(check); - checks = data_checks_to_map_.findKey(to); - if (checks) - checks->erase(check); - delete check; + checks->erase(check); + checks = findKey(data_checks_to_map_, to); + if (checks) + checks->erase(check); + delete check; } } } @@ -2617,27 +2647,27 @@ Sdc::removeDataCheck(Pin *from, DataCheckSet * Sdc::dataChecksFrom(const Pin *from) const { - return data_checks_from_map_.findKey(from); + return findKey(data_checks_from_map_, from); } DataCheckSet * Sdc::dataChecksTo(const Pin *to) const { - return data_checks_to_map_.findKey(to); + return findKey(data_checks_to_map_, to); } //////////////////////////////////////////////////////////////// void Sdc::setLatchBorrowLimit(const Pin *pin, - float limit) + float limit) { pin_latch_borrow_limit_map_[pin] = limit; } void Sdc::setLatchBorrowLimit(const Instance *inst, - float limit) + float limit) { inst_latch_borrow_limit_map_[inst] = limit; } @@ -2657,20 +2687,20 @@ Sdc::deleteLatchBorrowLimitsReferencing(Clock *clk) void Sdc::latchBorrowLimit(const Pin *data_pin, - const Pin *enable_pin, - const Clock *clk, - // Return values. - float &limit, - bool &exists) + const Pin *enable_pin, + const Clock *clk, + // Return values. + float &limit, + bool &exists) { - pin_latch_borrow_limit_map_.findKey(data_pin, limit, exists); + findKeyValue(pin_latch_borrow_limit_map_, data_pin, limit, exists); if (!exists) { - pin_latch_borrow_limit_map_.findKey(enable_pin, limit, exists); + findKeyValue(pin_latch_borrow_limit_map_, enable_pin, limit, exists); if (!exists) { Instance *inst = network_->instance(data_pin); - inst_latch_borrow_limit_map_.findKey(inst, limit, exists); + findKeyValue(inst_latch_borrow_limit_map_, inst, limit, exists); if (!exists) - clk_latch_borrow_limit_map_.findKey(clk, limit, exists); + findKeyValue(clk_latch_borrow_limit_map_, clk, limit, exists); } } } @@ -2679,7 +2709,7 @@ Sdc::latchBorrowLimit(const Pin *data_pin, void Sdc::setMinPulseWidth(const RiseFallBoth *rf, - float min_width) + float min_width) { for (auto rf1 : rf->range()) min_pulse_width_.setValue(rf1, min_width); @@ -2687,10 +2717,10 @@ Sdc::setMinPulseWidth(const RiseFallBoth *rf, void Sdc::setMinPulseWidth(const Pin *pin, - const RiseFallBoth *rf, - float min_width) + const RiseFallBoth *rf, + float min_width) { - RiseFallValues *widths = pin_min_pulse_width_map_.findKey(pin); + RiseFallValues *widths = findKey(pin_min_pulse_width_map_, pin); if (widths == nullptr) { widths = new RiseFallValues; pin_min_pulse_width_map_[pin] = widths; @@ -2701,10 +2731,10 @@ Sdc::setMinPulseWidth(const Pin *pin, void Sdc::setMinPulseWidth(const Instance *inst, - const RiseFallBoth *rf, - float min_width) + const RiseFallBoth *rf, + float min_width) { - RiseFallValues *widths = inst_min_pulse_width_map_.findKey(inst); + RiseFallValues *widths = findKey(inst_min_pulse_width_map_, inst); if (widths == nullptr) { widths = new RiseFallValues; inst_min_pulse_width_map_[inst] = widths; @@ -2715,10 +2745,10 @@ Sdc::setMinPulseWidth(const Instance *inst, void Sdc::setMinPulseWidth(const Clock *clk, - const RiseFallBoth *rf, - float min_width) + const RiseFallBoth *rf, + float min_width) { - RiseFallValues *widths = clk_min_pulse_width_map_.findKey(clk); + RiseFallValues *widths = findKey(clk_min_pulse_width_map_, clk); if (widths == nullptr) { widths = new RiseFallValues; clk_min_pulse_width_map_[clk] = widths; @@ -2729,21 +2759,21 @@ Sdc::setMinPulseWidth(const Clock *clk, void Sdc::minPulseWidth(const Pin *pin, - const Clock *clk, - const RiseFall *hi_low, - float &min_width, - bool &exists) const + const Clock *clk, + const RiseFall *hi_low, + float &min_width, + bool &exists) const { - RiseFallValues *widths = pin_min_pulse_width_map_.findKey(pin); + RiseFallValues *widths = findKey(pin_min_pulse_width_map_, pin); if (widths) widths->value(hi_low, min_width, exists); else { if (pin) { const Instance *inst = network_->instance(pin); - widths = inst_min_pulse_width_map_.findKey(inst); + widths = findKey(inst_min_pulse_width_map_, inst); } if (widths == nullptr) - widths = clk_min_pulse_width_map_.findKey(clk); + widths = findKey(clk_min_pulse_width_map_, clk); if (widths) widths->value(hi_low, min_width, exists); else @@ -2754,7 +2784,7 @@ Sdc::minPulseWidth(const Pin *pin, void Sdc::deleteMinPulseWidthReferencing(Clock *clk) { - RiseFallValues *widths = clk_min_pulse_width_map_.findKey(clk); + RiseFallValues *widths = findKey(clk_min_pulse_width_map_, clk); if (widths) { delete widths; clk_min_pulse_width_map_.erase(clk); @@ -2764,22 +2794,22 @@ Sdc::deleteMinPulseWidthReferencing(Clock *clk) //////////////////////////////////////////////////////////////// InputDrive * -Sdc::findInputDrive(Port *port) +Sdc::findInputDrive(Port *port) const { - return input_drive_map_.findKey(port); + return findKey(input_drive_map_, port); } void Sdc::setInputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const Pin *ref_pin, - bool source_latency_included, - bool network_latency_included, - const MinMaxAll *min_max, - bool add, - float delay) + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, + float delay) { ClockEdge *clk_edge = clk ? clk->edge(clk_rf) : nullptr; InputDelay *input_delay = findInputDelay(pin, clk_edge); @@ -2796,7 +2826,7 @@ Sdc::setInputDelay(const Pin *pin, } if (ref_pin) { - InputDelaySet *ref_inputs = input_delay_ref_pin_map_.findKey(ref_pin); + InputDelaySet *ref_inputs = findKey(input_delay_ref_pin_map_, ref_pin); if (ref_inputs == nullptr) { ref_inputs = new InputDelaySet; input_delay_ref_pin_map_[ref_pin] = ref_inputs; @@ -2811,12 +2841,12 @@ Sdc::setInputDelay(const Pin *pin, InputDelay * Sdc::makeInputDelay(const Pin *pin, - const ClockEdge *clk_edge) + const ClockEdge *clk_edge) { InputDelay *input_delay = new InputDelay(pin, clk_edge, input_delay_index_++, - network_); + network_); input_delays_.insert(input_delay); - InputDelaySet *inputs = input_delay_pin_map_.findKey(pin); + InputDelaySet *inputs = findKey(input_delay_pin_map_, pin); if (inputs == nullptr) { inputs = new InputDelaySet; input_delay_pin_map_[pin] = inputs; @@ -2834,8 +2864,8 @@ Sdc::makeInputDelay(const Pin *pin, if (!network_->isTopLevelPort(lpin)) { InputDelaySet *internal_inputs = input_delay_internal_pin_map_[lpin]; if (internal_inputs == nullptr) { - internal_inputs = new InputDelaySet; - input_delay_internal_pin_map_[pin] = internal_inputs; + internal_inputs = new InputDelaySet; + input_delay_internal_pin_map_[pin] = internal_inputs; } internal_inputs->insert(input_delay); } @@ -2845,13 +2875,13 @@ Sdc::makeInputDelay(const Pin *pin, InputDelay * Sdc::findInputDelay(const Pin *pin, - const ClockEdge *clk_edge) + const ClockEdge *clk_edge) { - InputDelaySet *inputs = input_delay_pin_map_.findKey(pin); + InputDelaySet *inputs = findKey(input_delay_pin_map_, pin); if (inputs) { for (InputDelay *input_delay : *inputs) { if (input_delay->clkEdge() == clk_edge) - return input_delay; + return input_delay; } } return nullptr; @@ -2859,10 +2889,10 @@ Sdc::findInputDelay(const Pin *pin, void Sdc::removeInputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMaxAll *min_max) + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMaxAll *min_max) { ClockEdge *clk_edge = clk ? clk->edge(clk_rf) : nullptr; InputDelay *input_delay = findInputDelay(pin, clk_edge); @@ -2876,50 +2906,56 @@ Sdc::removeInputDelay(const Pin *pin, void Sdc::deleteInputDelays(const Pin *pin, - InputDelay *except) + InputDelay *except) { InputDelaySet *input_delays = input_delay_pin_map_[pin]; - InputDelaySet::Iterator iter(input_delays); - while (iter.hasNext()) { - InputDelay *input_delay = iter.next(); - if (input_delay != except) + for (auto itr = input_delays->begin(); itr != input_delays->end(); /* no incr */) { + InputDelay *input_delay = *itr; + if (input_delay != except) { + itr = input_delays->erase(itr); deleteInputDelay(input_delay); + } + else + itr++; } } InputDelaySet * Sdc::refPinInputDelays(const Pin *ref_pin) const { - return input_delay_ref_pin_map_.findKey(ref_pin); + return findKey(input_delay_ref_pin_map_, ref_pin); } InputDelaySet * -Sdc::inputDelaysLeafPin(const Pin *leaf_pin) +Sdc::inputDelaysLeafPin(const Pin *leaf_pin) const { - return input_delay_leaf_pin_map_.findKey(leaf_pin); + return findKey(input_delay_leaf_pin_map_, leaf_pin); } bool Sdc::hasInputDelay(const Pin *leaf_pin) const { - InputDelaySet *input_delays = input_delay_leaf_pin_map_.findKey(leaf_pin); + InputDelaySet *input_delays = findKey(input_delay_leaf_pin_map_, leaf_pin); return input_delays && !input_delays->empty(); } bool Sdc::isInputDelayInternal(const Pin *pin) const { - return input_delay_internal_pin_map_.hasKey(pin); + return input_delay_internal_pin_map_.contains(pin); } void Sdc::deleteInputDelaysReferencing(const Clock *clk) { - InputDelaySet::Iterator iter(input_delays_); - while (iter.hasNext()) { - InputDelay *input_delay = iter.next(); - if (input_delay->clock() == clk) + for (auto itr = input_delays_.begin(); itr != input_delays_.end(); ) { + InputDelay *input_delay = *itr; + if (input_delay->clock() == clk) { + itr = input_delays_.erase(itr); deleteInputDelay(input_delay); + } + else + itr++; } } @@ -2944,32 +2980,32 @@ void Sdc::swapPortDelays(Sdc *sdc1, Sdc *sdc2) { - swap(sdc1->input_delays_, sdc2->input_delays_); - swap(sdc1->input_delay_pin_map_, sdc2->input_delay_pin_map_); - swap(sdc1->input_delay_ref_pin_map_, sdc2->input_delay_ref_pin_map_); - swap(sdc1->input_delay_leaf_pin_map_, sdc2->input_delay_leaf_pin_map_); - swap(sdc1->input_delay_internal_pin_map_, sdc2->input_delay_internal_pin_map_); - swap(sdc1->input_delay_index_, sdc2->input_delay_index_); + std::swap(sdc1->input_delays_, sdc2->input_delays_); + std::swap(sdc1->input_delay_pin_map_, sdc2->input_delay_pin_map_); + std::swap(sdc1->input_delay_ref_pin_map_, sdc2->input_delay_ref_pin_map_); + std::swap(sdc1->input_delay_leaf_pin_map_, sdc2->input_delay_leaf_pin_map_); + std::swap(sdc1->input_delay_internal_pin_map_, sdc2->input_delay_internal_pin_map_); + std::swap(sdc1->input_delay_index_, sdc2->input_delay_index_); - swap(sdc1->output_delays_, sdc2->output_delays_); - swap(sdc1->output_delay_pin_map_, sdc2->output_delay_pin_map_); - swap(sdc1->output_delay_ref_pin_map_, sdc2->output_delay_ref_pin_map_); - swap(sdc1->output_delay_leaf_pin_map_, sdc2->output_delay_leaf_pin_map_); + std::swap(sdc1->output_delays_, sdc2->output_delays_); + std::swap(sdc1->output_delay_pin_map_, sdc2->output_delay_pin_map_); + std::swap(sdc1->output_delay_ref_pin_map_, sdc2->output_delay_ref_pin_map_); + std::swap(sdc1->output_delay_leaf_pin_map_, sdc2->output_delay_leaf_pin_map_); } //////////////////////////////////////////////////////////////// void Sdc::setOutputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const Pin *ref_pin, - bool source_latency_included, - bool network_latency_included, - const MinMaxAll *min_max, - bool add, - float delay) + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, + float delay) { ClockEdge *clk_edge = clk ? clk->edge(clk_rf) : nullptr; OutputDelay *output_delay = findOutputDelay(pin, clk_edge); @@ -2986,7 +3022,7 @@ Sdc::setOutputDelay(const Pin *pin, } if (ref_pin) { - OutputDelaySet *ref_outputs = output_delay_ref_pin_map_.findKey(ref_pin); + OutputDelaySet *ref_outputs = findKey(output_delay_ref_pin_map_, ref_pin); if (ref_outputs == nullptr) { ref_outputs = new OutputDelaySet; output_delay_ref_pin_map_[ref_pin] = ref_outputs; @@ -3001,13 +3037,13 @@ Sdc::setOutputDelay(const Pin *pin, OutputDelay * Sdc::findOutputDelay(const Pin *pin, - const ClockEdge *clk_edge) + const ClockEdge *clk_edge) { - OutputDelaySet *outputs = output_delay_pin_map_.findKey(pin); + OutputDelaySet *outputs = findKey(output_delay_pin_map_, pin); if (outputs) { for (OutputDelay *output_delay : *outputs) { if (output_delay->clkEdge() == clk_edge) - return output_delay; + return output_delay; } } return nullptr; @@ -3015,11 +3051,11 @@ Sdc::findOutputDelay(const Pin *pin, OutputDelay * Sdc::makeOutputDelay(const Pin *pin, - const ClockEdge *clk_edge) + const ClockEdge *clk_edge) { OutputDelay *output_delay = new OutputDelay(pin, clk_edge, network_); output_delays_.insert(output_delay); - OutputDelaySet *outputs = output_delay_pin_map_.findKey(pin); + OutputDelaySet *outputs = findKey(output_delay_pin_map_, pin); if (outputs == nullptr) { outputs = new OutputDelaySet; output_delay_pin_map_[pin] = outputs; @@ -3039,10 +3075,10 @@ Sdc::makeOutputDelay(const Pin *pin, void Sdc::removeOutputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMaxAll *min_max) + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMaxAll *min_max) { ClockEdge *clk_edge = clk ? clk->edge(clk_rf) : nullptr; OutputDelay *output_delay = findOutputDelay(pin, clk_edge); @@ -3054,37 +3090,43 @@ Sdc::removeOutputDelay(const Pin *pin, void Sdc::deleteOutputDelays(const Pin *pin, - OutputDelay *except) + OutputDelay *except) { OutputDelaySet *output_delays = output_delay_pin_map_[pin]; - OutputDelaySet::Iterator iter(output_delays); - while (iter.hasNext()) { - OutputDelay *output_delay = iter.next(); - if (output_delay != except) + for (auto itr = output_delays->begin(); itr != output_delays->end(); ) { + OutputDelay *output_delay = *itr; + if (output_delay != except) { + itr = output_delays->erase(itr); deleteOutputDelay(output_delay); + } + else + itr++; } } OutputDelaySet * -Sdc::outputDelaysLeafPin(const Pin *leaf_pin) +Sdc::outputDelaysLeafPin(const Pin *leaf_pin) const { - return output_delay_leaf_pin_map_.findKey(leaf_pin); + return findKey(output_delay_leaf_pin_map_, leaf_pin); } bool Sdc::hasOutputDelay(const Pin *leaf_pin) const { - return output_delay_leaf_pin_map_.hasKey(leaf_pin); + return output_delay_leaf_pin_map_.contains(leaf_pin); } void Sdc::deleteOutputDelaysReferencing(const Clock *clk) { - OutputDelaySet::Iterator iter(output_delays_); - while (iter.hasNext()) { - OutputDelay *output_delay = iter.next(); - if (output_delay->clock() == clk) + for (auto itr = output_delays_.begin(); itr != output_delays_.end(); ) { + OutputDelay *output_delay = *itr; + if (output_delay->clock() == clk) { + itr = output_delays_.erase(itr); deleteOutputDelay(output_delay); + } + else + itr++; } } @@ -3109,64 +3151,54 @@ Sdc::deleteOutputDelay(OutputDelay *output_delay) void Sdc::setPortExtPinCap(const Port *port, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float cap) + const RiseFall *rf, + const MinMax *min_max, + float cap) { - PortExtCap *port_cap = ensurePortExtPinCap(port, corner); - port_cap->setPinCap(cap, rf, min_max); + PortExtCap &port_cap = port_ext_cap_map_[port]; + port_cap.setPinCap(port, cap, rf, min_max); } void Sdc::setPortExtWireCap(const Port *port, - bool subtract_pin_cap, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float cap) + const RiseFall *rf, + const MinMax *min_max, + float cap) { - PortExtCap *port_cap = ensurePortExtPinCap(port, corner); - if (subtract_pin_cap) { - Pin *pin = network_->findPin(network_->name(port)); - cap -= connectedPinCap(pin, rf, corner, min_max); - if (cap < 0.0) - cap = 0.0; - } - port_cap->setWireCap(cap, rf, min_max); + PortExtCap &port_cap = port_ext_cap_map_[port]; + port_cap.setWireCap(port, cap, rf, min_max); } -PortExtCap * -Sdc::portExtCap(const Port *port, - const Corner *corner) const +const PortExtCap * +Sdc::portExtCap(const Port *port) const { - return port_ext_cap_maps_[corner->index()].findKey(port); + auto itr = port_ext_cap_map_.find(port); + if (itr != port_ext_cap_map_.end()) + return &itr->second; + else + return nullptr; } bool Sdc::hasPortExtCap(const Port *port) const { - for (int corner_index = 0; corner_index < corners_->count(); corner_index++) { - if (port_ext_cap_maps_[corner_index].hasKey(port)) - return true; - } - return false; + auto itr = port_ext_cap_map_.find(port); + return itr != port_ext_cap_map_.end(); } void Sdc::portExtCap(const Port *port, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &pin_cap, - bool &has_pin_cap, - float &wire_cap, - bool &has_wire_cap, - int &fanout, - bool &has_fanout) const -{ - PortExtCap *port_cap = port_ext_cap_maps_[corner->index()].findKey(port); + const RiseFall *rf, + const MinMax *min_max, + // Return values. + float &pin_cap, + bool &has_pin_cap, + float &wire_cap, + bool &has_wire_cap, + int &fanout, + bool &has_fanout) const +{ + const PortExtCap *port_cap = portExtCap(port); if (port_cap) { port_cap->pinCap(rf, min_max, pin_cap, has_pin_cap); port_cap->wireCap(rf, min_max, wire_cap, has_wire_cap); @@ -3184,17 +3216,16 @@ Sdc::portExtCap(const Port *port, float Sdc::portExtCap(const Port *port, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max) const + const RiseFall *rf, + const MinMax *min_max) const { float pin_cap, wire_cap; int fanout; bool has_pin_cap, has_wire_cap, has_fanout; - portExtCap(port, rf, corner, min_max, - pin_cap, has_pin_cap, - wire_cap, has_wire_cap, - fanout, has_fanout); + portExtCap(port, rf, min_max, + pin_cap, has_pin_cap, + wire_cap, has_wire_cap, + fanout, has_fanout); float cap = 0.0; if (has_pin_cap) cap += pin_cap; @@ -3204,22 +3235,20 @@ Sdc::portExtCap(const Port *port, } bool -Sdc::drvrPinHasWireCap(const Pin *pin, - const Corner *corner) +Sdc::drvrPinHasWireCap(const Pin *pin) const { - return drvr_pin_wire_cap_maps_[corner->index()].hasKey(pin); + return drvr_pin_wire_cap_map_.contains(pin); } void Sdc::drvrPinWireCap(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &cap, - bool &exists, + const MinMax *min_max, + // Return values. + float &cap, + bool &exists, bool &subtract_pin_cap) const { - NetWireCaps *net_caps = drvr_pin_wire_cap_maps_[corner->index()].findKey(pin); + NetWireCaps *net_caps = findKey(drvr_pin_wire_cap_map_, pin); if (net_caps) { net_caps->value(min_max, cap, exists); subtract_pin_cap = net_caps->subtractPinCap(min_max); @@ -3233,47 +3262,42 @@ Sdc::drvrPinWireCap(const Pin *pin, void Sdc::setNetWireCap(const Net *net, - bool subtract_pin_cap, - const Corner *corner, - const MinMax *min_max, - float wire_cap) + bool subtract_pin_cap, + const MinMax *min_max, + float wire_cap) { - NetWireCaps &net_caps = net_wire_cap_maps_[corner->index()][net]; + NetWireCaps &net_caps = net_wire_cap_map_[net]; net_caps.setValue(min_max, wire_cap); net_caps.setSubtractPinCap(subtract_pin_cap, min_max); for (const Pin *pin : *network_->drivers(net)) - drvr_pin_wire_cap_maps_[corner->index()][pin] = &net_caps; + drvr_pin_wire_cap_map_[pin] = &net_caps; } bool Sdc::hasNetWireCap(const Net *net) const { - for (int i = 0; i < corners_->count(); i++) { - if (net_wire_cap_maps_[i].hasKey(net)) - return true; - } - return false; + return net_wire_cap_map_.contains(net); } //////////////////////////////////////////////////////////////// void Sdc::connectedCap(const Pin *pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &pin_cap, - float &wire_cap, - float &fanout, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, bool &has_net_load) const { - netCaps(pin, rf, corner, min_max, pin_cap, wire_cap, fanout, has_net_load); + netCaps(pin, rf, scene, min_max, pin_cap, wire_cap, fanout, has_net_load); float net_wire_cap; bool subtract_pin_cap; - drvrPinWireCap(pin, corner, min_max, net_wire_cap, has_net_load, subtract_pin_cap); + drvrPinWireCap(pin, min_max, net_wire_cap, has_net_load, subtract_pin_cap); if (subtract_pin_cap) pin_cap = 0.0; if (has_net_load) @@ -3282,14 +3306,14 @@ Sdc::connectedCap(const Pin *pin, float Sdc::connectedPinCap(const Pin *pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max) + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) { float pin_cap, wire_cap, fanout; bool has_net_load; - connectedCap(pin, rf, corner, min_max, - pin_cap, wire_cap, fanout, has_net_load); + connectedCap(pin, rf, scene, min_max, + pin_cap, wire_cap, fanout, has_net_load); return pin_cap; } @@ -3297,18 +3321,18 @@ class FindNetCaps : public PinVisitor { public: FindNetCaps(const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float &pin_cap, - float &wire_cap, - float &fanout, - bool &has_net_load, - const Sdc *sdc); - virtual void operator()(const Pin *pin); + const Scene *scene, + const MinMax *min_max, + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_net_load, + const Sdc *sdc); + void operator()(const Pin *pin) override; protected: const RiseFall *rf_; - const Corner *corner_; + const Scene *scene_; const MinMax *min_max_; float &pin_cap_; float &wire_cap_; @@ -3318,16 +3342,16 @@ class FindNetCaps : public PinVisitor }; FindNetCaps::FindNetCaps(const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float &pin_cap, - float &wire_cap, - float &fanout, - bool &has_net_load, - const Sdc *sdc) : + const Scene *scene, + const MinMax *min_max, + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_net_load, + const Sdc *sdc) : PinVisitor(), rf_(rf), - corner_(corner), + scene_(scene), min_max_(min_max), pin_cap_(pin_cap), wire_cap_(wire_cap), @@ -3340,40 +3364,40 @@ FindNetCaps::FindNetCaps(const RiseFall *rf, void FindNetCaps::operator()(const Pin *pin) { - sdc_->pinCaps(pin, rf_, corner_, min_max_, - pin_cap_, wire_cap_, fanout_); + sdc_->pinCaps(pin, rf_, scene_, min_max_, + pin_cap_, wire_cap_, fanout_); } // Capacitances for all pins connected to drvr_pin's net. void Sdc::netCaps(const Pin *drvr_pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &pin_cap, - float &wire_cap, - float &fanout, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, bool &has_net_load) const { pin_cap = 0.0; wire_cap = 0.0; fanout = 0.0; has_net_load = false; - FindNetCaps visitor(rf, corner, min_max, pin_cap, - wire_cap, fanout, has_net_load, this); + FindNetCaps visitor(rf, scene, min_max, pin_cap, + wire_cap, fanout, has_net_load, this); network_->visitConnectedPins(drvr_pin, visitor); } void Sdc::pinCaps(const Pin *pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &pin_cap, - float &wire_cap, - float &fanout) const + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout) const { if (network_->isTopLevelPort(pin)) { Port *port = network_->port(pin); @@ -3381,17 +3405,17 @@ Sdc::pinCaps(const Pin *pin, float port_pin_cap, port_wire_cap; int port_fanout; bool has_pin_cap, has_wire_cap, has_fanout; - portExtCap(port, rf, corner, min_max, - port_pin_cap, has_pin_cap, - port_wire_cap, has_wire_cap, - port_fanout, has_fanout); + portExtCap(port, rf, min_max, + port_pin_cap, has_pin_cap, + port_wire_cap, has_wire_cap, + port_fanout, has_fanout); if (has_pin_cap) pin_cap += port_pin_cap; if (has_wire_cap) wire_cap += port_wire_cap; if (is_output) { if (has_fanout) - fanout += port_fanout; + fanout += port_fanout; // Output port counts as a fanout. fanout++; } @@ -3400,38 +3424,40 @@ Sdc::pinCaps(const Pin *pin, LibertyPort *port = network_->libertyPort(pin); if (port) { Instance *inst = network_->instance(pin); - pin_cap += portCapacitance(inst, port, rf, corner, min_max); + pin_cap += portCapacitance(inst, port, rf, scene, min_max); if (port->direction()->isAnyInput()) - fanout++; + fanout++; } } } float Sdc::portCapacitance(Instance *inst, - LibertyPort *port, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max) const + LibertyPort *port, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) const { const Pvt *inst_pvt = nullptr; if (inst) inst_pvt = pvt(inst, min_max); - LibertyPort *corner_port = port->cornerPort(corner, min_max); + LibertyPort *scene_port = port->scenePort(scene, min_max); + if (scene_port == nullptr) + scene_port = port; OperatingConditions *op_cond = operatingConditions(min_max); - return corner_port->capacitance(rf, min_max, op_cond, inst_pvt); + return scene_port->capacitance(rf, min_max, op_cond, inst_pvt); } float Sdc::pinCapacitance(const Pin *pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max) + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) const { LibertyPort *port = network_->libertyPort(pin); if (port) { Instance *inst = network_->instance(pin); - return portCapacitance(inst, port, rf, corner, min_max); + return portCapacitance(inst, port, rf, scene, min_max); } else return 0.0; @@ -3441,8 +3467,8 @@ Sdc::pinCapacitance(const Pin *pin, void Sdc::setResistance(const Net *net, - const MinMaxAll *min_max, - float res) + const MinMaxAll *min_max, + float res) { MinMaxFloatValues &values = net_res_map_[net]; values.setValue(min_max, res); @@ -3450,36 +3476,34 @@ Sdc::setResistance(const Net *net, void Sdc::resistance(const Net *net, - const MinMax *min_max, - float &res, - bool &exists) + const MinMax *min_max, + float &res, + bool &exists) const { res = 0.0; MinMaxFloatValues values; - net_res_map_.findKey(net, values, exists); + findKeyValue(net_res_map_, net, values, exists); if (exists) values.value(min_max, res, exists); } void Sdc::setPortExtFanout(const Port *port, - const Corner *corner, - const MinMax *min_max, - int fanout) + const MinMax *min_max, + int fanout) { - PortExtCap *port_cap = ensurePortExtPinCap(port, corner); - port_cap->setFanout(fanout, min_max); + PortExtCap &port_cap = port_ext_cap_map_[port]; + port_cap.setFanout(port, fanout, min_max); } void Sdc::portExtFanout(const Port *port, - const Corner *corner, - const MinMax *min_max, - // Return values. - int &fanout, - bool &exists) + const MinMax *min_max, + // Return values. + int &fanout, + bool &exists) const { - PortExtCap *port_cap = portExtCap(port, corner); + const PortExtCap *port_cap = portExtCap(port); if (port_cap) port_cap->fanout(min_max, fanout, exists); else { @@ -3490,95 +3514,62 @@ Sdc::portExtFanout(const Port *port, int Sdc::portExtFanout(Port *port, - const Corner *corner, - const MinMax *min_max) + const MinMax *min_max) const { int fanout; bool exists; - portExtFanout(port, corner, min_max, fanout, exists); + portExtFanout(port, min_max, fanout, exists); if (exists) return fanout; else return 0.0; } -PortExtCap * -Sdc::ensurePortExtPinCap(const Port *port, - const Corner *corner) -{ - PortExtCap *port_cap = port_ext_cap_maps_[corner->index()].findKey(port); - if (port_cap == nullptr) { - port_cap = new PortExtCap(port); - port_ext_cap_maps_[corner->index()][port] = port_cap; - } - return port_cap; -} - void Sdc::swapPortExtCaps(Sdc *sdc1, Sdc *sdc2) { - for (int corner_index = 0; corner_index < sdc1->corners()->count(); corner_index++) { - swap(sdc1->port_ext_cap_maps_[corner_index], sdc2->port_ext_cap_maps_[corner_index]); - swap(sdc1->net_wire_cap_maps_[corner_index], sdc2->net_wire_cap_maps_[corner_index]); - } + std::swap(sdc1->port_ext_cap_map_, sdc2->port_ext_cap_map_); + std::swap(sdc1->net_wire_cap_map_, sdc2->net_wire_cap_map_); } //////////////////////////////////////////////////////////////// void Sdc::disable(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to) { - DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell); + DisabledCellPorts *disabled_cell = findKey(disabled_cell_ports_, cell); if (disabled_cell == nullptr) { disabled_cell = new DisabledCellPorts(cell); disabled_cell_ports_[cell] = disabled_cell; } - if (from && to) { + if (from && to) disabled_cell->setDisabledFromTo(from, to); - for (TimingArcSet *arc_set : cell->timingArcSets(from, to)) - arc_set->setIsDisabledConstraint(true); - } - else if (from) { + else if (from) disabled_cell->setDisabledFrom(from); - from->setIsDisabledConstraint(true); - } - else if (to) { + else if (to) disabled_cell->setDisabledTo(to); - to->setIsDisabledConstraint(true); - } - else { + else disabled_cell->setDisabledAll(); - cell->setIsDisabledConstraint(true); - } } void Sdc::removeDisable(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to) { - DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell); + DisabledCellPorts *disabled_cell = findKey(disabled_cell_ports_, cell); if (disabled_cell) { - if (from && to) { + if (from && to) disabled_cell->removeDisabledFromTo(from, to); - for (TimingArcSet *arc_set : cell->timingArcSets(from, to)) - arc_set->setIsDisabledConstraint(false); - } - else if (from) { + else if (from) disabled_cell->removeDisabledFrom(from); - from->setIsDisabledConstraint(false); - } - else if (to) { + else if (to) disabled_cell->removeDisabledTo(to); - to->setIsDisabledConstraint(false); - } - else { + else disabled_cell->removeDisabledAll(); - cell->setIsDisabledConstraint(false); - } } } @@ -3586,38 +3577,33 @@ void Sdc::disable(TimingArcSet *arc_set) { LibertyCell *cell = arc_set->libertyCell(); - DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell); + DisabledCellPorts *disabled_cell = findKey(disabled_cell_ports_, cell); if (disabled_cell == nullptr) { disabled_cell = new DisabledCellPorts(cell); disabled_cell_ports_[cell] = disabled_cell; } disabled_cell->setDisabled(arc_set); - arc_set->setIsDisabledConstraint(true); } void Sdc::removeDisable(TimingArcSet *arc_set) { LibertyCell *cell = arc_set->libertyCell(); - DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell); - if (disabled_cell) { + DisabledCellPorts *disabled_cell = findKey(disabled_cell_ports_, cell); + if (disabled_cell) disabled_cell->removeDisabled(arc_set); - arc_set->setIsDisabledConstraint(false); - } } void Sdc::disable(LibertyPort *port) { disabled_lib_ports_.insert(port); - port->setIsDisabledConstraint(true); } void Sdc::removeDisable(LibertyPort *port) { disabled_lib_ports_.erase(port); - port->setIsDisabledConstraint(false); } void @@ -3634,10 +3620,10 @@ Sdc::removeDisable(Port *port) void Sdc::disable(Instance *inst, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to) { - DisabledInstancePorts *disabled_inst = disabled_inst_ports_.findKey(inst); + DisabledInstancePorts *disabled_inst = findKey(disabled_inst_ports_, inst); if (disabled_inst == nullptr) { disabled_inst = new DisabledInstancePorts(inst); disabled_inst_ports_[inst] = disabled_inst; @@ -3654,10 +3640,10 @@ Sdc::disable(Instance *inst, void Sdc::removeDisable(Instance *inst, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to) { - DisabledInstancePorts *disabled_inst = disabled_inst_ports_.findKey(inst); + DisabledInstancePorts *disabled_inst = findKey(disabled_inst_ports_, inst); if (disabled_inst) { if (from && to) disabled_inst->removeDisabledFromTo(from, to); @@ -3671,58 +3657,72 @@ Sdc::removeDisable(Instance *inst, } void -Sdc::disable(Pin *from, - Pin *to) +Sdc::disableWire(const Pin *from, + const Pin *to) { - PinPair pair(from, to); - disabled_wire_edges_.insert(pair); + disabled_wire_edges_.insert({from, to}); } void -Sdc::removeDisable(Pin *from, - Pin *to) +Sdc::removeDisableWire(Pin *from, + Pin *to) { - PinPair probe(from, to); - disabled_wire_edges_.erase(probe); + disabled_wire_edges_.erase({from, to}); +} + +bool +Sdc::isDisabledWire(const Pin *from, + const Pin *to) const +{ + return disabled_wire_edges_.contains({from, to}); } void Sdc::disable(Edge *edge) { disabled_edges_.insert(edge); - edge->setIsDisabledConstraint(true); } void Sdc::removeDisable(Edge *edge) { disabled_edges_.erase(edge); - edge->setIsDisabledConstraint(false); } bool -Sdc::isDisabled(Edge *edge) +Sdc::isDisabled(const Edge *edge) const { - return disabled_edges_.hasKey(edge); + return disabled_edges_.contains(const_cast(edge)); +} + +bool +Sdc::isDisabledConstraint(const Edge *edge) const +{ + Pin *from_pin = edge->from(graph_)->pin(); + Pin *to_pin = edge->to(graph_)->pin(); + const Instance *inst = network_->instance(from_pin); + TimingArcSet *arc_set = edge->timingArcSet(); + return isDisabled(inst, from_pin, to_pin, edge->role()) + || isDisabled(edge) + || isDisabledWire(from_pin, to_pin) + || isDisabled(arc_set); } class DisableEdgesThruHierPin : public HierPinThruVisitor { public: DisableEdgesThruHierPin(PinPairSet *pairs, - Graph *graph); + Graph *graph); + void visit(const Pin *drvr, + const Pin *load) override; protected: - virtual void visit(const Pin *drvr, - const Pin *load); - PinPairSet *pairs_; Graph *graph_; }; DisableEdgesThruHierPin::DisableEdgesThruHierPin(PinPairSet *pairs, - Graph *graph) : - HierPinThruVisitor(), + Graph *graph) : pairs_(pairs), graph_(graph) { @@ -3730,10 +3730,9 @@ DisableEdgesThruHierPin::DisableEdgesThruHierPin(PinPairSet *pairs, void DisableEdgesThruHierPin::visit(const Pin *drvr, - const Pin *load) + const Pin *load) { - PinPair pair(drvr, load); - pairs_->insert(pair); + pairs_->insert({drvr, load}); } void @@ -3752,19 +3751,17 @@ class RemoveDisableEdgesThruHierPin : public HierPinThruVisitor { public: RemoveDisableEdgesThruHierPin(PinPairSet *pairs, - Graph *graph); + Graph *graph); + void visit(const Pin *drvr, + const Pin *load) override; protected: - virtual void visit(const Pin *drvr, - const Pin *load); - PinPairSet *pairs_; Graph *graph_; }; RemoveDisableEdgesThruHierPin::RemoveDisableEdgesThruHierPin(PinPairSet *pairs, - Graph *graph) : - HierPinThruVisitor(), + Graph *graph) : pairs_(pairs), graph_(graph) { @@ -3772,10 +3769,9 @@ RemoveDisableEdgesThruHierPin::RemoveDisableEdgesThruHierPin(PinPairSet *pairs, void RemoveDisableEdgesThruHierPin::visit(const Pin *drvr, - const Pin *load) + const Pin *load) { - PinPair pair(drvr, load); - pairs_->erase(pair); + pairs_->erase({drvr, load}); } void @@ -3791,36 +3787,35 @@ Sdc::removeDisable(Pin *pin) } bool -Sdc::isDisabled(const Pin *pin) const +Sdc::isDisabledConstraint(const Pin *pin) const { Port *port = network_->port(pin); LibertyPort *lib_port = network_->libertyPort(pin); - return disabled_pins_.hasKey(pin) - || disabled_ports_.hasKey(port) - || disabled_lib_ports_.hasKey(lib_port); + return disabled_pins_.contains(pin) + || disabled_ports_.contains(port) + || disabled_lib_ports_.contains(lib_port); } bool Sdc::isDisabled(const Instance *inst, - const Pin *from_pin, - const Pin *to_pin, - const TimingRole *role) const + const Pin *from_pin, + const Pin *to_pin, + const TimingRole *role) const { if (role == TimingRole::wire()) { // Hierarchical thru pin disables. - PinPair pair(from_pin, to_pin); - return disabled_wire_edges_.hasKey(pair); + return disabled_wire_edges_.contains({from_pin, to_pin}); } else { LibertyCell *cell = network_->libertyCell(inst); LibertyPort *from_port = network_->libertyPort(from_pin); LibertyPort *to_port = network_->libertyPort(to_pin); - DisabledInstancePorts *disabled_inst = disabled_inst_ports_.findKey(inst); - DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell); + DisabledInstancePorts *disabled_inst = findKey(disabled_inst_ports_, inst); + DisabledCellPorts *disabled_cell = findKey(disabled_cell_ports_, cell); return (disabled_inst - && disabled_inst->isDisabled(from_port, to_port, role)) + && disabled_inst->isDisabled(from_port, to_port, role)) || (disabled_cell - && disabled_cell->isDisabled(from_port, to_port, role)); + && disabled_cell->isDisabled(from_port, to_port, role)); } } @@ -3829,7 +3824,7 @@ Sdc::isDisabled(TimingArcSet *arc_set) const { LibertyCell *cell = arc_set->libertyCell(); if (cell) { - DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell); + DisabledCellPorts *disabled_cell = findKey(disabled_cell_ports_, cell); return disabled_cell && disabled_cell->isDisabled(arc_set); } @@ -3843,12 +3838,26 @@ Sdc::disabledInstancePorts() const return &disabled_inst_ports_; } -DisabledCellPortsMap * -Sdc::disabledCellPorts() +const DisabledCellPortsMap * +Sdc::disabledCellPorts() const { return &disabled_cell_ports_; } +//////////////////////////////////////////////////////////////// + +bool +Sdc::isConstrainedEnd(const Pin *pin) const +{ + // All output pins are considered constrained because + // they may be downstream from a set_min/max_delay -from that + // does not have a set_output_delay. + return (network_->isTopLevelPort(pin) + && network_->direction(pin)->isAnyOutput()) + || output_delay_leaf_pin_map_.contains(pin) + || data_checks_to_map_.contains(pin); +} + void Sdc::disableClockGatingCheck(Instance *inst) { @@ -3886,24 +3895,24 @@ Sdc::removeDisableClockGatingCheck(LibertyCell *cell) } bool -Sdc::isDisableClockGatingCheck(const Instance *inst) +Sdc::isDisableClockGatingCheck(const Instance *inst) const { - if (disabled_clk_gating_checks_inst_.hasKey(inst)) + if (disabled_clk_gating_checks_inst_.count(inst)) return true; LibertyCell *cell = network_->libertyCell(inst); - return cell && disabled_clk_gating_checks_lib_cell_.hasKey(cell); + return cell && disabled_clk_gating_checks_lib_cell_.count(cell); } bool -Sdc::isDisableClockGatingCheck(const Pin *pin) +Sdc::isDisableClockGatingCheck(const Pin *pin) const { - return disabled_clk_gating_checks_pin_.hasKey(pin); + return disabled_clk_gating_checks_pin_.contains(pin); } bool -Sdc::isDisableClockGatingCheck(const LibertyCell *cell) +Sdc::isDisableClockGatingCheck(const LibertyCell *cell) const { - return disabled_clk_gating_checks_lib_cell_.hasKey(const_cast(cell)); + return disabled_clk_gating_checks_lib_cell_.count(const_cast(cell)); } //////////////////////////////////////////////////////////////// @@ -3917,15 +3926,15 @@ Sdc::setLogicValue(const Pin *pin, void Sdc::logicValue(const Pin *pin, - LogicValue &value, - bool &exists) + LogicValue &value, + bool &exists) const { - logic_value_map_.findKey(pin, value, exists); + findKeyValue(logic_value_map_, pin, value, exists); } void Sdc::setCaseAnalysis(const Pin *pin, - LogicValue value) + LogicValue value) { case_value_map_[pin] = value; } @@ -3938,26 +3947,26 @@ Sdc::removeCaseAnalysis(const Pin *pin) void Sdc::caseLogicValue(const Pin *pin, - LogicValue &value, - bool &exists) + LogicValue &value, + bool &exists) const { - case_value_map_.findKey(pin, value, exists); + findKeyValue(case_value_map_, pin, value, exists); } bool -Sdc::hasLogicValue(const Pin *pin) +Sdc::hasLogicValue(const Pin *pin) const { - return case_value_map_.hasKey(pin) - || logic_value_map_.hasKey(pin); + return case_value_map_.contains(pin) + || logic_value_map_.contains(pin); } //////////////////////////////////////////////////////////////// ExceptionFrom * Sdc::makeExceptionFrom(PinSet *from_pins, - ClockSet *from_clks, - InstanceSet *from_insts, - const RiseFallBoth *from_rf) + ClockSet *from_clks, + InstanceSet *from_insts, + const RiseFallBoth *from_rf) const { if ((from_pins && !from_pins->empty()) || (from_clks && !from_clks->empty()) @@ -3986,9 +3995,9 @@ Sdc::isExceptionStartpoint(const Pin *pin) const ExceptionThru * Sdc::makeExceptionThru(PinSet *pins, - NetSet *nets, - InstanceSet *insts, - const RiseFallBoth *rf) + NetSet *nets, + InstanceSet *insts, + const RiseFallBoth *rf) const { if ((pins && !pins->empty()) || (nets && !nets->empty()) @@ -4000,10 +4009,10 @@ Sdc::makeExceptionThru(PinSet *pins, ExceptionTo * Sdc::makeExceptionTo(PinSet *pins, - ClockSet *clks, - InstanceSet *insts, - const RiseFallBoth *rf, - const RiseFallBoth *end_rf) + ClockSet *clks, + InstanceSet *insts, + const RiseFallBoth *rf, + const RiseFallBoth *end_rf) const { if ((pins && !pins->empty()) || (clks && !clks->empty()) @@ -4018,7 +4027,7 @@ Sdc::makeExceptionTo(PinSet *pins, // Valid endpoints include gated clock enables which are not // known until clock arrivals are determined. bool -Sdc::isExceptionEndpoint(const Pin *pin) +Sdc::isExceptionEndpoint(const Pin *pin) const { Net *net = network_->net(pin); bool has_checks = false; @@ -4027,9 +4036,9 @@ Sdc::isExceptionEndpoint(const Pin *pin) // Look for timing checks to the pin witihout using the graph because // it may not exist. LibertyCell *cell = port->libertyCell(); - for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, port)) { + for (TimingArcSet *arc_set : cell->timingArcSetsTo(port)) { if (arc_set->role()->isTimingCheck()) { - has_checks = true; + has_checks = true; break; } } @@ -4045,49 +4054,51 @@ Sdc::isExceptionEndpoint(const Pin *pin) && !network_->isHierarchical(pin); } +//////////////////////////////////////////////////////////////// + void Sdc::makeFalsePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - const char *comment) + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + std::string_view comment) { checkFromThrusTo(from, thrus, to); FalsePath *exception = new FalsePath(from, thrus, to, min_max, true, - comment); + comment); addException(exception); } void Sdc::makeMulticyclePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool use_end_clk, - int path_multiplier, - const char *comment) + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, + std::string_view comment) { checkFromThrusTo(from, thrus, to); MultiCyclePath *exception = new MultiCyclePath(from, thrus, to, - min_max, use_end_clk, - path_multiplier, true, - comment); + min_max, use_end_clk, + path_multiplier, true, + comment); addException(exception); } void Sdc::makePathDelay(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMax *min_max, - bool ignore_clk_latency, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMax *min_max, + bool ignore_clk_latency, bool break_path, - float delay, - const char *comment) + float delay, + std::string_view comment) { checkFromThrusTo(from, thrus, to); PathDelay *exception = new PathDelay(from, thrus, to, min_max, - ignore_clk_latency, break_path, + ignore_clk_latency, break_path, delay, true, comment); addException(exception); } @@ -4100,7 +4111,7 @@ Sdc::recordPathDelayInternalFrom(ExceptionPath *exception) && from->hasPins()) { for (const Pin *pin : *from->pins()) { if (!isExceptionStartpoint(pin)) { - path_delay_internal_from_.insert(pin); + path_delay_internal_from_.insert(pin); if (exception->breakPath()) path_delay_internal_from_break_.insert(pin); } @@ -4117,8 +4128,8 @@ Sdc::unrecordPathDelayInternalFrom(ExceptionPath *exception) && !path_delay_internal_from_.empty()) { for (const Pin *pin : *from->pins()) { if (!isExceptionStartpoint(pin) - && !pathDelayFrom(pin)) { - path_delay_internal_from_.erase(pin); + && !pathDelayFrom(pin)) { + path_delay_internal_from_.erase(pin); if (exception->breakPath()) path_delay_internal_from_break_.erase(pin); } @@ -4126,28 +4137,14 @@ Sdc::unrecordPathDelayInternalFrom(ExceptionPath *exception) } } -template -const ExceptionPathSet * -findExceptions(const UnorderedMap &map, - const OBJ *obj) -{ - const auto itr = map.find(obj); - if (itr != map.end()) - return &itr->second; - else - return nullptr; -} - bool Sdc::pathDelayFrom(const Pin *pin) { - - const ExceptionPathSet *exceptions = - findExceptions(first_from_pin_exceptions_, pin); + const ExceptionPathSet *exceptions = findKeyValuePtr(first_from_pin_exceptions_, pin); if (exceptions) { for (ExceptionPath *exception : *exceptions) { if (exception->isPathDelay()) - return true; + return true; } } return false; @@ -4156,13 +4153,13 @@ Sdc::pathDelayFrom(const Pin *pin) bool Sdc::isPathDelayInternalFrom(const Pin *pin) const { - return path_delay_internal_from_.hasKey(pin); + return path_delay_internal_from_.contains(pin); } bool Sdc::isPathDelayInternalFromBreak(const Pin *pin) const { - return path_delay_internal_from_break_.hasKey(pin); + return path_delay_internal_from_break_.contains(pin); } const PinSet & @@ -4179,8 +4176,8 @@ Sdc::recordPathDelayInternalTo(ExceptionPath *exception) && to->hasPins()) { for (const Pin *pin : *to->pins()) { if (!(hasLibertyCheckTo(pin) - || network_->isTopLevelPort(pin))) { - path_delay_internal_to_.insert(pin); + || network_->isTopLevelPort(pin))) { + path_delay_internal_to_.insert(pin); if (exception->breakPath()) path_delay_internal_to_break_.insert(pin); } @@ -4197,9 +4194,9 @@ Sdc::unrecordPathDelayInternalTo(ExceptionPath *exception) && !path_delay_internal_to_.empty()) { for (const Pin *pin : *to->pins()) { if (!(hasLibertyCheckTo(pin) - || network_->isTopLevelPort(pin)) - && !pathDelayTo(pin)) { - path_delay_internal_to_.erase(pin); + || network_->isTopLevelPort(pin)) + && !pathDelayTo(pin)) { + path_delay_internal_to_.erase(pin); if (exception->breakPath()) path_delay_internal_to_break_.erase(pin); } @@ -4215,9 +4212,9 @@ Sdc::hasLibertyCheckTo(const Pin *pin) if (cell) { LibertyPort *port = network_->libertyPort(pin); if (port) { - for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, port)) { - if (arc_set->role()->isTimingCheckBetween()) - return true; + for (TimingArcSet *arc_set : cell->timingArcSetsTo(port)) { + if (arc_set->role()->isTimingCheckBetween()) + return true; } } } @@ -4227,12 +4224,11 @@ Sdc::hasLibertyCheckTo(const Pin *pin) bool Sdc::pathDelayTo(const Pin *pin) { - const ExceptionPathSet *exceptions = - findExceptions(first_to_pin_exceptions_, pin); + const ExceptionPathSet *exceptions = findKeyValuePtr(first_to_pin_exceptions_, pin); if (exceptions) { for (ExceptionPath *exception : *exceptions) { if (exception->isPathDelay()) - return true; + return true; } } return false; @@ -4241,13 +4237,13 @@ Sdc::pathDelayTo(const Pin *pin) bool Sdc::isPathDelayInternalTo(const Pin *pin) const { - return path_delay_internal_to_.hasKey(pin); + return path_delay_internal_to_.contains(pin); } bool Sdc::isPathDelayInternalToBreak(const Pin *pin) const { - return path_delay_internal_to_break_.hasKey(pin); + return path_delay_internal_to_break_.contains(pin); } //////////////////////////////////////////////////////////////// @@ -4256,29 +4252,28 @@ void Sdc::clearGroupPathMap() { // GroupPath exceptions are deleted with other exceptions. - // Delete group_path name strings. - for (auto [name, groups] : group_path_map_) { - stringDelete(name); - groups->deleteContents(); + for (auto &[name, groups] : group_path_map_) { + deleteContents(*groups); delete groups; } group_path_map_.clear(); } void -Sdc::makeGroupPath(const char *name, - bool is_default, - ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const char *comment) +Sdc::makeGroupPath(std::string_view name, + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + std::string_view comment) { checkFromThrusTo(from, thrus, to); - if (name && is_default) + if (!name.empty() && is_default) report_->critical(1490, "group path name and is_default are mutually exclusive."); - else if (name) { + else if (!name.empty()) { + const std::string name_key(name); GroupPath *group_path = new GroupPath(name, is_default, from, thrus, to, - true, comment); + true, comment); // Clone the group_path because it may get merged and hence deleted // by addException. ExceptionFrom *from1 = group_path->from() @@ -4288,12 +4283,12 @@ Sdc::makeGroupPath(const char *name, ExceptionPath *clone = group_path->clone(from1, thrus1, to1, true); addException(clone); // A named group path can have multiple exceptions. - GroupPathSet *groups = group_path_map_.findKey(name); + GroupPathSet *groups = findKey(group_path_map_, name_key); if (groups == nullptr) { groups = new GroupPathSet(network_); - group_path_map_[stringCopy(name)] = groups; + group_path_map_[name_key] = groups; } - if (groups->hasKey(group_path)) + if (groups->contains(group_path)) // Exact copy of existing group path. delete group_path; else @@ -4302,23 +4297,23 @@ Sdc::makeGroupPath(const char *name, else { // is_default GroupPath *group_path = new GroupPath(name, is_default, from, thrus, to, - true, comment); + true, comment); addException(group_path); } } bool -Sdc::isGroupPathName(const char *group_name) +Sdc::isGroupPathName(std::string_view group_name) const { - return group_path_map_.hasKey(group_name); + return group_path_map_.contains(group_name); } //////////////////////////////////////////////////////////////// FilterPath * Sdc::makeFilterPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to) + ExceptionThruSeq *thrus, + ExceptionTo *to) { checkFromThrusTo(from, thrus, to); FilterPath *exception = new FilterPath(from, thrus, to, true); @@ -4328,6 +4323,22 @@ Sdc::makeFilterPath(ExceptionFrom *from, return exception; } +void +Sdc::makeFilter(ExceptionFrom *from, + ExceptionThruSeq *thrus) +{ + filter_ = makeFilterPath(from, thrus, nullptr); +} + +void +Sdc::deleteFilter() +{ + if (filter_) { + deleteException(filter_); + filter_ = nullptr; + } +} + //////////////////////////////////////////////////////////////// void @@ -4353,10 +4364,10 @@ Sdc::makeLoopExceptions(GraphLoop *loop) while (in_edge_iter.hasNext()) { Edge *in_edge = in_edge_iter.next(); if (in_edge != edge) { - Pin *loop_input_pin = in_edge->from(graph_)->pin(); - makeLoopException(loop_input_pin, to_pin, from_pin); - // Prevent sub-loops by blocking paths on the main loop also. - makeLoopException(from_pin, to_pin, loop_input_pin); + Pin *loop_input_pin = in_edge->from(graph_)->pin(); + makeLoopException(loop_input_pin, to_pin, from_pin); + // Prevent sub-loops by blocking paths on the main loop also. + makeLoopException(from_pin, to_pin, loop_input_pin); } } } @@ -4364,8 +4375,8 @@ Sdc::makeLoopExceptions(GraphLoop *loop) void Sdc::makeLoopException(const Pin *loop_input_pin, - const Pin *loop_pin, - const Pin *loop_prev_pin) + const Pin *loop_pin, + const Pin *loop_prev_pin) { ExceptionThruSeq *thrus = new ExceptionThruSeq; makeLoopExceptionThru(loop_input_pin, thrus); @@ -4384,13 +4395,13 @@ Sdc::makeLoopPath(ExceptionThruSeq *thrus) void Sdc::makeLoopExceptionThru(const Pin *pin, - ExceptionThruSeq *thrus) + ExceptionThruSeq *thrus) { - debugPrint(debug_, "levelize", 2, " %s", network_->pathName(pin)); + debugPrint(debug_, "levelize", 2, " {}", network_->pathName(pin)); PinSet *pins = new PinSet(network_); pins->insert(pin); ExceptionThru *thru = makeExceptionThru(pins, nullptr, nullptr, - RiseFallBoth::riseFall()); + RiseFallBoth::riseFall()); thrus->push_back(thru); } @@ -4414,8 +4425,8 @@ Sdc::deleteLoopExceptions() void Sdc::addException(ExceptionPath *exception) { - debugPrint(debug_, "exception_merge", 1, "add exception for %s", - exception->asString(network_)); + debugPrint(debug_, "exception_merge", 1, "add exception for {}", + exception->to_string(network_)); if (exception->isPathDelay()) { recordPathDelayInternalFrom(exception); @@ -4437,23 +4448,23 @@ Sdc::addException(ExceptionPath *exception) InstanceSet *insts1 = from->instances() ? new InstanceSet(*from->instances()) : nullptr; ExceptionFrom *from1 = new ExceptionFrom(pins1, nullptr, insts1, - from->transition(), true, network_); + from->transition(), true, network_); ExceptionThruSeq *thrus1 = exceptionThrusClone(exception->thrus(), network_); ExceptionTo *to = exception->to(); ExceptionTo *to1 = to ? to->clone(network_) : nullptr; ExceptionPath *exception1 = exception->clone(from1, thrus1, to1, true); - debugPrint(debug_, "exception_merge", 1, " split exception for %s", - exception1->asString(network_)); + debugPrint(debug_, "exception_merge", 1, " split exception for {}", + exception1->to_string(network_)); addException1(exception1); ClockSet *clks2 = new ClockSet(*from->clks()); ExceptionFrom *from2 = new ExceptionFrom(nullptr, clks2, nullptr, - from->transition(), true, network_); + from->transition(), true, network_); ExceptionThruSeq *thrus2 = exceptionThrusClone(exception->thrus(), network_); ExceptionTo *to2 = to ? to->clone(network_) : nullptr; ExceptionPath *exception2 = exception->clone(from2, thrus2, to2, true); - debugPrint(debug_, "exception_merge", 1, " split exception for %s", - exception2->asString(network_)); + debugPrint(debug_, "exception_merge", 1, " split exception for {}", + exception2->to_string(network_)); addException1(exception2); delete exception; @@ -4474,20 +4485,20 @@ Sdc::addException1(ExceptionPath *exception) PinSet *pins1 = to->pins() ? new PinSet(*to->pins()) : nullptr; InstanceSet *insts1 = to->instances() ? new InstanceSet(*to->instances()) : nullptr; ExceptionTo *to1 = new ExceptionTo(pins1, nullptr, insts1, to->transition(), - to->endTransition(), true, network_); + to->endTransition(), true, network_); ExceptionPath *exception1 = exception->clone(from1, thrus1, to1, true); - debugPrint(debug_, "exception_merge", 1, " split exception for %s", - exception1->asString(network_)); + debugPrint(debug_, "exception_merge", 1, " split exception for {}", + exception1->to_string(network_)); addException2(exception1); ExceptionFrom *from2 = exception->from() ? exception->from()->clone(network_):nullptr; ExceptionThruSeq *thrus2 = exceptionThrusClone(exception->thrus(), network_); ClockSet *clks2 = new ClockSet(*to->clks()); ExceptionTo *to2 = new ExceptionTo(nullptr, clks2, nullptr, to->transition(), - to->endTransition(), true, network_); + to->endTransition(), true, network_); ExceptionPath *exception2 = exception->clone(from2, thrus2, to2, true); - debugPrint(debug_, "exception_merge", 1, " split exception for %s", - exception2->asString(network_)); + debugPrint(debug_, "exception_merge", 1, " split exception for {}", + exception2->to_string(network_)); addException2(exception2); delete exception; @@ -4549,28 +4560,28 @@ Sdc::addException2(ExceptionPath *exception) void Sdc::deleteMatchingExceptions(ExceptionPath *exception) { - debugPrint(debug_, "exception_merge", 1, "find matches for %s", - exception->asString(network_)); + debugPrint(debug_, "exception_merge", 1, "find matches for {}", + exception->to_string(network_)); ExceptionPathSet matches; findMatchingExceptions(exception, matches); - ExceptionPathSet expanded_matches; + ExceptionPathSet expansions; for (ExceptionPath *match : matches) // Expand the matching exception into a set of exceptions that // that do not cover the new exception. Do not record them // to prevent merging with the match, which will be deleted. - expandExceptionExcluding(match, exception, expanded_matches); + expandExceptionExcluding(match, exception, expansions); for (ExceptionPath *match : matches) deleteException(match); - for (ExceptionPath *match : expanded_matches) + for (ExceptionPath *match : expansions) addException(match); } void Sdc::findMatchingExceptions(ExceptionPath *exception, - ExceptionPathSet &matches) + ExceptionPathSet &matches) { if (exception->from()) findMatchingExceptionsFirstFrom(exception, matches); @@ -4582,7 +4593,7 @@ Sdc::findMatchingExceptions(ExceptionPath *exception, void Sdc::findMatchingExceptionsFirstFrom(ExceptionPath *exception, - ExceptionPathSet &matches) + ExceptionPathSet &matches) { ExceptionFrom *from = exception->from(); findMatchingExceptionsPins(exception, from->pins(), @@ -4597,30 +4608,30 @@ Sdc::findMatchingExceptionsFirstFrom(ExceptionPath *exception, void Sdc::findMatchingExceptionsFirstThru(ExceptionPath *exception, - ExceptionPathSet &matches) + ExceptionPathSet &matches) { ExceptionThru *thru = (*exception->thrus())[0]; findMatchingExceptionsPins(exception, thru->pins(), - first_thru_pin_exceptions_, - matches); + first_thru_pin_exceptions_, + matches); findMatchingExceptionsInsts(exception, thru->instances(), - first_thru_inst_exceptions_, - matches); + first_thru_inst_exceptions_, + matches); if (!first_thru_net_exceptions_.empty() && thru->nets()) { for (const Net *net : *thru->nets()) { // Potential matches includes exceptions that match net that are not // the first exception point. const ExceptionPathSet *potential_matches = - findExceptions(first_thru_net_exceptions_, net); + findKeyValuePtr(first_thru_net_exceptions_, net); if (potential_matches) { - for (ExceptionPath *match : *potential_matches) { - ExceptionThru *match_thru = (*match->thrus())[0]; - if (match_thru->nets()->hasKey(net) - && match->overrides(exception) - && match->intersectsPts(exception, network_)) - matches.insert(match); - } + for (ExceptionPath *match : *potential_matches) { + ExceptionThru *match_thru = (*match->thrus())[0]; + if (match_thru->nets()->contains(net) + && match->overrides(exception) + && match->intersectsPts(exception, network_)) + matches.insert(match); + } } } } @@ -4628,30 +4639,30 @@ Sdc::findMatchingExceptionsFirstThru(ExceptionPath *exception, void Sdc::findMatchingExceptionsFirstTo(ExceptionPath *exception, - ExceptionPathSet &matches) + ExceptionPathSet &matches) { ExceptionTo *to = exception->to(); findMatchingExceptionsPins(exception, to->pins(), first_to_pin_exceptions_, - matches); + matches); findMatchingExceptionsInsts(exception, to->instances(), - first_to_inst_exceptions_, - matches); + first_to_inst_exceptions_, + matches); findMatchingExceptionsClks(exception, to->clks(), first_to_clk_exceptions_, - matches); + matches); } void Sdc::findMatchingExceptionsClks(ExceptionPath *exception, - ClockSet *clks, - ClockExceptionsMap &exception_map, - ExceptionPathSet &matches) + ClockSet *clks, + ClockExceptionsMap &exception_map, + ExceptionPathSet &matches) { if (clks) { ExceptionPathSet clks_matches; for (Clock *clk : *clks) { auto itr = exception_map.find(clk); if (itr != exception_map.end()) - clks_matches.insert(itr->second.begin(), itr->second.end()); + clks_matches.insert(itr->second.begin(), itr->second.end()); } findMatchingExceptions(exception, &clks_matches, matches); } @@ -4659,16 +4670,16 @@ Sdc::findMatchingExceptionsClks(ExceptionPath *exception, void Sdc::findMatchingExceptionsPins(ExceptionPath *exception, - PinSet *pins, - PinExceptionsMap &exception_map, - ExceptionPathSet &matches) + PinSet *pins, + PinExceptionsMap &exception_map, + ExceptionPathSet &matches) { if (pins) { ExceptionPathSet pins_matches; for (const Pin *pin : *pins) { auto itr = exception_map.find(pin); if (itr != exception_map.end()) - pins_matches.insert(itr->second.begin(), itr->second.end()); + pins_matches.insert(itr->second.begin(), itr->second.end()); } findMatchingExceptions(exception, &pins_matches, matches); } @@ -4676,16 +4687,16 @@ Sdc::findMatchingExceptionsPins(ExceptionPath *exception, void Sdc::findMatchingExceptionsInsts(ExceptionPath *exception, - InstanceSet *insts, - InstanceExceptionsMap &exception_map, - ExceptionPathSet &matches) + InstanceSet *insts, + InstanceExceptionsMap &exception_map, + ExceptionPathSet &matches) { if (insts) { ExceptionPathSet inst_matches; for (const Instance *inst : *insts) { auto itr = exception_map.find(inst); if (itr != exception_map.end()) - inst_matches.insert(itr->second.begin(), itr->second.end()); + inst_matches.insert(itr->second.begin(), itr->second.end()); } findMatchingExceptions(exception, &inst_matches, matches); } @@ -4693,22 +4704,22 @@ Sdc::findMatchingExceptionsInsts(ExceptionPath *exception, void Sdc::findMatchingExceptions(ExceptionPath *exception, - ExceptionPathSet *potential_matches, - ExceptionPathSet &matches) + ExceptionPathSet *potential_matches, + ExceptionPathSet &matches) { if (potential_matches) { for (ExceptionPath *match : *potential_matches) { if (match->overrides(exception) - && match->intersectsPts(exception, network_)) - matches.insert(match); + && match->intersectsPts(exception, network_)) + matches.insert(match); } } } void Sdc::expandExceptionExcluding(ExceptionPath *exception, - ExceptionPath *excluding, - ExceptionPathSet &expansions) + ExceptionPath *excluding, + ExceptionPathSet &expansions) { ExceptionFrom *from = exception->from(); ExceptionThruSeq *thrus = exception->thrus(); @@ -4719,10 +4730,10 @@ Sdc::expandExceptionExcluding(ExceptionPath *exception, if (from_cpy->hasObjects()) { ExceptionThruSeq *thrus_cpy = nullptr; if (thrus) - thrus_cpy = clone(thrus, network_); + thrus_cpy = clone(thrus, network_); ExceptionTo *to_cpy = nullptr; if (to) - to_cpy = to->clone(network_); + to_cpy = to->clone(network_); ExceptionPath *expand = exception->clone(from_cpy,thrus_cpy,to_cpy,true); expansions.insert(expand); } @@ -4730,36 +4741,37 @@ Sdc::expandExceptionExcluding(ExceptionPath *exception, delete from_cpy; } if (thrus) { - ExceptionThruSeq::Iterator thru_iter(thrus); - ExceptionThruSeq::Iterator thru_iter2(excluding->thrus()); - while (thru_iter.hasNext() - && thru_iter2.hasNext()) { - ExceptionThru *thru = thru_iter.next(); - ExceptionThru *thru2 = thru_iter2.next(); + ExceptionThruSeq *excluding_thrus = excluding->thrus(); + ExceptionThruSeq::iterator thru_iter = thrus->begin(); + ExceptionThruSeq::iterator thru_iter2 = excluding_thrus->begin(); + while (thru_iter != thrus->end() + && thru_iter2 != excluding_thrus->end()) { + ExceptionThru *thru = *thru_iter++; + ExceptionThru *thru2 = *thru_iter2++; ExceptionThru *thru_cpy = thru->clone(network_); thru_cpy->deleteObjects(thru2, network_); if (thru_cpy->hasObjects()) { - ExceptionFrom *from_cpy = nullptr; - if (from) - from_cpy = from->clone(network_); - ExceptionThruSeq *thrus_cpy = new ExceptionThruSeq; + ExceptionFrom *from_cpy = nullptr; + if (from) + from_cpy = from->clone(network_); + ExceptionThruSeq *thrus_cpy = new ExceptionThruSeq; for (ExceptionThru *thru1 : *thrus) { - if (thru1 == thru) - thrus_cpy->push_back(thru_cpy); - else { - ExceptionThru *thru_cpy = thru->clone(network_); - thrus_cpy->push_back(thru_cpy); - } - } - ExceptionTo *to_cpy = nullptr; - if (to) - to_cpy = to->clone(network_); - ExceptionPath *expand = exception->clone(from_cpy, thrus_cpy, to_cpy, - true); - expansions.insert(expand); + if (thru1 == thru) + thrus_cpy->push_back(thru_cpy); + else { + ExceptionThru *thru_cpy = thru->clone(network_); + thrus_cpy->push_back(thru_cpy); + } + } + ExceptionTo *to_cpy = nullptr; + if (to) + to_cpy = to->clone(network_); + ExceptionPath *expand = exception->clone(from_cpy, thrus_cpy, to_cpy, + true); + expansions.insert(expand); } else - delete thru_cpy; + delete thru_cpy; } } if (to) { @@ -4768,10 +4780,10 @@ Sdc::expandExceptionExcluding(ExceptionPath *exception, if (to_cpy->hasObjects()) { ExceptionFrom *from_cpy = nullptr; if (from) - from_cpy = from->clone(network_); + from_cpy = from->clone(network_); ExceptionThruSeq *thrus_cpy = nullptr; if (thrus) - thrus_cpy = clone(thrus, network_); + thrus_cpy = clone(thrus, network_); ExceptionPath *expand = exception->clone(from_cpy,thrus_cpy,to_cpy,true); expansions.insert(expand); } @@ -4831,14 +4843,14 @@ Sdc::recordMergeHashes(ExceptionPath *exception) void Sdc::recordMergeHash(ExceptionPath *exception, - ExceptionPt *missing_pt) + ExceptionPt *missing_pt) { size_t hash = exception->hash(missing_pt); debugPrint(debug_, "exception_merge", 3, - "record merge hash %zu %s missing %s", + "record merge hash {} {} missing {}", hash, - exception->asString(network_), - missing_pt->asString(network_)); + exception->to_string(network_), + missing_pt->to_string(network_)); ExceptionPathSet &set = exception_merge_hash_[hash]; set.insert(exception); } @@ -4864,7 +4876,7 @@ Sdc::recordExceptionFirstFrom(ExceptionPath *exception) ExceptionFrom *from = exception->from(); recordExceptionPins(exception, from->pins(), first_from_pin_exceptions_); recordExceptionInsts(exception, from->instances(), - first_from_inst_exceptions_); + first_from_inst_exceptions_); recordExceptionClks(exception, from->clks(), first_from_clk_exceptions_); } @@ -4890,7 +4902,7 @@ Sdc::recordExceptionFirstThru(ExceptionPath *exception) ExceptionThru *thru = (*exception->thrus())[0]; recordExceptionPins(exception, thru->pins(), first_thru_pin_exceptions_); recordExceptionInsts(exception, thru->instances(), - first_thru_inst_exceptions_); + first_thru_inst_exceptions_); recordExceptionEdges(exception, thru->edges(), first_thru_edge_exceptions_); for (ExceptionThru *thru : *exception->thrus()) recordExceptionNets(exception, thru->nets(), first_thru_net_exceptions_); @@ -4907,8 +4919,8 @@ Sdc::recordExceptionFirstTo(ExceptionPath *exception) void Sdc::recordExceptionClks(ExceptionPath *exception, - ClockSet *clks, - ClockExceptionsMap &exception_map) + ClockSet *clks, + ClockExceptionsMap &exception_map) { if (clks) { for (Clock *clk : *clks) { @@ -4920,8 +4932,8 @@ Sdc::recordExceptionClks(ExceptionPath *exception, void Sdc::recordExceptionEdges(ExceptionPath *exception, - EdgePinsSet *edges, - EdgeExceptionsMap &exception_map) + EdgePinsSet *edges, + EdgeExceptionsMap &exception_map) { if (edges) { for (const EdgePins &edge : *edges) { @@ -4933,8 +4945,8 @@ Sdc::recordExceptionEdges(ExceptionPath *exception, void Sdc::recordExceptionPins(ExceptionPath *exception, - PinSet *pins, - PinExceptionsMap &exception_map) + PinSet *pins, + PinExceptionsMap &exception_map) { if (pins) { for (const Pin *pin : *pins) { @@ -4946,8 +4958,8 @@ Sdc::recordExceptionPins(ExceptionPath *exception, void Sdc::recordExceptionHpin(ExceptionPath *exception, - Pin *pin, - PinExceptionsMap &exception_map) + Pin *pin, + PinExceptionsMap &exception_map) { ExceptionPathSet &set = exception_map[pin]; set.insert(exception); @@ -4955,8 +4967,8 @@ Sdc::recordExceptionHpin(ExceptionPath *exception, void Sdc::recordExceptionInsts(ExceptionPath *exception, - InstanceSet *insts, - InstanceExceptionsMap &exception_map) + InstanceSet *insts, + InstanceExceptionsMap &exception_map) { if (insts) { for (const Instance *inst : *insts) { @@ -4968,8 +4980,8 @@ Sdc::recordExceptionInsts(ExceptionPath *exception, void Sdc::recordExceptionNets(ExceptionPath *exception, - NetSet *nets, - NetExceptionsMap &exception_map) + NetSet *nets, + NetExceptionsMap &exception_map) { if (nets) { for (const Net *net : *nets) { @@ -5012,31 +5024,31 @@ Sdc::findMergeMatch(ExceptionPath *exception) if (itr != exception_merge_hash_.end()) { ExceptionPathSet &matches = itr->second; for (ExceptionPath *match : matches) { - ExceptionPt *match_missing_pt; - if (match != exception - // Exceptions are not merged if their priorities are - // different. This allows exceptions to be pruned during - // search at the endpoint. - && exception->mergeable(match) - && match->mergeablePts(exception, missing_pt, match_missing_pt)) { - debugPrint(debug_, "exception_merge", 1, "merge %s", - exception->asString(network_)); - debugPrint(debug_, "exception_merge", 1, " with %s", - match->asString(network_)); - // Unrecord the exception that is being merged away. - unrecordException(exception); - unrecordMergeHashes(match); - missing_pt->mergeInto(match_missing_pt, network_); - recordMergeHashes(match); - // First point maps only change if the exception point that - // is being merged is the first exception point. - if (first_pt) - recordExceptionFirstPts(match); + ExceptionPt *match_missing_pt; + if (match != exception + // Exceptions are not merged if their priorities are + // different. This allows exceptions to be pruned during + // search at the endpoint. + && exception->mergeable(match) + && match->mergeablePts(exception, missing_pt, match_missing_pt)) { + debugPrint(debug_, "exception_merge", 1, "merge {}", + exception->to_string(network_)); + debugPrint(debug_, "exception_merge", 1, " with {}", + match->to_string(network_)); + // Unrecord the exception that is being merged away. + unrecordException(exception); + unrecordMergeHashes(match); + missing_pt->mergeInto(match_missing_pt, network_); + recordMergeHashes(match); + // First point maps only change if the exception point that + // is being merged is the first exception point. + if (first_pt) + recordExceptionFirstPts(match); // Have to wait until after exception point merge to delete // the exception. - delete exception; - return match; - } + delete exception; + return match; + } } } first_pt = false; @@ -5088,32 +5100,32 @@ Sdc::deleteExceptionsReferencing(Clock *clk) ExceptionFrom *from = exception->from(); if (from) { ClockSet *clks = from->clks(); - if (clks && clks->hasKey(clk)) { - itr = exceptions_.erase(itr); - unrecordException(exception); - deleted = true; - from->deleteClock(clk); - if (from->hasObjects()) - recordException(exception); - else - deleteException(exception); + if (clks && clks->contains(clk)) { + itr = exceptions_.erase(itr); + unrecordException(exception); + deleted = true; + from->deleteClock(clk); + if (from->hasObjects()) + recordException(exception); + else + deleteException(exception); } } if (!deleted) { ExceptionTo *to = exception->to(); if (to) { - ClockSet *clks = to->clks(); - if (clks && clks->hasKey(clk)) { - itr = exceptions_.erase(itr); - deleted = true; - unrecordException(exception); - to->deleteClock(clk); - if (to->hasObjects()) - recordException(exception); - else - deleteException(exception); - } + ClockSet *clks = to->clks(); + if (clks && clks->contains(clk)) { + itr = exceptions_.erase(itr); + deleted = true; + unrecordException(exception); + to->deleteClock(clk); + if (to->hasObjects()) + recordException(exception); + else + deleteException(exception); + } } } if (!deleted) @@ -5124,8 +5136,8 @@ Sdc::deleteExceptionsReferencing(Clock *clk) void Sdc::deleteException(ExceptionPath *exception) { - debugPrint(debug_, "exception_merge", 2, "delete %s", - exception->asString(network_)); + debugPrint(debug_, "exception_merge", 2, "delete {}", + exception->to_string(network_)); unrecordException(exception); delete exception; } @@ -5151,14 +5163,14 @@ Sdc::unrecordMergeHashes(ExceptionPath *exception) void Sdc::unrecordMergeHash(ExceptionPath *exception, - ExceptionPt *missing_pt) + ExceptionPt *missing_pt) { size_t hash = exception->hash(missing_pt); debugPrint(debug_, "exception_merge", 3, - "unrecord merge hash %zu %s missing %s", + "unrecord merge hash {} {} missing {}", hash, - exception->asString(network_), - missing_pt->asString(network_)); + exception->to_string(network_), + missing_pt->to_string(network_)); auto itr = exception_merge_hash_.find(hash); if (itr != exception_merge_hash_.end()) { ExceptionPathSet &matches = itr->second; @@ -5181,16 +5193,16 @@ Sdc::unrecordExceptionFirstPts(ExceptionPath *exception) ExceptionThru *thru = (*thrus)[0]; unrecordExceptionPins(exception, thru->pins(), first_thru_pin_exceptions_); unrecordExceptionInsts(exception, thru->instances(), - first_thru_inst_exceptions_); + first_thru_inst_exceptions_); unrecordExceptionNets(exception, thru->nets(), first_thru_net_exceptions_); unrecordExceptionEdges(exception, thru->edges(), - first_thru_edge_exceptions_); + first_thru_edge_exceptions_); } else if (to) { unrecordExceptionPins(exception, to->pins(), first_to_pin_exceptions_); unrecordExceptionClks(exception, to->clks(), first_to_clk_exceptions_); unrecordExceptionInsts(exception, to->instances(), - first_to_inst_exceptions_); + first_to_inst_exceptions_); } } @@ -5212,14 +5224,14 @@ Sdc::unrecordExceptionPins(ExceptionPath *exception) void Sdc::unrecordExceptionClks(ExceptionPath *exception, - ClockSet *clks, - ClockExceptionsMap &exception_map) + ClockSet *clks, + ClockExceptionsMap &exception_map) { if (clks) { for (Clock *clk : *clks) { auto itr = exception_map.find(clk); if (itr != exception_map.end()) { - ExceptionPathSet &set = itr->second; + ExceptionPathSet &set = itr->second; set.erase(exception); } } @@ -5228,14 +5240,14 @@ Sdc::unrecordExceptionClks(ExceptionPath *exception, void Sdc::unrecordExceptionPins(ExceptionPath *exception, - PinSet *pins, - PinExceptionsMap &exception_map) + PinSet *pins, + PinExceptionsMap &exception_map) { if (pins) { for (const Pin *pin : *pins) { auto itr = exception_map.find(pin); if (itr != exception_map.end()) { - ExceptionPathSet &set = itr->second; + ExceptionPathSet &set = itr->second; set.erase(exception); } } @@ -5244,14 +5256,14 @@ Sdc::unrecordExceptionPins(ExceptionPath *exception, void Sdc::unrecordExceptionInsts(ExceptionPath *exception, - InstanceSet *insts, - InstanceExceptionsMap &exception_map) + InstanceSet *insts, + InstanceExceptionsMap &exception_map) { if (insts) { for (const Instance *inst : *insts) { auto itr = exception_map.find(inst); if (itr != exception_map.end()) { - ExceptionPathSet &set = itr->second; + ExceptionPathSet &set = itr->second; set.erase(exception); } } @@ -5260,14 +5272,14 @@ Sdc::unrecordExceptionInsts(ExceptionPath *exception, void Sdc::unrecordExceptionEdges(ExceptionPath *exception, - EdgePinsSet *edges, - EdgeExceptionsMap &exception_map) + EdgePinsSet *edges, + EdgeExceptionsMap &exception_map) { if (edges) { for (const EdgePins &edge : *edges) { auto itr = exception_map.find(edge); if (itr != exception_map.end()) { - ExceptionPathSet &set = itr->second; + ExceptionPathSet &set = itr->second; set.erase(exception); } } @@ -5276,14 +5288,14 @@ Sdc::unrecordExceptionEdges(ExceptionPath *exception, void Sdc::unrecordExceptionNets(ExceptionPath *exception, - NetSet *nets, - NetExceptionsMap &exception_map) + NetSet *nets, + NetExceptionsMap &exception_map) { if (nets) { for (const Net *net : *nets) { auto itr = exception_map.find(net); if (itr != exception_map.end()) { - ExceptionPathSet &set = itr->second; + ExceptionPathSet &set = itr->second; set.erase(exception); } } @@ -5292,8 +5304,8 @@ Sdc::unrecordExceptionNets(ExceptionPath *exception, void Sdc::unrecordExceptionHpin(ExceptionPath *exception, - Pin *pin, - PinExceptionsMap &exception_map) + Pin *pin, + PinExceptionsMap &exception_map) { auto itr = exception_map.find(pin); if (itr != exception_map.end()) { @@ -5308,19 +5320,19 @@ class ExpandException : public ExpandedExceptionVisitor { public: ExpandException(ExceptionPath *exception, - ExceptionPathSet &expansions, - Network *network); - virtual void visit(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to); + ExceptionPathSet &expansions, + Network *network); + void visit(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to) override; private: ExceptionPathSet &expansions_; }; ExpandException::ExpandException(ExceptionPath *exception, - ExceptionPathSet &expansions, - Network *network) : + ExceptionPathSet &expansions, + Network *network) : ExpandedExceptionVisitor(exception, network), expansions_(expansions) { @@ -5328,8 +5340,8 @@ ExpandException::ExpandException(ExceptionPath *exception, void ExpandException::visit(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to) + ExceptionThruSeq *thrus, + ExceptionTo *to) { ExceptionFrom *from_clone = nullptr; if (from) @@ -5346,7 +5358,7 @@ ExpandException::visit(ExceptionFrom *from, if (to) to_clone = to->clone(network_); ExceptionPath *expand = exception_->clone(from_clone, thrus_clone, - to_clone, true); + to_clone, true); expansions_.insert(expand); } @@ -5354,7 +5366,7 @@ ExpandException::visit(ExceptionFrom *from, // point in each from/thru/to. void Sdc::expandException(ExceptionPath *exception, - ExceptionPathSet &expansions) + ExceptionPathSet &expansions) { ExpandException expander(exception, expansions, network_); expander.visitExpansions(); @@ -5364,30 +5376,30 @@ Sdc::expandException(ExceptionPath *exception, void Sdc::resetPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max) + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max) { checkFromThrusTo(from, thrus, to); // erase prevents range iteration. for (auto itr = exceptions_.begin(); itr != exceptions_.end(); ) { ExceptionPath *match = *itr; if (match->resetMatch(from, thrus, to, min_max, network_)) { - debugPrint(debug_, "exception_match", 3, "reset match %s", - match->asString(network_)); + debugPrint(debug_, "exception_match", 3, "reset match {}", + match->to_string(network_)); ExceptionPathSet expansions; expandException(match, expansions); itr = exceptions_.erase(itr); deleteException(match); for (ExceptionPath *expand : expansions) { - if (expand->resetMatch(from, thrus, to, min_max, network_)) { - unrecordPathDelayInternalFrom(expand); - unrecordPathDelayInternalTo(expand); - delete expand; - } - else - addException(expand); + if (expand->resetMatch(from, thrus, to, min_max, network_)) { + unrecordPathDelayInternalFrom(expand); + unrecordPathDelayInternalTo(expand); + delete expand; + } + else + addException(expand); } } else @@ -5399,57 +5411,56 @@ Sdc::resetPath(ExceptionFrom *from, bool Sdc::exceptionFromStates(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - ExceptionStateSet *&states) const + const RiseFall *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMax *min_max, + ExceptionStateSet *&states) { return exceptionFromStates(pin, rf, clk, clk_rf, min_max, true, states); } bool Sdc::exceptionFromStates(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - bool include_filter, - ExceptionStateSet *&states) const + const RiseFall *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMax *min_max, + bool include_filter, + ExceptionStateSet *&states) { bool srch_from = true; if (pin) { if (srch_from) { const ExceptionPathSet *exceptions = - findExceptions(first_from_pin_exceptions_, pin); + findKeyValuePtr(first_from_pin_exceptions_, pin); srch_from &= exceptionFromStates(exceptions, pin, rf, min_max, - include_filter, states); + include_filter, states); } if (srch_from) { const ExceptionPathSet *exceptions = - findExceptions(first_thru_pin_exceptions_, pin); + findKeyValuePtr(first_thru_pin_exceptions_, pin); srch_from &= exceptionFromStates(exceptions, pin, rf, min_max, - include_filter, states); + include_filter, states); } if (srch_from - && (!first_from_inst_exceptions_.empty() + && (!first_from_inst_exceptions_.empty() || !first_thru_inst_exceptions_.empty())) { Instance *inst = network_->instance(pin); const ExceptionPathSet *exceptions = - findExceptions(first_from_inst_exceptions_, inst); + findKeyValuePtr(first_from_inst_exceptions_, inst); srch_from &= exceptionFromStates(exceptions, pin, rf, min_max, - include_filter, states); + include_filter, states); const ExceptionPathSet *exceptions2 = - findExceptions(first_thru_inst_exceptions_, inst); + findKeyValuePtr(first_thru_inst_exceptions_, inst); srch_from &= exceptionFromStates(exceptions2, pin, rf, min_max, - include_filter, states); + include_filter, states); } } if (srch_from && clk) { - const ExceptionPathSet *exceptions = - findExceptions(first_from_clk_exceptions_, clk); + const ExceptionPathSet *exceptions = findKeyValuePtr(first_from_clk_exceptions_, clk); srch_from &= exceptionFromStates(exceptions, pin, clk_rf, min_max, - include_filter, states); + include_filter, states); } if (!srch_from) { delete states; @@ -5460,40 +5471,40 @@ Sdc::exceptionFromStates(const Pin *pin, bool Sdc::exceptionFromStates(const ExceptionPathSet *exceptions, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - bool include_filter, - ExceptionStateSet *&states) const + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + bool include_filter, + ExceptionStateSet *&states) const { if (exceptions) { for (ExceptionPath *exception : *exceptions) { if (exception->matches(min_max, false) - && (exception->from() == nullptr - || exception->from()->transition()->matches(rf)) - && (include_filter || !exception->isFilter())) { - ExceptionState *state = exception->firstState(); - if (state->matchesNextThru(nullptr, pin, rf, min_max, network_)) - // -from clk -thru reg/clk - state = state->nextState(); - // If the exception is -from and has no -to transition it is - // complete out of the gate. - if (state->isComplete() - && exception->isFalse()) { - // Leave the completed false path state as a marker on the tag, - // but flush all other exception states because they are lower - // priority. - if (states == nullptr) - states = new ExceptionStateSet(); - states->clear(); - states->insert(state); - // No need to examine other exceptions from this - // pin/clock/instance. - return false; - } - if (states == nullptr) - states = new ExceptionStateSet(); - states->insert(state); + && (exception->from() == nullptr + || exception->from()->transition()->matches(rf)) + && (include_filter || !exception->isFilter())) { + ExceptionState *state = exception->firstState(); + if (state->matchesNextThru(nullptr, pin, rf, min_max, network_)) + // -from clk -thru reg/clk + state = state->nextState(); + // If the exception is -from and has no -to transition it is + // complete out of the gate. + if (state->isComplete() + && exception->isFalse()) { + // Leave the completed false path state as a marker on the tag, + // but flush all other exception states because they are lower + // priority. + if (states == nullptr) + states = new ExceptionStateSet(); + states->clear(); + states->insert(state); + // No need to examine other exceptions from this + // pin/clock/instance. + return false; + } + if (states == nullptr) + states = new ExceptionStateSet(); + states->insert(state); } } } @@ -5502,50 +5513,48 @@ Sdc::exceptionFromStates(const ExceptionPathSet *exceptions, void Sdc::exceptionFromClkStates(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - ExceptionStateSet *&states) const + const RiseFall *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMax *min_max, + ExceptionStateSet *&states) { if (pin) { const ExceptionPathSet *exceptions = - findExceptions(first_from_pin_exceptions_, pin); + findKeyValuePtr(first_from_pin_exceptions_, pin); exceptionFromStates(exceptions, nullptr, rf, min_max, true, states); if (!first_from_inst_exceptions_.empty()) { Instance *inst = network_->instance(pin); const ExceptionPathSet *exceptions = - findExceptions(first_from_inst_exceptions_, inst); + findKeyValuePtr(first_from_inst_exceptions_, inst); exceptionFromStates(exceptions, pin, rf, min_max, true, states); } - const ExceptionPathSet *exceptions2 = - findExceptions(first_thru_pin_exceptions_, pin); + const ExceptionPathSet *exceptions2 = findKeyValuePtr(first_thru_pin_exceptions_, pin); exceptionThruStates(exceptions2, rf, min_max, states); } - const ExceptionPathSet *exceptions = - findExceptions(first_from_clk_exceptions_, clk); + const ExceptionPathSet *exceptions = findKeyValuePtr(first_from_clk_exceptions_, clk); exceptionFromStates(exceptions, pin, clk_rf, min_max, true, states); } void Sdc::filterRegQStates(const Pin *to_pin, - const RiseFall *to_rf, - const MinMax *min_max, - ExceptionStateSet *&states) const + const RiseFall *to_rf, + const MinMax *min_max, + ExceptionStateSet *&states) const { if (!first_from_pin_exceptions_.empty()) { auto itr = first_from_pin_exceptions_.find(to_pin); if (itr != first_from_pin_exceptions_.end()) { const ExceptionPathSet &exceptions = itr->second; for (ExceptionPath *exception : exceptions) { - // Hack for filter -from reg/Q. - if (exception->isFilter() - && exception->matchesFirstPt(to_rf, min_max)) { - ExceptionState *state = exception->firstState(); - if (states == nullptr) - states = new ExceptionStateSet(); - states->insert(state); - } + // Hack for filter -from reg/Q. + if (exception->isFilter() + && exception->matchesFirstPt(to_rf, min_max)) { + ExceptionState *state = exception->firstState(); + if (states == nullptr) + states = new ExceptionStateSet(); + states->insert(state); + } } } } @@ -5553,13 +5562,13 @@ Sdc::filterRegQStates(const Pin *to_pin, void Sdc::exceptionThruStates(const Pin *from_pin, - const Pin *to_pin, - const RiseFall *to_rf, - const MinMax *min_max, - ExceptionStateSet *&states) const + const Pin *to_pin, + const RiseFall *to_rf, + const MinMax *min_max, + ExceptionStateSet *&states) { const ExceptionPathSet *exceptions = - findExceptions(first_thru_pin_exceptions_, to_pin); + findKeyValuePtr(first_thru_pin_exceptions_, to_pin); exceptionThruStates(exceptions, to_rf, min_max, states); if (!first_thru_edge_exceptions_.empty()) { @@ -5572,28 +5581,28 @@ Sdc::exceptionThruStates(const Pin *from_pin, } if (!first_thru_inst_exceptions_.empty() && (network_->direction(to_pin)->isAnyOutput() - || network_->isLatchData(to_pin))) { + || network_->isLatchData(to_pin))) { const Instance *to_inst = network_->instance(to_pin); const ExceptionPathSet *exceptions = - findExceptions(first_thru_inst_exceptions_, to_inst); + findKeyValuePtr(first_thru_inst_exceptions_, to_inst); exceptionThruStates(exceptions, to_rf, min_max, states); } } void Sdc::exceptionThruStates(const ExceptionPathSet *exceptions, - const RiseFall *to_rf, - const MinMax *min_max, - // Return value. - ExceptionStateSet *&states) const + const RiseFall *to_rf, + const MinMax *min_max, + // Return value. + ExceptionStateSet *&states) const { if (exceptions) { for (ExceptionPath *exception : *exceptions) { if (exception->matchesFirstPt(to_rf, min_max)) { - ExceptionState *state = exception->firstState(); - if (states == nullptr) - states = new ExceptionStateSet(); - states->insert(state); + ExceptionState *state = exception->firstState(); + if (states == nullptr) + states = new ExceptionStateSet(); + states->insert(state); } } } @@ -5603,81 +5612,79 @@ Sdc::exceptionThruStates(const ExceptionPathSet *exceptions, void Sdc::exceptionTo(ExceptionPathType type, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - // Return values. - ExceptionPath *&hi_priority_exception, - int &hi_priority) const + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + // Return values. + ExceptionPath *&hi_priority_exception, + int &hi_priority) { if (!first_to_inst_exceptions_.empty()) { Instance *inst = network_->instance(pin); - const ExceptionPathSet *exceptions = - findExceptions(first_to_inst_exceptions_, inst); + const ExceptionPathSet *exceptions = findKeyValuePtr(first_to_inst_exceptions_, inst); exceptionTo(exceptions, type, pin, rf, - clk_edge, min_max, match_min_max_exactly, - hi_priority_exception, hi_priority); + clk_edge, min_max, match_min_max_exactly, + hi_priority_exception, hi_priority); } if (!first_to_pin_exceptions_.empty()) { - const ExceptionPathSet *exceptions = - findExceptions(first_to_pin_exceptions_, pin); + const ExceptionPathSet *exceptions = findKeyValuePtr(first_to_pin_exceptions_, pin); exceptionTo(exceptions, type, pin, rf, - clk_edge, min_max, match_min_max_exactly, - hi_priority_exception, hi_priority); + clk_edge, min_max, match_min_max_exactly, + hi_priority_exception, hi_priority); } if (clk_edge && !first_to_clk_exceptions_.empty()) { const ExceptionPathSet *exceptions = - findExceptions(first_to_clk_exceptions_, clk_edge->clock()); + findKeyValuePtr(first_to_clk_exceptions_, clk_edge->clock()); exceptionTo(exceptions, type, pin, rf, clk_edge, - min_max, match_min_max_exactly, - hi_priority_exception, hi_priority); + min_max, match_min_max_exactly, + hi_priority_exception, hi_priority); } } void Sdc::exceptionTo(const ExceptionPathSet *to_exceptions, - ExceptionPathType type, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - // Return values. - ExceptionPath *&hi_priority_exception, - int &hi_priority) const + ExceptionPathType type, + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + // Return values. + ExceptionPath *&hi_priority_exception, + int &hi_priority) const { if (to_exceptions) { for (ExceptionPath *exception : *to_exceptions) { exceptionTo(exception, type, pin, rf, clk_edge, - min_max, match_min_max_exactly, - hi_priority_exception, hi_priority); + min_max, match_min_max_exactly, + hi_priority_exception, hi_priority); } } } void Sdc::exceptionTo(ExceptionPath *exception, - ExceptionPathType type, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - // Return values. - ExceptionPath *&hi_priority_exception, - int &hi_priority) const + ExceptionPathType type, + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + // Return values. + ExceptionPath *&hi_priority_exception, + int &hi_priority) const { if ((type == ExceptionPathType::any || exception->type() == type) && exceptionMatchesTo(exception, pin, rf, clk_edge, min_max, - match_min_max_exactly, false)) { + match_min_max_exactly, false)) { int priority = exception->priority(min_max); if (hi_priority_exception == nullptr - || priority > hi_priority - || (priority == hi_priority - && exception->tighterThan(hi_priority_exception))) { + || priority > hi_priority + || (priority == hi_priority + && exception->tighterThan(hi_priority_exception))) { hi_priority = priority; hi_priority_exception = exception; } @@ -5686,40 +5693,40 @@ Sdc::exceptionTo(ExceptionPath *exception, bool Sdc::exceptionMatchesTo(ExceptionPath *exception, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - bool require_to_pin) const + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + bool require_to_pin) const { ExceptionTo *to = exception->to(); return exception->matches(min_max, match_min_max_exactly) && ((to == nullptr - && !require_to_pin) - || (to - && to->matches(pin, clk_edge, rf, network_))); + && !require_to_pin) + || (to + && to->matches(pin, clk_edge, rf, network_))); } bool Sdc::isCompleteTo(ExceptionState *state, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - bool require_to_pin) const + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + bool require_to_pin) const { return state->nextThru() == nullptr && exceptionMatchesTo(state->exception(), pin, rf, clk_edge, - min_max, match_min_max_exactly, require_to_pin); + min_max, match_min_max_exactly, require_to_pin); } bool Sdc::isCompleteTo(ExceptionState *state, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max) const + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max) const { ExceptionPath *exception = state->exception(); ExceptionTo *to = exception->to(); @@ -5733,44 +5740,42 @@ Sdc::isCompleteTo(ExceptionState *state, void Sdc::groupPathsTo(const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - // Return value. - ExceptionPathSeq &group_paths) const + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + // Return value. + ExceptionPathSeq &group_paths) { if (!first_to_inst_exceptions_.empty()) { Instance *inst = network_->instance(pin); - const ExceptionPathSet *exceptions = - findExceptions(first_to_inst_exceptions_, inst); + const ExceptionPathSet *exceptions = findKeyValuePtr(first_to_inst_exceptions_, inst); groupPathsTo(exceptions, pin, rf, clk_edge, min_max, group_paths); } if (!first_to_pin_exceptions_.empty()) { - const ExceptionPathSet *exceptions = - findExceptions(first_to_pin_exceptions_, pin); + const ExceptionPathSet *exceptions = findKeyValuePtr(first_to_pin_exceptions_, pin); groupPathsTo(exceptions, pin, rf, clk_edge, min_max, group_paths); } if (clk_edge && !first_to_clk_exceptions_.empty()) { const ExceptionPathSet *exceptions = - findExceptions(first_to_clk_exceptions_, clk_edge->clock()); + findKeyValuePtr(first_to_clk_exceptions_, clk_edge->clock()); groupPathsTo(exceptions, pin, rf, clk_edge, min_max, group_paths); } } void Sdc::groupPathsTo(const ExceptionPathSet *to_exceptions, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - // Return value. - ExceptionPathSeq &group_paths) const + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + // Return value. + ExceptionPathSeq &group_paths) const { if (to_exceptions) { for (ExceptionPath *exception : *to_exceptions) { if (exception->isGroupPath() - && exceptionMatchesTo(exception, pin, rf, clk_edge, min_max, true, false)) - group_paths.push_back(exception); + && exceptionMatchesTo(exception, pin, rf, clk_edge, min_max, true, false)) + group_paths.push_back(exception); } } } @@ -5778,14 +5783,14 @@ Sdc::groupPathsTo(const ExceptionPathSet *to_exceptions, //////////////////////////////////////////////////////////////// Wireload * -Sdc::wireload(const MinMax *min_max) +Sdc::wireload(const MinMax *min_max) const { return wireload_[min_max->index()]; } void Sdc::setWireload(Wireload *wireload, - const MinMaxAll *min_max) + const MinMaxAll *min_max) { for (auto mm_index : min_max->rangeIndex()) wireload_[mm_index] = wireload; @@ -5797,12 +5802,6 @@ Sdc::setWireloadMode(WireloadMode mode) wireload_mode_ = mode; } -WireloadMode -Sdc::wireloadMode() -{ - return wireload_mode_; -} - const WireloadSelection * Sdc::wireloadSelection(const MinMax *min_max) { @@ -5811,10 +5810,10 @@ Sdc::wireloadSelection(const MinMax *min_max) // Look for a default. LibertyLibrary *lib = network_->defaultLibertyLibrary(); if (lib) { - WireloadSelection *default_sel = lib->defaultWireloadSelection(); + const WireloadSelection *default_sel = lib->defaultWireloadSelection(); if (default_sel) { - sel = default_sel; - setWireloadSelection(default_sel, MinMaxAll::all()); + sel = default_sel; + setWireloadSelection(default_sel, MinMaxAll::all()); } } } @@ -5822,8 +5821,8 @@ Sdc::wireloadSelection(const MinMax *min_max) } void -Sdc::setWireloadSelection(WireloadSelection *selection, - const MinMaxAll *min_max) +Sdc::setWireloadSelection(const WireloadSelection *selection, + const MinMaxAll *min_max) { for (auto mm_index : min_max->rangeIndex()) wireload_selection_[mm_index] = selection; @@ -5861,19 +5860,19 @@ Sdc::deletePinBefore(const Pin *pin) for (ExceptionPath *exception : itr->second) { ExceptionFrom *from = exception->from(); if (from) - from->deletePinBefore(pin, network_); + from->deletePinBefore(pin, network_); ExceptionTo *to = exception->to(); if (to) - to->deletePinBefore(pin, network_); + to->deletePinBefore(pin, network_); ExceptionPt *first_pt = exception->firstPt(); ExceptionThruSeq *thrus = exception->thrus(); if (thrus) { - for (ExceptionThru *thru : *exception->thrus()) { - thru->deletePinBefore(pin, network_); - if (thru == first_pt) - recordExceptionEdges(exception, thru->edges(), - first_thru_edge_exceptions_); - } + for (ExceptionThru *thru : *exception->thrus()) { + thru->deletePinBefore(pin, network_); + if (thru == first_pt) + recordExceptionEdges(exception, thru->edges(), + first_thru_edge_exceptions_); + } } } first_from_pin_exceptions_.erase(pin); @@ -5881,9 +5880,7 @@ Sdc::deletePinBefore(const Pin *pin) first_to_pin_exceptions_.erase(pin); pin_exceptions_.erase(pin); } - - for (int corner_index = 0; corner_index < corners_->count(); corner_index++) - drvr_pin_wire_cap_maps_[corner_index].erase(pin); + drvr_pin_wire_cap_map_.erase(pin); } void @@ -5901,8 +5898,8 @@ Sdc::clkHpinDisablesChanged(const Pin *pin) // hierarchical output - load pins outside the hierarchical instance void findLeafLoadPins(const Pin *pin, - const Network *network, - PinSet *leaf_pins) + const Network *network, + PinSet *leaf_pins) { if (network->isHierarchical(pin)) { PortDirection *dir = network->direction(pin); @@ -5914,9 +5911,9 @@ findLeafLoadPins(const Pin *pin, const Pin *pin1 = pin_iter->next(); bool is_inside = network->isInside(pin1, hinst); if (((is_input && is_inside) - || (is_output && !is_inside)) - && network->isLoad(pin1)) - leaf_pins->insert(pin1); + || (is_output && !is_inside)) + && network->isLoad(pin1)) + leaf_pins->insert(pin1); } delete pin_iter; } @@ -5930,8 +5927,8 @@ findLeafLoadPins(const Pin *pin, // hierarchical output - driver pins inside the hierarchical instance void findLeafDriverPins(const Pin *pin, - const Network *network, - PinSet *leaf_pins) + const Network *network, + PinSet *leaf_pins) { if (network->isHierarchical(pin)) { PortDirection *dir = network->direction(pin); @@ -5943,9 +5940,9 @@ findLeafDriverPins(const Pin *pin, const Pin *pin1 = pin_iter->next(); bool is_inside = network->isInside(pin1, hinst); if (((is_input && !is_inside) - || (is_output && is_inside)) - && network->isDriver(pin1)) - leaf_pins->insert(pin1); + || (is_output && is_inside)) + && network->isDriver(pin1)) + leaf_pins->insert(pin1); } delete pin_iter; } @@ -5955,11 +5952,6 @@ findLeafDriverPins(const Pin *pin, //////////////////////////////////////////////////////////////// -NetWireCaps::NetWireCaps() : - subtract_pin_cap_{false, false} -{ -} - bool NetWireCaps::subtractPinCap(const MinMax *min_max) { @@ -5973,4 +5965,4 @@ NetWireCaps::setSubtractPinCap(bool subtrace_pin_cap, subtract_pin_cap_[min_max->index()] = subtrace_pin_cap; } -} // namespace +} // namespace sta diff --git a/sdc/Sdc.i b/sdc/Sdc.i index a1ee1e5a4..07cc38a7d 100644 --- a/sdc/Sdc.i +++ b/sdc/Sdc.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,18 +22,21 @@ // // This notice may not be removed or altered from any source distribution. -%module sdc +%include "std_string.i" %{ -#include "Sdc.hh" -#include "Wireload.hh" #include "Clock.hh" +#include "FilterObjects.hh" +#include "FindObjects.hh" #include "PortDelay.hh" #include "Property.hh" +#include "SdcClass.hh" #include "Sta.hh" -#include "FindObjects.hh" +#include "Sdc.hh" +#include "Wireload.hh" #include +#include using namespace sta; @@ -91,50 +94,60 @@ private: %inline %{ void -write_sdc_cmd(const char *filename, - bool leaf, - bool compatible, - int digits, - bool gzip, - bool no_timestamp) +write_sdc_cmd(std::string filename, + bool leaf, + bool compatible, + int digits, + bool gzip, + bool no_timestamp) { - Sta::sta()->writeSdc(filename, leaf, compatible, digits, gzip, no_timestamp); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + sta->writeSdc(sdc, filename, leaf, compatible, digits, gzip, no_timestamp); } void -set_analysis_type_cmd(const char *analysis_type) +set_analysis_type_cmd(std::string analysis_type) { + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); AnalysisType type; - if (stringEq(analysis_type, "single")) + if (analysis_type == "single") type = AnalysisType::single; - else if (stringEq(analysis_type, "bc_wc")) + else if (analysis_type == "bc_wc") type = AnalysisType::bc_wc; - else if (stringEq(analysis_type, "on_chip_variation")) + else if (analysis_type == "on_chip_variation") type = AnalysisType::ocv; else { - Sta::sta()->report()->warn(2121, "unknown analysis type"); + sta->report()->warn(2121, "unknown analysis type"); type = AnalysisType::single; } - Sta::sta()->setAnalysisType(type); + sta->setAnalysisType(type, sdc); } OperatingConditions * operating_conditions(const MinMax *min_max) { - return Sta::sta()->operatingConditions(min_max); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + return sta->operatingConditions(min_max, sdc); } void set_operating_conditions_cmd(OperatingConditions *op_cond, - const MinMaxAll *min_max) + const MinMaxAll *min_max) { - Sta::sta()->setOperatingConditions(op_cond, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setOperatingConditions(op_cond, min_max, sdc); } const char * operating_condition_analysis_type() { - switch (Sta::sta()->sdc()->analysisType()){ + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + switch (sdc->analysisType()) { case AnalysisType::single: return "single"; case AnalysisType::bc_wc: @@ -148,585 +161,709 @@ operating_condition_analysis_type() void set_instance_pvt(Instance *inst, - const MinMaxAll *min_max, - float process, - float voltage, - float temperature) + const MinMaxAll *min_max, + float process, + float voltage, + float temperature) { + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); Pvt pvt(process, voltage, temperature); - Sta::sta()->setPvt(inst, min_max, pvt); + sta->setPvt(inst, min_max, pvt, sdc); } float port_ext_pin_cap(const Port *port, - const Corner *corner, - const MinMax *min_max) + const MinMax *min_max) { + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); float pin_cap, wire_cap; int fanout; - Sta::sta()->portExtCaps(port, corner, min_max, pin_cap, wire_cap, fanout); + sta->portExtCaps(port, min_max, sdc, pin_cap, wire_cap, fanout); return pin_cap; } void set_port_ext_pin_cap(const Port *port, const RiseFallBoth *rf, - const Corner *corner, const MinMaxAll *min_max, float cap) { - Sta::sta()->setPortExtPinCap(port, rf, corner, min_max, cap); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setPortExtPinCap(port, rf, min_max, cap, sdc); } float port_ext_wire_cap(const Port *port, - const Corner *corner, const MinMax *min_max) { + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); float pin_cap, wire_cap; int fanout; - Sta::sta()->portExtCaps(port, corner, min_max, pin_cap, wire_cap, fanout); + sta->portExtCaps(port, min_max, sdc, pin_cap, wire_cap, fanout); return wire_cap; } void set_port_ext_wire_cap(const Port *port, - bool subtract_pin_cap, const RiseFallBoth *rf, - const Corner *corner, const MinMaxAll *min_max, float cap) { - Sta::sta()->setPortExtWireCap(port, subtract_pin_cap, rf, corner, min_max, cap); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setPortExtWireCap(port, rf, min_max, cap, sdc); } void set_port_ext_fanout_cmd(const Port *port, - int fanout, - const Corner *corner, - const MinMaxAll *min_max) + int fanout, + const MinMaxAll *min_max) { - Sta::sta()->setPortExtFanout(port, fanout, corner, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setPortExtFanout(port, fanout, min_max, sdc); } float port_ext_fanout(const Port *port, - const Corner *corner, const MinMax *min_max) { + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); float pin_cap, wire_cap; int fanout; - Sta::sta()->portExtCaps(port, corner, min_max, pin_cap, wire_cap, fanout); + Sta::sta()->portExtCaps(port, min_max, sdc, pin_cap, wire_cap, fanout); return fanout; } void set_net_wire_cap(const Net *net, - bool subtract_pin_cap, - const Corner *corner, - const MinMaxAll *min_max, - float cap) + bool subtract_pin_cap, + const MinMaxAll *min_max, + float cap) { - Sta::sta()->setNetWireCap(net, subtract_pin_cap, corner, min_max, cap); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setNetWireCap(net, subtract_pin_cap, min_max, cap, sdc); } void -set_wire_load_mode_cmd(const char *mode_name) +set_wire_load_mode_cmd(std::string mode_name) { + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); WireloadMode mode = stringWireloadMode(mode_name); if (mode == WireloadMode::unknown) - Sta::sta()->report()->warn(2122, "unknown wire load mode"); + sta->report()->warn(2122, "unknown wire load mode"); else - Sta::sta()->setWireloadMode(mode); -} - -void -set_net_resistance(Net *net, - const MinMaxAll *min_max, - float res) -{ - Sta::sta()->setResistance(net, min_max, res); + sta->setWireloadMode(mode, sdc); } void set_wire_load_cmd(Wireload *wireload, - const MinMaxAll *min_max) + const MinMaxAll *min_max) { - Sta::sta()->setWireload(wireload, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setWireload(wireload, min_max, sdc); } void set_wire_load_selection_group_cmd(WireloadSelection *selection, - const MinMaxAll *min_max) + const MinMaxAll *min_max) { - Sta::sta()->setWireloadSelection(selection, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setWireloadSelection(selection, min_max, sdc); } void -make_clock(const char *name, - PinSet *pins, - bool add_to_pins, - float period, - FloatSeq *waveform, - char *comment) +set_net_resistance(Net *net, + const MinMaxAll *min_max, + float res) { - Sta::sta()->makeClock(name, pins, add_to_pins, period, waveform, comment); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setResistance(net, min_max, res, sdc); } void -make_generated_clock(const char *name, - PinSet *pins, - bool add_to_pins, - Pin *src_pin, - Clock *master_clk, - int divide_by, - int multiply_by, - float duty_cycle, - bool invert, - bool combinational, - IntSeq *edges, - FloatSeq *edge_shifts, - char *comment) +make_clock(std::string name, + PinSet pins, + bool add_to_pins, + float period, + FloatSeq waveform, + std::string comment) { - Sta::sta()->makeGeneratedClock(name, pins, add_to_pins, - src_pin, master_clk, - divide_by, multiply_by, duty_cycle, invert, - combinational, edges, edge_shifts, - comment); + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + sta->makeClock(name, pins, add_to_pins, period, waveform, + comment, mode); +} + +void +make_generated_clock(std::string name, + PinSet pins, + bool add_to_pins, + Pin *src_pin, + Clock *master_clk, + int divide_by, + int multiply_by, + float duty_cycle, + bool invert, + bool combinational, + IntSeq edges, + FloatSeq edge_shifts, + std::string comment) +{ + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + sta->makeGeneratedClock(name, pins, add_to_pins, + src_pin, master_clk, + divide_by, multiply_by, duty_cycle, invert, + combinational, edges, edge_shifts, + comment, mode); } void remove_clock_cmd(Clock *clk) { - Sta::sta()->removeClock(clk); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClock(clk, sdc); } void set_propagated_clock_cmd(Clock *clk) { - Sta::sta()->setPropagatedClock(clk); + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + sta->setPropagatedClock(clk, mode); } void set_propagated_clock_pin_cmd(Pin *pin) { - Sta::sta()->setPropagatedClock(pin); + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + sta->setPropagatedClock(pin, mode); } void unset_propagated_clock_cmd(Clock *clk) { - Sta::sta()->removePropagatedClock(clk); + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + sta->removePropagatedClock(clk, mode); } void unset_propagated_clock_pin_cmd(Pin *pin) { - Sta::sta()->removePropagatedClock(pin); + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + sta->removePropagatedClock(pin, mode); } void set_clock_slew_cmd(Clock *clk, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew) + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew) { - Sta::sta()->setClockSlew(clk, rf, min_max, slew); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockSlew(clk, rf, min_max, slew, sdc); } void unset_clock_slew_cmd(Clock *clk) { - Sta::sta()->removeClockSlew(clk); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockSlew(clk, sdc); } void set_clock_latency_cmd(Clock *clk, - Pin *pin, - const RiseFallBoth *rf, - MinMaxAll *min_max, float delay) + Pin *pin, + const RiseFallBoth *rf, + MinMaxAll *min_max, float delay) { - Sta::sta()->setClockLatency(clk, pin, rf, min_max, delay); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockLatency(clk, pin, rf, min_max, delay, sdc); } void set_clock_insertion_cmd(Clock *clk, - Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - const EarlyLateAll *early_late, - float delay) + Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay) { - Sta::sta()->setClockInsertion(clk, pin, rf, min_max, early_late, delay); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockInsertion(clk, pin, rf, min_max, early_late, delay, sdc); } void unset_clock_latency_cmd(Clock *clk, - Pin *pin) + Pin *pin) { - Sta::sta()->removeClockLatency(clk, pin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockLatency(clk, pin, sdc); } void unset_clock_insertion_cmd(Clock *clk, - Pin *pin) + Pin *pin) { - Sta::sta()->removeClockInsertion(clk, pin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockInsertion(clk, pin, sdc); } void set_clock_uncertainty_clk(Clock *clk, - const SetupHoldAll *setup_hold, - float uncertainty) + const SetupHoldAll *setup_hold, + float uncertainty) { - Sta::sta()->setClockUncertainty(clk, setup_hold, uncertainty); + Sta *sta = Sta::sta(); + sta->setClockUncertainty(clk, setup_hold, uncertainty); } void unset_clock_uncertainty_clk(Clock *clk, - const SetupHoldAll *setup_hold) + const SetupHoldAll *setup_hold) { - Sta::sta()->removeClockUncertainty(clk, setup_hold); + Sta *sta = Sta::sta(); + sta->removeClockUncertainty(clk, setup_hold); } void set_clock_uncertainty_pin(Pin *pin, - const MinMaxAll *min_max, - float uncertainty) + const MinMaxAll *min_max, + float uncertainty) { - Sta::sta()->setClockUncertainty(pin, min_max, uncertainty); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockUncertainty(pin, min_max, uncertainty, sdc); } void unset_clock_uncertainty_pin(Pin *pin, - const MinMaxAll *min_max) + const MinMaxAll *min_max) { - Sta::sta()->removeClockUncertainty(pin, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockUncertainty(pin, min_max, sdc); } void set_inter_clock_uncertainty(Clock *from_clk, - const RiseFallBoth *from_tr, - Clock *to_clk, - const RiseFallBoth *to_tr, - const MinMaxAll *min_max, - float uncertainty) + const RiseFallBoth *from_tr, + Clock *to_clk, + const RiseFallBoth *to_tr, + const MinMaxAll *min_max, + float uncertainty) { - Sta::sta()->setClockUncertainty(from_clk, from_tr, to_clk, to_tr, min_max, - uncertainty); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockUncertainty(from_clk, from_tr, to_clk, to_tr, min_max, + uncertainty, sdc); } void unset_inter_clock_uncertainty(Clock *from_clk, - const RiseFallBoth *from_tr, - Clock *to_clk, - const RiseFallBoth *to_tr, - const MinMaxAll *min_max) + const RiseFallBoth *from_tr, + Clock *to_clk, + const RiseFallBoth *to_tr, + const MinMaxAll *min_max) { - Sta::sta()->removeClockUncertainty(from_clk, from_tr, to_clk, to_tr, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockUncertainty(from_clk, from_tr, to_clk, to_tr, min_max, sdc); } void set_clock_gating_check_cmd(const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin) + const SetupHold *setup_hold, + float margin) { - Sta::sta()->setClockGatingCheck(rf, setup_hold, margin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockGatingCheck(rf, setup_hold, margin, sdc); } void set_clock_gating_check_clk_cmd(Clock *clk, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin) + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin) { - Sta::sta()->setClockGatingCheck(clk, rf, setup_hold, margin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockGatingCheck(clk, rf, setup_hold, margin, sdc); } void set_clock_gating_check_pin_cmd(Pin *pin, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value) + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value) { - Sta::sta()->setClockGatingCheck(pin, rf, setup_hold, margin, active_value); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockGatingCheck(pin, rf, setup_hold, margin, active_value, sdc); } void set_clock_gating_check_instance_cmd(Instance *inst, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value) + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value) { - Sta::sta()->setClockGatingCheck(inst, rf, setup_hold, margin, active_value); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockGatingCheck(inst, rf, setup_hold, margin, active_value, sdc); } void set_data_check_cmd(Pin *from, - const RiseFallBoth *from_rf, - Pin *to, - const RiseFallBoth *to_rf, - Clock *clk, - const SetupHoldAll *setup_hold, - float margin) + const RiseFallBoth *from_rf, + Pin *to, + const RiseFallBoth *to_rf, + Clock *clk, + const SetupHoldAll *setup_hold, + float margin) { - Sta::sta()->setDataCheck(from, from_rf, to, to_rf, clk, setup_hold, margin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setDataCheck(from, from_rf, to, to_rf, clk, setup_hold, margin, sdc); } void unset_data_check_cmd(Pin *from, - const RiseFallBoth *from_tr, - Pin *to, - const RiseFallBoth *to_tr, - Clock *clk, - const SetupHoldAll *setup_hold) + const RiseFallBoth *from_tr, + Pin *to, + const RiseFallBoth *to_tr, + Clock *clk, + const SetupHoldAll *setup_hold) { - Sta::sta()->removeDataCheck(from, from_tr, to, to_tr, clk, setup_hold); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDataCheck(from, from_tr, to, to_tr, clk, setup_hold, sdc); } void set_input_delay_cmd(Pin *pin, - RiseFallBoth *rf, - Clock *clk, - RiseFall *clk_rf, - Pin *ref_pin, - bool source_latency_included, - bool network_latency_included, - MinMaxAll *min_max, - bool add, - float delay) + RiseFallBoth *rf, + Clock *clk, + RiseFall *clk_rf, + Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + MinMaxAll *min_max, + bool add, + float delay) { - Sta::sta()->setInputDelay(pin, rf, clk, clk_rf, ref_pin, - source_latency_included, network_latency_included, - min_max, add, delay); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setInputDelay(pin, rf, clk, clk_rf, ref_pin, + source_latency_included, network_latency_included, + min_max, add, delay, sdc); } void unset_input_delay_cmd(Pin *pin, - RiseFallBoth *rf, - Clock *clk, - RiseFall *clk_rf, - MinMaxAll *min_max) + RiseFallBoth *rf, + Clock *clk, + RiseFall *clk_rf, + MinMaxAll *min_max) { - Sta::sta()->removeInputDelay(pin, rf, clk, clk_rf, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeInputDelay(pin, rf, clk, clk_rf, min_max, sdc); } void set_output_delay_cmd(Pin *pin, - const RiseFallBoth *rf, - Clock *clk, - const RiseFall *clk_rf, - Pin *ref_pin, - bool source_latency_included, - bool network_latency_included, - const MinMaxAll *min_max, - bool add, - float delay) + const RiseFallBoth *rf, + Clock *clk, + const RiseFall *clk_rf, + Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, + float delay) { - Sta::sta()->setOutputDelay(pin, rf, clk, clk_rf, ref_pin, - source_latency_included, network_latency_included, - min_max, add, delay); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setOutputDelay(pin, rf, clk, clk_rf, ref_pin, + source_latency_included, network_latency_included, + min_max, add, delay, sdc); } void unset_output_delay_cmd(Pin *pin, - RiseFallBoth *rf, - Clock *clk, - RiseFall *clk_rf, - MinMaxAll *min_max) + RiseFallBoth *rf, + Clock *clk, + RiseFall *clk_rf, + MinMaxAll *min_max) { - Sta::sta()->removeOutputDelay(pin, rf, clk, clk_rf, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeOutputDelay(pin, rf, clk, clk_rf, min_max, sdc); } void disable_cell(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to) { - Sta::sta()->disable(cell, from, to); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disable(cell, from, to, sdc); } void unset_disable_cell(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to) { - Sta::sta()->removeDisable(cell, from, to); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisable(cell, from, to, sdc); } void disable_lib_port(LibertyPort *port) { - Sta::sta()->disable(port); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disable(port, sdc); } void unset_disable_lib_port(LibertyPort *port) { - Sta::sta()->removeDisable(port); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisable(port, sdc); } void disable_port(Port *port) { - Sta::sta()->disable(port); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disable(port, sdc); } void unset_disable_port(Port *port) { - Sta::sta()->removeDisable(port); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisable(port, sdc); } void disable_instance(Instance *instance, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to) { - Sta::sta()->disable(instance, from, to); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disable(instance, from, to, sdc); } void unset_disable_instance(Instance *instance, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to) { - Sta::sta()->removeDisable(instance, from, to); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisable(instance, from, to, sdc); } void disable_pin(Pin *pin) { - Sta::sta()->disable(pin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disable(pin, sdc); } void unset_disable_pin(Pin *pin) { - Sta::sta()->removeDisable(pin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisable(pin, sdc); } void disable_edge(Edge *edge) { - Sta::sta()->disable(edge); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disable(edge, sdc); } void unset_disable_edge(Edge *edge) { - Sta::sta()->removeDisable(edge); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisable(edge, sdc); } void disable_timing_arc_set(TimingArcSet *arc_set) { - Sta::sta()->disable(arc_set); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disable(arc_set, sdc); } void unset_disable_timing_arc_set(TimingArcSet *arc_set) { - Sta::sta()->removeDisable(arc_set); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisable(arc_set, sdc); } void disable_clock_gating_check_inst(Instance *inst) { - Sta::sta()->disableClockGatingCheck(inst); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disableClockGatingCheck(inst, sdc); } void disable_clock_gating_check_pin(Pin *pin) { - Sta::sta()->disableClockGatingCheck(pin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disableClockGatingCheck(pin, sdc); } void disable_clock_gating_check_lib_cell(LibertyCell *cell) { - Sta::sta()->disableClockGatingCheck(cell); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disableClockGatingCheck(cell, sdc); } void unset_disable_clock_gating_check_inst(Instance *inst) { - Sta::sta()->removeDisableClockGatingCheck(inst); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisableClockGatingCheck(inst, sdc); } void unset_disable_clock_gating_check_pin(Pin *pin) { - Sta::sta()->removeDisableClockGatingCheck(pin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisableClockGatingCheck(pin, sdc); } void unset_disable_clock_gating_check_lib_cell(LibertyCell *cell) { - Sta::sta()->removeDisableClockGatingCheck(cell); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisableClockGatingCheck(cell, sdc); } EdgeSeq disabled_edges_sorted() { - return Sta::sta()->disabledEdgesSorted(); + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + return sta->disabledEdgesSorted(mode); } bool timing_arc_disabled(Edge *edge, - TimingArc *arc) + TimingArc *arc) { - Graph *graph = Sta::sta()->graph(); - return !searchThru(edge, arc, graph); + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + return !searchThru(edge, arc, mode); } void make_false_path(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - const char *comment) + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + std::string comment) { - Sta::sta()->makeFalsePath(from, thrus, to, min_max, comment); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->makeFalsePath(from, thrus, to, min_max, std::move(comment), sdc); } void make_multicycle_path(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool use_end_clk, - int path_multiplier, - const char *comment) + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, + std::string comment) { - Sta::sta()->makeMulticyclePath(from, thrus, to, min_max, use_end_clk, - path_multiplier, comment); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->makeMulticyclePath(from, thrus, to, min_max, use_end_clk, + path_multiplier, std::move(comment), sdc); } void make_path_delay(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMax *min_max, - bool ignore_clk_latency, - bool break_path, - float delay, - const char *comment) + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMax *min_max, + bool ignore_clk_latency, + bool break_path, + float delay, + std::string comment) { - Sta::sta()->makePathDelay(from, thrus, to, min_max, - ignore_clk_latency, break_path, - delay, comment); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->makePathDelay(from, thrus, to, min_max, + ignore_clk_latency, break_path, + delay, std::move(comment), sdc); } void reset_path_cmd(ExceptionFrom * - from, ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max) + from, ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max) { - Sta::sta()->resetPath(from, thrus, to, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->resetPath(from, thrus, to, min_max, sdc); // from/to and thru are owned and deleted by the caller. // ExceptionThruSeq thrus arg is made by TclListSeqExceptionThru // in the swig converter so it is deleted here. @@ -734,433 +871,544 @@ reset_path_cmd(ExceptionFrom * } void -make_group_path(const char *name, - bool is_default, - ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const char *comment) +make_group_path(std::string name, + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + std::string comment) { - if (name[0] == '\0') - name = nullptr; - Sta::sta()->makeGroupPath(name, is_default, from, thrus, to, comment); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->makeGroupPath(name, is_default, from, thrus, to, std::move(comment), sdc); } bool -is_path_group_name(const char *name) +is_path_group_name(std::string name) { - return Sta::sta()->isPathGroupName(name); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + return Sta::sta()->isPathGroupName(name, sdc); } ExceptionFrom * make_exception_from(PinSet *from_pins, - ClockSet *from_clks, - InstanceSet *from_insts, - const RiseFallBoth *from_tr) + ClockSet *from_clks, + InstanceSet *from_insts, + const RiseFallBoth *from_rf) { - return Sta::sta()->makeExceptionFrom(from_pins, from_clks, from_insts, - from_tr); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + return sta->makeExceptionFrom(from_pins, from_clks, from_insts, from_rf, sdc); } void delete_exception_from(ExceptionFrom *from) { - Sta::sta()->deleteExceptionFrom(from); + Sta *sta = Sta::sta(); + sta->deleteExceptionFrom(from); } void check_exception_from_pins(ExceptionFrom *from, - const char *file, - int line) + const char *filename, + int line) { - Sta::sta()->checkExceptionFromPins(from, file, line); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + sta->checkExceptionFromPins(from, filename, line, sdc); } ExceptionThru * make_exception_thru(PinSet *pins, - NetSet *nets, - InstanceSet *insts, - const RiseFallBoth *rf) + NetSet *nets, + InstanceSet *insts, + const RiseFallBoth *rf) { - return Sta::sta()->makeExceptionThru(pins, nets, insts, rf); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + return sta->makeExceptionThru(pins, nets, insts, rf, sdc); } void delete_exception_thru(ExceptionThru *thru) { - Sta::sta()->deleteExceptionThru(thru); + Sta *sta = Sta::sta(); + sta->deleteExceptionThru(thru); } ExceptionTo * make_exception_to(PinSet *to_pins, - ClockSet *to_clks, - InstanceSet *to_insts, - const RiseFallBoth *rf, - RiseFallBoth *end_rf) + ClockSet *to_clks, + InstanceSet *to_insts, + const RiseFallBoth *rf, + RiseFallBoth *end_rf) { - return Sta::sta()->makeExceptionTo(to_pins, to_clks, to_insts, rf, end_rf); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + return sta->makeExceptionTo(to_pins, to_clks, to_insts, rf, end_rf, sdc); } void delete_exception_to(ExceptionTo *to) { - Sta::sta()->deleteExceptionTo(to); + Sta *sta = Sta::sta(); + sta->deleteExceptionTo(to); } void check_exception_to_pins(ExceptionTo *to, - const char *file, - int line) + const char *file, + int line) { - Sta::sta()->checkExceptionToPins(to, file, line); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + sta->checkExceptionToPins(to, file, line, sdc); } +//////////////////////////////////////////////////////////////// + ClockGroups * -make_clock_groups(const char *name, - bool logically_exclusive, - bool physically_exclusive, - bool asynchronous, - bool allow_paths, - const char *comment) +make_clock_groups(std::string name, + bool logically_exclusive, + bool physically_exclusive, + bool asynchronous, + bool allow_paths, + std::string comment) { - return Sta::sta()->makeClockGroups(name, logically_exclusive, - physically_exclusive, asynchronous, - allow_paths, comment); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + return sta->makeClockGroups(name, logically_exclusive, + physically_exclusive, asynchronous, + allow_paths, std::move(comment), sdc); } void clock_groups_make_group(ClockGroups *clk_groups, - ClockSet *clks) + ClockSet *clks) +{ + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->makeClockGroup(clk_groups, clks, sdc); +} + +void +unset_clock_groups_logically_exclusive() +{ + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockGroupsLogicallyExclusive(sdc); +} + +void +unset_clock_groups_logically_exclusive(std::string name) +{ + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockGroupsLogicallyExclusive(name, sdc); +} + +void +unset_clock_groups_physically_exclusive() { - Sta::sta()->makeClockGroup(clk_groups, clks); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockGroupsPhysicallyExclusive(sdc); } void -unset_clock_groups_logically_exclusive(const char *name) +unset_clock_groups_physically_exclusive(std::string name) { - Sta::sta()->removeClockGroupsLogicallyExclusive(name); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockGroupsPhysicallyExclusive(name, sdc); } void -unset_clock_groups_physically_exclusive(const char *name) +unset_clock_groups_asynchronous() { - Sta::sta()->removeClockGroupsPhysicallyExclusive(name); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockGroupsAsynchronous(sdc); } void -unset_clock_groups_asynchronous(const char *name) +unset_clock_groups_asynchronous(std::string name) { - Sta::sta()->removeClockGroupsAsynchronous(name); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockGroupsAsynchronous(name, sdc); } // Debugging function. bool same_clk_group(Clock *clk1, - Clock *clk2) + Clock *clk2) { Sta *sta = Sta::sta(); - Sdc *sdc = sta->sdc(); + Sdc *sdc = sta->cmdSdc(); return sdc->sameClockGroupExplicit(clk1, clk2); } void set_clock_sense_cmd(PinSet *pins, - ClockSet *clks, - bool positive, - bool negative, - bool stop_propagation) + ClockSet *clks, + bool positive, + bool negative, + bool stop_propagation) { Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); if (positive) - sta->setClockSense(pins, clks, ClockSense::positive); + sta->setClockSense(pins, clks, ClockSense::positive, sdc); else if (negative) - sta->setClockSense(pins, clks, ClockSense::negative); + sta->setClockSense(pins, clks, ClockSense::negative, sdc); else if (stop_propagation) - sta->setClockSense(pins, clks, ClockSense::stop); + sta->setClockSense(pins, clks, ClockSense::stop, sdc); else sta->report()->critical(2123, "unknown clock sense"); } void set_input_slew_cmd(Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew) + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew) { - Sta::sta()->setInputSlew(port, rf, min_max, slew); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setInputSlew(port, rf, min_max, slew, sdc); } void set_drive_cell_cmd(LibertyLibrary *library, - LibertyCell *cell, - Port *port, - LibertyPort *from_port, - float from_slew_rise, - float from_slew_fall, - LibertyPort *to_port, - const RiseFallBoth *rf, - const MinMaxAll *min_max) -{ - float from_slews[RiseFall::index_count]; + LibertyCell *cell, + Port *port, + LibertyPort *from_port, + float from_slew_rise, + float from_slew_fall, + LibertyPort *to_port, + const RiseFallBoth *rf, + const MinMaxAll *min_max) +{ + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + DriveCellSlews from_slews; from_slews[RiseFall::riseIndex()] = from_slew_rise; from_slews[RiseFall::fallIndex()] = from_slew_fall; - Sta::sta()->setDriveCell(library, cell, port, from_port, from_slews, - to_port, rf, min_max); + sta->setDriveCell(library, cell, port, from_port, from_slews, + to_port, rf, min_max, sdc); } void set_drive_resistance_cmd(Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float res) + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float res) { - Sta::sta()->setDriveResistance(port, rf, min_max, res); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setDriveResistance(port, rf, min_max, res, sdc); } void set_slew_limit_clk(Clock *clk, - const RiseFallBoth *rf, - PathClkOrData clk_data, - const MinMax *min_max, - float slew) + const RiseFallBoth *rf, + PathClkOrData clk_data, + const MinMax *min_max, + float slew) { - Sta::sta()->setSlewLimit(clk, rf, clk_data, min_max, slew); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setSlewLimit(clk, rf, clk_data, min_max, slew, sdc); } void set_slew_limit_port(Port *port, - const MinMax *min_max, - float slew) + const MinMax *min_max, + float slew) { - Sta::sta()->setSlewLimit(port, min_max, slew); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setSlewLimit(port, min_max, slew, sdc); } void set_slew_limit_cell(Cell *cell, - const MinMax *min_max, - float slew) + const MinMax *min_max, + float slew) { - Sta::sta()->setSlewLimit(cell, min_max, slew); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setSlewLimit(cell, min_max, slew, sdc); } void set_port_capacitance_limit(Port *port, - const MinMax *min_max, - float cap) + const MinMax *min_max, + float cap) { - Sta::sta()->setCapacitanceLimit(port, min_max, cap); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setCapacitanceLimit(port, min_max, cap, sdc); } void set_pin_capacitance_limit(Pin *pin, - const MinMax *min_max, - float cap) + const MinMax *min_max, + float cap) { - Sta::sta()->setCapacitanceLimit(pin, min_max, cap); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setCapacitanceLimit(pin, min_max, cap, sdc); } void set_cell_capacitance_limit(Cell *cell, - const MinMax *min_max, - float cap) + const MinMax *min_max, + float cap) { - Sta::sta()->setCapacitanceLimit(cell, min_max, cap); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setCapacitanceLimit(cell, min_max, cap, sdc); } void set_latch_borrow_limit_pin(Pin *pin, - float limit) + float limit) { - Sta::sta()->setLatchBorrowLimit(pin, limit); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setLatchBorrowLimit(pin, limit, sdc); } void set_latch_borrow_limit_inst(Instance *inst, - float limit) + float limit) { - Sta::sta()->setLatchBorrowLimit(inst, limit); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setLatchBorrowLimit(inst, limit, sdc); } void set_latch_borrow_limit_clk(Clock *clk, float limit) { - Sta::sta()->setLatchBorrowLimit(clk, limit); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setLatchBorrowLimit(clk, limit, sdc); } void set_min_pulse_width_global(const RiseFallBoth *rf, - float min_width) + float min_width) { - Sta::sta()->setMinPulseWidth(rf, min_width); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setMinPulseWidth(rf, min_width, sdc); } void set_min_pulse_width_pin(Pin *pin, - const RiseFallBoth *rf, - float min_width) + const RiseFallBoth *rf, + float min_width) { - Sta::sta()->setMinPulseWidth(pin, rf, min_width); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setMinPulseWidth(pin, rf, min_width, sdc); } void set_min_pulse_width_clk(Clock *clk, - const RiseFallBoth *rf, - float min_width) + const RiseFallBoth *rf, + float min_width) { - Sta::sta()->setMinPulseWidth(clk, rf, min_width); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setMinPulseWidth(clk, rf, min_width, sdc); } void set_min_pulse_width_inst(Instance *inst, - const RiseFallBoth *rf, - float min_width) + const RiseFallBoth *rf, + float min_width) { - Sta::sta()->setMinPulseWidth(inst, rf, min_width); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setMinPulseWidth(inst, rf, min_width, sdc); } void set_max_area_cmd(float area) { - Sta::sta()->setMaxArea(area); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setMaxArea(area, sdc); } float max_area() { - return Sta::sta()->sdc()->maxArea(); + return Sta::sta()->cmdSdc()->maxArea(); } void set_max_dynamic_power_cmd(float power) { - Sta::sta()->setMaxDynamicPower(power); + Sta::sta()->cmdSdc()->setMaxDynamicPower(power); } float max_dynamic_power() { - return Sta::sta()->sdc()->maxDynamicPower(); + return Sta::sta()->cmdSdc()->maxDynamicPower(); } void set_max_leakage_power_cmd(float power) { - Sta::sta()->setMaxLeakagePower(power); + Sta::sta()->cmdSdc()->setMaxLeakagePower(power); } float max_leakage_power() { - return Sta::sta()->sdc()->maxLeakagePower(); + return Sta::sta()->cmdSdc()->maxLeakagePower(); } void set_port_fanout_limit(Port *port, - const MinMax *min_max, - float fanout) + const MinMax *min_max, + float fanout) { - Sta::sta()->setFanoutLimit(port, min_max, fanout); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setFanoutLimit(port, min_max, fanout, sdc); } void set_cell_fanout_limit(Cell *cell, - const MinMax *min_max, - float fanout) + const MinMax *min_max, + float fanout) { - Sta::sta()->setFanoutLimit(cell, min_max, fanout); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setFanoutLimit(cell, min_max, fanout, sdc); } void set_logic_value_cmd(Pin *pin, - LogicValue value) + LogicValue value) { - Sta::sta()->setLogicValue(pin, value); + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + sta->setLogicValue(pin, value, mode); } void set_case_analysis_cmd(Pin *pin, - LogicValue value) + LogicValue value) { - Sta::sta()->setCaseAnalysis(pin, value); + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + sta->setCaseAnalysis(pin, value, mode); } void unset_case_analysis_cmd(Pin *pin) { - Sta::sta()->removeCaseAnalysis(pin); + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + sta->removeCaseAnalysis(pin, mode); } void set_timing_derate_cmd(TimingDerateType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) { - Sta::sta()->setTimingDerate(type, clk_data, rf, early_late, derate); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setTimingDerate(type, clk_data, rf, early_late, derate, sdc); } void set_timing_derate_net_cmd(const Net *net, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) { - Sta::sta()->setTimingDerate(net, clk_data, rf, early_late, derate); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setTimingDerate(net, clk_data, rf, early_late, derate, sdc); } void set_timing_derate_inst_cmd(const Instance *inst, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) { - Sta::sta()->setTimingDerate(inst, type, clk_data, rf, early_late, derate); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setTimingDerate(inst, type, clk_data, rf, early_late, derate, sdc); } void set_timing_derate_cell_cmd(const LibertyCell *cell, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) { - Sta::sta()->setTimingDerate(cell, type, clk_data, rf, early_late, derate); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setTimingDerate(cell, type, clk_data, rf, early_late, derate, sdc); } void unset_timing_derate_cmd() { - Sta::sta()->unsetTimingDerate(); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->unsetTimingDerate(sdc); } Clock * -find_clock(const char *name) +find_clock(std::string name) { - return Sta::sta()->sdc()->findClock(name); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + return sdc->findClock(name); } bool is_clock_src(const Pin *pin) { - return Sta::sta()->isClockSrc(pin); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + return sta->isClockSrc(pin, sdc); } Clock * default_arrival_clock() { - return Sta::sta()->sdc()->defaultArrivalClock(); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + return sdc->defaultArrivalClock(); } const char clock_typename[] = "clock"; ClockSeq * find_clocks_complete(ClockSeq *collection, - StringSeq *patterns, + StringSeq patterns, bool regexp, bool nocase, bool quiet, @@ -1169,9 +1417,9 @@ find_clocks_complete(ClockSeq *collection, { collection = collection ? new ClockSeq(*collection) : new ClockSeq(); Sta *sta = Sta::sta(); - Sdc *sdc = sta->sdc(); + Sdc *sdc = sta->cmdSdc(); - return find_objects_complete( + return find_objects_complete( collection, patterns, regexp, @@ -1184,12 +1432,12 @@ find_clocks_complete(ClockSeq *collection, } ClockSeq -find_clocks_matching(const char *pattern, - bool regexp, - bool nocase) +find_clocks_matching(std::string pattern, + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); - Sdc *sdc = sta->sdc(); + const Sdc *sdc = sta->cmdSdc(); PatternMatch matcher(pattern, regexp, nocase, sta->tclInterp()); return sdc->findClocksMatching(&matcher); } @@ -1203,177 +1451,206 @@ update_generated_clks() bool is_clock(Pin *pin) { - return Sta::sta()->isClock(pin); + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + return sta->isClock(pin, mode); } +// variable sta_clock_through_tristate_enabled bool -is_ideal_clock(Pin *pin) +clk_thru_tristate_enabled() { - return Sta::sta()->isIdealClock(pin); + return Sta::sta()->clkThruTristateEnabled(); } -bool -is_clock_search(const Pin *pin) +// variable sta_clock_through_tristate_enabled +void +set_clk_thru_tristate_enabled(bool enabled) { - Sta *sta = Sta::sta(); - Graph *graph = sta->graph(); - Search *search = sta->search(); - Vertex *vertex, *bidirect_drvr_vertex; - graph->pinVertices(pin, vertex, bidirect_drvr_vertex); - return search->isClock(vertex); + Sta::sta()->setClkThruTristateEnabled(enabled); } -bool -is_genclk_src(const Pin *pin) +PortSeq +all_inputs_cmd(bool no_clocks) { Sta *sta = Sta::sta(); - Graph *graph = sta->graph(); - Search *search = sta->search(); - Vertex *vertex, *bidirect_drvr_vertex; - graph->pinVertices(pin, vertex, bidirect_drvr_vertex); - return search->isGenClkSrc(vertex); + const Sdc *sdc = sta->cmdSdc(); + sta->ensureLinked(); + return sdc->allInputs(no_clocks); } -bool -pin_is_constrained(Pin *pin) +PortSeq +all_outputs_cmd() { - return Sta::sta()->sdc()->isConstrained(pin); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + sta->ensureLinked(); + return sdc->allOutputs(); } -bool -instance_is_constrained(Instance *inst) -{ - return Sta::sta()->sdc()->isConstrained(inst); -} +//////////////////////////////////////////////////////////////// -bool -net_is_constrained(Net *net) -{ - return Sta::sta()->sdc()->isConstrained(net); -} +// all_register variants -bool -clk_thru_tristate_enabled() +InstanceSet +find_register_instances(ClockSet *clks, + const RiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) { - return Sta::sta()->clkThruTristateEnabled(); + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + InstanceSet insts = sta->findRegisterInstances(clks, clk_tr, + edge_triggered, + latches, mode); + delete clks; + return insts; } -void -set_clk_thru_tristate_enabled(bool enabled) +PinSet +find_register_data_pins(ClockSet *clks, + const RiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) { - Sta::sta()->setClkThruTristateEnabled(enabled); + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + PinSet pins = sta->findRegisterDataPins(clks, clk_tr, edge_triggered, + latches, mode); + delete clks; + return pins; } -void -remove_constraints() +PinSet +find_register_clk_pins(ClockSet *clks, + const RiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) { - Sta::sta()->removeConstraints(); + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + PinSet pins = sta->findRegisterClkPins(clks, clk_tr, edge_triggered, + latches, mode); + delete clks; + return pins; } -PortSeq -all_inputs_cmd(bool no_clocks) +PinSet +find_register_async_pins(ClockSet *clks, + const RiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) { Sta *sta = Sta::sta(); - sta->ensureLinked(); - return sta->sdc()->allInputs(no_clocks); + Mode *mode = sta->cmdMode(); + PinSet pins = sta->findRegisterAsyncPins(clks, clk_tr, edge_triggered, + latches, mode); + delete clks; + return pins; } -PortSeq -all_outputs_cmd() +PinSet +find_register_output_pins(ClockSet *clks, + const RiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) { Sta *sta = Sta::sta(); - sta->ensureLinked(); - return sta->sdc()->allOutputs(); + Mode *mode = sta->cmdMode(); + PinSet pins = sta->findRegisterOutputPins(clks, clk_tr, edge_triggered, + latches, mode); + delete clks; + return pins; } +//////////////////////////////////////////////////////////////// + PortSeq filter_ports(const char *filter_expression, - PortSeq *ports, - bool sta_boolean_props_as_int) + PortSeq *ports) { - return filter_objects(filter_expression, ports, sta_boolean_props_as_int); + sta::Sta *sta = Sta::sta(); + return filterPorts(filter_expression, ports, sta); } InstanceSeq filter_insts(const char *filter_expression, - InstanceSeq *insts, - bool sta_boolean_props_as_int) + InstanceSeq *insts) { - return filter_objects(filter_expression, insts, sta_boolean_props_as_int); + sta::Sta *sta = Sta::sta(); + return filterInstances(filter_expression, insts, sta); } PinSeq filter_pins(const char *filter_expression, - PinSeq *pins, - bool sta_boolean_props_as_int) + PinSeq *pins) { - return filter_objects(filter_expression, pins, sta_boolean_props_as_int); + sta::Sta *sta = Sta::sta(); + return filterPins(filter_expression, pins, sta); } NetSeq filter_nets(const char *filter_expression, - NetSeq *nets, - bool sta_boolean_props_as_int) + NetSeq *nets) { - return filter_objects(filter_expression, nets, sta_boolean_props_as_int); + sta::Sta *sta = Sta::sta(); + return filterNets(filter_expression, nets, sta); } ClockSeq filter_clocks(const char *filter_expression, - ClockSeq *clocks, - bool sta_boolean_props_as_int) + ClockSeq *clocks) { - return filter_objects(filter_expression, clocks, sta_boolean_props_as_int); + sta::Sta *sta = Sta::sta(); + return filterClocks(filter_expression, clocks, sta); } LibertyCellSeq filter_lib_cells(const char *filter_expression, - LibertyCellSeq *cells, - bool sta_boolean_props_as_int) + LibertyCellSeq *cells) { - return filter_objects(filter_expression, cells, sta_boolean_props_as_int); + sta::Sta *sta = Sta::sta(); + return filterLibCells(filter_expression, cells, sta); } LibertyPortSeq filter_lib_pins(const char *filter_expression, - LibertyPortSeq *pins, - bool sta_boolean_props_as_int) + LibertyPortSeq *pins) { - return filter_objects(filter_expression, pins, sta_boolean_props_as_int); + sta::Sta *sta = Sta::sta(); + return filterLibPins(filter_expression, pins, sta); } LibertyLibrarySeq filter_liberty_libraries(const char *filter_expression, - LibertyLibrarySeq *libs, - bool sta_boolean_props_as_int) + LibertyLibrarySeq *libs) { - return filter_objects(filter_expression, libs, sta_boolean_props_as_int); + sta::Sta *sta = Sta::sta(); + return filterLibertyLibraries(filter_expression, libs, sta); } EdgeSeq filter_timing_arcs(const char *filter_expression, - EdgeSeq *edges, - bool sta_boolean_props_as_int) + EdgeSeq *edges) { - return filter_objects(filter_expression, edges, sta_boolean_props_as_int); + sta::Sta *sta = Sta::sta(); + return filterTimingArcs(filter_expression, edges, sta); } PathEndSeq filter_path_ends(const char *filter_expression, - PathEndSeq *path_ends, - bool sta_boolean_props_as_int) + PathEndSeq *path_ends) { - return filter_objects(filter_expression, path_ends, sta_boolean_props_as_int); + sta::Sta *sta = Sta::sta(); + return filterPathEnds(filter_expression, path_ends, sta); } -// This exists primarily so unit tests can be written for FilterExpr -StdStringSeq _filter_expr_to_postfix(const char* infix, bool sta_boolean_props_as_int) { - StdStringSeq result; - auto postfix = sta::FilterExpr(infix).postfix(sta_boolean_props_as_int); - for (auto pToken: postfix) { - result.push_back(pToken->text); - } - return result; +// For FilterExpr unit tests. +StringSeq +filter_expr_to_postfix(const char* expr) +{ + Report *report = Sta::sta()->report(); + return filterExprToPostfix(expr, report); } //////////////////////////////////////////////////////////////// @@ -1381,8 +1658,10 @@ StdStringSeq _filter_expr_to_postfix(const char* infix, bool sta_boolean_props_a StringSeq group_path_names() { + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); StringSeq pg_names; - for (auto const& [name, group] : Sta::sta()->sdc()->groupPaths()) + for (auto const& [name, group] : sdc->groupPaths()) pg_names.push_back(name); return pg_names; } @@ -1393,7 +1672,9 @@ void set_voltage_global(const MinMax *min_max, float voltage) { - Sta::sta()->setVoltage(min_max, voltage); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setVoltage(min_max, voltage, sdc); } void @@ -1401,7 +1682,9 @@ set_voltage_net(const Net *net, const MinMax *min_max, float voltage) { - Sta::sta()->setVoltage(net, min_max, voltage); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setVoltage(net, min_max, voltage, sdc); } //////////////////////////////////////////////////////////////// @@ -1410,7 +1693,7 @@ char pin_case_logic_value(const Pin *pin) { Sta *sta = Sta::sta(); - Sdc *sdc = sta->sdc(); + const Sdc *sdc = sta->cmdSdc(); LogicValue value = LogicValue::unknown; bool exists; sdc->caseLogicValue(pin, value, exists); @@ -1421,7 +1704,7 @@ char pin_logic_value(const Pin *pin) { Sta *sta = Sta::sta(); - Sdc *sdc = sta->sdc(); + const Sdc *sdc = sta->cmdSdc(); LogicValue value = LogicValue::unknown; bool exists; sdc->logicValue(pin, value, exists); @@ -1446,6 +1729,14 @@ set_propagate_all_clocks(bool prop) Sta::sta()->setPropagateAllClocks(prop); } +bool +pin_is_constrained(const Pin *pin) +{ + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + return sdc->isConstrained(pin); +} + %} // inline //////////////////////////////////////////////////////////////// @@ -1456,7 +1747,7 @@ set_propagate_all_clocks(bool prop) %extend Clock { float period() { return self->period(); } -FloatSeq *waveform() { return self->waveform(); } +FloatSeq waveform() { return self->waveform(); } float time(RiseFall *rf) { return self->edge(rf)->time(); } bool is_generated() { return self->isGenerated(); } bool waveform_valid() { return self->waveformValid(); } diff --git a/sdc/Sdc.tcl b/sdc/Sdc.tcl index d23f8af89..d19914ff1 100644 --- a/sdc/Sdc.tcl +++ b/sdc/Sdc.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -34,16 +34,27 @@ namespace eval sta { -define_cmd_args "read_sdc" {[-echo] filename} +define_cmd_args "read_sdc" {[-echo] [-mode mode_name] filename} proc_redirect read_sdc { - parse_key_args "read_sdc" args keys {} flags {-echo} + parse_key_args "read_sdc" args keys {-mode} flags {-echo} check_argc_eq1 "read_sdc" $args set echo [info exists flags(-echo)] set filename [file nativename [lindex $args 0]] - set prev_filename [info script] - include_file $filename $echo 0 + set mode_name {} + if { [info exists keys(-mode)] } { + set mode_name $keys(-mode) + } + set prev_mode [cmd_mode_name] + try { + set_mode_cmd $mode_name + include_file $filename $echo 0 + } finally { + if { $prev_mode != "default" } { + set_mode_cmd $prev_mode + } + } } ################################################################ @@ -107,7 +118,7 @@ proc set_hierarchy_separator { separator } { proc check_path_divider { divider } { set sdc_dividers "/@^#.|" if { !([string length $divider] == 1 - && [string first $divider $sdc_dividers] != -1)} { + && [string first $divider $sdc_dividers] != -1)} { sta_error 342 "hierarchy separator must be one of '$sdc_dividers'." } } @@ -145,13 +156,13 @@ proc check_unit { unit key suffix key_var } { if { [string match -nocase $arg_suffix $suffix] } { set arg_prefix [string range $value 0 end-$suffix_length] if { [regexp "^(10*\\\.?0*)?(\[Mkmunpf\])?$" $arg_prefix ignore mult prefix] } { - if { $mult == "" } { - set mult 1 - } - set scale [unit_prefix_scale $unit $prefix] - check_unit_scale $unit [expr $scale * $mult] + if { $mult == "" } { + set mult 1 + } + set scale [unit_prefix_scale $unit $prefix] + check_unit_scale $unit [expr $scale * $mult] } else { - sta_error 343 "unknown unit $unit prefix '${arg_prefix}'." + sta_error 343 "unknown unit $unit prefix '${arg_prefix}'." } } else { sta_error 501 "incorrect unit suffix '$arg_suffix'." @@ -237,11 +248,11 @@ proc all_registers { args } { } if {[info exists flags(-edge_triggered)] \ - && ![info exists flags(-level_sensitive)]} { + && ![info exists flags(-level_sensitive)]} { set edge_triggered 1 set level_sensitive 0 } elseif {[info exists flags(-level_sensitive)] \ - && ![info exists flags(-edge_triggered)]} { + && ![info exists flags(-edge_triggered)]} { set level_sensitive 1 set edge_triggered 0 } else { @@ -249,31 +260,31 @@ proc all_registers { args } { set level_sensitive 1 } if { [expr [info exists flags(-cells)] \ - + [info exists flags(-data_pins)] \ - + [info exists flags(-clock_pins)] \ - + [info exists flags(-async_pins)] \ - + [info exists flags(-output_pins)]] > 1 } { + + [info exists flags(-data_pins)] \ + + [info exists flags(-clock_pins)] \ + + [info exists flags(-async_pins)] \ + + [info exists flags(-output_pins)]] > 1 } { sta_error 346 "only one of -cells, -data_pins, -clock_pins, -async_pins, -output_pins are suppported." } if [info exists flags(-cells)] { return [find_register_instances $clks $clk_rf \ - $edge_triggered $level_sensitive] + $edge_triggered $level_sensitive] } elseif [info exists flags(-data_pins)] { return [find_register_data_pins $clks $clk_rf \ - $edge_triggered $level_sensitive] + $edge_triggered $level_sensitive] } elseif [info exists flags(-clock_pins)] { return [find_register_clk_pins $clks $clk_rf \ - $edge_triggered $level_sensitive] + $edge_triggered $level_sensitive] } elseif [info exists flags(-async_pins)] { return [find_register_async_pins $clks $clk_rf \ - $edge_triggered $level_sensitive] + $edge_triggered $level_sensitive] } elseif [info exists flags(-output_pins)] { return [find_register_output_pins $clks $clk_rf \ - $edge_triggered $level_sensitive] + $edge_triggered $level_sensitive] } else { # -cells is the default. return [find_register_instances $clks $clk_rf \ - $edge_triggered $level_sensitive] + $edge_triggered $level_sensitive] } } @@ -306,7 +317,7 @@ proc current_design { {design ""} } { # Error handling wrapper for get_* (generic) proc filter_objs { filter objects filter_function object_type } { - if {[catch {set result [$filter_function $filter $objects $::sta_boolean_props_as_int]} error]} { + if {[catch {set result [$filter_function $filter $objects]} error]} { sta_error 350 "unsupported $object_type -filter expression: $error." } return $result @@ -351,19 +362,19 @@ proc get_cells { args } { parse_port_pin_net_arg $keys(-of_objects) pins nets foreach_in_collection pin $pins { if { [$pin is_top_level_port] } { - set net [get_nets [get_name $pin]] - if { $net != "NULL" } { - lappend nets $net - } + set net [get_nets [get_name $pin]] + if { $net != "NULL" } { + lappend nets $net + } } else { - lappend insts [$pin instance] + lappend insts [$pin instance] } } foreach_in_collection net $nets { set pin_iter [$net pin_iterator] while { [$pin_iter has_next] } { - set pin [$pin_iter next] - lappend insts [$pin instance] + set pin [$pin_iter next] + lappend insts [$pin instance] } $pin_iter finish } @@ -377,21 +388,21 @@ proc get_cells { args } { set patterns_regsubbed "" foreach pattern $patterns { if { [is_object $pattern] } { - set object_type [object_type $pattern] - if { $object_type == "Instance" } { - lappend directly_referenced_objects $pattern - } elseif { $object_type == "InstanceSeq" } { - foreach_in_collection inst $pattern { - lappend directly_referenced_objects $inst - } - } else { - sta_error 326 "object '$pattern' is not an instance." - } + set object_type [object_type $pattern] + if { $object_type == "Instance" } { + lappend directly_referenced_objects $pattern + } elseif { $object_type == "InstanceSeq" } { + foreach_in_collection inst $pattern { + lappend directly_referenced_objects $inst + } + } else { + sta_error 326 "object '$pattern' is not an instance." + } } else { - if { $divider != $hierarchy_separator } { - regsub $divider $pattern $hierarchy_separator pattern - } - lappend patterns_regsubbed $pattern + if { $divider != $hierarchy_separator } { + regsub $divider $pattern $hierarchy_separator pattern + } + lappend patterns_regsubbed $pattern } } set filter_expression_arg "" @@ -428,13 +439,13 @@ proc get_clocks { args } { if { [is_object $pattern] } { set object_type [object_type $pattern] if { $object_type == "Clock" } { - lappend directly_referenced_objects $pattern + lappend directly_referenced_objects $pattern } elseif { $object_type == "ClockSeq" } { - foreach_in_collection clk $pattern { - lappend directly_referenced_objects $clk - } + foreach_in_collection clk $pattern { + lappend directly_referenced_objects $clk + } } else { - sta_error 327 "object '$pattern' is not an clock." + sta_error 327 "object '$pattern' is not an clock." } } else { lappend patterns_to_match $pattern @@ -490,46 +501,46 @@ proc get_lib_cells { args } { set quiet [info exists flags(-quiet)] foreach pattern $patterns { if { [is_object $pattern] } { - set object_type [object_type $pattern] - if { $object_type == "LibertyCell" } { - set cells [add_to_collection $cells $pattern] - } elseif { $object_type == "LibertyCellSeq" } { - foreach_in_collection cell $pattern { - set cells [add_to_collection $cells $cell] - } - } else { - sta_error 328 "object '$pattern' is not a liberty cell." - } + set object_type [object_type $pattern] + if { $object_type == "LibertyCell" } { + set cells [add_to_collection $cells $pattern] + } elseif { $object_type == "LibertyCellSeq" } { + foreach_in_collection cell $pattern { + set cells [add_to_collection $cells $cell] + } + } else { + sta_error 328 "object '$pattern' is not a liberty cell." + } } else { - if { ![regexp $cell_regexp $pattern ignore lib_name cell_pattern]} { - set lib_name "*" - set cell_pattern $pattern - } - # Allow wildcards in the library name (incompatible). - set libs [get_libs -quiet $lib_name] - if { $libs == {} } { - if {!$quiet} { - sta_warn 353 "library '$lib_name' not found." - } - } else { - foreach_in_collection lib $libs { - set matches [$lib find_liberty_cells_matching $cell_pattern \ - $regexp $nocase] - if {[sizeof_collection $matches] > 0} { - set cells [add_to_collection $cells $matches] - } - } - if { [sizeof_collection $cells] == 0 } { - if {!$quiet} { - sta_warn 354 "cell '$cell_pattern' not found." - } - } - } + if { ![regexp $cell_regexp $pattern ignore lib_name cell_pattern]} { + set lib_name "*" + set cell_pattern $pattern + } + # Allow wildcards in the library name (incompatible). + set libs [get_libs -quiet $lib_name] + if { $libs == {} } { + if {!$quiet} { + sta_warn 353 "library '$lib_name' not found." + } + } else { + foreach_in_collection lib $libs { + set matches [$lib find_liberty_cells_matching $cell_pattern \ + $regexp $nocase] + if {[sizeof_collection $matches] > 0} { + set cells [add_to_collection $cells $matches] + } + } + if { [sizeof_collection $cells] == 0 } { + if {!$quiet} { + sta_warn 354 "cell '$cell_pattern' not found." + } + } + } } } } if [info exists keys(-filter)] { - set cells [filter_objs $keys(-filter) $cells filter_lib_cells "liberty cell"] + set cells [filter_lib_cells $keys(-filter) $cells] } return [copy_collection $cells] } @@ -575,71 +586,71 @@ proc get_lib_pins { args } { set libcells [get_libcells_error "objects" $keys(-of_objects)] foreach_in_collection libcell $libcells { foreach_in_collection port [$libcell find_liberty_ports_matching * 0 1] { - # Filter pg ports. - if { ![$port is_pwr_gnd] } { - lappend ports $port - } + # Filter pg ports. + if { ![$port is_pwr_gnd] } { + lappend ports $port + } } } } else { foreach pattern $patterns { if { [is_object $pattern] } { - set object_type [object_type $pattern] - if { $object_type == "LibertyPort" } { - set ports [add_to_collection $ports $pattern] - } elseif { $object_type == "LibertyPortSeq" } { - foreach_in_collection port $pattern { - set ports [add_to_collection $ports $port] - } - } else { - sta_error 329 "object '$pattern' is not a liberty pin." - } + set object_type [object_type $pattern] + if { $object_type == "LibertyPort" } { + set ports [add_to_collection $ports $pattern] + } elseif { $object_type == "LibertyPortSeq" } { + foreach_in_collection port $pattern { + set ports [add_to_collection $ports $port] + } + } else { + sta_error 329 "object '$pattern' is not a liberty pin." + } } else { - # match library/cell/port - set libs {} - if { [regexp $port_regexp1 $pattern ignore lib_name cell_name port_pattern] } { - set libs [get_libs -quiet $lib_name] - # match cell/port - } elseif { [regexp $port_regexp2 $pattern ignore cell_name port_pattern] } { - set libs [get_libs *] - } else { - if { !$quiet } { - sta_warn 355 "library/cell/port '$pattern' not found." - } - return {} - } - if { [sizeof_collection $libs] > 0 } { - set found_match 0 - set cells {} - foreach_in_collection lib $libs { - set cells [$lib find_liberty_cells_matching $cell_name $regexp $nocase] - foreach_in_collection cell $cells { - set matches [$cell find_liberty_ports_matching $port_pattern \ - $regexp $nocase] - foreach_in_collection match $matches { - # Filter pg ports. - if { ![$match is_pwr_gnd] } { - lappend ports $match - set found_match 1 - } - } - } - } - if { !$found_match } { - if { !$quiet } { - sta_warn 356 "port '$port_pattern' not found." - } - } - } else { - if { !$quiet } { - sta_warn 357 "library '$lib_name' not found." - } - } + # match library/cell/port + set libs {} + if { [regexp $port_regexp1 $pattern ignore lib_name cell_name port_pattern] } { + set libs [get_libs -quiet $lib_name] + # match cell/port + } elseif { [regexp $port_regexp2 $pattern ignore cell_name port_pattern] } { + set libs [get_libs *] + } else { + if { !$quiet } { + sta_warn 355 "library/cell/port '$pattern' not found." + } + return {} + } + if { [sizeof_collection $libs] > 0 } { + set found_match 0 + set cells {} + foreach_in_collection lib $libs { + set cells [$lib find_liberty_cells_matching $cell_name $regexp $nocase] + foreach_in_collection cell $cells { + set matches [$cell find_liberty_ports_matching $port_pattern \ + $regexp $nocase] + foreach_in_collection match $matches { + # Filter pg ports. + if { ![$match is_pwr_gnd] } { + lappend ports $match + set found_match 1 + } + } + } + } + if { !$found_match } { + if { !$quiet } { + sta_warn 356 "port '$port_pattern' not found." + } + } + } else { + if { !$quiet } { + sta_warn 357 "library '$lib_name' not found." + } + } } } } if [info exists keys(-filter)] { - set ports [filter_objs $keys(-filter) $ports filter_lib_pins "liberty port"] + set ports [filter_lib_pins $keys(-filter) $ports] } return [copy_collection $ports] } @@ -675,27 +686,27 @@ proc get_libs { args } { if { [is_object $pattern] } { set object_type [object_type $pattern] if { $object_type == "LibertyLibrary" } { - set libs [add_to_collection $libs $pattern] + set libs [add_to_collection $libs $pattern] } elseif { $object_type == "LibertyLibrarySeq" } { - foreach_in_collection lib $pattern { - set libs [add_to_collection $libs $lib] - } + foreach_in_collection lib $pattern { + set libs [add_to_collection $libs $lib] + } } else { - sta_error 330 "object '$pattern' is not a liberty library." + sta_error 330 "object '$pattern' is not a liberty library." } } else { set matches [find_liberty_libraries_matching $pattern $regexp $nocase] if { [sizeof_collection $matches] > 0 } { - set libs [add_to_collection $libs $matches] + set libs [add_to_collection $libs $matches] } else { - if {![info exists flags(-quiet)]} { - sta_warn 359 "library '$pattern' not found." - } + if {![info exists flags(-quiet)]} { + sta_warn 359 "library '$pattern' not found." + } } } } if [info exists keys(-filter)] { - set libs [filter_objs $keys(-filter) $libs filter_liberty_libraries "liberty library"] + set libs [filter_liberty_libraries $keys(-filter) $libs] } return [copy_collection $libs] } @@ -718,8 +729,8 @@ proc find_liberty_libraries_matching { pattern regexp nocase } { set lib [$lib_iter next] set lib_name [get_name $lib] if { (!$regexp && [string match $pattern2 $lib_name]) \ - || ($regexp && $nocase && [regexp -nocase $pattern2 $lib_name]) \ - || ($regexp && !$nocase && [regexp $pattern2 $lib_name]) } { + || ($regexp && $nocase && [regexp -nocase $pattern2 $lib_name]) \ + || ($regexp && !$nocase && [regexp $pattern2 $lib_name]) } { lappend matches $lib } } @@ -766,8 +777,8 @@ proc get_nets { args } { foreach_in_collection inst $insts { set pin_iter [$inst pin_iterator] while { [$pin_iter has_next] } { - set pin [$pin_iter next] - lappend nets [$pin net] + set pin [$pin_iter next] + lappend nets [$pin net] } $pin_iter finish } @@ -784,18 +795,18 @@ proc get_nets { args } { set patterns_to_match "" foreach pattern $patterns { if { [is_object $pattern] } { - set object_type [object_type $pattern] - if { $object_type == "Net" } { - lappend directly_referenced_objects $pattern - } elseif { $object_type == "NetSeq" } { - foreach_in_collection net $pattern { - lappend directly_referenced_objects $net - } - } else { - sta_error 331 "object '$pattern' is not a net." - } + set object_type [object_type $pattern] + if { $object_type == "Net" } { + lappend directly_referenced_objects $pattern + } elseif { $object_type == "NetSeq" } { + foreach_in_collection net $pattern { + lappend directly_referenced_objects $net + } + } else { + sta_error 331 "object '$pattern' is not a net." + } } else { - lappend patterns_to_match $pattern + lappend patterns_to_match $pattern } } @@ -835,22 +846,22 @@ proc get_pins { args } { foreach_in_collection inst $insts { set pin_iter [$inst pin_iterator] while { [$pin_iter has_next] } { - set pin [$pin_iter next] - # Filter pg ports. - if { ![$pin is_pwr_gnd] } { - lappend pins $pin - } + set pin [$pin_iter next] + # Filter pg ports. + if { ![$pin is_pwr_gnd] } { + lappend pins $pin + } } $pin_iter finish } foreach_in_collection net $nets { set pin_iter [$net pin_iterator] while { [$pin_iter has_next] } { - set pin [$pin_iter next] - # Filter pg ports. - if { ![$pin is_pwr_gnd] } { - lappend pins $pin - } + set pin [$pin_iter next] + # Filter pg ports. + if { ![$pin is_pwr_gnd] } { + lappend pins $pin + } } $pin_iter finish } @@ -876,18 +887,18 @@ proc get_pins { args } { set patterns_to_match "" foreach pattern $patterns { if { [is_object $pattern] } { - set object_type [object_type $pattern] - if { $object_type == "Pin" } { - lappend directly_referenced_objects $pattern - } elseif { $object_type == "PinSeq" } { - foreach_in_collection pin $pattern { - lappend directly_referenced_objects $pin - } - } else { - sta_error 332 "object '$pattern' is not a pin." - } + set object_type [object_type $pattern] + if { $object_type == "Pin" } { + lappend directly_referenced_objects $pattern + } elseif { $object_type == "PinSeq" } { + foreach_in_collection pin $pattern { + lappend directly_referenced_objects $pin + } + } else { + sta_error 332 "object '$pattern' is not a pin." + } } else { - lappend patterns_to_match $pattern + lappend patterns_to_match $pattern } } set filter_expression_arg "" @@ -939,18 +950,18 @@ proc get_ports { args } { set patterns_to_match "" foreach pattern $patterns { if { [is_object $pattern] } { - set object_type [object_type $pattern] - if { $object_type == "Port" } { - lappend directly_referenced_objects $pattern - } elseif { $object_type == "PortSeq" } { - foreach_in_collection port $pattern { - lappend directly_referenced_objects $port - } - } else { - sta_error 333 "object '$pattern' is not a port." - } + set object_type [object_type $pattern] + if { $object_type == "Port" } { + lappend directly_referenced_objects $pattern + } elseif { $object_type == "PortSeq" } { + foreach_in_collection port $pattern { + lappend directly_referenced_objects $port + } + } else { + sta_error 333 "object '$pattern' is not a port." + } } else { - lappend patterns_to_match $pattern + lappend patterns_to_match $pattern } } set filter_expression_arg "" @@ -1017,10 +1028,10 @@ proc create_clock { args } { check_float "-waveform edge" $edge set edge [time_ui_sta $edge] if { !$first_edge && $edge < $prev_edge } { - sta_error 372 "non-increasing clock -waveform edge times." + sta_error 372 "non-increasing clock -waveform edge times." } if { $edge > [expr $period * 2] } { - sta_error 373 "-waveform time greater than two periods." + sta_error 373 "-waveform time greater than two periods." } lappend waveform $edge set prev_edge $edge @@ -1137,8 +1148,8 @@ proc create_generated_clock { args } { if {[info exists keys(-duty_cycle)]} { set duty_cycle $keys(-duty_cycle) if {![string is double $duty_cycle] \ - || $duty_cycle < 0.0 || $duty_cycle > 100.0} { - sta_error 384 "-duty_cycle is not a float between 0 and 100." + || $duty_cycle < 0.0 || $duty_cycle > 100.0} { + sta_error 384 "-duty_cycle is not a float between 0 and 100." } } } elseif {[info exists keys(-edges)]} { @@ -1147,16 +1158,16 @@ proc create_generated_clock { args } { foreach edge $edges { check_cardinal "-edges" $edge if { $edge <= $prev_edge } { - sta_error 386 "edges times are not monotonically increasing." + sta_error 386 "edges times are not monotonically increasing." } } if [info exists keys(-edge_shift)] { foreach shift $keys(-edge_shift) { - check_float "-edge_shift" $shift - lappend edge_shifts [time_ui_sta $shift] + check_float "-edge_shift" $shift + lappend edge_shifts [time_ui_sta $shift] } if { [llength $edge_shifts] != [llength $edges] } { - sta_error 387 "-edge_shift length does not match -edges length." + sta_error 387 "-edge_shift length does not match -edges length." } } } elseif { $combinational } { @@ -1168,8 +1179,8 @@ proc create_generated_clock { args } { set invert 0 if {[info exists flags(-invert)]} { if {!([info exists keys(-divide_by)] \ - || [info exists keys(-multiply_by)] \ - || [info exists flags(-combinational)])} { + || [info exists keys(-multiply_by)] \ + || [info exists flags(-combinational)])} { sta_error 389 "cannot specify -invert without -multiply_by, -divide_by or -combinational." } set invert 1 @@ -1224,8 +1235,8 @@ define_cmd_args "group_path" \ proc group_path { args } { parse_key_args "group_path" args \ keys {-name -weight -critical_range \ - -from -rise_from -fall_from \ - -to -rise_to -fall_to -comment} \ + -from -rise_from -fall_from \ + -to -rise_to -fall_to -comment} \ flags {-default} 0 set cmd "group_path" @@ -1329,11 +1340,11 @@ proc set_clock_gating_check1 { args rf setup_hold margin active_value } { } foreach_in_collection pin $pins { set_clock_gating_check_pin_cmd $pin $rf $setup_hold \ - $margin $active_value + $margin $active_value } foreach_in_collection inst $insts { set_clock_gating_check_instance_cmd $inst $rf $setup_hold \ - $margin $active_value + $margin $active_value } } } @@ -1348,7 +1359,7 @@ proc set_clock_groups { args } { parse_key_args "set_clock_groups" args \ keys {-name -comment} \ flags {-logically_exclusive -physically_exclusive \ - -asynchronous -allow_paths} 0 + -asynchronous -allow_paths} 0 if {[info exists keys(-name)]} { set name $keys(-name) @@ -1370,22 +1381,22 @@ proc set_clock_groups { args } { set comment [parse_comment_key keys] set clk_groups [make_clock_groups $name $logically_exclusive \ - $physically_exclusive $asynchronous $allow_paths \ - $comment] + $physically_exclusive $asynchronous $allow_paths \ + $comment] while { $args != "" } { set arg [lindex $args 0] if {[string match $arg "-group"]} { set group_clks [get_clocks_warn "clocks" [lindex $args 1]] if { [sizeof_collection $group_clks] > 0 } { - clock_groups_make_group $clk_groups $group_clks + clock_groups_make_group $clk_groups $group_clks } set args [lrange $args 2 end] } else { if {[is_keyword_arg $arg]} { - sta_warn 402 "unknown keyword argument $arg." + sta_warn 402 "unknown keyword argument $arg." } else { - sta_warn 403 "extra positional argument $arg." + sta_warn 403 "extra positional argument $arg." } set args [lrange $args 1 end] } @@ -1397,7 +1408,7 @@ proc set_clock_groups { args } { define_cmd_args "unset_clock_groups" \ {[-logically_exclusive] [-physically_exclusive]\ [-asynchronous] [-name names] [-all]} - + proc unset_clock_groups { args } { unset_clk_groups_cmd "unset_clock_groups" $args } @@ -1433,20 +1444,20 @@ proc unset_clk_groups_cmd { cmd cmd_args } { if { $all } { if { $logically_exclusive } { - unset_clock_groups_logically_exclusive "NULL" + unset_clock_groups_logically_exclusive_all } elseif { $physically_exclusive } { - unset_clock_groups_physically_exclusive "NULL" + unset_clock_groups_physically_exclusive_all } elseif { $asynchronous } { - unset_clock_groups_asynchronous "NULL" + unset_clock_groups_asynchronous_all } } else { foreach name $names { if { $logically_exclusive } { - unset_clock_groups_logically_exclusive $name + unset_clock_groups_logically_exclusive $name } elseif { $physically_exclusive } { - unset_clock_groups_physically_exclusive $name + unset_clock_groups_physically_exclusive $name } elseif { $asynchronous } { - unset_clock_groups_asynchronous $name + unset_clock_groups_asynchronous $name } } } @@ -1492,7 +1503,7 @@ proc set_clock_latency { args } { foreach_in_collection pin $pins { # Source only allowed on clocks and clock pins. if { ![is_clock_src $pin] } { - sta_error 409 "-source '[get_full_name $pin]' is not a clock pin." + sta_error 409 "-source '[get_full_name $pin]' is not a clock pin." } set_clock_insertion_cmd $pin_clk $pin $rf $min_max $early_late $delay } @@ -1540,7 +1551,7 @@ proc unset_clk_latency_cmd { cmd cmd_args } { foreach_in_collection pin $pins { # Source only allowed on clocks and clock pins. if { ![is_clock_pin $pin] } { - sta_error 412 "-source '[$pin path_name]' is not a clock pin." + sta_error 412 "-source '[$pin path_name]' is not a clock pin." } unset_clock_insertion_cmd $pin_clk $pin } @@ -1601,8 +1612,8 @@ proc set_clock_sense_cmd1 { cmd cmd_args } { set negative [info exists flags(-negative)] set stop_propagation [info exists flags(-stop_propagation)] if { ($positive && ($negative || $stop_propagation || $pulse)) \ - || ($negative && ($positive || $stop_propagation || $pulse)) \ - || ($stop_propagation && ($positive || $negative || $pulse)) + || ($negative && ($positive || $stop_propagation || $pulse)) \ + || ($stop_propagation && ($positive || $negative || $pulse)) || ($pulse && ($positive || $negative || $stop_propagation)) } { sta_warn 417 "-positive, -negative, -stop_propagation and -pulse are mutually exclusive." } @@ -1721,7 +1732,7 @@ proc set_clock_uncertainty { args } { } if { $from_key != "none" && $to_key == "none" \ - || $from_key == "none" && $to_key != "none" } { + || $from_key == "none" && $to_key != "none" } { sta_error 421 "-from/-to must be used together." } elseif { $from_key != "none" && $to_key != "none" } { # Inter-clock uncertainty. @@ -1733,15 +1744,15 @@ proc set_clock_uncertainty { args } { foreach_in_collection from_clk $from_clks { foreach_in_collection to_clk $to_clks { - set_inter_clock_uncertainty $from_clk $from_rf \ - $to_clk $to_rf $min_max $uncertainty + set_inter_clock_uncertainty $from_clk $from_rf \ + $to_clk $to_rf $min_max $uncertainty } } } else { # Single clock uncertainty. check_argc_eq2 "set_clock_uncertainty" $args if { [info exists flags(-rise)] \ - || [info exists flags(-fall)] } { + || [info exists flags(-fall)] } { sta_error 422 "-rise, -fall options not allowed for single clock uncertainty." } set objects [lindex $args 1] @@ -1807,7 +1818,7 @@ proc unset_clk_uncertainty_cmd { cmd cmd_args } { } if { $from_key != "none" && $to_key == "none" \ - || $from_key == "none" && $to_key != "none" } { + || $from_key == "none" && $to_key != "none" } { sta_error 423 "-from/-to must be used together." } elseif { $from_key != "none" && $to_key != "none" } { # Inter-clock uncertainty. @@ -1819,15 +1830,15 @@ proc unset_clk_uncertainty_cmd { cmd cmd_args } { foreach_in_collection from_clk $from_clks { foreach_in_collection to_clk $to_clks { - unset_inter_clock_uncertainty $from_clk $from_rf \ - $to_clk $to_rf $min_max + unset_inter_clock_uncertainty $from_clk $from_rf \ + $to_clk $to_rf $min_max } } } else { # Single clock uncertainty. check_argc_eq1 $cmd $cmd_args if { [info exists keys(-rise)] \ - || [info exists keys(-fall)] } { + || [info exists keys(-fall)] } { sta_error 424 "-rise, -fall options not allowed for single clock uncertainty." } set objects [lindex $cmd_args 0] @@ -1982,7 +1993,7 @@ proc set_disable_timing { args } { libcells libports insts ports pins edges timing_arc_sets if { ([info exists keys(-from)] || [info exists keys(-to)]) \ - && ([sizeof_collection $libports] > 0 || [sizeof_collection $pins] > 0 || [sizeof_collection $ports] > 0) } { + && ([sizeof_collection $libports] > 0 || [sizeof_collection $pins] > 0 || [sizeof_collection $ports] > 0) } { sta_warn 429 "-from/-to keywords ignored for lib_pin, port and pin arguments." } @@ -2028,7 +2039,7 @@ proc set_disable_timing_instance { inst from to } { } else { foreach_in_collection from_port $from_ports { foreach_in_collection to_port $to_ports { - disable_instance $inst $from_port $to_port + disable_instance $inst $from_port $to_port } } } @@ -2086,7 +2097,7 @@ proc set_disable_timing_cell { cell from to } { } else { foreach_in_collection from_port $from_ports { foreach_in_collection to_port $to_ports { - disable_cell $cell $from_port $to_port + disable_cell $cell $from_port $to_port } } } @@ -2135,7 +2146,7 @@ proc unset_disable_cmd { cmd cmd_args } { libcells libports insts ports pins edges timing_arc_sets if { ([info exists keys(-from)] || [info exists keys(-to)]) \ - && ([sizeof_collection $libports] > 0 || [sizeof_collection $pins] > 0 || [sizeof_collection $ports] > 0) } { + && ([sizeof_collection $libports] > 0 || [sizeof_collection $pins] > 0 || [sizeof_collection $ports] > 0) } { sta_warn 434 "-from/-to keywords ignored for lib_pin, port and pin arguments." } @@ -2178,7 +2189,7 @@ proc unset_disable_timing_cell { cell from to } { } else { foreach_in_collection from_port $from_ports { foreach_in_collection to_port $to_ports { - unset_disable_cell $cell $from_port $to_port + unset_disable_cell $cell $from_port $to_port } } } @@ -2203,7 +2214,7 @@ proc unset_disable_timing_instance { inst from to } { } else { foreach_in_collection from_port $from_ports { foreach_in_collection to_port $to_ports { - unset_disable_instance $inst $from_port $to_port + unset_disable_instance $inst $from_port $to_port } } } @@ -2249,7 +2260,7 @@ proc set_false_path { args } { sta_warn 437 "-from, -through or -to required." } else { if [info exists flags(-reset_path)] { - reset_path_cmd $from $thrus $to $min_max + reset_path_cmd $from $thrus $to $min_max } set comment [parse_comment_key keys] @@ -2308,7 +2319,7 @@ proc set_port_delay { cmd sta_cmd cmd_args port_dirs } { parse_key_args $cmd cmd_args \ keys {-clock -reference_pin} \ flags {-rise -fall -max -min -clock_fall -add_delay \ - -source_latency_included -network_latency_included} + -source_latency_included -network_latency_included} check_argc_eq2 $cmd $cmd_args set delay_arg [lindex $cmd_args 0] @@ -2346,14 +2357,14 @@ proc set_port_delay { cmd sta_cmd cmd_args port_dirs } { foreach_in_collection pin $pins { if { [$pin is_top_level_port] \ - && [lsearch $port_dirs [pin_direction $pin]] == -1 } { + && [lsearch $port_dirs [pin_direction $pin]] == -1 } { sta_warn 440 "$cmd not allowed on [pin_direction $pin] port '[get_full_name $pin]'." } elseif { $clk != "NULL" && [lsearch [$clk sources] $pin] != -1 } { sta_warn 441 "$cmd relative to a clock defined on the same port/pin not allowed." } else { $sta_cmd $pin $rf $clk $clk_rf $ref_pin\ - $source_latency_included $network_latency_included \ - $min_max $add $delay + $source_latency_included $network_latency_included \ + $min_max $add $delay } } } @@ -2711,13 +2722,13 @@ define_cmd_args "set_case_analysis" \ proc set_case_analysis { value pins } { if { !($value == "0" \ - || $value == "1" \ - || $value == "zero" \ - || $value == "one" \ - || $value == "rise" \ - || $value == "rising" \ - || $value == "fall" \ - || $value == "falling") } { + || $value == "1" \ + || $value == "zero" \ + || $value == "one" \ + || $value == "rise" \ + || $value == "rising" \ + || $value == "fall" \ + || $value == "falling") } { sta_error 451 "value must be 0, zero, 1, one, rise, rising, fall, or falling." } set pins1 [get_port_pins_error "pins" $pins] @@ -2740,7 +2751,7 @@ proc unset_case_analysis { pins } { ################################################################ define_cmd_args "set_drive" {[-rise] [-fall] [-min] [-max] \ - resistance ports} + resistance ports} proc set_drive { args } { parse_key_args "set_drive" args keys {} flags {-rise -fall -min -max} @@ -2770,7 +2781,7 @@ define_cmd_args "set_driving_cell" \ proc set_driving_cell { args } { parse_key_args "set_driving_cell" args \ keys {-lib_cell -cell -library -pin -from_pin -multiply_by \ - -input_transition_rise -input_transition_fall} \ + -input_transition_rise -input_transition_fall} \ flags {-rise -fall -min -max -dont_scale -no_design_rule} set rf [parse_rise_fall_flags flags] @@ -2787,20 +2798,20 @@ proc set_driving_cell { args } { set library [get_liberty_error "library" $keys(-library)] set cell [$library find_liberty_cell $cell_name] if { $cell == "NULL" } { - sta_error 452 "cell '$lib_name:$cell_name' not found." + sta_error 452 "cell '$lib_name:$cell_name' not found." } } else { set library "NULL" if { [is_object $cell_name] } { - if { [object_type $cell_name] != "LibertyCell" } { - sta_error 334 "object '$cell_name' is not a liberty cell." - } - set cell $cell_name + if { [object_type $cell_name] != "LibertyCell" } { + sta_error 334 "object '$cell_name' is not a liberty cell." + } + set cell $cell_name } else { - set cell [find_liberty_cell $cell_name] + set cell [find_liberty_cell $cell_name] } if { $cell == "NULL" } { - sta_error 453 "'$cell_name' not found." + sta_error 453 "'$cell_name' not found." } } } else { @@ -2821,14 +2832,14 @@ proc set_driving_cell { args } { set port [$port_iter next] set dir [liberty_port_direction $port] if { [port_direction_any_output $dir] } { - incr output_count - if { $output_count > 1 } { - $port_iter finish - sta_error 456 "-pin argument required for cells with multiple outputs." - } - set to_port $port - # No break. Keep looking for output ports to make sure there - # is only one. + incr output_count + if { $output_count > 1 } { + $port_iter finish + sta_error 456 "-pin argument required for cells with multiple outputs." + } + set to_port $port + # No break. Keep looking for output ports to make sure there + # is only one. } } $port_iter finish @@ -2877,9 +2888,9 @@ proc set_driving_cell { args } { proc port_direction_any_output { dir } { return [expr { $dir == "output" \ - || $dir == "out" \ - || $dir == "bidirect" \ - || $dir == "tristate" } ] + || $dir == "out" \ + || $dir == "bidirect" \ + || $dir == "tristate" } ] } ################################################################ @@ -2929,11 +2940,11 @@ proc set_input_transition { args } { # set_load port same as -pin_load # set_load net overrides parasitics define_cmd_args "set_load" \ - {[-corner corner] [-rise] [-fall] [-max] [-min] [-subtract_pin_load]\ + {[-rise] [-fall] [-max] [-min] [-subtract_pin_load]\ [-pin_load] [-wire_load] capacitance objects} proc set_load { args } { - parse_key_args "set_load" args keys {-corner} \ + parse_key_args "set_load" args keys {} \ flags {-rise -fall -min -max -subtract_pin_load -pin_load -wire_load}\ check_argc_eq2 "set_load" $args @@ -2941,7 +2952,6 @@ proc set_load { args } { set pin_load [info exists flags(-pin_load)] set wire_load [info exists flags(-wire_load)] set subtract_pin_load [info exists flags(-subtract_pin_load)] - set corner [parse_corner_or_all keys] set min_max [parse_min_max_all_check_flags flags] set rf [parse_rise_fall_flags flags] @@ -2949,7 +2959,7 @@ proc set_load { args } { check_positive_float "capacitance" $cap set cap [capacitance_ui_sta $cap] parse_port_net_args [lindex $args 1] ports nets - + if { [sizeof_collection $ports] > 0 } { if { $subtract_pin_load } { sta_warn 486 "-subtract_pin_load not allowed for port objects." @@ -2957,11 +2967,11 @@ proc set_load { args } { # -pin_load is the default. if { $pin_load || (!$pin_load && !$wire_load) } { foreach_in_collection port $ports { - set_port_ext_pin_cap $port $rf $corner $min_max $cap + set_port_ext_pin_cap $port $rf $min_max $cap } } elseif { $wire_load } { foreach_in_collection port $ports { - set_port_ext_wire_cap $port 0 $rf $corner $min_max $cap + set_port_ext_wire_cap $port $rf $min_max $cap } } } @@ -2976,7 +2986,7 @@ proc set_load { args } { sta_warn 466 "-rise/-fall not allowed for net objects." } foreach_in_collection net $nets { - set_net_wire_cap $net $subtract_pin_load $corner $min_max $cap + set_net_wire_cap $net $subtract_pin_load $min_max $cap } } } @@ -3096,7 +3106,7 @@ proc set_max_transition { args } { set path_types {} if { ![info exists flags(-clock_path)] \ - && ![info exists flags(-data_path)] } { + && ![info exists flags(-data_path)] } { # Derate clk and data if neither -clock_path or -data_path. set path_types {"clk" "data"} } @@ -3108,10 +3118,10 @@ proc set_max_transition { args } { } if { ([sizeof_collection $ports] > 0 || [sizeof_collection $cells] > 0) \ - && ([info exists flags(-clock_path)] \ - || [info exists flags(-data_path)] - || [info exists flags(-rise)] - || [info exists flags(-fall)]) } { + && ([info exists flags(-clock_path)] \ + || [info exists flags(-data_path)] + || [info exists flags(-rise)] + || [info exists flags(-fall)]) } { sta_warn 468 "-data_path, -clock_path, -rise, -fall ignored for ports and designs." } @@ -3132,10 +3142,10 @@ proc set_max_transition { args } { ################################################################ define_cmd_args "set_port_fanout_number" \ - {[-corner corner] [-max] [-min] fanout ports} + {[-max] [-min] fanout ports} proc set_port_fanout_number { args } { - parse_key_args "set_port_fanout_number" args keys {-corner} flags {-max -min} + parse_key_args "set_port_fanout_number" args keys {} flags {-max -min} set min_max [parse_min_max_all_check_flags flags] check_argc_eq2 "set_port_fanout_number" $args @@ -3143,9 +3153,8 @@ proc set_port_fanout_number { args } { set fanout [lindex $args 0] check_positive_integer "fanout" $fanout set ports [get_ports_error "ports" [lindex $args 1]] - set corner [parse_corner_or_all keys] foreach_in_collection port $ports { - set_port_ext_fanout_cmd $port $fanout $corner $min_max + set_port_ext_fanout_cmd $port $fanout $min_max } } @@ -3177,7 +3186,7 @@ define_cmd_args "set_timing_derate" \ proc set_timing_derate { args } { parse_key_args "set_timing_derate" args keys {} \ flags {-rise -fall -early -late -clock -data \ - -net_delay -cell_delay -cell_check} + -net_delay -cell_delay -cell_check} check_argc_eq1or2 "set_timing_derate" $args set derate [lindex $args 0] @@ -3191,7 +3200,7 @@ proc set_timing_derate { args } { set path_types {} if { ![info exists flags(-clock)] \ - && ![info exists flags(-data)] } { + && ![info exists flags(-data)] } { # Derate clk and data if neither -clock or -data. lappend path_types "clk" lappend path_types "data" @@ -3219,42 +3228,42 @@ proc set_timing_derate { args } { parse_libcell_inst_net_arg $objects libcells insts nets if { [sizeof_collection $nets] > 0 } { if { [info exists flags(-cell_delay)] \ - || [info exists flags(-cell_check)] } { - sta_warn 470 "-cell_delay and -cell_check flags ignored for net objects." + || [info exists flags(-cell_check)] } { + sta_warn 470 "-cell_delay and -cell_check flags ignored for net objects." } foreach_in_collection net $nets { - foreach path_type $path_types { - set_timing_derate_net_cmd $net $path_type $rf $early_late $derate - } + foreach path_type $path_types { + set_timing_derate_net_cmd $net $path_type $rf $early_late $derate + } } } if { ![info exists flags(-cell_delay)] \ - && ![info exists flags(-cell_check)] } { + && ![info exists flags(-cell_check)] } { # Cell checks are not derated if no flags are specified. set derate_types {cell_delay} } foreach derate_type $derate_types { foreach path_type $path_types { - foreach_in_collection inst $insts { - set_timing_derate_inst_cmd $inst $derate_type $path_type \ - $rf $early_late $derate - } - foreach_in_collection libcell $libcells { - set_timing_derate_cell_cmd $libcell $derate_type $path_type \ - $rf $early_late $derate - } + foreach_in_collection inst $insts { + set_timing_derate_inst_cmd $inst $derate_type $path_type \ + $rf $early_late $derate + } + foreach_in_collection libcell $libcells { + set_timing_derate_cell_cmd $libcell $derate_type $path_type \ + $rf $early_late $derate + } } } } else { if { ![info exists flags(-net_delay)] \ - && ![info exists flags(-cell_delay)] \ - && ![info exists flags(-cell_check)] } { + && ![info exists flags(-cell_delay)] \ + && ![info exists flags(-cell_check)] } { # Cell checks are not derated if no flags are specified. set derate_types {net_delay cell_delay} } foreach derate_type $derate_types { foreach path_type $path_types { - set_timing_derate_cmd $derate_type $path_type $rf $early_late $derate + set_timing_derate_cmd $derate_type $path_type $rf $early_late $derate } } } @@ -3317,16 +3326,16 @@ proc parse_thrus_arg { args_var arg_error_var } { } if { $rf != "" } { if { [llength $args] > 1 } { - set args [lrange $args 1 end] - set arg [lindex $args 0] - parse_inst_port_pin_net_arg $arg insts pins nets - if {$pins == {} && $insts == {} && $nets == {}} { - upvar 1 $arg_error_var arg_error - set arg_error 1 - sta_warn 472 "no valid objects specified for $key" - } else { - lappend thrus [make_exception_thru $pins $nets $insts $rf] - } + set args [lrange $args 1 end] + set arg [lindex $args 0] + parse_inst_port_pin_net_arg $arg insts pins nets + if {$pins == {} && $insts == {} && $nets == {}} { + upvar 1 $arg_error_var arg_error + set arg_error 1 + sta_warn 472 "no valid objects specified for $key" + } else { + lappend thrus [make_exception_thru $pins $nets $insts $rf] + } } } else { lappend args_rtn $arg @@ -3451,9 +3460,9 @@ proc parse_op_cond { op_cond_name lib_key min_max key_var } { set lib [$lib_iter next] set op_cond [$lib find_operating_conditions $op_cond_name] if { $op_cond != "NULL" } { - set_operating_conditions_cmd $op_cond $min_max - set found 1 - break + set_operating_conditions_cmd $op_cond $min_max + set found 1 + break } } $lib_iter finish @@ -3468,8 +3477,8 @@ proc parse_op_cond_analysis_type { key_var } { if [info exists keys(-analysis_type)] { set analysis_type $keys(-analysis_type) if { $analysis_type == "single" \ - || $analysis_type == "bc_wc" \ - || $analysis_type == "on_chip_variation" } { + || $analysis_type == "bc_wc" \ + || $analysis_type == "on_chip_variation" } { set_analysis_type_cmd $analysis_type } else { sta_error 476 "-analysis_type must be single, bc_wc or on_chip_variation." @@ -3493,8 +3502,8 @@ define_cmd_args "set_wire_load_mode" "top|enclosed|segmented" proc set_wire_load_mode { mode } { if { $mode == "top" \ - || $mode == "enclosed" \ - || $mode == "segmented" } { + || $mode == "enclosed" \ + || $mode == "segmented" } { set_wire_load_mode_cmd $mode } else { sta_error 478 "mode must be top, enclosed or segmented." @@ -3528,7 +3537,7 @@ proc set_wire_load_model { args } { set lib [$lib_iter next] set wireload [$lib find_wireload $model_name] if {$wireload != "NULL"} { - break; + break; } } $lib_iter finish @@ -3567,7 +3576,7 @@ proc set_wire_load_selection_group { args } { set lib [$lib_iter next] set selection [$lib find_wireload_selection $selection_name] if {$selection != "NULL"} { - break; + break; } } $lib_iter finish @@ -3671,20 +3680,6 @@ proc get_max_leakage_power {} { # ################################################################ -define_cmd_args "define_corners" { corner1 [corner2]... } - -proc define_corners { args } { - if { [sizeof_collection [get_libs -quiet *]] > 0 } { - sta_error 482 "define_corners must be called before read_liberty." - } - if { [sizeof_collection $args] == 0 } { - sta_error 577 "define_corners must define at least one corner." - } - define_corners_cmd $args -} - -################################################################ - define_cmd_args "set_pvt"\ {insts [-min] [-max] [-process process] [-voltage voltage]\ [-temperature temperature]} diff --git a/sdc/SdcCmdComment.cc b/sdc/SdcCmdComment.cc index 667cacd09..adbf6e4a8 100644 --- a/sdc/SdcCmdComment.cc +++ b/sdc/SdcCmdComment.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,35 +22,21 @@ // // This notice may not be removed or altered from any source distribution. -#include "StringUtil.hh" #include "SdcCmdComment.hh" +#include "StringUtil.hh" + namespace sta { -SdcCmdComment::SdcCmdComment() : - comment_(nullptr) +SdcCmdComment::SdcCmdComment(std::string_view comment) : + comment_(comment) { } -SdcCmdComment::SdcCmdComment(const char *comment) -{ - comment_ = nullptr; - if (comment && comment[0] != '\0') - comment_ = stringCopy(comment); -} - -SdcCmdComment::~SdcCmdComment() -{ - stringDelete(comment_); -} void -SdcCmdComment::setComment(const char *comment) +SdcCmdComment::setComment(std::string_view comment) { - if (comment_) - stringDelete(comment_); - comment_ = nullptr; - if (comment && comment[0] != '\0') - comment_ = stringCopy(comment); + comment_ = comment; } -} // namespace +} // namespace sta diff --git a/sdc/SdcGraph.cc b/sdc/SdcGraph.cc deleted file mode 100644 index afd14c70e..000000000 --- a/sdc/SdcGraph.cc +++ /dev/null @@ -1,396 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#include "Stats.hh" -#include "PortDirection.hh" -#include "Network.hh" -#include "Graph.hh" -#include "DisabledPorts.hh" -#include "PortDelay.hh" -#include "ClockLatency.hh" -#include "Sdc.hh" - -namespace sta { - -static void -annotateGraphDisabledWireEdge(const Pin *from_pin, - const Pin *to_pin, - Graph *graph); - -// Annotate constraints to the timing graph. -void -Sdc::annotateGraph() -{ - Stats stats(debug_, report_); - // All output pins are considered constrained because - // they may be downstream from a set_min/max_delay -from that - // does not have a set_output_delay. - annotateGraphConstrainOutputs(); - annotateDisables(); - annotateGraphOutputDelays(); - annotateGraphDataChecks(); - annotateHierClkLatency(); - stats.report("Annotate constraints to graph"); -} - -void -Sdc::annotateGraphConstrainOutputs() -{ - Instance *top_inst = network_->topInstance(); - InstancePinIterator *pin_iter = network_->pinIterator(top_inst); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - if (network_->direction(pin)->isAnyOutput()) - annotateGraphConstrained(pin); - } - delete pin_iter; -} - -void -Sdc::annotateDisables() -{ - PinSet::Iterator pin_iter(disabled_pins_); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - annotateGraphDisabled(pin); - } - - if (!disabled_lib_ports_.empty()) { - VertexIterator vertex_iter(graph_); - while (vertex_iter.hasNext()) { - Vertex *vertex = vertex_iter.next(); - Pin *pin = vertex->pin(); - LibertyPort *port = network_->libertyPort(pin); - if (disabled_lib_ports_.hasKey(port)) - annotateGraphDisabled(pin); - } - } - - Instance *top_inst = network_->topInstance(); - PortSet::Iterator port_iter(disabled_ports_); - while (port_iter.hasNext()) { - const Port *port = port_iter.next(); - Pin *pin = network_->findPin(top_inst, port); - annotateGraphDisabled(pin); - } - - for (const PinPair &pair : disabled_wire_edges_) - annotateGraphDisabledWireEdge(pair.first, pair.second, graph_); - - for (Edge *edge : disabled_edges_) - edge->setIsDisabledConstraint(true); - - DisabledInstancePortsMap::Iterator disable_inst_iter(disabled_inst_ports_); - while (disable_inst_iter.hasNext()) { - DisabledInstancePorts *disabled_inst = disable_inst_iter.next(); - setEdgeDisabledInstPorts(disabled_inst); - } -} - -class DisableHpinEdgeVisitor : public HierPinThruVisitor -{ -public: - DisableHpinEdgeVisitor(Graph *graph); - virtual void visit(const Pin *from_pin, - const Pin *to_pin); - -protected: - bool annotate_; - Graph *graph_; -}; - -DisableHpinEdgeVisitor::DisableHpinEdgeVisitor(Graph *graph) : - HierPinThruVisitor(), - graph_(graph) -{ -} - -void -DisableHpinEdgeVisitor::visit(const Pin *from_pin, - const Pin *to_pin) -{ - annotateGraphDisabledWireEdge(from_pin, to_pin, graph_); -} - -static void -annotateGraphDisabledWireEdge(const Pin *from_pin, - const Pin *to_pin, - Graph *graph) -{ - Vertex *from_vertex = graph->pinDrvrVertex(from_pin); - Vertex *to_vertex = graph->pinLoadVertex(to_pin); - if (from_vertex && to_vertex) { - VertexOutEdgeIterator edge_iter(from_vertex, graph); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->isWire() - && edge->to(graph) == to_vertex) - edge->setIsDisabledConstraint(true); - } - } -} - -void -Sdc::annotateGraphDisabled(const Pin *pin) -{ - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - vertex->setIsDisabledConstraint(true); - if (bidirect_drvr_vertex) - bidirect_drvr_vertex->setIsDisabledConstraint(true); -} - -void -Sdc::setEdgeDisabledInstPorts(DisabledInstancePorts *disabled_inst) -{ - setEdgeDisabledInstPorts(disabled_inst, disabled_inst->instance()); -} - -void -Sdc::setEdgeDisabledInstPorts(DisabledPorts *disabled_port, - Instance *inst) -{ - if (disabled_port->all()) { - InstancePinIterator *pin_iter = network_->pinIterator(inst); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - // set_disable_timing instance does not disable timing checks. - setEdgeDisabledInstFrom(pin, false); - } - delete pin_iter; - } - - // Disable from pins. - LibertyPortSet::Iterator from_iter(disabled_port->from()); - while (from_iter.hasNext()) { - LibertyPort *from_port = from_iter.next(); - Pin *from_pin = network_->findPin(inst, from_port); - if (from_pin) - setEdgeDisabledInstFrom(from_pin, true); - } - - // Disable to pins. - LibertyPortSet::Iterator to_iter(disabled_port->to()); - while (to_iter.hasNext()) { - LibertyPort *to_port = to_iter.next(); - Pin *to_pin = network_->findPin(inst, to_port); - if (to_pin) { - if (network_->direction(to_pin)->isAnyOutput()) { - Vertex *vertex = graph_->pinDrvrVertex(to_pin); - if (vertex) { - VertexInEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - edge->setIsDisabledConstraint(true); - } - } - } - } - } - - // Disable from/to pins. - if (disabled_port->fromTo()) { - for (const LibertyPortPair &from_to : *disabled_port->fromTo()) { - const LibertyPort *from_port = from_to.first; - const LibertyPort *to_port = from_to.second; - Pin *from_pin = network_->findPin(inst, from_port); - Pin *to_pin = network_->findPin(inst, to_port); - if (from_pin && network_->direction(from_pin)->isAnyInput() - && to_pin) { - Vertex *from_vertex = graph_->pinLoadVertex(from_pin); - Vertex *to_vertex = graph_->pinDrvrVertex(to_pin); - if (from_vertex && to_vertex) { - VertexOutEdgeIterator edge_iter(from_vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->to(graph_) == to_vertex) - edge->setIsDisabledConstraint(true); - } - } - } - } - } -} - -void -Sdc::setEdgeDisabledInstFrom(Pin *from_pin, - bool disable_checks) -{ - if (network_->direction(from_pin)->isAnyInput()) { - Vertex *from_vertex = graph_->pinLoadVertex(from_pin); - if (from_vertex) { - VertexOutEdgeIterator edge_iter(from_vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (disable_checks - || !edge->role()->isTimingCheck()) - edge->setIsDisabledConstraint(true); - } - } - } -} - -void -Sdc::annotateGraphOutputDelays() -{ - for (OutputDelay *output_delay : output_delays_) { - for (const Pin *lpin : output_delay->leafPins()) - annotateGraphConstrained(lpin); - } -} - -void -Sdc::annotateGraphDataChecks() -{ - DataChecksMap::Iterator data_checks_iter(data_checks_to_map_); - while (data_checks_iter.hasNext()) { - DataCheckSet *checks = data_checks_iter.next(); - DataCheckSet::Iterator check_iter(checks); - // There may be multiple data checks on a single pin, - // but we only need to mark it as constrained once. - if (check_iter.hasNext()) { - DataCheck *check = check_iter.next(); - annotateGraphConstrained(check->to()); - } - } -} - -void -Sdc::annotateGraphConstrained(const PinSet *pins) -{ - PinSet::ConstIterator pin_iter(pins); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - annotateGraphConstrained(pin); - } -} - -void -Sdc::annotateGraphConstrained(const InstanceSet *insts) -{ - InstanceSet::ConstIterator inst_iter(insts); - while (inst_iter.hasNext()) { - const Instance *inst = inst_iter.next(); - annotateGraphConstrained(inst); - } -} - -void -Sdc::annotateGraphConstrained(const Instance *inst) -{ - InstancePinIterator *pin_iter = network_->pinIterator(inst); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - if (network_->direction(pin)->isAnyInput()) - annotateGraphConstrained(pin); - } - delete pin_iter; -} - -void -Sdc::annotateGraphConstrained(const Pin *pin) -{ - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - // Pin may be hierarchical and have no vertex. - if (vertex) - vertex->setIsConstrained(true); - if (bidirect_drvr_vertex) - bidirect_drvr_vertex->setIsConstrained(true); -} - -void -Sdc::annotateHierClkLatency() -{ - ClockLatencies::Iterator latency_iter(clk_latencies_); - while (latency_iter.hasNext()) { - ClockLatency *latency = latency_iter.next(); - const Pin *pin = latency->pin(); - if (pin && network_->isHierarchical(pin)) - annotateHierClkLatency(pin, latency); - } -} - -void -Sdc::annotateHierClkLatency(const Pin *hpin, - ClockLatency *latency) -{ - EdgesThruHierPinIterator edge_iter(hpin, network_, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - edge_clk_latency_[edge] = latency; - } -} - -ClockLatency * -Sdc::clockLatency(Edge *edge) const -{ - return edge_clk_latency_.findKey(edge); -} - -void -Sdc::clockLatency(Edge *edge, - const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &latency, - bool &exists) const -{ - ClockLatency *latencies = edge_clk_latency_.findKey(edge); - if (latencies) - latencies->delay(rf, min_max, latency, exists); - else { - latency = 0.0; - exists = false; - } -} - -//////////////////////////////////////////////////////////////// - -void -Sdc::removeGraphAnnotations() -{ - VertexIterator vertex_iter(graph_); - while (vertex_iter.hasNext()) { - Vertex *vertex = vertex_iter.next(); - vertex->setIsDisabledConstraint(false); - vertex->setIsConstrained(false); - - VertexOutEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - edge->setIsDisabledConstraint(false); - } - } - edge_clk_latency_.clear(); -} - -void -Sdc::searchPreamble() -{ - ensureClkHpinDisables(); - ensureClkGroupExclusions(); -} - -} // namespace diff --git a/sdc/Variables.cc b/sdc/Variables.cc index 85e4220ba..81650eb4a 100644 --- a/sdc/Variables.cc +++ b/sdc/Variables.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,31 +26,6 @@ namespace sta { -Variables::Variables() : - crpr_enabled_(true), - crpr_mode_(CrprMode::same_pin), - propagate_gated_clock_enable_(true), - preset_clr_arcs_enabled_(false), - cond_default_arcs_enabled_(true), - bidirect_net_paths_enabled_(false), - bidirect_inst_paths_enabled_(false), - recovery_removal_checks_enabled_(true), - gated_clk_checks_enabled_(true), - clk_thru_tristate_enabled_(false), - dynamic_loop_breaking_(false), - propagate_all_clks_(false), - use_default_arrival_clock_(false), - pocv_enabled_(false), - boolean_props_as_int_(true), - direction_props_short_(false), - liberty_line_debug_(false), - no_inv_delay_calc_(false), - no_inv_power_calc_(false), - strip_escaped_bus_(false), - enable_collections_(false) -{ -} - void Variables::setCrprEnabled(bool enabled) { @@ -87,12 +62,6 @@ Variables::setBidirectInstPathsEnabled(bool enabled) bidirect_inst_paths_enabled_ = enabled; } -void -Variables::setBidirectNetPathsEnabled(bool enabled) -{ - bidirect_net_paths_enabled_ = enabled; -} - void Variables::setRecoveryRemovalChecksEnabled(bool enabled) { @@ -129,10 +98,24 @@ Variables::setUseDefaultArrivalClock(bool enable) use_default_arrival_clock_ = enable; } +//////////////////////////////////////////////////////////////// + +bool +Variables::pocvEnabled() const +{ + return pocv_mode_ != PocvMode::scalar; +} + +void +Variables::setPocvMode(PocvMode mode) +{ + pocv_mode_ = mode; +} + void -Variables::setPocvEnabled(bool enabled) +Variables::setPocvQuantile(float quantile) { - pocv_enabled_ = enabled; + pocv_quantile_ = quantile; } - -} // namespace + +} // namespace sta diff --git a/sdc/WriteSdc.cc b/sdc/WriteSdc.cc index b3095d915..9ae97feac 100644 --- a/sdc/WriteSdc.cc +++ b/sdc/WriteSdc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,60 +24,63 @@ #include "WriteSdc.hh" -#include #include #include +#include +#include +#include +#include -#include "Zlib.hh" -#include "Report.hh" +#include "ClockGroups.hh" +#include "ClockInsertion.hh" +#include "ClockLatency.hh" +#include "ContainerHelpers.hh" +#include "DataCheck.hh" +#include "DeratingFactors.hh" +#include "DisabledPorts.hh" #include "Error.hh" -#include "Units.hh" -#include "Transition.hh" +#include "ExceptionPath.hh" +#include "Format.hh" +#include "Fuzzy.hh" +#include "Graph.hh" +#include "GraphCmp.hh" +#include "InputDrive.hh" #include "Liberty.hh" -#include "Wireload.hh" #include "Network.hh" -#include "PortDirection.hh" #include "NetworkCmp.hh" -#include "Graph.hh" -#include "GraphCmp.hh" -#include "RiseFallValues.hh" #include "PortDelay.hh" -#include "ExceptionPath.hh" +#include "PortDirection.hh" #include "PortExtCap.hh" -#include "DisabledPorts.hh" -#include "ClockGroups.hh" -#include "ClockInsertion.hh" -#include "ClockLatency.hh" -#include "InputDrive.hh" -#include "DataCheck.hh" -#include "DeratingFactors.hh" +#include "Report.hh" +#include "RiseFallValues.hh" +#include "Scene.hh" #include "Sdc.hh" -#include "Fuzzy.hh" #include "StaState.hh" -#include "Corner.hh" +#include "Transition.hh" +#include "Units.hh" #include "Variables.hh" +#include "Wireload.hh" #include "WriteSdcPvt.hh" +#include "Zlib.hh" namespace sta { -using std::string; +using ClockSenseSet = std::set; +using ClockSenseSeq = std::vector; -typedef Set ClockSenseSet; -typedef Vector ClockSenseSeq; - -static const char * +static std::string_view transRiseFallFlag(const RiseFall *rf); -static const char * +static std::string_view transRiseFallFlag(const RiseFallBoth *rf); -static const char * +static std::string_view minMaxFlag(const MinMaxAll *min_max); -static const char * +static std::string_view minMaxFlag(const MinMax *min_max); -static const char * +static std::string_view earlyLateFlag(const MinMax *early_late); -static const char * +static std::string_view setupHoldFlag(const MinMax *min_max); -static const char * +static std::string_view timingDerateTypeKeyword(TimingDerateType type); //////////////////////////////////////////////////////////////// @@ -85,8 +88,8 @@ timingDerateTypeKeyword(TimingDerateType type); class WriteSdcObject { public: - WriteSdcObject() {} - virtual ~WriteSdcObject() {} + WriteSdcObject() = default; + virtual ~WriteSdcObject() = default; virtual void write() const = 0; }; @@ -94,8 +97,8 @@ class WriteGetPort : public WriteSdcObject { public: WriteGetPort(const Port *port, - const WriteSdc *writer); - virtual void write() const; + const WriteSdc *writer); + void write() const override; private: const Port *port_; @@ -119,10 +122,10 @@ class WriteGetPinAndClkKey : public WriteSdcObject { public: WriteGetPinAndClkKey(const Pin *pin, - bool map_hpin_to_drvr, - const Clock *clk, - const WriteSdc *writer); - virtual void write() const; + bool map_hpin_to_drvr, + const Clock *clk, + const WriteSdc *writer); + void write() const override; private: const Pin *pin_; @@ -132,9 +135,9 @@ class WriteGetPinAndClkKey : public WriteSdcObject }; WriteGetPinAndClkKey::WriteGetPinAndClkKey(const Pin *pin, - bool map_hpin_to_drvr, - const Clock *clk, - const WriteSdc *writer) : + bool map_hpin_to_drvr, + const Clock *clk, + const WriteSdc *writer) : pin_(pin), map_hpin_to_drvr_(map_hpin_to_drvr), clk_(clk), @@ -146,7 +149,7 @@ void WriteGetPinAndClkKey::write() const { writer_->writeClockKey(clk_); - gzprintf(writer_->stream(), " "); + sta::print(writer_->stream(), " "); writer_->writeGetPin(pin_, map_hpin_to_drvr_); } @@ -154,9 +157,9 @@ class WriteGetPin : public WriteSdcObject { public: WriteGetPin(const Pin *pin, - bool map_hpin_to_drvr, - const WriteSdc *writer); - virtual void write() const; + bool map_hpin_to_drvr, + const WriteSdc *writer); + void write() const override; private: const Pin *pin_; @@ -165,7 +168,7 @@ class WriteGetPin : public WriteSdcObject }; WriteGetPin::WriteGetPin(const Pin *pin, - bool map_hpin_to_drvr, + bool map_hpin_to_drvr, const WriteSdc *writer) : pin_(pin), map_hpin_to_drvr_(map_hpin_to_drvr), @@ -183,8 +186,8 @@ class WriteGetNet : public WriteSdcObject { public: WriteGetNet(const Net *net, - const WriteSdc *writer); - virtual void write() const; + const WriteSdc *writer); + void write() const override; private: const Net *net_; @@ -192,7 +195,7 @@ class WriteGetNet : public WriteSdcObject }; WriteGetNet::WriteGetNet(const Net *net, - const WriteSdc *writer) : + const WriteSdc *writer) : net_(net), writer_(writer) { @@ -208,8 +211,8 @@ class WriteGetInstance : public WriteSdcObject { public: WriteGetInstance(const Instance *inst, - const WriteSdc *writer); - virtual void write() const; + const WriteSdc *writer); + void write() const override; private: const Instance *inst_; @@ -217,7 +220,7 @@ class WriteGetInstance : public WriteSdcObject }; WriteGetInstance::WriteGetInstance(const Instance *inst, - const WriteSdc *writer) : + const WriteSdc *writer) : inst_(inst), writer_(writer) { @@ -233,8 +236,8 @@ class WriteGetLibCell : public WriteSdcObject { public: WriteGetLibCell(const LibertyCell *cell, - const WriteSdc *writer); - virtual void write() const; + const WriteSdc *writer); + void write() const override; private: const LibertyCell *cell_; @@ -242,7 +245,7 @@ class WriteGetLibCell : public WriteSdcObject }; WriteGetLibCell::WriteGetLibCell(const LibertyCell *cell, - const WriteSdc *writer) : + const WriteSdc *writer) : cell_(cell), writer_(writer) { @@ -258,8 +261,8 @@ class WriteGetClock : public WriteSdcObject { public: WriteGetClock(const Clock *clk, - const WriteSdc *writer); - virtual void write() const; + const WriteSdc *writer); + void write() const override; private: const Clock *clk_; @@ -267,7 +270,7 @@ class WriteGetClock : public WriteSdcObject }; WriteGetClock::WriteGetClock(const Clock *clk, - const WriteSdc *writer) : + const WriteSdc *writer) : clk_(clk), writer_(writer) { @@ -282,29 +285,30 @@ WriteGetClock::write() const //////////////////////////////////////////////////////////////// void -writeSdc(Instance *instance, - const char *filename, - const char *creator, - bool map_hpins, - bool native, - int digits, +writeSdc(const Sdc *sdc, + Instance *instance, + std::string_view filename, + std::string_view creator, + bool map_hpins, + bool native, + int digits, bool gzip, - bool no_timestamp, - Sdc *sdc) + bool no_timestamp) { - WriteSdc writer(instance, creator, map_hpins, native, - digits, no_timestamp, sdc); + WriteSdc writer(sdc, instance, creator, map_hpins, native, + digits, no_timestamp); writer.write(filename, gzip); } -WriteSdc::WriteSdc(Instance *instance, - const char *creator, - bool map_hpins, - bool native, - int digits, - bool no_timestamp, - Sdc *sdc) : +WriteSdc::WriteSdc(const Sdc *sdc, + Instance *instance, + std::string_view creator, + bool map_hpins, + bool native, + int digits, + bool no_timestamp) : StaState(sdc), + sdc_(sdc), instance_(instance), creator_(creator), map_hpins_(map_hpins), @@ -312,17 +316,13 @@ WriteSdc::WriteSdc(Instance *instance, digits_(digits), no_timestamp_(no_timestamp), top_instance_(instance == sdc_network_->topInstance()), - instance_name_length_(strlen(sdc_network_->pathName(instance))), + instance_name_length_(sdc_network_->pathName(instance).size()), cell_(sdc_network_->cell(instance)) { } -WriteSdc::~WriteSdc() -{ -} - void -WriteSdc::write(const char *filename, +WriteSdc::write(std::string_view filename, bool gzip) { openFile(filename, gzip); @@ -335,10 +335,11 @@ WriteSdc::write(const char *filename, } void -WriteSdc::openFile(const char *filename, +WriteSdc::openFile(std::string_view filename, bool gzip) { - stream_ = gzopen(filename, gzip ? "wb" : "wT"); + std::string filename_str(filename); + stream_ = gzopen(filename_str.c_str(), gzip ? "wb" : "wT"); if (stream_ == nullptr) throw FileNotWritable(filename); } @@ -353,18 +354,18 @@ void WriteSdc::writeHeader() const { writeCommentSeparator(); - gzprintf(stream_, "# Created by %s\n", creator_); + sta::print(stream_, "# Created by {}\n", creator_); if (!no_timestamp_) { time_t now; time(&now); - char *time_str = ctime(&now); - // Remove trailing \n. - time_str[strlen(time_str) - 1] = '\0'; - gzprintf(stream_, "# %s\n", time_str); + std::string time_str(ctime(&now)); + if (!time_str.empty() && time_str.back() == '\n') + time_str.pop_back(); + sta::print(stream_, "# {}\n", time_str); } writeCommentSeparator(); - gzprintf(stream_, "current_design %s\n", sdc_network_->name(cell_)); + sta::print(stream_, "current_design {}\n", sdc_network_->name(cell_)); } //////////////////////////////////////////////////////////////// @@ -401,9 +402,9 @@ WriteSdc::writeClocks() const writeClockSlews(clk); writeClockUncertainty(clk); if (clk->isPropagated()) { - gzprintf(stream_, "set_propagated_clock "); + sta::print(stream_, "set_propagated_clock "); writeGetClock(clk); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } } @@ -411,69 +412,69 @@ WriteSdc::writeClocks() const void WriteSdc::writeClock(Clock *clk) const { - gzprintf(stream_, "create_clock -name %s", - clk->name()); + sta::print(stream_, "create_clock -name {}", + clk->name()); if (clk->addToPins()) - gzprintf(stream_, " -add"); - gzprintf(stream_, " -period "); + sta::print(stream_, " -add"); + sta::print(stream_, " -period "); float period = clk->period(); writeTime(period); - FloatSeq *waveform = clk->waveform(); - if (!(waveform->size() == 2 - && (*waveform)[0] == 0.0 - && fuzzyEqual((*waveform)[1], period / 2.0))) { - gzprintf(stream_, " -waveform "); + const FloatSeq &waveform = clk->waveform(); + if (!(waveform.size() == 2 + && waveform[0] == 0.0 + && fuzzyEqual(waveform[1], period / 2.0))) { + sta::print(stream_, " -waveform "); writeFloatSeq(waveform, scaleTime(1.0)); } writeCmdComment(clk); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeClockPins(clk); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void WriteSdc::writeGeneratedClock(Clock *clk) const { - gzprintf(stream_, "create_generated_clock -name %s", - clk->name()); + sta::print(stream_, "create_generated_clock -name {}", + clk->name()); if (clk->addToPins()) - gzprintf(stream_, " -add"); - gzprintf(stream_, " -source "); + sta::print(stream_, " -add"); + sta::print(stream_, " -source "); writeGetPin(clk->srcPin(), true); Clock *master = clk->masterClk(); if (master && !clk->masterClkInfered()) { - gzprintf(stream_, " -master_clock "); + sta::print(stream_, " -master_clock "); writeGetClock(master); } if (clk->combinational()) - gzprintf(stream_, " -combinational"); + sta::print(stream_, " -combinational"); int divide_by = clk->divideBy(); if (divide_by != 0) - gzprintf(stream_, " -divide_by %d", divide_by); + sta::print(stream_, " -divide_by {}", divide_by); int multiply_by = clk->multiplyBy(); if (multiply_by != 0) - gzprintf(stream_, " -multiply_by %d", multiply_by); + sta::print(stream_, " -multiply_by {}", multiply_by); float duty_cycle = clk->dutyCycle(); if (duty_cycle != 0.0) { - gzprintf(stream_, " -duty_cycle "); + sta::print(stream_, " -duty_cycle "); writeFloat(duty_cycle); } if (clk->invert()) - gzprintf(stream_, " -invert"); - IntSeq *edges = clk->edges(); - if (edges && !edges->empty()) { - gzprintf(stream_, " -edges "); + sta::print(stream_, " -invert"); + const IntSeq &edges = clk->edges(); + if (!edges.empty()) { + sta::print(stream_, " -edges "); writeIntSeq(edges); - FloatSeq *edge_shifts = clk->edgeShifts(); - if (edge_shifts && !edge_shifts->empty()) { - gzprintf(stream_, " -edge_shift "); + const FloatSeq &edge_shifts = clk->edgeShifts(); + if (!edge_shifts.empty()) { + sta::print(stream_, " -edge_shift "); writeFloatSeq(edge_shifts, scaleTime(1.0)); } } writeCmdComment(clk); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeClockPins(clk); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void @@ -482,7 +483,7 @@ WriteSdc::writeClockPins(const Clock *clk) const const PinSet &pins = clk->pins(); if (!pins.empty()) { if (pins.size() > 1) - gzprintf(stream_, "\\\n "); + sta::print(stream_, "\\\n "); writeGetPins(&pins, true); } } @@ -517,29 +518,24 @@ WriteSdc::writeClockUncertainty(const Clock *clk) const void WriteSdc::writeClockUncertainty(const Clock *clk, - const char *setup_hold, - float value) const + std::string_view setup_hold, + float value) const { - gzprintf(stream_, "set_clock_uncertainty %s", setup_hold); + sta::print(stream_, "set_clock_uncertainty {}", setup_hold); writeTime(value); - gzprintf(stream_, " %s\n", clk->name()); + sta::print(stream_, " {}\n", clk->name()); } void WriteSdc::writeClockUncertaintyPins() const { - PinClockUncertaintyMap::Iterator iter(sdc_->pin_clk_uncertainty_map_); - while (iter.hasNext()) { - const Pin *pin; - ClockUncertainties *uncertainties; - iter.next(pin, uncertainties); + for (const auto [pin, uncertainties] : sdc_->pin_clk_uncertainty_map_) writeClockUncertaintyPin(pin, uncertainties); - } } void WriteSdc::writeClockUncertaintyPin(const Pin *pin, - ClockUncertainties *uncertainties) + ClockUncertainties *uncertainties) const { float setup; @@ -559,38 +555,36 @@ WriteSdc::writeClockUncertaintyPin(const Pin *pin, } void WriteSdc::writeClockUncertaintyPin(const Pin *pin, - const char *setup_hold, - float value) const + std::string_view setup_hold, + float value) const { - gzprintf(stream_, "set_clock_uncertainty %s", setup_hold); + sta::print(stream_, "set_clock_uncertainty {}", setup_hold); writeTime(value); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetPin(pin, true); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void WriteSdc::writeClockLatencies() const { - ClockLatencies::Iterator latency_iter(sdc_->clockLatencies()); - while (latency_iter.hasNext()) { - ClockLatency *latency = latency_iter.next(); + for (ClockLatency *latency : *sdc_->clockLatencies()) { const Pin *pin = latency->pin(); const Clock *clk = latency->clock(); if (pin && clk) { WriteGetPinAndClkKey write_pin(pin, true, clk, this); writeRiseFallMinMaxTimeCmd("set_clock_latency", latency->delays(), - write_pin); + write_pin); } else if (pin) { WriteGetPin write_pin(pin, true, this); writeRiseFallMinMaxTimeCmd("set_clock_latency", latency->delays(), - write_pin); + write_pin); } else if (clk) { WriteGetClock write_clk(clk, this); writeRiseFallMinMaxTimeCmd("set_clock_latency", latency->delays(), - write_clk); + write_clk); } } } @@ -618,42 +612,36 @@ WriteSdc::writeClockInsertions() const void WriteSdc::writeClockInsertion(ClockInsertion *insert, - WriteSdcObject &write_obj) const + WriteSdcObject &write_obj) const { RiseFallMinMax *early_values = insert->delays(EarlyLate::early()); RiseFallMinMax *late_values = insert->delays(EarlyLate::late()); if (early_values->equal(late_values)) writeRiseFallMinMaxTimeCmd("set_clock_latency -source", - late_values, write_obj); + late_values, write_obj); else { writeRiseFallMinMaxTimeCmd("set_clock_latency -source -early", - early_values, write_obj); + early_values, write_obj); writeRiseFallMinMaxTimeCmd("set_clock_latency -source -late", - late_values, write_obj); + late_values, write_obj); } } void WriteSdc::writePropagatedClkPins() const { - PinSet::Iterator pin_iter(sdc_->propagated_clk_pins_); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - gzprintf(stream_, "set_propagated_clock "); + for (const Pin *pin : sdc_->propagated_clk_pins_) { + sta::print(stream_, "set_propagated_clock "); writeGetPin(pin, true); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } void WriteSdc::writeInterClockUncertainties() const { - InterClockUncertaintySet::Iterator - uncertainty_iter(sdc_->inter_clk_uncertainties_); - while (uncertainty_iter.hasNext()) { - InterClockUncertainty *uncertainty = uncertainty_iter.next(); + for (InterClockUncertainty *uncertainty : sdc_->inter_clk_uncertainties_) writeInterClockUncertainty(uncertainty); - } } void @@ -669,35 +657,35 @@ writeInterClockUncertainty(InterClockUncertainty *uncertainty) const float value; if (src_rise->equal(src_fall) && src_rise->isOneValue(value)) { - gzprintf(stream_, "set_clock_uncertainty -from "); + sta::print(stream_, "set_clock_uncertainty -from "); writeGetClock(src_clk); - gzprintf(stream_, " -to "); + sta::print(stream_, " -to "); writeGetClock(tgt_clk); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeTime(value); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } else { for (auto src_rf : RiseFall::range()) { for (auto tgt_rf : RiseFall::range()) { - for (auto setup_hold : SetupHold::range()) { - float value; - bool exists; - sdc_->clockUncertainty(src_clk, src_rf, tgt_clk, tgt_rf, - setup_hold, value, exists); - if (exists) { - gzprintf(stream_, "set_clock_uncertainty -%s_from ", - src_rf == RiseFall::rise() ? "rise" : "fall"); - writeGetClock(uncertainty->src()); - gzprintf(stream_, " -%s_to ", - tgt_rf == RiseFall::rise() ? "rise" : "fall"); - writeGetClock(uncertainty->target()); - gzprintf(stream_, " %s ", - setupHoldFlag(setup_hold)); - writeTime(value); - gzprintf(stream_, "\n"); - } - } + for (auto setup_hold : SetupHold::range()) { + float value; + bool exists; + sdc_->clockUncertainty(src_clk, src_rf, tgt_clk, tgt_rf, + setup_hold, value, exists); + if (exists) { + sta::print(stream_, "set_clock_uncertainty -{}_from ", + src_rf == RiseFall::rise() ? "rise" : "fall"); + writeGetClock(uncertainty->src()); + sta::print(stream_, " -{}_to ", + tgt_rf == RiseFall::rise() ? "rise" : "fall"); + writeGetClock(uncertainty->target()); + sta::print(stream_, " {} ", + setupHoldFlag(setup_hold)); + writeTime(value); + sta::print(stream_, "\n"); + } + } } } } @@ -714,11 +702,8 @@ WriteSdc::writeInputDelays() const PortDelayLess port_delay_less(sdc_network_); sort(delays, port_delay_less); - PortDelaySeq::Iterator delay_iter(delays); - while (delay_iter.hasNext()) { - PortDelay *input_delay = delay_iter.next(); + for (PortDelay *input_delay : delays) writePortDelay(input_delay, true, "set_input_delay"); - } } void @@ -737,20 +722,20 @@ WriteSdc::writeOutputDelays() const void WriteSdc::writePortDelay(PortDelay *port_delay, - bool is_input_delay, - const char *sdc_cmd) const + bool is_input_delay, + std::string_view sdc_cmd) const { RiseFallMinMax *delays = port_delay->delays(); float rise_min, rise_max, fall_min, fall_max; bool rise_min_exists, rise_max_exists, fall_min_exists, fall_max_exists; delays->value(RiseFall::rise(), MinMax::min(), - rise_min, rise_min_exists); + rise_min, rise_min_exists); delays->value(RiseFall::rise(), MinMax::max(), - rise_max, rise_max_exists); + rise_max, rise_max_exists); delays->value(RiseFall::fall(), MinMax::min(), - fall_min, fall_min_exists); + fall_min, fall_min_exists); delays->value(RiseFall::fall(), MinMax::max(), - fall_max, fall_max_exists); + fall_max, fall_max_exists); // Try to compress the four port delays. if (rise_min_exists && rise_max_exists @@ -760,72 +745,72 @@ WriteSdc::writePortDelay(PortDelay *port_delay, && fall_min == rise_min && fall_max == rise_min) writePortDelay(port_delay, is_input_delay, rise_min, - RiseFallBoth::riseFall(), MinMaxAll::all(), sdc_cmd); + RiseFallBoth::riseFall(), MinMaxAll::all(), sdc_cmd); else if (rise_min_exists - && rise_max_exists - && rise_max == rise_min - && fall_min_exists - && fall_max_exists - && fall_min == fall_max) { + && rise_max_exists + && rise_max == rise_min + && fall_min_exists + && fall_max_exists + && fall_min == fall_max) { writePortDelay(port_delay, is_input_delay, rise_min, - RiseFallBoth::rise(), MinMaxAll::all(), sdc_cmd); + RiseFallBoth::rise(), MinMaxAll::all(), sdc_cmd); writePortDelay(port_delay, is_input_delay, fall_min, - RiseFallBoth::fall(), MinMaxAll::all(), sdc_cmd); + RiseFallBoth::fall(), MinMaxAll::all(), sdc_cmd); } else if (rise_min_exists - && fall_min_exists - && rise_min == fall_min - && rise_max_exists - && fall_max_exists - && rise_max == fall_max) { + && fall_min_exists + && rise_min == fall_min + && rise_max_exists + && fall_max_exists + && rise_max == fall_max) { writePortDelay(port_delay, is_input_delay, rise_min, - RiseFallBoth::riseFall(), MinMaxAll::min(), sdc_cmd); + RiseFallBoth::riseFall(), MinMaxAll::min(), sdc_cmd); writePortDelay(port_delay, is_input_delay, rise_max, - RiseFallBoth::riseFall(), MinMaxAll::max(), sdc_cmd); + RiseFallBoth::riseFall(), MinMaxAll::max(), sdc_cmd); } else { if (rise_min_exists) writePortDelay(port_delay, is_input_delay, rise_min, - RiseFallBoth::rise(), MinMaxAll::min(), sdc_cmd); + RiseFallBoth::rise(), MinMaxAll::min(), sdc_cmd); if (rise_max_exists) writePortDelay(port_delay, is_input_delay, rise_max, - RiseFallBoth::rise(), MinMaxAll::max(), sdc_cmd); + RiseFallBoth::rise(), MinMaxAll::max(), sdc_cmd); if (fall_min_exists) writePortDelay(port_delay, is_input_delay, fall_min, - RiseFallBoth::fall(), MinMaxAll::min(), sdc_cmd); + RiseFallBoth::fall(), MinMaxAll::min(), sdc_cmd); if (fall_max_exists) writePortDelay(port_delay, is_input_delay, fall_max, - RiseFallBoth::fall(), MinMaxAll::max(), sdc_cmd); + RiseFallBoth::fall(), MinMaxAll::max(), sdc_cmd); } } void WriteSdc::writePortDelay(PortDelay *port_delay, - bool is_input_delay, - float delay, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - const char *sdc_cmd) const + bool is_input_delay, + float delay, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + std::string_view sdc_cmd) const { - gzprintf(stream_, "%s ", sdc_cmd); + sta::print(stream_, "{} ", sdc_cmd); writeTime(delay); const ClockEdge *clk_edge = port_delay->clkEdge(); if (clk_edge) { writeClockKey(clk_edge->clock()); if (clk_edge->transition() == RiseFall::fall()) - gzprintf(stream_, " -clock_fall"); + sta::print(stream_, " -clock_fall"); } - gzprintf(stream_, "%s%s -add_delay ", - transRiseFallFlag(rf), - minMaxFlag(min_max)); + sta::print(stream_, "{}{} -add_delay ", + transRiseFallFlag(rf), + minMaxFlag(min_max)); const Pin *ref_pin = port_delay->refPin(); if (ref_pin) { - gzprintf(stream_, "-reference_pin "); + sta::print(stream_, "-reference_pin "); writeGetPin(ref_pin, true); - gzprintf(stream_, " "); + sta::print(stream_, " "); } writeGetPin(port_delay->pin(), is_input_delay); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } class PinClockPairNameLess @@ -833,7 +818,7 @@ class PinClockPairNameLess public: PinClockPairNameLess(const Network *network); bool operator()(const PinClockPair &pin_clk1, - const PinClockPair &pin_clk2) const; + const PinClockPair &pin_clk2) const; private: PinPathNameLess pin_less_; @@ -846,7 +831,7 @@ PinClockPairNameLess::PinClockPairNameLess(const Network *network) : bool PinClockPairNameLess::operator()(const PinClockPair &pin_clk1, - const PinClockPair &pin_clk2) const + const PinClockPair &pin_clk2) const { const Pin *pin1 = pin_clk1.first; const Pin *pin2 = pin_clk2.first; @@ -854,15 +839,15 @@ PinClockPairNameLess::operator()(const PinClockPair &pin_clk1, const Clock *clk2 = pin_clk2.second; return pin_less_(pin1, pin2) || (pin1 == pin2 - && ((clk1 == nullptr && clk2) - || (clk1 && clk2 - && clk1->index() < clk2->index()))); + && ((clk1 == nullptr && clk2) + || (clk1 && clk2 + && clk1->index() < clk2->index()))); } void WriteSdc::writeClockSenses() const { - Vector pin_clks; + std::vector pin_clks; for (const auto& [pin_clk, sense] : sdc_->clk_sense_map_) pin_clks.push_back(pin_clk); @@ -872,7 +857,7 @@ WriteSdc::writeClockSenses() const for (auto pin_clk : pin_clks) { ClockSense sense; bool exists; - sdc_->clk_sense_map_.findKey(pin_clk, sense, exists); + findKeyValue(sdc_->clk_sense_map_, pin_clk, sense, exists); if (exists) writeClockSense(pin_clk, sense); } @@ -880,37 +865,37 @@ WriteSdc::writeClockSenses() const void WriteSdc::writeClockSense(PinClockPair &pin_clk, - ClockSense sense) const + ClockSense sense) const { - const char *flag = nullptr; + std::string_view flag; if (sense == ClockSense::positive) flag = "-positive"; else if (sense == ClockSense::negative) flag = "-negative"; else if (sense == ClockSense::stop) flag = "-stop_propagation"; - gzprintf(stream_, "set_sense -type clock %s ", flag); + sta::print(stream_, "set_sense -type clock {} ", flag); const Clock *clk = pin_clk.second; if (clk) { - gzprintf(stream_, "-clock "); + sta::print(stream_, "-clock "); writeGetClock(clk); - gzprintf(stream_, " "); + sta::print(stream_, " "); } writeGetPin(pin_clk.first, true); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } class ClockGroupLess { public: bool operator()(const ClockGroup *clk_group1, - const ClockGroup *clk_group2) const; + const ClockGroup *clk_group2) const; }; bool ClockGroupLess::operator()(const ClockGroup *clk_group1, - const ClockGroup *clk_group2) const + const ClockGroup *clk_group2) const { size_t size1 = clk_group1->size(); size_t size2 = clk_group2->size(); @@ -929,17 +914,17 @@ ClockGroupLess::operator()(const ClockGroup *clk_group1, clks2.push_back(clk2); sort(clks2, ClockNameLess()); - ClockSeq::Iterator clk_iter3(clks1); - ClockSeq::Iterator clk_iter4(clks2); - while (clk_iter3.hasNext() - && clk_iter4.hasNext()) { - Clock *clk1 = clk_iter3.next(); - Clock *clk2 = clk_iter4.next(); - int cmp = strcmp(clk1->name(), clk2->name()); + ClockSeq::iterator clk_iter3 = clks1.begin(); + ClockSeq::iterator clk_iter4 = clks2.begin(); + while (clk_iter3 != clks1.end() + && clk_iter4 != clks2.end()) { + Clock *clk1 = *clk_iter3++; + Clock *clk2 = *clk_iter4++; + int cmp = clk1->name().compare(clk2->name()); if (cmp < 0) - return true; + return true; else if (cmp > 0) - return false; + return false; } return false; } @@ -948,41 +933,36 @@ ClockGroupLess::operator()(const ClockGroup *clk_group1, void WriteSdc::writeClockGroups() const { - for (const auto [name, clk_groups] : sdc_->clk_groups_name_map_) + for (const auto &[name, clk_groups] : sdc_->clk_groups_name_map_) writeClockGroups(clk_groups); } void WriteSdc::writeClockGroups(ClockGroups *clk_groups) const { - gzprintf(stream_, "set_clock_groups -name %s ", clk_groups->name()); + sta::print(stream_, "set_clock_groups -name {} ", clk_groups->name()); if (clk_groups->logicallyExclusive()) - gzprintf(stream_, "-logically_exclusive \\\n"); + sta::print(stream_, "-logically_exclusive \\\n"); else if (clk_groups->physicallyExclusive()) - gzprintf(stream_, "-physically_exclusive \\\n"); + sta::print(stream_, "-physically_exclusive \\\n"); else if (clk_groups->asynchronous()) - gzprintf(stream_, "-asynchronous \\\n"); + sta::print(stream_, "-asynchronous \\\n"); if (clk_groups->allowPaths()) - gzprintf(stream_, "-allow_paths \\\n"); - Vector groups; - ClockGroupSet::Iterator group_iter1(clk_groups->groups()); - while (group_iter1.hasNext()) { - ClockGroup *clk_group = group_iter1.next(); + sta::print(stream_, "-allow_paths \\\n"); + std::vector groups; + for (ClockGroup *clk_group : *clk_groups->groups()) groups.push_back(clk_group); - } sort(groups, ClockGroupLess()); bool first = true; - Vector::Iterator group_iter2(groups); - while (group_iter2.hasNext()) { - ClockGroup *clk_group = group_iter2.next(); + for (ClockGroup *clk_group : groups) { if (!first) - gzprintf(stream_, "\\\n"); - gzprintf(stream_, " -group "); + sta::print(stream_, "\\\n"); + sta::print(stream_, " -group "); writeGetClocks(clk_group); first = false; } writeCmdComment(clk_groups); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } //////////////////////////////////////////////////////////////// @@ -1006,46 +986,46 @@ WriteSdc::writeDisabledCells() const for (const DisabledCellPorts *disable : disables) { const LibertyCell *cell = disable->cell(); if (disable->all()) { - gzprintf(stream_, "set_disable_timing "); + sta::print(stream_, "set_disable_timing "); writeGetLibCell(cell); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } if (disable->fromTo()) { LibertyPortPairSeq from_tos = sortByName(disable->fromTo()); for (const LibertyPortPair &from_to : from_tos) { - const LibertyPort *from = from_to.first; - const LibertyPort *to = from_to.second; - gzprintf(stream_, "set_disable_timing -from {%s} -to {%s} ", - from->name(), - to->name()); - writeGetLibCell(cell); - gzprintf(stream_, "\n"); + const LibertyPort *from = from_to.first; + const LibertyPort *to = from_to.second; + sta::print(stream_, "set_disable_timing -from {{{}}} -to {{{}}} ", + from->name(), + to->name()); + writeGetLibCell(cell); + sta::print(stream_, "\n"); } } if (disable->from()) { LibertyPortSeq from = sortByName(disable->from()); for (const LibertyPort *from_port : from) { - gzprintf(stream_, "set_disable_timing -from {%s} ", - from_port->name()); - writeGetLibCell(cell); - gzprintf(stream_, "\n"); + sta::print(stream_, "set_disable_timing -from {{{}}} ", + from_port->name()); + writeGetLibCell(cell); + sta::print(stream_, "\n"); } } if (disable->to()) { LibertyPortSeq to = sortByName(disable->to()); for (const LibertyPort *to_port : to) { - gzprintf(stream_, "set_disable_timing -to {%s} ", - to_port->name()); - writeGetLibCell(cell); - gzprintf(stream_, "\n"); + sta::print(stream_, "set_disable_timing -to {{{}}} ", + to_port->name()); + writeGetLibCell(cell); + sta::print(stream_, "\n"); } } if (disable->timingArcSets()) { // The only syntax to disable timing arc sets disables all of the // cell's timing arc sets. - gzprintf(stream_, "set_disable_timing "); + sta::print(stream_, "set_disable_timing "); writeGetTimingArcsOfOjbects(cell); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } } @@ -1055,9 +1035,9 @@ WriteSdc::writeDisabledPorts() const { const PortSeq ports = sortByName(sdc_->disabledPorts(), sdc_network_); for (const Port *port : ports) { - gzprintf(stream_, "set_disable_timing "); + sta::print(stream_, "set_disable_timing "); writeGetPort(port); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } @@ -1066,9 +1046,9 @@ WriteSdc::writeDisabledLibPorts() const { LibertyPortSeq ports = sortByName(sdc_->disabledLibPorts()); for (LibertyPort *port : ports) { - gzprintf(stream_, "set_disable_timing "); + sta::print(stream_, "set_disable_timing "); writeGetLibPin(port); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } @@ -1080,38 +1060,38 @@ WriteSdc::writeDisabledInstances() const for (DisabledInstancePorts *disable : disables) { Instance *inst = disable->instance(); if (disable->all()) { - gzprintf(stream_, "set_disable_timing "); + sta::print(stream_, "set_disable_timing "); writeGetInstance(inst); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } else if (disable->fromTo()) { LibertyPortPairSeq from_tos = sortByName(disable->fromTo()); for (LibertyPortPair &from_to : from_tos) { - const LibertyPort *from_port = from_to.first; - const LibertyPort *to_port = from_to.second; - gzprintf(stream_, "set_disable_timing -from {%s} -to {%s} ", - from_port->name(), - to_port->name()); - writeGetInstance(inst); - gzprintf(stream_, "\n"); + const LibertyPort *from_port = from_to.first; + const LibertyPort *to_port = from_to.second; + sta::print(stream_, "set_disable_timing -from {{{}}} -to {{{}}} ", + from_port->name(), + to_port->name()); + writeGetInstance(inst); + sta::print(stream_, "\n"); } } if (disable->from()) { LibertyPortSeq from = sortByName(disable->from()); for (const LibertyPort *from_port : from) { - gzprintf(stream_, "set_disable_timing -from {%s} ", - from_port->name()); - writeGetInstance(inst); - gzprintf(stream_, "\n"); + sta::print(stream_, "set_disable_timing -from {{{}}} ", + from_port->name()); + writeGetInstance(inst); + sta::print(stream_, "\n"); } } if (disable->to()) { LibertyPortSeq to = sortByName(disable->to()); for (const LibertyPort *to_port : to) { - gzprintf(stream_, "set_disable_timing -to {%s} ", - to_port->name()); - writeGetInstance(inst); - gzprintf(stream_, "\n"); + sta::print(stream_, "set_disable_timing -to {{{}}} ", + to_port->name()); + writeGetInstance(inst); + sta::print(stream_, "\n"); } } } @@ -1122,9 +1102,9 @@ WriteSdc::writeDisabledPins() const { PinSeq pins = sortByPathName(sdc_->disabledPins(), sdc_network_); for (const Pin *pin : pins) { - gzprintf(stream_, "set_disable_timing "); + sta::print(stream_, "set_disable_timing "); writeGetPin(pin, false); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } @@ -1138,7 +1118,7 @@ WriteSdc::writeDisabledClockGatingChecks() const sorted.push_back(cell); std::sort(sorted.begin(), sorted.end(), [] (const LibertyCell *a, const LibertyCell *b) { - return strcmp(a->name(), b->name()) < 0; + return strcmp(a->name().c_str(), b->name().c_str()) < 0; }); for (const LibertyCell *cell : sorted) { gzprintf(stream_, "set_disable_clock_gating_check "); @@ -1188,7 +1168,7 @@ WriteSdc::writeDisabledEdges() const void WriteSdc::findMatchingEdges(Edge *edge, - EdgeSet &matches) const + EdgeSet &matches) const { Vertex *from_vertex = edge->from(graph_); Vertex *to_vertex = edge->to(graph_); @@ -1203,11 +1183,11 @@ WriteSdc::findMatchingEdges(Edge *edge, bool WriteSdc::edgeSenseIsUnique(Edge *edge, - EdgeSet &matches) const + EdgeSet &matches) const { for (Edge *match : matches) { if (match != edge - && match->sense() == edge->sense()) + && match->sense() == edge->sense()) return false; } return true; @@ -1216,20 +1196,18 @@ WriteSdc::edgeSenseIsUnique(Edge *edge, void WriteSdc::writeDisabledEdge(Edge *edge) const { - gzprintf(stream_, "set_disable_timing "); + sta::print(stream_, "set_disable_timing "); writeGetTimingArcs(edge); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void WriteSdc::writeDisabledEdgeSense(Edge *edge) const { - gzprintf(stream_, "set_disable_timing "); - const char *sense = to_string(edge->sense()); - string filter; - stringPrint(filter, "sense == %s", sense); - writeGetTimingArcs(edge, filter.c_str()); - gzprintf(stream_, "\n"); + sta::print(stream_, "set_disable_timing "); + std::string filter = sta::format("sense == {}", to_string(edge->sense())); + writeGetTimingArcs(edge, filter); + sta::print(stream_, "\n"); } //////////////////////////////////////////////////////////////// @@ -1243,7 +1221,7 @@ WriteSdc::writeExceptions() const sort(exceptions, ExceptionPathLess(network_)); for (ExceptionPath *exception : exceptions) { if (!exception->isFilter() - && !exception->isLoop()) + && !exception->isLoop()) writeException(exception); } } @@ -1262,44 +1240,44 @@ WriteSdc::writeException(ExceptionPath *exception) const writeExceptionTo(exception->to()); writeExceptionValue(exception); writeCmdComment(exception); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void WriteSdc::writeExceptionCmd(ExceptionPath *exception) const { if (exception->isFalse()) { - gzprintf(stream_, "set_false_path"); + sta::print(stream_, "set_false_path"); writeSetupHoldFlag(exception->minMax()); } else if (exception->isMultiCycle()) { - gzprintf(stream_, "set_multicycle_path"); + sta::print(stream_, "set_multicycle_path"); const MinMaxAll *min_max = exception->minMax(); writeSetupHoldFlag(min_max); if (min_max == MinMaxAll::min()) { // For hold MCPs default is -start. if (exception->useEndClk()) - gzprintf(stream_, " -end"); + sta::print(stream_, " -end"); } else { // For setup MCPs default is -end. if (!exception->useEndClk()) - gzprintf(stream_, " -start"); + sta::print(stream_, " -start"); } } else if (exception->isPathDelay()) { if (exception->minMax() == MinMaxAll::max()) - gzprintf(stream_, "set_max_delay"); + sta::print(stream_, "set_max_delay"); else - gzprintf(stream_, "set_min_delay"); + sta::print(stream_, "set_min_delay"); if (exception->ignoreClkLatency()) - gzprintf(stream_, " -ignore_clock_latency"); + sta::print(stream_, " -ignore_clock_latency"); } else if (exception->isGroupPath()) { if (exception->isDefault()) - gzprintf(stream_, "group_path -default"); + sta::print(stream_, "group_path -default"); else - gzprintf(stream_, "group_path -name %s", exception->name()); + sta::print(stream_, "group_path -name {}", exception->name()); } else report_->critical(1620, "unknown exception type"); @@ -1309,10 +1287,10 @@ void WriteSdc::writeExceptionValue(ExceptionPath *exception) const { if (exception->isMultiCycle()) - gzprintf(stream_, " %d", - exception->pathMultiplier()); + sta::print(stream_, " {}", + exception->pathMultiplier()); else if (exception->isPathDelay()) { - gzprintf(stream_, " "); + sta::print(stream_, " "); writeTime(exception->delay()); } } @@ -1328,35 +1306,35 @@ WriteSdc::writeExceptionTo(ExceptionTo *to) const { const RiseFallBoth *end_rf = to->endTransition(); if (end_rf != RiseFallBoth::riseFall()) - gzprintf(stream_, "%s ", transRiseFallFlag(end_rf)); + sta::print(stream_, "{} ", transRiseFallFlag(end_rf)); if (to->hasObjects()) writeExceptionFromTo(to, "to", false); } void WriteSdc::writeExceptionFromTo(ExceptionFromTo *from_to, - const char *from_to_key, - bool map_hpin_to_drvr) const + std::string_view from_to_key, + bool map_hpin_to_drvr) const { const RiseFallBoth *rf = from_to->transition(); - const char *rf_prefix = "-"; + std::string_view rf_prefix = "-"; if (rf == RiseFallBoth::rise()) rf_prefix = "-rise_"; else if (rf == RiseFallBoth::fall()) rf_prefix = "-fall_"; - gzprintf(stream_, "\\\n %s%s ", rf_prefix, from_to_key); + sta::print(stream_, "\\\n {}{} ", rf_prefix, from_to_key); bool multi_objs = ((from_to->pins() ? from_to->pins()->size() : 0) + (from_to->clks() ? from_to->clks()->size() : 0) + (from_to->instances() ? from_to->instances()->size() : 0)) > 1; if (multi_objs) - gzprintf(stream_, "[list "); + sta::print(stream_, "[list "); bool first = true; if (from_to->pins()) { PinSeq pins = sortByPathName(from_to->pins(), sdc_network_); for (const Pin *pin : pins) { if (multi_objs && !first) - gzprintf(stream_, "\\\n "); + sta::print(stream_, "\\\n "); writeGetPin(pin, map_hpin_to_drvr); first = false; } @@ -1367,25 +1345,25 @@ WriteSdc::writeExceptionFromTo(ExceptionFromTo *from_to, InstanceSeq insts = sortByPathName(from_to->instances(), sdc_network_); for (const Instance *inst : insts) { if (multi_objs && !first) - gzprintf(stream_, "\\\n "); + sta::print(stream_, "\\\n "); writeGetInstance(inst); first = false; } } if (multi_objs) - gzprintf(stream_, "]"); + sta::print(stream_, "]"); } void WriteSdc::writeExceptionThru(ExceptionThru *thru) const { const RiseFallBoth *rf = thru->transition(); - const char *rf_prefix = "-"; + std::string_view rf_prefix = "-"; if (rf == RiseFallBoth::rise()) rf_prefix = "-rise_"; else if (rf == RiseFallBoth::fall()) rf_prefix = "-fall_"; - gzprintf(stream_, "\\\n %sthrough ", rf_prefix); + sta::print(stream_, "\\\n {}through ", rf_prefix); PinSeq pins; mapThruHpins(thru, pins); bool multi_objs = @@ -1393,12 +1371,12 @@ WriteSdc::writeExceptionThru(ExceptionThru *thru) const + (thru->nets() ? thru->nets()->size() : 0) + (thru->instances() ? thru->instances()->size() : 0)) > 1; if (multi_objs) - gzprintf(stream_, "[list "); + sta::print(stream_, "[list "); bool first = true; sort(pins, PinPathNameLess(network_)); for (const Pin *pin : pins) { if (multi_objs && !first) - gzprintf(stream_, "\\\n "); + sta::print(stream_, "\\\n "); writeGetPin(pin); first = false; } @@ -1407,7 +1385,7 @@ WriteSdc::writeExceptionThru(ExceptionThru *thru) const NetSeq nets = sortByPathName(thru->nets(), sdc_network_); for (const Net *net : nets) { if (multi_objs && !first) - gzprintf(stream_, "\\\n "); + sta::print(stream_, "\\\n "); writeGetNet(net); first = false; } @@ -1416,39 +1394,39 @@ WriteSdc::writeExceptionThru(ExceptionThru *thru) const InstanceSeq insts = sortByPathName(thru->instances(), sdc_network_); for (const Instance *inst : insts) { if (multi_objs && !first) - gzprintf(stream_, "\\\n "); + sta::print(stream_, "\\\n "); writeGetInstance(inst); first = false; } } if (multi_objs) - gzprintf(stream_, "]"); + sta::print(stream_, "]"); } void WriteSdc::mapThruHpins(ExceptionThru *thru, - PinSeq &pins) const + PinSeq &pins) const { if (thru->pins()) { for (const Pin *pin : *thru->pins()) { // Map hierarical pins to load pins outside of outputs or inside of inputs. if (network_->isHierarchical(pin)) { - Instance *hinst = network_->instance(pin); - bool hpin_is_output = network_->direction(pin)->isAnyOutput(); - PinConnectedPinIterator *cpin_iter = network_->connectedPinIterator(pin); - while (cpin_iter->hasNext()) { - const Pin *cpin = cpin_iter->next(); - if (network_->isLoad(cpin) - && ((hpin_is_output - && !network_->isInside(network_->instance(cpin), hinst)) - || (!hpin_is_output - && network_->isInside(network_->instance(cpin), hinst)))) - pins.push_back(cpin); - } - delete cpin_iter; + Instance *hinst = network_->instance(pin); + bool hpin_is_output = network_->direction(pin)->isAnyOutput(); + PinConnectedPinIterator *cpin_iter = network_->connectedPinIterator(pin); + while (cpin_iter->hasNext()) { + const Pin *cpin = cpin_iter->next(); + if (network_->isLoad(cpin) + && ((hpin_is_output + && !network_->isInside(network_->instance(cpin), hinst)) + || (!hpin_is_output + && network_->isInside(network_->instance(cpin), hinst)))) + pins.push_back(cpin); + } + delete cpin_iter; } else - pins.push_back(pin); + pins.push_back(pin); } } } @@ -1458,7 +1436,7 @@ WriteSdc::mapThruHpins(ExceptionThru *thru, void WriteSdc::writeDataChecks() const { - Vector checks; + std::vector checks; for (const auto [pin, checks1] : sdc_->data_checks_to_map_) { for (DataCheck *check : *checks1) checks.push_back(check); @@ -1477,18 +1455,18 @@ WriteSdc::writeDataCheck(DataCheck *check) const check->marginIsOneValue(setup_hold, margin, one_value); if (one_value) writeDataCheck(check, RiseFallBoth::riseFall(), - RiseFallBoth::riseFall(), setup_hold, margin); + RiseFallBoth::riseFall(), setup_hold, margin); else { for (auto from_rf : RiseFall::range()) { - for (auto to_rf : RiseFall::range()) { - float margin; - bool margin_exists; - check->margin(from_rf, to_rf, setup_hold, margin, margin_exists); - if (margin_exists) { - writeDataCheck(check, from_rf->asRiseFallBoth(), - to_rf->asRiseFallBoth(), setup_hold, margin); - } - } + for (auto to_rf : RiseFall::range()) { + float margin; + bool margin_exists; + check->margin(from_rf, to_rf, setup_hold, margin, margin_exists); + if (margin_exists) { + writeDataCheck(check, from_rf->asRiseFallBoth(), + to_rf->asRiseFallBoth(), setup_hold, margin); + } + } } } } @@ -1496,29 +1474,29 @@ WriteSdc::writeDataCheck(DataCheck *check) const void WriteSdc::writeDataCheck(DataCheck *check, - const RiseFallBoth *from_rf, - const RiseFallBoth *to_rf, - const SetupHold *setup_hold, - float margin) const + const RiseFallBoth *from_rf, + const RiseFallBoth *to_rf, + const SetupHold *setup_hold, + float margin) const { - const char *from_key = "-from"; + std::string_view from_key = "-from"; if (from_rf == RiseFallBoth::rise()) from_key = "-rise_from"; else if (from_rf == RiseFallBoth::fall()) from_key = "-fall_from"; - gzprintf(stream_, "set_data_check %s ", from_key); + sta::print(stream_, "set_data_check {} ", from_key); writeGetPin(check->from(), true); - const char *to_key = "-to"; + std::string_view to_key = "-to"; if (to_rf == RiseFallBoth::rise()) to_key = "-rise_to"; else if (to_rf == RiseFallBoth::fall()) to_key = "-fall_to"; - gzprintf(stream_, " %s ", to_key); + sta::print(stream_, " {} ", to_key); writeGetPin(check->to(), false); - gzprintf(stream_, "%s ", - setupHoldFlag(setup_hold)); + sta::print(stream_, "{} ", + setupHoldFlag(setup_hold)); writeTime(margin); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } //////////////////////////////////////////////////////////////// @@ -1546,7 +1524,7 @@ WriteSdc::writeOperatingConditions() const { OperatingConditions *cond = sdc_->operatingConditions(MinMax::max()); if (cond) - gzprintf(stream_, "set_operating_conditions %s\n", cond->name()); + sta::print(stream_, "set_operating_conditions {}\n", cond->name()); } void @@ -1554,15 +1532,14 @@ WriteSdc::writeWireload() const { WireloadMode wireload_mode = sdc_->wireloadMode(); if (wireload_mode != WireloadMode::unknown) - gzprintf(stream_, "set_wire_load_mode \"%s\"\n", - wireloadModeString(wireload_mode)); + sta::print(stream_, "set_wire_load_mode \"{}\"\n", + wireloadModeString(wireload_mode)); } void WriteSdc::writeNetLoads() const { - int corner_index = 0; // missing corner arg - for (const auto [net, caps] : sdc_->net_wire_cap_maps_[corner_index]) { + for (const auto [net, caps] : sdc_->net_wire_cap_map_) { float min_cap, max_cap; bool min_exists, max_exists; caps.value(MinMax::min(), min_cap, min_exists); @@ -1581,15 +1558,15 @@ WriteSdc::writeNetLoads() const void WriteSdc::writeNetLoad(const Net *net, - const MinMaxAll *min_max, - float cap) const + const MinMaxAll *min_max, + float cap) const { - gzprintf(stream_, "set_load "); - gzprintf(stream_, "%s ", minMaxFlag(min_max)); + sta::print(stream_, "set_load "); + sta::print(stream_, "{} ", minMaxFlag(min_max)); writeCapacitance(cap); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetNet(net); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void @@ -1606,18 +1583,17 @@ WriteSdc::writePortLoads() const void WriteSdc::writePortLoads(const Port *port) const { - const Corner *corner = corners_->findCorner(0); // missing corner arg - PortExtCap *ext_cap = sdc_->portExtCap(port, corner); + const PortExtCap *ext_cap = sdc_->portExtCap(port); if (ext_cap) { WriteGetPort write_port(port, this); writeRiseFallMinMaxCapCmd("set_load -pin_load", - ext_cap->pinCap(), - write_port); + ext_cap->pinCap(), + write_port); writeRiseFallMinMaxCapCmd("set_load -wire_load", - ext_cap->wireCap(), - write_port); + ext_cap->wireCap(), + write_port); writeMinMaxIntValuesCmd("set_port_fanout_number", - ext_cap->fanout(), write_port); + ext_cap->fanout(), write_port); } } @@ -1630,33 +1606,33 @@ WriteSdc::writeDriveResistances() const InputDrive *drive = sdc_->findInputDrive(port); if (drive) { for (auto rf : RiseFall::range()) { - if (drive->driveResistanceMinMaxEqual(rf)) { - float res; - bool exists; - drive->driveResistance(rf, MinMax::max(), res, exists); - gzprintf(stream_, "set_drive %s ", - transRiseFallFlag(rf)); - writeResistance(res); - gzprintf(stream_, " "); - writeGetPort(port); - gzprintf(stream_, "\n"); - } - else { - for (auto min_max : MinMax::range()) { - float res; - bool exists; - drive->driveResistance(rf, min_max, res, exists); - if (exists) { - gzprintf(stream_, "set_drive %s %s ", - transRiseFallFlag(rf), - minMaxFlag(min_max)); - writeResistance(res); - gzprintf(stream_, " "); - writeGetPort(port); - gzprintf(stream_, "\n"); - } - } - } + if (drive->driveResistanceMinMaxEqual(rf)) { + float res; + bool exists; + drive->driveResistance(rf, MinMax::max(), res, exists); + sta::print(stream_, "set_drive {} ", + transRiseFallFlag(rf)); + writeResistance(res); + sta::print(stream_, " "); + writeGetPort(port); + sta::print(stream_, "\n"); + } + else { + for (auto min_max : MinMax::range()) { + float res; + bool exists; + drive->driveResistance(rf, min_max, res, exists); + if (exists) { + sta::print(stream_, "set_drive {} {} ", + transRiseFallFlag(rf), + minMaxFlag(min_max)); + writeResistance(res); + sta::print(stream_, " "); + writeGetPort(port); + sta::print(stream_, "\n"); + } + } + } } } } @@ -1672,47 +1648,47 @@ WriteSdc::writeDrivingCells() const InputDrive *drive = sdc_->findInputDrive(port); if (drive) { InputDriveCell *drive_rise_min = drive->driveCell(RiseFall::rise(), - MinMax::min()); + MinMax::min()); InputDriveCell *drive_rise_max = drive->driveCell(RiseFall::rise(), - MinMax::max()); + MinMax::max()); InputDriveCell *drive_fall_min = drive->driveCell(RiseFall::fall(), - MinMax::min()); + MinMax::min()); InputDriveCell *drive_fall_max = drive->driveCell(RiseFall::fall(), - MinMax::max()); + MinMax::max()); if (drive_rise_min - && drive_rise_max - && drive_fall_min - && drive_fall_max - && drive_rise_min->equal(drive_rise_max) - && drive_rise_min->equal(drive_fall_min) - && drive_rise_min->equal(drive_fall_max)) - // Only write one set_driving_cell if possible. - writeDrivingCell(port, drive_rise_min, nullptr, nullptr); + && drive_rise_max + && drive_fall_min + && drive_fall_max + && drive_rise_min->equal(drive_rise_max) + && drive_rise_min->equal(drive_fall_min) + && drive_rise_min->equal(drive_fall_max)) + // Only write one set_driving_cell if possible. + writeDrivingCell(port, drive_rise_min, nullptr, nullptr); else { - if (drive_rise_min - && drive_rise_max - && drive_rise_min->equal(drive_rise_max)) - writeDrivingCell(port, drive_rise_min, RiseFall::rise(), nullptr); - else { - if (drive_rise_min) - writeDrivingCell(port, drive_rise_min, RiseFall::rise(), - MinMax::min()); - if (drive_rise_max) - writeDrivingCell(port, drive_rise_max, RiseFall::rise(), - MinMax::max()); - } - if (drive_fall_min - && drive_fall_max - && drive_fall_min->equal(drive_fall_max)) - writeDrivingCell(port, drive_fall_min, RiseFall::fall(), nullptr); - else { - if (drive_fall_min) - writeDrivingCell(port, drive_fall_min, RiseFall::fall(), - MinMax::min()); - if (drive_fall_max) - writeDrivingCell(port, drive_fall_max, RiseFall::fall(), - MinMax::max()); - } + if (drive_rise_min + && drive_rise_max + && drive_rise_min->equal(drive_rise_max)) + writeDrivingCell(port, drive_rise_min, RiseFall::rise(), nullptr); + else { + if (drive_rise_min) + writeDrivingCell(port, drive_rise_min, RiseFall::rise(), + MinMax::min()); + if (drive_rise_max) + writeDrivingCell(port, drive_rise_max, RiseFall::rise(), + MinMax::max()); + } + if (drive_fall_min + && drive_fall_max + && drive_fall_min->equal(drive_fall_max)) + writeDrivingCell(port, drive_fall_min, RiseFall::fall(), nullptr); + else { + if (drive_fall_min) + writeDrivingCell(port, drive_fall_min, RiseFall::fall(), + MinMax::min()); + if (drive_fall_max) + writeDrivingCell(port, drive_fall_max, RiseFall::fall(), + MinMax::max()); + } } } } @@ -1721,36 +1697,36 @@ WriteSdc::writeDrivingCells() const void WriteSdc::writeDrivingCell(Port *port, - InputDriveCell *drive_cell, - const RiseFall *rf, - const MinMax *min_max) const + InputDriveCell *drive_cell, + const RiseFall *rf, + const MinMax *min_max) const { const LibertyCell *cell = drive_cell->cell(); const LibertyPort *from_port = drive_cell->fromPort(); const LibertyPort *to_port = drive_cell->toPort(); - float *from_slews = drive_cell->fromSlews(); + const DriveCellSlews &from_slews = drive_cell->fromSlews(); const LibertyLibrary *lib = drive_cell->library(); - gzprintf(stream_, "set_driving_cell"); + sta::print(stream_, "set_driving_cell"); if (rf) - gzprintf(stream_, " %s", transRiseFallFlag(rf)); + sta::print(stream_, " {}", transRiseFallFlag(rf)); if (min_max) - gzprintf(stream_, " %s", minMaxFlag(min_max)); + sta::print(stream_, " {}", minMaxFlag(min_max)); // Only write -library if it was specified in the sdc. if (lib) - gzprintf(stream_, " -library %s", lib->name()); - gzprintf(stream_, " -lib_cell %s", cell->name()); + sta::print(stream_, " -library {}", lib->name()); + sta::print(stream_, " -lib_cell {}", cell->name()); if (from_port) - gzprintf(stream_, " -from_pin {%s}", - from_port->name()); - gzprintf(stream_, - " -pin {%s} -input_transition_rise ", - to_port->name()); + sta::print(stream_, " -from_pin {{{}}}", + from_port->name()); + sta::print(stream_, + " -pin {{{}}} -input_transition_rise ", + to_port->name()); writeTime(from_slews[RiseFall::riseIndex()]); - gzprintf(stream_, " -input_transition_fall "); + sta::print(stream_, " -input_transition_fall "); writeTime(from_slews[RiseFall::fallIndex()]); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetPort(port); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void @@ -1782,27 +1758,27 @@ WriteSdc::writeNetResistances() const sdc_->resistance(net, MinMax::min(), min_res, min_exists); sdc_->resistance(net, MinMax::max(), max_res, max_exists); if (min_exists && max_exists - && min_res == max_res) + && min_res == max_res) writeNetResistance(net, MinMaxAll::all(), min_res); else { if (min_exists) - writeNetResistance(net, MinMaxAll::min(), min_res); + writeNetResistance(net, MinMaxAll::min(), min_res); if (max_exists) - writeNetResistance(net, MinMaxAll::max(), max_res); + writeNetResistance(net, MinMaxAll::max(), max_res); } } } void WriteSdc::writeNetResistance(const Net *net, - const MinMaxAll *min_max, - float res) const + const MinMaxAll *min_max, + float res) const { - gzprintf(stream_, "set_resistance "); + sta::print(stream_, "set_resistance "); writeResistance(res); - gzprintf(stream_, "%s ", minMaxFlag(min_max)); + sta::print(stream_, "{} ", minMaxFlag(min_max)); writeGetNet(net); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void @@ -1817,13 +1793,13 @@ WriteSdc::writeConstants() const void WriteSdc::writeConstant(const Pin *pin) const { - const char *cmd = setConstantCmd(pin); - gzprintf(stream_, "%s ", cmd); + std::string_view cmd = setConstantCmd(pin); + sta::print(stream_, "{} ", cmd); writeGetPin(pin, false); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } -const char * +std::string_view WriteSdc::setConstantCmd(const Pin *pin) const { LogicValue value; @@ -1840,7 +1816,7 @@ WriteSdc::setConstantCmd(const Pin *pin) const case LogicValue::fall: default: report_->critical(1621, "illegal set_logic value"); - return nullptr; + return {}; } } @@ -1857,13 +1833,13 @@ WriteSdc::writeCaseAnalysis() const void WriteSdc::writeCaseAnalysis(const Pin *pin) const { - const char *value_str = caseAnalysisValueStr(pin); - gzprintf(stream_, "set_case_analysis %s ", value_str); + std::string_view value_str = caseAnalysisValueStr(pin); + sta::print(stream_, "set_case_analysis {} ", value_str); writeGetPin(pin, false); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } -const char * +std::string_view WriteSdc::caseAnalysisValueStr(const Pin *pin) const { LogicValue value; @@ -1881,13 +1857,13 @@ WriteSdc::caseAnalysisValueStr(const Pin *pin) const case LogicValue::unknown: default: report_->critical(1622, "invalid set_case_analysis value"); - return nullptr; + return {}; } } void -WriteSdc::sortedLogicValuePins(LogicValueMap &value_map, - PinSeq &pins) const +WriteSdc::sortedLogicValuePins(const LogicValueMap &value_map, + PinSeq &pins) const { for (const auto [pin, value] : value_map) pins.push_back(pin); @@ -1908,7 +1884,7 @@ WriteSdc::writeDeratings() const WriteGetNet write_net(net, this); for (auto early_late : EarlyLate::range()) { writeDerating(factors, TimingDerateType::net_delay, early_late, - &write_net); + &write_net); } } @@ -1930,32 +1906,32 @@ WriteSdc::writeDerating(DeratingFactorsGlobal *factors) const bool delay_is_one_value, check_is_one_value, net_is_one_value; float delay_value, check_value, net_value; factors->factors(TimingDerateType::cell_delay)->isOneValue(early_late, - delay_is_one_value, - delay_value); + delay_is_one_value, + delay_value); factors->factors(TimingDerateType::net_delay)->isOneValue(early_late, - net_is_one_value, - net_value); + net_is_one_value, + net_value); DeratingFactors *cell_check_factors = factors->factors(TimingDerateType::cell_check); cell_check_factors->isOneValue(early_late, check_is_one_value, check_value); if (delay_is_one_value - && net_is_one_value - && delay_value == net_value - && (!cell_check_factors->hasValue() - || (check_is_one_value && check_value == 1.0))) { + && net_is_one_value + && delay_value == net_value + && (!cell_check_factors->hasValue() + || (check_is_one_value && check_value == 1.0))) { if (delay_value != 1.0) { - gzprintf(stream_, "set_timing_derate %s ", earlyLateFlag(early_late)); - writeFloat(delay_value); - gzprintf(stream_, "\n"); + sta::print(stream_, "set_timing_derate {} ", earlyLateFlag(early_late)); + writeFloat(delay_value); + sta::print(stream_, "\n"); } } else { - for (int type_index = 0; - type_index < timing_derate_type_count; - type_index++) { - TimingDerateType type = static_cast(type_index); - DeratingFactors *type_factors = factors->factors(type); - writeDerating(type_factors, type, early_late, nullptr); + for (size_t type_index = 0; + type_index < timing_derate_type_count; + type_index++) { + TimingDerateType type = static_cast(type_index); + DeratingFactors *type_factors = factors->factors(type); + writeDerating(type_factors, type, early_late, nullptr); } } } @@ -1963,7 +1939,7 @@ WriteSdc::writeDerating(DeratingFactorsGlobal *factors) const void WriteSdc::writeDerating(DeratingFactorsCell *factors, - WriteSdcObject *write_obj) const + WriteSdcObject *write_obj) const { for (auto early_late : EarlyLate::range()) { DeratingFactors *delay_factors=factors->factors(TimingDerateCellType::cell_delay); @@ -1975,77 +1951,78 @@ WriteSdc::writeDerating(DeratingFactorsCell *factors, void WriteSdc::writeDerating(DeratingFactors *factors, - TimingDerateType type, - const MinMax *early_late, - WriteSdcObject *write_obj) const + TimingDerateType type, + const MinMax *early_late, + WriteSdcObject *write_obj) const { - const char *type_key = timingDerateTypeKeyword(type); + std::string_view type_key = timingDerateTypeKeyword(type); bool is_one_value; float value; factors->isOneValue(early_late, is_one_value, value); if (is_one_value) { if (value != 1.0) { - gzprintf(stream_, "set_timing_derate %s %s ", - type_key, - earlyLateFlag(early_late)); + sta::print(stream_, "set_timing_derate {} {} ", + type_key, + earlyLateFlag(early_late)); writeFloat(value); if (write_obj) { - gzprintf(stream_, " "); - write_obj->write(); + sta::print(stream_, " "); + write_obj->write(); } - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } else { - for (int clk_data_index = 0; - clk_data_index < path_clk_or_data_count; - clk_data_index++) { + for (size_t clk_data_index = 0; + clk_data_index < path_clk_or_data_count; + clk_data_index++) { PathClkOrData clk_data = static_cast(clk_data_index); - static const char *clk_data_keys[] = {"-clock", "-data"}; - const char *clk_data_key = clk_data_keys[clk_data_index]; + static constexpr std::string_view clk_data_keys[] = {"-clock", "-data"}; + std::string_view clk_data_key = clk_data_keys[clk_data_index]; factors->isOneValue(clk_data, early_late, is_one_value, value); if (is_one_value) { - if (value != 1.0) { - gzprintf(stream_, "set_timing_derate %s %s %s ", - type_key, - earlyLateFlag(early_late), - clk_data_key); - writeFloat(value); - if (write_obj) { - gzprintf(stream_, " "); - write_obj->write(); - } - gzprintf(stream_, "\n"); - } + if (value != 1.0) { + sta::print(stream_, "set_timing_derate {} {} {} ", + type_key, + earlyLateFlag(early_late), + clk_data_key); + writeFloat(value); + if (write_obj) { + sta::print(stream_, " "); + write_obj->write(); + } + sta::print(stream_, "\n"); + } } else { - for (auto rf : RiseFall::range()) { - float factor; - bool exists; - factors->factor(clk_data, rf, early_late, factor, exists); - if (exists) { - gzprintf(stream_, "set_timing_derate %s %s %s %s ", - type_key, - clk_data_key, - transRiseFallFlag(rf), - earlyLateFlag(early_late)); - writeFloat(factor); - if (write_obj) { - gzprintf(stream_, " "); - write_obj->write(); - } - gzprintf(stream_, "\n"); - } - } + for (auto rf : RiseFall::range()) { + float factor; + bool exists; + factors->factor(clk_data, rf, early_late, factor, exists); + if (exists) { + sta::print(stream_, "set_timing_derate {} {} {} {} ", + type_key, + clk_data_key, + transRiseFallFlag(rf), + earlyLateFlag(early_late)); + writeFloat(factor); + if (write_obj) { + sta::print(stream_, " "); + write_obj->write(); + } + sta::print(stream_, "\n"); + } + } } } } } -static const char * +static std::string_view timingDerateTypeKeyword(TimingDerateType type) { - static const char *type_keys[] = {"-cell_delay","-cell_check","-net_delay"}; + static constexpr std::string_view type_keys[] = { + "-cell_delay", "-cell_check", "-net_delay"}; return type_keys[static_cast(type)]; } @@ -2060,11 +2037,11 @@ WriteSdc::writeVoltages() const if (exists_max) { sdc_->voltage(MinMax::min(), voltage_min, exists_min); if (exists_min) - gzprintf(stream_, "set_voltage -min %.3f %.3f\n", - voltage_min, - voltage_max); + sta::print(stream_, "set_voltage -min {:.3f} {:.3f}\n", + voltage_min, + voltage_max); else - gzprintf(stream_, "set_voltage %.3f\n", voltage_max); + sta::print(stream_, "set_voltage {:.3f}\n", voltage_max); } for (const auto& [net, volts] : sdc_->net_voltage_map_) { @@ -2072,14 +2049,14 @@ WriteSdc::writeVoltages() const if (exists_max) { volts.value(MinMax::min(), voltage_min, exists_min); if (exists_min) - gzprintf(stream_, "set_voltage -object_list %s -min %.3f %.3f\n", - sdc_network_->pathName(net), - voltage_min, - voltage_max); + sta::print(stream_, "set_voltage -object_list {} -min {:.3f} {:.3f}\n", + sdc_network_->pathName(net), + voltage_min, + voltage_max); else - gzprintf(stream_, "set_voltage -object_list %s %.3f\n", - sdc_network_->pathName(net), - voltage_max); + sta::print(stream_, "set_voltage -object_list {} {:.3f}\n", + sdc_network_->pathName(net), + voltage_max); } } } @@ -2121,7 +2098,7 @@ WriteSdc::writeMinPulseWidths() const void WriteSdc::writeMinPulseWidths(RiseFallValues *min_widths, - WriteSdcObject &write_obj) const + WriteSdcObject &write_obj) const { bool hi_exists, low_exists; float hi, low; @@ -2139,15 +2116,15 @@ WriteSdc::writeMinPulseWidths(RiseFallValues *min_widths, } void -WriteSdc::writeMinPulseWidth(const char *hi_low, - float value, - WriteSdcObject &write_obj) const +WriteSdc::writeMinPulseWidth(std::string_view hi_low, + float value, + WriteSdcObject &write_obj) const { - gzprintf(stream_, "set_min_pulse_width %s", hi_low); + sta::print(stream_, "set_min_pulse_width {}", hi_low); writeTime(value); - gzprintf(stream_, " "); + sta::print(stream_, " "); write_obj.write(); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } //////////////////////////////////////////////////////////////// @@ -2156,27 +2133,27 @@ void WriteSdc::writeLatchBorowLimits() const { for (const auto [pin, limit] : sdc_->pin_latch_borrow_limit_map_) { - gzprintf(stream_, "set_max_time_borrow "); + sta::print(stream_, "set_max_time_borrow "); writeTime(limit); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetPin(pin, false); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } for (const auto [inst, limit] : sdc_->inst_latch_borrow_limit_map_) { - gzprintf(stream_, "set_max_time_borrow "); + sta::print(stream_, "set_max_time_borrow "); writeTime(limit); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetInstance(inst); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } for (const auto [clk, limit] : sdc_->clk_latch_borrow_limit_map_) { - gzprintf(stream_, "set_max_time_borrow "); + sta::print(stream_, "set_max_time_borrow "); writeTime(limit); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetClock(clk); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } @@ -2190,9 +2167,9 @@ WriteSdc::writeSlewLimits() const bool exists; sdc_->slewLimit(cell_, min_max, slew, exists); if (exists) { - gzprintf(stream_, "set_max_transition "); + sta::print(stream_, "set_max_transition "); writeTime(slew); - gzprintf(stream_, " [current_design]\n"); + sta::print(stream_, " [current_design]\n"); } CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_); @@ -2200,11 +2177,11 @@ WriteSdc::writeSlewLimits() const Port *port = port_iter->next(); sdc_->slewLimit(port, min_max, slew, exists); if (exists) { - gzprintf(stream_, "set_max_transition "); + sta::print(stream_, "set_max_transition "); writeTime(slew); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetPort(port); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } delete port_iter; @@ -2216,60 +2193,59 @@ void WriteSdc::writeClkSlewLimits() const { const MinMax *min_max = MinMax::max(); - ClockSeq clks; - sdc_->sortedClocks(clks); + ClockSeq clks = sdc_->sortedClocks(); for (const Clock *clk : clks) { float rise_clk_limit, fall_clk_limit, rise_data_limit, fall_data_limit; bool rise_clk_exists, fall_clk_exists, rise_data_exists, fall_data_exists; clk->slewLimit(RiseFall::rise(), PathClkOrData::clk, min_max, - rise_clk_limit, rise_clk_exists); + rise_clk_limit, rise_clk_exists); clk->slewLimit(RiseFall::fall(), PathClkOrData::clk, min_max, - fall_clk_limit, fall_clk_exists); + fall_clk_limit, fall_clk_exists); clk->slewLimit(RiseFall::rise(), PathClkOrData::data, min_max, - rise_data_limit, rise_data_exists); + rise_data_limit, rise_data_exists); clk->slewLimit(RiseFall::fall(), PathClkOrData::data, min_max, - fall_data_limit, fall_data_exists); + fall_data_limit, fall_data_exists); if (rise_clk_exists && fall_clk_exists - && rise_data_exists && fall_data_exists - && fall_clk_limit == rise_clk_limit - && rise_data_limit == rise_clk_limit - && fall_data_limit == rise_clk_limit) + && rise_data_exists && fall_data_exists + && fall_clk_limit == rise_clk_limit + && rise_data_limit == rise_clk_limit + && fall_data_limit == rise_clk_limit) writeClkSlewLimit("", "", clk, rise_clk_limit); else { if (rise_clk_exists && fall_clk_exists - && fall_clk_limit == rise_clk_limit) - writeClkSlewLimit("-clock_path ", "", clk, rise_clk_limit); + && fall_clk_limit == rise_clk_limit) + writeClkSlewLimit("-clock_path ", "", clk, rise_clk_limit); else { - if (rise_clk_exists) - writeClkSlewLimit("-clock_path ", "-rise ", clk, rise_clk_limit); - if (fall_clk_exists) - writeClkSlewLimit("-clock_path ", "-fall ", clk, fall_clk_limit); + if (rise_clk_exists) + writeClkSlewLimit("-clock_path ", "-rise ", clk, rise_clk_limit); + if (fall_clk_exists) + writeClkSlewLimit("-clock_path ", "-fall ", clk, fall_clk_limit); } if (rise_data_exists && fall_data_exists - && fall_data_limit == rise_data_limit) - writeClkSlewLimit("-data_path ", "", clk, rise_data_limit); + && fall_data_limit == rise_data_limit) + writeClkSlewLimit("-data_path ", "", clk, rise_data_limit); else { - if (rise_data_exists) - writeClkSlewLimit("-data_path ", "-rise ", clk, rise_data_limit); - if (fall_data_exists) { - writeClkSlewLimit("-data_path ", "-fall ", clk, fall_data_limit); - } + if (rise_data_exists) + writeClkSlewLimit("-data_path ", "-rise ", clk, rise_data_limit); + if (fall_data_exists) { + writeClkSlewLimit("-data_path ", "-fall ", clk, fall_data_limit); + } } } } } void -WriteSdc::writeClkSlewLimit(const char *clk_data, - const char *rise_fall, - const Clock *clk, - float limit) const +WriteSdc::writeClkSlewLimit(std::string_view clk_data, + std::string_view rise_fall, + const Clock *clk, + float limit) const { - gzprintf(stream_, "set_max_transition %s%s", clk_data, rise_fall); + sta::print(stream_, "set_max_transition {}{}", clk_data, rise_fall); writeTime(limit); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetClock(clk); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void @@ -2281,15 +2257,15 @@ WriteSdc::writeCapLimits() const void WriteSdc::writeCapLimits(const MinMax *min_max, - const char *cmd) const + std::string_view cmd) const { float cap; bool exists; sdc_->capacitanceLimit(cell_, min_max, cap, exists); if (exists) { - gzprintf(stream_, "%s ", cmd); + sta::print(stream_, "{} ", cmd); writeCapacitance(cap); - gzprintf(stream_, " [current_design]\n"); + sta::print(stream_, " [current_design]\n"); } for (const auto [port, limits] : sdc_->port_cap_limit_map_) { @@ -2297,11 +2273,11 @@ WriteSdc::writeCapLimits(const MinMax *min_max, bool exists; limits.value(min_max, cap, exists); if (exists) { - gzprintf(stream_, "%s ", cmd); + sta::print(stream_, "{} ", cmd); writeCapacitance(cap); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetPort(port); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } @@ -2310,11 +2286,11 @@ WriteSdc::writeCapLimits(const MinMax *min_max, bool exists; limits.value(min_max, cap, exists); if (exists) { - gzprintf(stream_, "%s ", cmd); + sta::print(stream_, "{} ", cmd); writeCapacitance(cap); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetPin(pin, false); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } } @@ -2324,9 +2300,9 @@ WriteSdc::writeMaxArea() const { float max_area = sdc_->maxArea(); if (max_area > 0.0) { - gzprintf(stream_, "set_max_area "); + sta::print(stream_, "set_max_area "); writeFloat(max_area); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } @@ -2361,15 +2337,15 @@ WriteSdc::writeFanoutLimits() const void WriteSdc::writeFanoutLimits(const MinMax *min_max, - const char *cmd) const + std::string_view cmd) const { float fanout; bool exists; sdc_->fanoutLimit(cell_, min_max, fanout, exists); if (exists) { - gzprintf(stream_, "%s ", cmd); + sta::print(stream_, "{} ", cmd); writeFloat(fanout); - gzprintf(stream_, " [current_design]\n"); + sta::print(stream_, " [current_design]\n"); } else { CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_); @@ -2377,11 +2353,11 @@ WriteSdc::writeFanoutLimits(const MinMax *min_max, Port *port = port_iter->next(); sdc_->fanoutLimit(port, min_max, fanout, exists); if (exists) { - gzprintf(stream_, "%s ", cmd); - writeFloat(fanout); - gzprintf(stream_, " "); - writeGetPort(port); - gzprintf(stream_, "\n"); + sta::print(stream_, "{} ", cmd); + writeFloat(fanout); + sta::print(stream_, " "); + writeGetPort(port); + sta::print(stream_, "\n"); } } delete port_iter; @@ -2395,15 +2371,15 @@ WriteSdc::writeVariables() const { if (variables_->propagateAllClocks()) { if (native_) - gzprintf(stream_, "set sta_propagate_all_clocks 1\n"); + sta::print(stream_, "set sta_propagate_all_clocks 1\n"); else - gzprintf(stream_, "set timing_all_clocks_propagated true\n"); + sta::print(stream_, "set timing_all_clocks_propagated true\n"); } if (variables_->presetClrArcsEnabled()) { if (native_) - gzprintf(stream_, "set sta_preset_clear_arcs_enabled 1\n"); + sta::print(stream_, "set sta_preset_clear_arcs_enabled 1\n"); else - gzprintf(stream_, "set timing_enable_preset_clear_arcs true\n"); + sta::print(stream_, "set timing_enable_preset_clear_arcs true\n"); } } @@ -2412,33 +2388,27 @@ WriteSdc::writeVariables() const void WriteSdc::writeGetTimingArcsOfOjbects(const LibertyCell *cell) const { - gzprintf(stream_, "[%s -of_objects ", getTimingArcsCmd()); + sta::print(stream_, "[{} -of_objects ", getTimingArcsCmd()); writeGetLibCell(cell); - gzprintf(stream_, "]"); -} - -void -WriteSdc::writeGetTimingArcs(Edge *edge) const -{ - writeGetTimingArcs(edge, nullptr); + sta::print(stream_, "]"); } void WriteSdc::writeGetTimingArcs(Edge *edge, - const char *filter) const + std::string_view filter) const { - gzprintf(stream_, "[%s -from ", getTimingArcsCmd()); + sta::print(stream_, "[{} -from ", getTimingArcsCmd()); Vertex *from_vertex = edge->from(graph_); writeGetPin(from_vertex->pin(), true); - gzprintf(stream_, " -to "); + sta::print(stream_, " -to "); Vertex *to_vertex = edge->to(graph_); writeGetPin(to_vertex->pin(), false); - if (filter) - gzprintf(stream_, " -filter {%s}", filter); - gzprintf(stream_, "]"); + if (!filter.empty()) + sta::print(stream_, " -filter {{{}}}", filter); + sta::print(stream_, "]"); } -const char * +std::string_view WriteSdc::getTimingArcsCmd() const { return native_ ? "get_timing_edges" : "get_timing_arcs"; @@ -2449,9 +2419,9 @@ WriteSdc::getTimingArcsCmd() const void WriteSdc::writeGetLibCell(const LibertyCell *cell) const { - gzprintf(stream_, "[get_lib_cells {%s/%s}]", - cell->libertyLibrary()->name(), - cell->name()); + sta::print(stream_, "[get_lib_cells {{{}/{}}}]", + cell->libertyLibrary()->name(), + cell->name()); } void @@ -2459,10 +2429,10 @@ WriteSdc::writeGetLibPin(const LibertyPort *port) const { LibertyCell *cell = port->libertyCell(); LibertyLibrary *lib = cell->libertyLibrary(); - gzprintf(stream_, "[get_lib_pins {%s/%s/%s}]", - lib->name(), - cell->name(), - port->name()); + sta::print(stream_, "[get_lib_pins {{{}/{}/{}}}]", + lib->name(), + cell->name(), + port->name()); } void @@ -2471,21 +2441,21 @@ WriteSdc::writeGetClocks(ClockSet *clks) const bool first = true; bool multiple = clks->size() > 1; if (multiple) - gzprintf(stream_, "[list "); + sta::print(stream_, "[list "); writeGetClocks(clks, multiple, first); if (multiple) - gzprintf(stream_, "]"); + sta::print(stream_, "]"); } void WriteSdc::writeGetClocks(ClockSet *clks, - bool multiple, - bool &first) const + bool multiple, + bool &first) const { ClockSeq clks1 = sortByName(clks); for (const Clock *clk : clks1) { if (multiple && !first) - gzprintf(stream_, "\\\n "); + sta::print(stream_, "\\\n "); writeGetClock(clk); first = false; } @@ -2494,31 +2464,31 @@ WriteSdc::writeGetClocks(ClockSet *clks, void WriteSdc::writeGetClock(const Clock *clk) const { - gzprintf(stream_, "[get_clocks {%s}]", - clk->name()); + sta::print(stream_, "[get_clocks {{{}}}]", + clk->name()); } void WriteSdc::writeGetPort(const Port *port) const { - gzprintf(stream_, "[get_ports {%s}]", sdc_network_->name(port)); + sta::print(stream_, "[get_ports {{{}}}]", sdc_network_->name(port)); } void WriteSdc::writeGetPins(const PinSet *pins, - bool map_hpin_to_drvr) const + bool map_hpin_to_drvr) const { if (map_hpins_) { PinSet leaf_pins(network_);; for (const Pin *pin : *pins) { if (network_->isHierarchical(pin)) { - if (map_hpin_to_drvr) - findLeafDriverPins(const_cast(pin), network_, &leaf_pins); - else - findLeafLoadPins(const_cast(pin), network_, &leaf_pins); + if (map_hpin_to_drvr) + findLeafDriverPins(const_cast(pin), network_, &leaf_pins); + else + findLeafLoadPins(const_cast(pin), network_, &leaf_pins); } else - leaf_pins.insert(pin); + leaf_pins.insert(pin); } PinSeq pins1 = sortByPathName(&leaf_pins, sdc_network_); writeGetPins1(&pins1); @@ -2534,30 +2504,30 @@ WriteSdc::writeGetPins1(PinSeq *pins) const { bool multiple = pins->size() > 1; if (multiple) - gzprintf(stream_, "[list "); + sta::print(stream_, "[list "); bool first = true; for (const Pin *pin : *pins) { if (multiple && !first) - gzprintf(stream_, "\\\n "); + sta::print(stream_, "\\\n "); writeGetPin(pin); first = false; } if (multiple) - gzprintf(stream_, "]"); + sta::print(stream_, "]"); } void WriteSdc::writeGetPin(const Pin *pin) const { if (sdc_network_->instance(pin) == instance_) - gzprintf(stream_, "[get_ports {%s}]", sdc_network_->portName(pin)); + sta::print(stream_, "[get_ports {{{}}}]", sdc_network_->portName(pin)); else - gzprintf(stream_, "[get_pins {%s}]", pathName(pin)); + sta::print(stream_, "[get_pins {{{}}}]", pathName(pin)); } void WriteSdc::writeGetPin(const Pin *pin, - bool map_hpin_to_drvr) const + bool map_hpin_to_drvr) const { if (map_hpins_ && network_->isHierarchical(pin)) { PinSet pins(network_); @@ -2571,178 +2541,178 @@ WriteSdc::writeGetPin(const Pin *pin, void WriteSdc::writeGetNet(const Net *net) const { - gzprintf(stream_, "[get_nets {%s}]", pathName(net)); + sta::print(stream_, "[get_nets {{{}}}]", pathName(net)); } void WriteSdc::writeGetInstance(const Instance *inst) const { - gzprintf(stream_, "[get_cells {%s}]", pathName(inst)); + sta::print(stream_, "[get_cells {{{}}}]", pathName(inst)); } -const char * +std::string WriteSdc::pathName(const Pin *pin) const { - const char *pin_path = sdc_network_->pathName(pin); + std::string pin_path = sdc_network_->pathName(pin); if (top_instance_) return pin_path; else - return pin_path + instance_name_length_ + 1; + return pin_path.substr(instance_name_length_ + 1); } -const char * +std::string WriteSdc::pathName(const Net *net) const { - const char *net_path = sdc_network_->pathName(net); + std::string net_path = sdc_network_->pathName(net); if (top_instance_) return net_path; else - return net_path + instance_name_length_ + 1; + return net_path.substr(instance_name_length_ + 1); } -const char * +std::string WriteSdc::pathName(const Instance *inst) const { - const char *inst_path = sdc_network_->pathName(inst); + std::string inst_path = sdc_network_->pathName(inst); if (top_instance_) return inst_path; else - return inst_path + instance_name_length_ + 1; + return inst_path.substr(instance_name_length_ + 1); } void -WriteSdc::writeCommentSection(const char *line) const +WriteSdc::writeCommentSection(std::string_view line) const { writeCommentSeparator(); - gzprintf(stream_, "# %s\n", line); + sta::print(stream_, "# {}\n", line); writeCommentSeparator(); } void WriteSdc::writeCommentSeparator() const { - gzprintf(stream_, "###############################################################################\n"); + sta::print(stream_, "###############################################################################\n"); } //////////////////////////////////////////////////////////////// void -WriteSdc::writeRiseFallMinMaxTimeCmd(const char *sdc_cmd, - const RiseFallMinMax *values, - WriteSdcObject &write_object) const +WriteSdc::writeRiseFallMinMaxTimeCmd(std::string_view sdc_cmd, + const RiseFallMinMax *values, + WriteSdcObject &write_object) const { writeRiseFallMinMaxCmd(sdc_cmd, values, units_->timeUnit()->scale(), - write_object); + write_object); } void -WriteSdc::writeRiseFallMinMaxCapCmd(const char *sdc_cmd, - const RiseFallMinMax *values, - WriteSdcObject &write_object) const +WriteSdc::writeRiseFallMinMaxCapCmd(std::string_view sdc_cmd, + const RiseFallMinMax *values, + WriteSdcObject &write_object) const { writeRiseFallMinMaxCmd(sdc_cmd, values, units_->capacitanceUnit()->scale(), - write_object); + write_object); } void -WriteSdc::writeRiseFallMinMaxCmd(const char *sdc_cmd, - const RiseFallMinMax *values, - float scale, - WriteSdcObject &write_object) const +WriteSdc::writeRiseFallMinMaxCmd(std::string_view sdc_cmd, + const RiseFallMinMax *values, + float scale, + WriteSdcObject &write_object) const { float fall_min, fall_max, rise_min, rise_max; bool fall_min_exists, fall_max_exists, rise_min_exists, rise_max_exists; values->value(RiseFall::fall(), MinMax::min(), - fall_min, fall_min_exists); + fall_min, fall_min_exists); values->value(RiseFall::fall(), MinMax::max(), - fall_max, fall_max_exists); + fall_max, fall_max_exists); values->value(RiseFall::rise(), MinMax::min(), - rise_min, rise_min_exists); + rise_min, rise_min_exists); values->value(RiseFall::rise(), MinMax::max(), - rise_max, rise_max_exists); + rise_max, rise_max_exists); if (fall_min_exists && fall_max_exists && rise_min_exists && rise_max_exists) { if (fall_min == rise_min - && rise_max == rise_min - && fall_max == rise_min) { + && rise_max == rise_min + && fall_max == rise_min) { // rise/fall/min/max match. writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale, - RiseFallBoth::riseFall(), MinMaxAll::all(), - write_object); + RiseFallBoth::riseFall(), MinMaxAll::all(), + write_object); } else if (rise_min == fall_min - && rise_max == fall_max) { + && rise_max == fall_max) { // rise/fall match. writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale, - RiseFallBoth::riseFall(), MinMaxAll::min(), - write_object); + RiseFallBoth::riseFall(), MinMaxAll::min(), + write_object); writeRiseFallMinMaxCmd(sdc_cmd, rise_max, scale, - RiseFallBoth::riseFall(), MinMaxAll::max(), - write_object); + RiseFallBoth::riseFall(), MinMaxAll::max(), + write_object); } else if (rise_min == rise_max - && fall_min == fall_max) { + && fall_min == fall_max) { // min/max match. writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale, - RiseFallBoth::rise(), MinMaxAll::all(), - write_object); + RiseFallBoth::rise(), MinMaxAll::all(), + write_object); writeRiseFallMinMaxCmd(sdc_cmd, fall_min, scale, - RiseFallBoth::fall(), MinMaxAll::all(), - write_object); + RiseFallBoth::fall(), MinMaxAll::all(), + write_object); } } else { if (rise_min_exists) writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale, - RiseFallBoth::rise(), MinMaxAll::min(), - write_object); + RiseFallBoth::rise(), MinMaxAll::min(), + write_object); if (rise_max_exists) writeRiseFallMinMaxCmd(sdc_cmd, rise_max, scale, - RiseFallBoth::rise(), MinMaxAll::max(), - write_object); + RiseFallBoth::rise(), MinMaxAll::max(), + write_object); if (fall_min_exists) writeRiseFallMinMaxCmd(sdc_cmd, fall_min, scale, - RiseFallBoth::fall(), MinMaxAll::min(), - write_object); + RiseFallBoth::fall(), MinMaxAll::min(), + write_object); if (fall_max_exists) writeRiseFallMinMaxCmd(sdc_cmd, fall_max, scale, - RiseFallBoth::fall(), MinMaxAll::max(), - write_object); + RiseFallBoth::fall(), MinMaxAll::max(), + write_object); } } void -WriteSdc::writeRiseFallMinMaxCmd(const char *sdc_cmd, - float value, - float scale, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - WriteSdcObject &write_object) const +WriteSdc::writeRiseFallMinMaxCmd(std::string_view sdc_cmd, + float value, + float scale, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + WriteSdcObject &write_object) const { - gzprintf(stream_, "%s%s%s ", - sdc_cmd, - transRiseFallFlag(rf), - minMaxFlag(min_max)); + sta::print(stream_, "{}{}{} ", + sdc_cmd, + transRiseFallFlag(rf), + minMaxFlag(min_max)); writeFloat(value / scale); - gzprintf(stream_, " "); + sta::print(stream_, " "); write_object.write(); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void WriteSdc::writeClockKey(const Clock *clk) const { - gzprintf(stream_, " -clock "); + sta::print(stream_, " -clock "); writeGetClock(clk); } //////////////////////////////////////////////////////////////// void -WriteSdc::writeMinMaxFloatValuesCmd(const char *sdc_cmd, - MinMaxFloatValues *values, - float scale, - WriteSdcObject &write_object) const +WriteSdc::writeMinMaxFloatValuesCmd(std::string_view sdc_cmd, + const MinMaxFloatValues *values, + float scale, + WriteSdcObject &write_object) const { float min, max; bool min_exists, max_exists; @@ -2762,25 +2732,25 @@ WriteSdc::writeMinMaxFloatValuesCmd(const char *sdc_cmd, } void -WriteSdc::writeMinMaxFloatCmd(const char *sdc_cmd, - float value, - float scale, - const MinMaxAll *min_max, - WriteSdcObject &write_object) const +WriteSdc::writeMinMaxFloatCmd(std::string_view sdc_cmd, + float value, + float scale, + const MinMaxAll *min_max, + WriteSdcObject &write_object) const { - gzprintf(stream_, "%s%s ", - sdc_cmd, - minMaxFlag(min_max)); + sta::print(stream_, "{}{} ", + sdc_cmd, + minMaxFlag(min_max)); writeFloat(value / scale); - gzprintf(stream_, " "); + sta::print(stream_, " "); write_object.write(); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void -WriteSdc::writeMinMaxIntValuesCmd(const char *sdc_cmd, - MinMaxIntValues *values, - WriteSdcObject &write_object) const +WriteSdc::writeMinMaxIntValuesCmd(std::string_view sdc_cmd, + const MinMaxIntValues *values, + WriteSdcObject &write_object) const { int min, max; bool min_exists, max_exists; @@ -2800,17 +2770,17 @@ WriteSdc::writeMinMaxIntValuesCmd(const char *sdc_cmd, } void -WriteSdc::writeMinMaxIntCmd(const char *sdc_cmd, - int value, - const MinMaxAll *min_max, - WriteSdcObject &write_object) const +WriteSdc::writeMinMaxIntCmd(std::string_view sdc_cmd, + int value, + const MinMaxAll *min_max, + WriteSdcObject &write_object) const { - gzprintf(stream_, "%s%s ", - sdc_cmd, - minMaxFlag(min_max)); - gzprintf(stream_, "%d ", value); + sta::print(stream_, "{}{} ", + sdc_cmd, + minMaxFlag(min_max)); + sta::print(stream_, "{} ", value); write_object.write(); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } //////////////////////////////////////////////////////////////// @@ -2836,66 +2806,66 @@ WriteSdc::scaleResistance(float res) const void WriteSdc::writeFloat(float value) const { - gzprintf(stream_, "%.*f", digits_, value); + sta::print(stream_, "{}", sta::formatRuntime("{:.{}f}", value, digits_)); } void WriteSdc::writeTime(float time) const { - gzprintf(stream_, "%.*f", digits_, scaleTime(time)); + sta::print(stream_, "{}", sta::formatRuntime("{:.{}f}", scaleTime(time), digits_)); } void WriteSdc::writeCapacitance(float cap) const { - gzprintf(stream_, "%.*f", digits_, scaleCapacitance(cap)); + sta::print(stream_, "{}", sta::formatRuntime("{:.{}f}", scaleCapacitance(cap), digits_)); } void WriteSdc::writeResistance(float res) const { - gzprintf(stream_, "%.*f", digits_, scaleResistance(res)); + sta::print(stream_, "{}", sta::formatRuntime("{:.{}f}", scaleResistance(res), digits_)); } void -WriteSdc::writeFloatSeq(FloatSeq *floats, - float scale) const +WriteSdc::writeFloatSeq(const FloatSeq &floats, + float scale) const { - gzprintf(stream_, "{"); + sta::print(stream_, "{{"); bool first = true; - for (float flt : *floats) { + for (float flt : floats) { if (!first) - gzprintf(stream_, " "); + sta::print(stream_, " "); writeFloat(flt * scale); first = false; } - gzprintf(stream_, "}"); + sta::print(stream_, "}}"); } void -WriteSdc::writeIntSeq(IntSeq *ints) const +WriteSdc::writeIntSeq(const IntSeq &ints) const { - gzprintf(stream_, "{"); + sta::print(stream_, "{{"); bool first = true; - for (int i : *ints) { + for (int i : ints) { if (!first) - gzprintf(stream_, " "); - gzprintf(stream_, "%d", i); + sta::print(stream_, " "); + sta::print(stream_, "{}", i); first = false; } - gzprintf(stream_, "}"); + sta::print(stream_, "}}"); } //////////////////////////////////////////////////////////////// -static const char * +static std::string_view transRiseFallFlag(const RiseFall *rf) { return (rf == RiseFall::rise()) ? "-rise" : "-fall"; } -static const char * +static std::string_view transRiseFallFlag(const RiseFallBoth *rf) { if (rf == RiseFallBoth::rise()) @@ -2906,7 +2876,7 @@ transRiseFallFlag(const RiseFallBoth *rf) return ""; } -static const char * +static std::string_view minMaxFlag(const MinMaxAll *min_max) { if (min_max == MinMaxAll::min()) @@ -2917,13 +2887,13 @@ minMaxFlag(const MinMaxAll *min_max) return ""; } -static const char * +static std::string_view minMaxFlag(const MinMax *min_max) { return (min_max == MinMax::min()) ? " -min" : " -max"; } -static const char * +static std::string_view earlyLateFlag(const MinMax *early_late) { return (early_late == MinMax::min()) ? "-early" : "-late"; @@ -2933,12 +2903,12 @@ void WriteSdc::writeSetupHoldFlag(const MinMaxAll *min_max) const { if (min_max == MinMaxAll::min()) - gzprintf(stream_, " -hold"); + sta::print(stream_, " -hold"); else if (min_max == MinMaxAll::max()) - gzprintf(stream_, " -setup"); + sta::print(stream_, " -setup"); } -static const char * +static std::string_view setupHoldFlag(const MinMax *min_max) { return (min_max == MinMax::min()) ? " -hold" : " -setup"; @@ -2947,10 +2917,9 @@ setupHoldFlag(const MinMax *min_max) void WriteSdc::writeCmdComment(SdcCmdComment *cmd) const { - const char *comment = cmd->comment(); - if (comment) { - gzprintf(stream_, " -comment {%s}", comment); - } + const std::string &comment = cmd->comment(); + if (!comment.empty()) + sta::print(stream_, " -comment {{{}}}", comment); } -} // namespace +} // namespace sta diff --git a/include/sta/WriteSdc.hh b/sdc/WriteSdc.hh similarity index 72% rename from include/sta/WriteSdc.hh rename to sdc/WriteSdc.hh index 3aa517687..15b8c7251 100644 --- a/include/sta/WriteSdc.hh +++ b/sdc/WriteSdc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,8 @@ #pragma once +#include + #include "NetworkClass.hh" #include "SdcClass.hh" @@ -32,16 +34,16 @@ namespace sta { // Write SDC to a file. // Allow SDC to apply to an instance to support write_context. void -writeSdc(Instance *instance, - const char *filename, - const char *creator, - // Map hierarchical pins and instances to leaf pins and instances. - bool map_hpins, - // Replace non-sdc get functions with OpenSTA equivalents. - bool native, - int digits, +writeSdc(const Sdc *sdc, + Instance *instance, + std::string_view filename, + std::string_view creator, + // Map hierarchical pins and instances to leaf pins and instances. + bool map_hpins, + // Replace non-sdc get functions with OpenSTA equivalents. + bool native, + int digits, bool gzip, - bool no_timestamp, - Sdc *sdc); + bool no_timestamp); -} // namespace +} // namespace sta diff --git a/sdc/WriteSdcPvt.hh b/sdc/WriteSdcPvt.hh index 2518bea49..4d58d93d4 100644 --- a/sdc/WriteSdcPvt.hh +++ b/sdc/WriteSdcPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,10 +24,13 @@ #pragma once -#include "Zlib.hh" -#include "NetworkClass.hh" +#include +#include + #include "GraphClass.hh" +#include "NetworkClass.hh" #include "Sdc.hh" +#include "Zlib.hh" namespace sta { @@ -36,18 +39,18 @@ class WriteSdcObject; class WriteSdc : public StaState { public: - WriteSdc(Instance *instance, - const char *creator, - bool map_hpins, - bool native, - int digits, - bool no_timestamp, - Sdc *sdc); - virtual ~WriteSdc(); - void write(const char *filename, + WriteSdc(const Sdc *sdc, + Instance *instance, + std::string_view creator, + bool map_hpins, + bool native, + int digits, + bool no_timestamp); + ~WriteSdc() override = default; + void write(std::string_view filename, bool gzip); - void openFile(const char *filename, + void openFile(std::string_view filename, bool gzip); void closeFile(); virtual void writeHeader() const; @@ -62,49 +65,49 @@ public: void writeDisabledClockGatingChecks() const; void writeDisabledEdge(Edge *edge) const; void findMatchingEdges(Edge *edge, - EdgeSet &matches) const; + EdgeSet &matches) const; bool edgeSenseIsUnique(Edge *edge, - EdgeSet &matches) const; + EdgeSet &matches) const; void writeDisabledEdgeSense(Edge *edge) const; void writeClocks() const; void writeClock(Clock *clk) const; void writeGeneratedClock(Clock *clk) const; void writeClockPins(const Clock *clk) const; - void writeFloatSeq(FloatSeq *floats, - float scale) const; - void writeIntSeq(IntSeq *ints) const; + void writeFloatSeq(const FloatSeq &floats, + float scale) const; + void writeIntSeq(const IntSeq &ints) const; void writeClockSlews(const Clock *clk) const; void writeClockUncertainty(const Clock *clk) const; void writeClockUncertainty(const Clock *clk, - const char *setup_hold, - float value) const; + std::string_view setup_hold, + float value) const; void writeClockUncertaintyPins() const; void writeClockUncertaintyPin(const Pin *pin, - ClockUncertainties *uncertainties) const; + ClockUncertainties *uncertainties) const; void writeClockUncertaintyPin(const Pin *pin, - const char *setup_hold, - float value) const; + std::string_view setup_hold, + float value) const; void writeClockLatencies() const; void writeClockInsertions() const; void writeClockInsertion(ClockInsertion *insert, - WriteSdcObject &write_obj) const; + WriteSdcObject &write_obj) const; void writeInterClockUncertainties() const; void writeInterClockUncertainty(InterClockUncertainty *uncertainty) const; void writePropagatedClkPins() const; void writeInputDelays() const; void writeOutputDelays() const; void writePortDelay(PortDelay *port_delay, - bool is_input_delay, - const char *sdc_cmd) const; + bool is_input_delay, + std::string_view sdc_cmd) const; void writePortDelay(PortDelay *port_delay, - bool is_input_delay, - float delay, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - const char *sdc_cmd) const; + bool is_input_delay, + float delay, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + std::string_view sdc_cmd) const; void writeClockSenses() const; void writeClockSense(PinClockPair &pin_clk, - ClockSense sense) const; + ClockSense sense) const; void writeClockGroups() const; void writeClockGroups(ClockGroups *clk_groups) const; void writeExceptions() const; @@ -114,25 +117,25 @@ public: void writeExceptionFrom(ExceptionFrom *from) const; void writeExceptionTo(ExceptionTo *to) const; void writeExceptionFromTo(ExceptionFromTo *from_to, - const char *from_to_key, - bool map_hpin_to_drvr) const; + std::string_view from_to_key, + bool map_hpin_to_drvr) const; void writeExceptionThru(ExceptionThru *thru) const; void mapThruHpins(ExceptionThru *thru, - PinSeq &pins) const; + PinSeq &pins) const; void writeDataChecks() const; void writeDataCheck(DataCheck *check) const; void writeDataCheck(DataCheck *check, - const RiseFallBoth *from_rf, - const RiseFallBoth *to_rf, - const SetupHold *setup_hold, - float margin) const; + const RiseFallBoth *from_rf, + const RiseFallBoth *to_rf, + const SetupHold *setup_hold, + float margin) const; void writeEnvironment() const; void writeOperatingConditions() const; void writeWireload() const; virtual void writeNetLoads() const; void writeNetLoad(const Net *net, - const MinMaxAll *min_max, - float cap) const; + const MinMaxAll *min_max, + float cap) const; void writePortLoads() const; void writePortLoads(const Port *port) const; void writePortFanout(const Port *port) const; @@ -140,75 +143,74 @@ public: void writeDrivingCells() const; void writeInputTransitions() const; void writeDrivingCell(Port *port, - InputDriveCell *drive_cell, - const RiseFall *rf, - const MinMax *min_max) const; + InputDriveCell *drive_cell, + const RiseFall *rf, + const MinMax *min_max) const; void writeConstants() const; virtual void writeConstant(const Pin *pin) const; - const char *setConstantCmd(const Pin *pin) const; + std::string_view setConstantCmd(const Pin *pin) const; void writeCaseAnalysis() const; virtual void writeCaseAnalysis(const Pin *pin) const; - const char *caseAnalysisValueStr(const Pin *pin) const; - void sortedLogicValuePins(LogicValueMap &value_map, - PinSeq &pins) const; + std::string_view caseAnalysisValueStr(const Pin *pin) const; + void sortedLogicValuePins(const LogicValueMap &value_map, + PinSeq &pins) const; void writeNetResistances() const; void writeNetResistance(const Net *net, - const MinMaxAll *min_max, - float res) const; + const MinMaxAll *min_max, + float res) const; void writeDesignRules() const; void writeMinPulseWidths() const; void writeMinPulseWidths(RiseFallValues *min_widths, - WriteSdcObject &write_obj) const; - void writeMinPulseWidth(const char *hi_low, - float value, - WriteSdcObject &write_obj) const; + WriteSdcObject &write_obj) const; + void writeMinPulseWidth(std::string_view hi_low, + float value, + WriteSdcObject &write_obj) const; void writeSlewLimits() const; void writeCapLimits() const; void writeCapLimits(const MinMax *min_max, - const char *cmd) const; + std::string_view cmd) const; void writeMaxArea() const; void writeMaxDynamicPower() const; void writeMaxLeakagePower() const; void writeFanoutLimits() const; void writeFanoutLimits(const MinMax *min_max, - const char *cmd) const; + std::string_view cmd) const; void writeLatchBorowLimits() const; void writeDeratings() const; void writeDerating(DeratingFactorsGlobal *factors) const; void writeDerating(DeratingFactorsCell *factors, - WriteSdcObject *write_obj) const; + WriteSdcObject *write_obj) const; void writeDerating(DeratingFactors *factors, - TimingDerateType type, - const MinMax *early_late, - WriteSdcObject *write_obj) const; + TimingDerateType type, + const MinMax *early_late, + WriteSdcObject *write_obj) const; void writeVoltages() const; - const char *pathName(const Pin *pin) const; - const char *pathName(const Net *net) const; - const char *pathName(const Instance *inst) const; - void writeCommentSection(const char *line) const; + std::string pathName(const Pin *pin) const; + std::string pathName(const Net *net) const; + std::string pathName(const Instance *inst) const; + void writeCommentSection(std::string_view line) const; void writeCommentSeparator() const; void writeGetTimingArcsOfOjbects(const LibertyCell *cell) const; - void writeGetTimingArcs(Edge *edge) const; void writeGetTimingArcs(Edge *edge, - const char *filter) const; - const char *getTimingArcsCmd() const; + std::string_view filter = {}) const; + std::string_view getTimingArcsCmd() const; void writeGetLibCell(const LibertyCell *cell) const; void writeGetLibPin(const LibertyPort *port) const; void writeGetClock(const Clock *clk) const; void writeGetClocks(ClockSet *clks) const; void writeGetClocks(ClockSet *clks, - bool multiple, - bool &first) const; + bool multiple, + bool &first) const; virtual void writeGetPort(const Port *port) const; virtual void writeGetNet(const Net *net) const; virtual void writeGetInstance(const Instance *inst) const; virtual void writeGetPin(const Pin *pin) const; void writeGetPin(const Pin *pin, - bool map_hpin_to_drvr) const; + bool map_hpin_to_drvr) const; void writeGetPins(const PinSet *pins, - bool map_hpin_to_drvr) const; + bool map_hpin_to_drvr) const; void writeGetPins1(PinSeq *pins) const; void writeClockKey(const Clock *clk) const; float scaleTime(float time) const; @@ -220,42 +222,42 @@ public: void writeResistance(float res) const; void writeClkSlewLimits() const; - void writeClkSlewLimit(const char *clk_data, - const char *rise_fall, - const Clock *clk, - float limit) const; - void writeRiseFallMinMaxTimeCmd(const char *sdc_cmd, - const RiseFallMinMax *values, - WriteSdcObject &write_object) const; - void writeRiseFallMinMaxCapCmd(const char *sdc_cmd, - const RiseFallMinMax *values, - WriteSdcObject &write_object) const; - void writeRiseFallMinMaxCmd(const char *sdc_cmd, - const RiseFallMinMax *values, - float scale, - WriteSdcObject &write_object) const; - void writeRiseFallMinMaxCmd(const char *sdc_cmd, - float value, - float scale, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - WriteSdcObject &write_object) const; - void writeMinMaxFloatValuesCmd(const char *sdc_cmd, - MinMaxFloatValues *values, - float scale, - WriteSdcObject &write_object) const; - void writeMinMaxFloatCmd(const char *sdc_cmd, - float value, - float scale, - const MinMaxAll *min_max, - WriteSdcObject &write_object) const; - void writeMinMaxIntValuesCmd(const char *sdc_cmd, - MinMaxIntValues *values, - WriteSdcObject &write_object) const; - void writeMinMaxIntCmd(const char *sdc_cmd, - int value, - const MinMaxAll *min_max, - WriteSdcObject &write_object) const; + void writeClkSlewLimit(std::string_view clk_data, + std::string_view rise_fall, + const Clock *clk, + float limit) const; + void writeRiseFallMinMaxTimeCmd(std::string_view sdc_cmd, + const RiseFallMinMax *values, + WriteSdcObject &write_object) const; + void writeRiseFallMinMaxCapCmd(std::string_view sdc_cmd, + const RiseFallMinMax *values, + WriteSdcObject &write_object) const; + void writeRiseFallMinMaxCmd(std::string_view sdc_cmd, + const RiseFallMinMax *values, + float scale, + WriteSdcObject &write_object) const; + void writeRiseFallMinMaxCmd(std::string_view sdc_cmd, + float value, + float scale, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + WriteSdcObject &write_object) const; + void writeMinMaxFloatValuesCmd(std::string_view sdc_cmd, + const MinMaxFloatValues *values, + float scale, + WriteSdcObject &write_object) const; + void writeMinMaxFloatCmd(std::string_view sdc_cmd, + float value, + float scale, + const MinMaxAll *min_max, + WriteSdcObject &write_object) const; + void writeMinMaxIntValuesCmd(std::string_view sdc_cmd, + const MinMaxIntValues *values, + WriteSdcObject &write_object) const; + void writeMinMaxIntCmd(std::string_view sdc_cmd, + int value, + const MinMaxAll *min_max, + WriteSdcObject &write_object) const; void writeSetupHoldFlag(const MinMaxAll *min_max) const; void writeVariables() const; void writeCmdComment(SdcCmdComment *cmd) const; @@ -263,8 +265,9 @@ public: gzFile stream() const { return stream_; } protected: + const Sdc *sdc_; Instance *instance_; - const char *creator_; + std::string creator_; bool map_hpins_; bool native_; int digits_; @@ -275,4 +278,4 @@ protected: gzFile stream_; }; -} // namespace +} // namespace sta diff --git a/sdf/ReportAnnotation.cc b/sdf/ReportAnnotation.cc index 649853e9c..cca76e65e 100644 --- a/sdf/ReportAnnotation.cc +++ b/sdf/ReportAnnotation.cc @@ -1,77 +1,81 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "sdf/ReportAnnotation.hh" -#include "StringUtil.hh" -#include "Report.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" -#include "Liberty.hh" -#include "Network.hh" +#include + #include "Graph.hh" #include "GraphCmp.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Report.hh" #include "Sdc.hh" -#include "DcalcAnalysisPt.hh" +#include "StringUtil.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" namespace sta { class ReportAnnotated : public StaState { public: - ReportAnnotated(bool report_cells, - bool report_nets, - bool report_in_ports, - bool report_out_ports, - int max_lines, - bool list_annotated, - bool list_unannotated, - bool constant_arcs, - StaState *sta); - ReportAnnotated(bool report_setup, - bool report_hold, - bool report_recovery, - bool report_removal, - bool report_nochange, - bool report_width, - bool report_period, - bool report_max_skew, - int max_lines, - bool list_annotated, - bool list_unannotated, - bool constant_arcs, - StaState *sta); + ReportAnnotated(const Scene *scene, + bool report_cells, + bool report_nets, + bool report_in_ports, + bool report_out_ports, + int max_lines, + bool report_annotated, + bool report_unannotated, + bool constant_arcs, + StaState *sta); + ReportAnnotated(const Scene *scene, + bool report_setup, + bool report_hold, + bool report_recovery, + bool report_removal, + bool report_nochange, + bool report_width, + bool report_period, + bool report_max_skew, + int max_lines, + bool report_annotated, + bool report_unannotated, + bool constant_arcs, + StaState *sta); void reportDelayAnnotation(); void reportCheckAnnotation(); protected: - enum CountIndex { + enum class CountIndex { count_internal_net = TimingRole::index_max, count_input_net, count_output_net, - count_index_max }; + static const int count_index_max = + static_cast(CountIndex::count_output_net) + 1; static int count_delay; void init(); @@ -80,29 +84,31 @@ class ReportAnnotated : public StaState void reportDelayCounts(); void reportCheckCounts(); void reportArcs(); - void reportArcs(const char *header, - bool report_annotated, - PinSet &pins); + void reportArcs(const std::string &header, + bool report_annotated, + PinSet &pins); void reportArcs(Vertex *vertex, - bool report_annotated, - int &i); + bool report_annotated, + int &i); void reportPeriodArcs(const Pin *pin, bool report_annotated, int &i); - void reportCount(const char *title, - int index, - int &total, - int &annotated_total); + void reportCount(std::string_view title, + int index, + int &total, + int &annotated_total); void reportCheckCount(const TimingRole *role, - int &total, - int &annotated_total); + int &total, + int &annotated_total); int roleIndex(const TimingRole *role, - const Pin *from_pin, - const Pin *to_pin); + const Pin *from_pin, + const Pin *to_pin); + bool delayAnnotated(Edge *edge); + const Scene *scene_; int max_lines_; - bool list_annotated_; - bool list_unannotated_; + bool report_annotated_; + bool report_unannotated_; bool report_constant_arcs_; int edge_count_[count_index_max]; @@ -114,49 +120,50 @@ class ReportAnnotated : public StaState PinSet annotated_pins_; }; - int ReportAnnotated::count_delay; void -reportAnnotatedDelay(bool report_cells, - bool report_nets, - bool from_in_ports, - bool to_out_ports, - int max_lines, - bool list_annotated, - bool list_unannotated, - bool report_constant_arcs, - StaState *sta) +reportAnnotatedDelay(const Scene *scene, + bool report_cells, + bool report_nets, + bool report_in_ports, + bool report_out_ports, + int max_lines, + bool report_annotated, + bool report_unannotated, + bool report_constant_arcs, + StaState *sta) { - ReportAnnotated report_annotated(report_cells, report_nets, - from_in_ports, to_out_ports, - max_lines, list_annotated, list_unannotated, - report_constant_arcs, sta); - report_annotated.reportDelayAnnotation(); + ReportAnnotated report(scene, report_cells, report_nets, report_in_ports, + report_out_ports, max_lines, report_annotated, + report_unannotated, report_constant_arcs, sta); + report.reportDelayAnnotation(); } -ReportAnnotated::ReportAnnotated(bool report_cells, - bool report_nets, - bool report_in_ports, - bool report_out_ports, - int max_lines, - bool list_annotated, - bool list_unannotated, - bool report_constant_arcs, - StaState *sta) : +ReportAnnotated::ReportAnnotated(const Scene *scene, + bool report_cells, + bool report_nets, + bool report_in_ports, + bool report_out_ports, + int max_lines, + bool report_annotated, + bool report_unannotated, + bool report_constant_arcs, + StaState *sta) : StaState(sta), + scene_(scene), max_lines_(max_lines), - list_annotated_(list_annotated), - list_unannotated_(list_unannotated), + report_annotated_(report_annotated), + report_unannotated_(report_unannotated), report_constant_arcs_(report_constant_arcs), unannotated_pins_(sta->network()), annotated_pins_(sta->network()) { init(); report_role_[TimingRole::sdfIopath()->index()] = report_cells; - report_role_[count_internal_net] = report_nets; - report_role_[count_input_net] = report_in_ports; - report_role_[count_output_net] = report_out_ports; + report_role_[static_cast(CountIndex::count_internal_net)] = report_nets; + report_role_[static_cast(CountIndex::count_input_net)] = report_in_ports; + report_role_[static_cast(CountIndex::count_output_net)] = report_out_ports; } void @@ -170,70 +177,74 @@ ReportAnnotated::reportDelayAnnotation() void ReportAnnotated::reportDelayCounts() { - report_->reportLine(" Not "); - report_->reportLine("Delay type Total Annotated Annotated"); - report_->reportLine("----------------------------------------------------------------"); + report_->report( + " Not "); + report_->report( + "Delay type Total Annotated Annotated"); + report_->report( + "----------------------------------------------------------------"); int total = 0; int annotated_total = 0; reportCount("cell arcs", count_delay, total, annotated_total); - reportCount("internal net arcs", count_internal_net, total, annotated_total); - reportCount("net arcs from primary inputs", count_input_net, - total, annotated_total); - reportCount("net arcs to primary outputs", count_output_net, - total, annotated_total); - report_->reportLine("----------------------------------------------------------------"); - report_->reportLine("%-28s %10u %10u %10u", - " ", - total, - annotated_total, - total - annotated_total); + reportCount("internal net arcs", static_cast(CountIndex::count_internal_net), + total, annotated_total); + reportCount("net arcs from primary inputs", + static_cast(CountIndex::count_input_net), total, annotated_total); + reportCount("net arcs to primary outputs", + static_cast(CountIndex::count_output_net), total, + annotated_total); + report_->report( + "----------------------------------------------------------------"); + report_->report("{:<28} {:10} {:10} {:10}", " ", total, annotated_total, + total - annotated_total); } //////////////////////////////////////////////////////////////// void -reportAnnotatedCheck(bool report_setup, - bool report_hold, - bool report_recovery, - bool report_removal, - bool report_nochange, - bool report_width, - bool report_period, - bool report_max_skew, - int max_lines, - bool list_annotated, - bool list_unannotated, - bool report_constant_arcs, - StaState *sta) +reportAnnotatedCheck(const Scene *scene, + bool report_setup, + bool report_hold, + bool report_recovery, + bool report_removal, + bool report_nochange, + bool report_width, + bool report_period, + bool report_max_skew, + int max_lines, + bool report_annotated, + bool report_unannotated, + bool report_constant_arcs, + StaState *sta) { - ReportAnnotated report_annotated(report_setup, report_hold, - report_recovery, report_removal, - report_nochange, report_width, - report_period, report_max_skew, - max_lines, list_annotated, list_unannotated, - report_constant_arcs, sta); - report_annotated.reportCheckAnnotation(); + ReportAnnotated report(scene, report_setup, report_hold, report_recovery, + report_removal, report_nochange, report_width, + report_period, report_max_skew, max_lines, report_annotated, + report_unannotated, report_constant_arcs, sta); + report.reportCheckAnnotation(); } -ReportAnnotated::ReportAnnotated(bool report_setup, - bool report_hold, - bool report_recovery, - bool report_removal, - bool report_nochange, - bool report_width, - bool report_period, - bool report_max_skew, - int max_lines, - bool list_annotated, - bool list_unannotated, - bool report_constant_arcs, - StaState *sta) : +ReportAnnotated::ReportAnnotated(const Scene *scene, + bool report_setup, + bool report_hold, + bool report_recovery, + bool report_removal, + bool report_nochange, + bool report_width, + bool report_period, + bool report_max_skew, + int max_lines, + bool report_annotated, + bool report_unannotated, + bool report_constant_arcs, + StaState *sta) : StaState(sta), + scene_(scene), max_lines_(max_lines), - list_annotated_(list_annotated), - list_unannotated_(list_unannotated), + report_annotated_(report_annotated), + report_unannotated_(report_unannotated), report_constant_arcs_(report_constant_arcs), unannotated_pins_(sta->network()), annotated_pins_(sta->network()) @@ -260,9 +271,12 @@ ReportAnnotated::reportCheckAnnotation() void ReportAnnotated::reportCheckCounts() { - report_->reportLine(" Not "); - report_->reportLine("Check type Total Annotated Annotated"); - report_->reportLine("----------------------------------------------------------------"); + report_->report( + " Not "); + report_->report( + "Check type Total Annotated Annotated"); + report_->report( + "----------------------------------------------------------------"); int total = 0; int annotated_total = 0; @@ -275,24 +289,21 @@ ReportAnnotated::reportCheckCounts() reportCheckCount(TimingRole::period(), total, annotated_total); reportCheckCount(TimingRole::skew(), total, annotated_total); - report_->reportLine("----------------------------------------------------------------"); - report_->reportLine("%-28s %10u %10u %10u", - " ", - total, - annotated_total, - total - annotated_total); + report_->report( + "----------------------------------------------------------------"); + report_->report("{:<28} {:10} {:10} {:10}", " ", total, annotated_total, + total - annotated_total); } void ReportAnnotated::reportCheckCount(const TimingRole *role, - int &total, - int &annotated_total) + int &total, + int &annotated_total) { int index = role->index(); if (edge_count_[index] > 0) { - std::string title; - stringPrint(title, "cell %s arcs", role->to_string().c_str()); - reportCount(title.c_str(), index, total, annotated_total); + std::string title = sta::format("cell {} arcs", role->to_string()); + reportCount(title, index, total, annotated_total); } } @@ -314,14 +325,14 @@ ReportAnnotated::init() void ReportAnnotated::findCounts() { + const Sdc *sdc = scene_->sdc(); VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *from_vertex = vertex_iter.next(); Pin *from_pin = from_vertex->pin(); LogicValue from_logic_value; bool from_logic_value_exists; - sdc_->logicValue(from_pin, from_logic_value, - from_logic_value_exists); + sdc->logicValue(from_pin, from_logic_value, from_logic_value_exists); VertexOutEdgeIterator edge_iter(from_vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); @@ -331,50 +342,62 @@ ReportAnnotated::findCounts() int index = roleIndex(role, from_pin, to_pin); LogicValue to_logic_value; bool to_logic_value_exists; - sdc_->logicValue(to_pin, to_logic_value, - to_logic_value_exists); + sdc->logicValue(to_pin, to_logic_value, to_logic_value_exists); edge_count_[index]++; if (from_logic_value_exists || to_logic_value_exists) - edge_constant_count_[index]++; + edge_constant_count_[index]++; if (report_role_[index]) { - if (graph_->delayAnnotated(edge)) { - edge_annotated_count_[index]++; - if (from_logic_value_exists || to_logic_value_exists) - edge_constant_annotated_count_[index]++; - if (list_annotated_) - annotated_pins_.insert(from_pin); - } - else { - if (list_unannotated_) - unannotated_pins_.insert(from_pin); - } + if (delayAnnotated(edge)) { + edge_annotated_count_[index]++; + if (from_logic_value_exists || to_logic_value_exists) + edge_constant_annotated_count_[index]++; + if (report_annotated_) + annotated_pins_.insert(from_pin); + } + else { + if (report_unannotated_) + unannotated_pins_.insert(from_pin); + } } } findPeriodCount(from_pin); } } +bool +ReportAnnotated::delayAnnotated(Edge *edge) +{ + TimingArcSet *arc_set = edge->timingArcSet(); + for (TimingArc *arc : arc_set->arcs()) { + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene_->dcalcAnalysisPtIndex(min_max); + if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) + return false; + } + } + return true; +} + int ReportAnnotated::roleIndex(const TimingRole *role, - const Pin *from_pin, - const Pin *to_pin) + const Pin *from_pin, + const Pin *to_pin) { if (role == TimingRole::wire()) { if (network_->isTopLevelPort(from_pin)) - return count_input_net; + return static_cast(CountIndex::count_input_net); else if (network_->isTopLevelPort(to_pin)) - return count_output_net; + return static_cast(CountIndex::count_output_net); else - return count_internal_net; + return static_cast(CountIndex::count_internal_net); } else if (role->sdfRole() == TimingRole::sdfIopath()) return count_delay; else { if (role->isTimingCheck() - && (role == TimingRole::latchSetup() - || role == TimingRole::latchHold())) + && (role == TimingRole::latchSetup() || role == TimingRole::latchHold())) role = role->genericRole(); return role->index(); } @@ -394,44 +417,38 @@ ReportAnnotated::findPeriodCount(Pin *pin) if (report_role_[period_index]) { port->minPeriod(value, exists); if (exists) { - edge_count_[period_index]++; - graph_->periodCheckAnnotation(pin, ap_index, value, annotated); - if (annotated) { - edge_annotated_count_[period_index]++; - if (list_annotated_) - annotated_pins_.insert(pin); - } - else { - if (list_unannotated_) - unannotated_pins_.insert(pin); - } + edge_count_[period_index]++; + graph_->periodCheckAnnotation(pin, ap_index, value, annotated); + if (annotated) { + edge_annotated_count_[period_index]++; + if (report_annotated_) + annotated_pins_.insert(pin); + } + else { + if (report_unannotated_) + unannotated_pins_.insert(pin); + } } } } } void -ReportAnnotated::reportCount(const char *title, - int index, - int &total, - int &annotated_total) +ReportAnnotated::reportCount(std::string_view title, + int index, + int &total, + int &annotated_total) { if (report_role_[index]) { int count = edge_count_[index]; int annotated_count = edge_annotated_count_[index]; - report_->reportLine("%-28s %10u %10u %10u", - title, - count, - annotated_count, - count - annotated_count); + report_->report("{:<28} {:10} {:10} {:10}", title, count, annotated_count, + count - annotated_count); if (report_constant_arcs_) { int const_count = edge_constant_count_[index]; int const_annotated_count = edge_constant_annotated_count_[index]; - report_->reportLine("%-28s %10s %10u %10u", - "constant arcs", - "", - const_annotated_count, - const_count - const_annotated_count); + report_->report("{:<28} {:10} {:10} {:10}", "constant arcs", "", + const_annotated_count, const_count - const_annotated_count); } total += count; annotated_total += annotated_count; @@ -441,19 +458,19 @@ ReportAnnotated::reportCount(const char *title, void ReportAnnotated::reportArcs() { - if (list_annotated_) + if (report_annotated_) reportArcs("Annotated Arcs", true, annotated_pins_); - if (list_unannotated_) + if (report_unannotated_) reportArcs("Unannotated Arcs", false, unannotated_pins_); } void -ReportAnnotated::reportArcs(const char *header, - bool report_annotated, - PinSet &pins) +ReportAnnotated::reportArcs(const std::string &header, + bool report_annotated, + PinSet &pins) { report_->reportBlankLine(); - report_->reportLineString(header); + report_->reportLine(header); PinSeq pins1 = sortByPathName(&pins, network_); int i = 0; for (const Pin *pin : pins1) { @@ -470,37 +487,33 @@ ReportAnnotated::reportArcs(const char *header, void ReportAnnotated::reportArcs(Vertex *vertex, - bool report_annotated, - int &i) + bool report_annotated, + int &i) { const Pin *from_pin = vertex->pin(); VertexOutEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext() - && (max_lines_ == 0 || i < max_lines_)) { + while (edge_iter.hasNext() && (max_lines_ == 0 || i < max_lines_)) { Edge *edge = edge_iter.next(); const TimingRole *role = edge->role(); const Pin *to_pin = edge->to(graph_)->pin(); - if (graph_->delayAnnotated(edge) == report_annotated - && report_role_[roleIndex(role, from_pin, to_pin)]) { - const char *role_name; + if (delayAnnotated(edge) == report_annotated + && report_role_[roleIndex(role, from_pin, to_pin)]) { + std::string_view role_name; if (role->isTimingCheck()) - role_name = role->to_string().c_str(); + role_name = role->to_string(); else if (role->isWire()) { - if (network_->isTopLevelPort(from_pin)) - role_name = "primary input net"; - else if (network_->isTopLevelPort(to_pin)) - role_name = "primary output net"; - else - role_name = "internal net"; + if (network_->isTopLevelPort(from_pin)) + role_name = "primary input net"; + else if (network_->isTopLevelPort(to_pin)) + role_name = "primary output net"; + else + role_name = "internal net"; } else - role_name = "delay"; - const char *cond = edge->timingArcSet()->sdfCond(); - report_->reportLine(" %-18s %s -> %s %s", - role_name, - network_->pathName(from_pin), - network_->pathName(to_pin), - cond ? cond : ""); + role_name = "delay"; + const std::string &cond = edge->timingArcSet()->sdfCond(); + report_->report(" {:<18} {} -> {} {}", role_name, network_->pathName(from_pin), + network_->pathName(to_pin), cond); i++; } } @@ -515,23 +528,20 @@ ReportAnnotated::reportPeriodArcs(const Pin *pin, if (port) { DcalcAPIndex ap_index = 0; int period_index = TimingRole::period()->index(); - if (report_role_[period_index] - && (max_lines_ == 0 || i < max_lines_)) { + if (report_role_[period_index] && (max_lines_ == 0 || i < max_lines_)) { float value; bool exists, annotated; port->minPeriod(value, exists); if (exists) { - edge_count_[period_index]++; - graph_->periodCheckAnnotation(pin, ap_index, value, annotated); - if (annotated == report_annotated) { - report_->reportLine(" %-18s %s", - "period", - network_->pathName(pin)); - i++; - } + edge_count_[period_index]++; + graph_->periodCheckAnnotation(pin, ap_index, value, annotated); + if (annotated == report_annotated) { + report_->report(" {:<18} {}", "period", network_->pathName(pin)); + i++; + } } } } } -} // namespace +} // namespace sta diff --git a/sdf/ReportAnnotation.hh b/sdf/ReportAnnotation.hh index eb1b5ff5b..c9291ceec 100644 --- a/sdf/ReportAnnotation.hh +++ b/sdf/ReportAnnotation.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -27,30 +27,33 @@ namespace sta { class StaState; +class Scene; void -reportAnnotatedDelay(bool report_cells, - bool report_nets, - bool report_in_ports, - bool report_out_ports, - int max_lines, - bool list_annotated, - bool list_unannotated, - bool report_constant_arcs, - StaState *sta); +reportAnnotatedDelay(const Scene *scene, + bool report_cells, + bool report_nets, + bool report_in_ports, + bool report_out_ports, + int max_lines, + bool report_annotated, + bool report_unannotated, + bool report_constant_arcs, + StaState *sta); void -reportAnnotatedCheck(bool report_setup, - bool report_hold, - bool report_recovery, - bool report_removal, - bool report_nochange, - bool report_width, - bool report_period, - bool report_max_skew, - int max_lines, - bool list_annotated, - bool list_unannotated, - bool report_constant_arcs, - StaState *sta); +reportAnnotatedCheck(const Scene *scene, + bool report_setup, + bool report_hold, + bool report_recovery, + bool report_removal, + bool report_nochange, + bool report_width, + bool report_period, + bool report_max_skew, + int max_lines, + bool report_annotated, + bool report_unannotated, + bool report_constant_arcs, + StaState *sta); -} // namespace +} // namespace sta diff --git a/sdf/Sdf.i b/sdf/Sdf.i index 8e35fd0eb..8cb3a6f1e 100644 --- a/sdf/Sdf.i +++ b/sdf/Sdf.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,21 +22,18 @@ // // This notice may not be removed or altered from any source distribution. -%module sdf - %{ -#include "sdf/SdfReader.hh" -#include "sdf/ReportAnnotation.hh" -#include "sdf/SdfWriter.hh" +#include + #include "Search.hh" #include "Sta.hh" +#include "sdf/ReportAnnotation.hh" +#include "sdf/SdfWriter.hh" using sta::Sta; using sta::AnalysisType; using sta::MinMax; using sta::MinMaxAllNull; -using sta::stringEq; -using sta::readSdf; using sta::reportAnnotatedDelay; using sta::reportAnnotatedCheck; @@ -51,82 +48,80 @@ using sta::reportAnnotatedCheck; // Return true if successful. bool -read_sdf_file(const char *filename, - const char *path, - Corner *corner, +read_sdf_file(std::string filename, + std::string path, + Scene *scene, bool unescaped_dividers, bool incremental_only, MinMaxAllNull *cond_use) { Sta *sta = Sta::sta(); - sta->ensureLibLinked(); - sta->ensureGraph(); - if (stringEq(path, "")) - path = NULL; - bool success = readSdf(filename, path, corner, unescaped_dividers, incremental_only, - cond_use, sta); - sta->search()->arrivalsInvalid(); - return success; + return sta->readSdf(filename, path, scene, unescaped_dividers, + incremental_only, cond_use); } void -report_annotated_delay_cmd(bool report_cells, - bool report_nets, - bool report_in_ports, - bool report_out_ports, - unsigned max_lines, - bool list_annotated, - bool list_not_annotated, - bool report_constant_arcs) +report_annotated_delay_cmd(const Scene *scene, + bool report_cells, + bool report_nets, + bool report_in_ports, + bool report_out_ports, + unsigned max_lines, + bool report_annotated, + bool report_not_annotated, + bool report_constant_arcs) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); sta->ensureGraph(); - reportAnnotatedDelay(report_cells, report_nets, - report_in_ports, report_out_ports, - max_lines, list_annotated, list_not_annotated, - report_constant_arcs, sta); + reportAnnotatedDelay(scene, report_cells, report_nets, + report_in_ports, report_out_ports, + max_lines, report_annotated, + report_not_annotated, + report_constant_arcs, sta); } void -report_annotated_check_cmd(bool report_setup, - bool report_hold, - bool report_recovery, - bool report_removal, - bool report_nochange, - bool report_width, - bool report_period, - bool report_max_skew, - unsigned max_lines, - bool list_annotated, - bool list_not_annotated, - bool report_constant_arcs) +report_annotated_check_cmd(const Scene *scene, + bool report_setup, + bool report_hold, + bool report_recovery, + bool report_removal, + bool report_nochange, + bool report_width, + bool report_period, + bool report_max_skew, + unsigned max_lines, + bool report_annotated, + bool report_not_annotated, + bool report_constant_arcs) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); sta->ensureGraph(); - reportAnnotatedCheck(report_setup, report_hold, - report_recovery, report_removal, - report_nochange, report_width, - report_period, report_max_skew, - max_lines, list_annotated, list_not_annotated, - report_constant_arcs, sta); + reportAnnotatedCheck(scene, report_setup, report_hold, + report_recovery, report_removal, + report_nochange, report_width, + report_period, report_max_skew, + max_lines, report_annotated, + report_not_annotated, + report_constant_arcs, sta); } void write_sdf_cmd(char *filename, - Corner *corner, - char divider, - bool include_typ, + Scene *scene, + char divider, + bool include_typ, int digits, - bool gzip, - bool no_timestamp, - bool no_version) + bool gzip, + bool no_timestamp, + bool no_version) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); - sta->writeSdf(filename, corner, divider, include_typ, digits, gzip, - no_timestamp, no_version); + sta->writeSdf(filename, scene, divider, include_typ, digits, gzip, + no_timestamp, no_version); } %} // inline diff --git a/sdf/Sdf.tcl b/sdf/Sdf.tcl index 32ce32d31..df0a775c0 100644 --- a/sdf/Sdf.tcl +++ b/sdf/Sdf.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -25,13 +25,13 @@ namespace eval sta { define_cmd_args "read_sdf" \ - {[-path path] [-corner corner]\ + {[-path path] [-scene scene]\ [-cond_use min|max|min_max]\ [-unescaped_dividers] filename} proc_redirect read_sdf { parse_key_args "read_sdf" args \ - keys {-path -corner -cond_use -analysis_type} \ + keys {-path -corner -scene -cond_use -analysis_type} \ flags {-unescaped_dividers -incremental_only} check_argc_eq1 "read_sdf" $args @@ -40,7 +40,7 @@ proc_redirect read_sdf { if [info exists keys(-path)] { set path $keys(-path) } - set corner [parse_corner keys] + set scene [parse_scene keys] set cond_use "NULL" if [info exists keys(-cond_use)] { @@ -50,31 +50,32 @@ proc_redirect read_sdf { set cond_use "NULL" } if { $cond_use == "min_max" \ - && { [operating_condition_analysis_type] == "single" }} { + && { [operating_condition_analysis_type] == "single" }} { sta_error 621 "-cond_use min_max cannot be used with analysis type single." } } set unescaped_dividers [info exists flags(-unescaped_dividers)] set incremental_only [info exists flags(-incremental_only)] - read_sdf_file $filename $path $corner $unescaped_dividers \ + read_sdf_file $filename $path $scene $unescaped_dividers \ $incremental_only $cond_use } ################################################################ define_cmd_args "report_annotated_delay" \ - {[-cell] [-net] [-from_in_ports] [-to_out_ports] [-max_lines lines]\ + {[-cell] [-net] [-from_in_ports] [-to_out_ports]\ + [-scene scene] [-max_lines lines]\ [-report_annotated] [-report_unannotated] [-constant_arcs]} proc_redirect report_annotated_delay { - parse_key_args "report_annotated_delay" args keys {-max_lines} \ + parse_key_args "report_annotated_delay" args keys {-scene -corner -max_lines} \ flags {-cell -net -from_in_ports -to_out_ports \ -report_annotated -report_unannotated -constant_arcs \ -list_not_annotated -list_annotated} if { [info exists flags(-cell)] || [info exists flags(-net)] \ - || [info exists flags(-from_in_ports)] \ - || [info exists flags(-to_out_ports)] } { + || [info exists flags(-from_in_ports)] \ + || [info exists flags(-to_out_ports)] } { set report_cells [info exists flags(-cell)] set report_nets [info exists flags(-net)] set report_in_nets [info exists flags(-from_in_ports)] @@ -86,6 +87,7 @@ proc_redirect report_annotated_delay { set report_out_nets 1 } + set scene [parse_scene keys] set max_lines 0 if { [info exists keys(-max_lines)] } { set max_lines $keys(-max_lines) @@ -105,26 +107,27 @@ proc_redirect report_annotated_delay { set report_unannotated 1 } - report_annotated_delay_cmd $report_cells $report_nets \ + report_annotated_delay_cmd $scene $report_cells $report_nets \ $report_in_nets $report_out_nets \ $max_lines $report_annotated $report_unannotated \ [info exists flags(-constant_arcs)] } define_cmd_args "report_annotated_check" \ - {[-setup] [-hold] [-recovery] [-removal] [-nochange] [-width] [-period]\ - [-max_skew] [-max_lines lines] [-report_annotated] [-report_unannotated]\ - [-constant_arcs]} + {[-setup] [-hold] [-recovery] [-removal] [-nochange]\ + [-width] [-period] [-max_skew]\ + [-scene scene] [-max_lines lines]\ + [-report_annotated] [-report_unannotated] [-constant_arcs]} proc_redirect report_annotated_check { - parse_key_args "report_annotated_check" args keys {-max_lines} \ + parse_key_args "report_annotated_check" args keys {-scene -max_lines} \ flags {-setup -hold -recovery -removal -nochange -width -period \ - -max_skew -report_annotated -report_unannotated -constant_arcs \ + -max_skew -report_annotated -report_unannotated -constant_arcs \ -list_annotated -list_not_annotated} if { [info exists flags(-setup)] || [info exists flags(-hold)] \ - || [info exists flags(-recovery)] || [info exists flags(-removal)] \ - || [info exists flags(-nochange)] || [info exists flags(-width)] \ - || [info exists flags(-period)] || [info exists flags(-max_skew)] } { + || [info exists flags(-recovery)] || [info exists flags(-removal)] \ + || [info exists flags(-nochange)] || [info exists flags(-width)] \ + || [info exists flags(-period)] || [info exists flags(-max_skew)] } { set report_setup [info exists flags(-setup)] set report_hold [info exists flags(-hold)] set report_recovery [info exists flags(-recovery)] @@ -144,6 +147,7 @@ proc_redirect report_annotated_check { set report_max_skew 1 } + set scene [parse_scene keys] set max_lines 0 if { [info exists keys(-max_lines)] } { set max_lines $keys(-max_lines) @@ -163,7 +167,7 @@ proc_redirect report_annotated_check { set report_unannotated 1 } - report_annotated_check_cmd $report_setup $report_hold \ + report_annotated_check_cmd $scene $report_setup $report_hold \ $report_recovery $report_removal $report_nochange \ $report_width $report_period $report_max_skew \ $max_lines $report_annotated $report_unannotated \ @@ -171,15 +175,15 @@ proc_redirect report_annotated_check { } define_cmd_args "write_sdf" \ - {[-corner corner] [-divider /|.] [-include_typ]\ + {[-scene scene] [-divider /|.] [-include_typ]\ [-digits digits] [-gzip] [-no_timestamp] [-no_version] filename} proc_redirect write_sdf { parse_key_args "write_sdf" args \ - keys {-corner -divider -digits -significant_digits} \ + keys {-corner -scene -divider -digits -significant_digits} \ flags {-include_typ -gzip -no_timestamp -no_version} check_argc_eq1 "write_sdf" $args - set corner [parse_corner keys] + set scene [parse_scene keys] set filename [file nativename [lindex $args 0]] set divider "/" if [info exists keys(-divider)] { @@ -198,7 +202,7 @@ proc_redirect write_sdf { set no_timestamp [info exists flags(-no_timestamp)] set no_version [info exists flags(-no_version)] set gzip [info exists flags(-gzip)] - write_sdf_cmd $filename $corner $divider $include_typ $digits $gzip \ + write_sdf_cmd $filename $scene $divider $include_typ $digits $gzip \ $no_timestamp $no_version } diff --git a/sdf/SdfLex.ll b/sdf/SdfLex.ll index 19849ff39..518915c71 100644 --- a/sdf/SdfLex.ll +++ b/sdf/SdfLex.ll @@ -23,6 +23,10 @@ // // This notice may not be removed or altered from any source distribution. +#include +#include +#include + #include "util/FlexDisableRegister.hh" #include "sdf/SdfReaderPvt.hh" #include "SdfParse.hh" @@ -82,7 +86,7 @@ EOL \r?\n "\"" { BEGIN INITIAL; - yylval->string = new std::string(token_); + yylval->emplace(std::move(token_)); return token::QSTRING; } @@ -98,12 +102,12 @@ EOL \r?\n "//"[^\n]*{EOL} { loc->lines(); loc->step(); } ("-"|"+")?([0-9]*)("."[0-9]+)([eE]("-"|"+")?[0-9]+)? { - yylval->number = atof(yytext); + yylval->emplace(static_cast(atof(yytext))); return token::FNUMBER; } "+"?[0-9]+ { - yylval->integer = atoi(yytext); + yylval->emplace(atoi(yytext)); return token::DNUMBER; } @@ -157,7 +161,7 @@ COND { "("{BLANK}*IOPATH { BEGIN INITIAL; - yylval->string = new std::string(token_); + yylval->emplace(std::move(token_)); return token::EXPR_OPEN_IOPATH; } @@ -167,7 +171,7 @@ COND { */ if (reader_->inTimingCheck()) { BEGIN INITIAL; - yylval->string = new std::string(token_); + yylval->emplace(std::move(token_)); return token::EXPR_OPEN; } else @@ -181,7 +185,7 @@ COND { /* remove trailing ")" */ std::string cond_id(token_); cond_id += yytext; - yylval->string = new std::string(cond_id.substr(0, cond_id.size() - 1)); + yylval->emplace(cond_id.substr(0, cond_id.size() - 1)); /* No way to pass expr and id separately, so pass them together. */ return token::EXPR_ID_CLOSE; } @@ -194,7 +198,7 @@ COND { . { token_ += yytext[0]; } {ID} { - yylval->string = new std::string(yytext); + yylval->emplace(yytext); return token::ID; } diff --git a/sdf/SdfParse.yy b/sdf/SdfParse.yy index 8475dafe8..68e05c57c 100644 --- a/sdf/SdfParse.yy +++ b/sdf/SdfParse.yy @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,8 @@ %{ #include +#include +#include #include "sdf/SdfReaderPvt.hh" #include "sdf/SdfScanner.hh" @@ -38,11 +40,26 @@ void sta::SdfParse::error(const location_type &loc, const std::string &msg) { - reader->report()->fileError(164,reader->filename().c_str(), - loc.begin.line,"%s",msg.c_str()); + reader->report()->fileError(170, reader->filename(), loc.begin.line,"{}",msg); } %} +%code requires { +#include +#include +#include "StringUtil.hh" + +namespace sta { +class SdfScanner; +class SdfReader; +class Transition; +class SdfPortSpec; +class SdfTriple; + +using SdfTripleSeq = std::vector; +} +} + %require "3.2" %skeleton "lalr1.cc" %debug @@ -53,21 +70,14 @@ sta::SdfParse::error(const location_type &loc, %parse-param { SdfScanner *scanner } %parse-param { SdfReader *reader } %define api.parser.class {SdfParse} +%define api.value.type variant // expected shift/reduce conflicts %expect 4 -%union { - char character; - std::string *string; - float number; - float *number_ptr; - int integer; - sta::SdfTriple *triple; - sta::SdfTripleSeq *delval_list; - sta::SdfPortSpec *port_spec; - const sta::Transition *transition; -} +%token QSTRING ID EXPR_OPEN_IOPATH EXPR_OPEN EXPR_ID_CLOSE +%token FNUMBER +%token DNUMBER %token DELAYFILE SDFVERSION DESIGN DATE VENDOR PROGRAM PVERSION %token DIVIDER VOLTAGE PROCESS TEMPERATURE TIMESCALE @@ -76,21 +86,15 @@ sta::SdfParse::error(const location_type &loc, %token IOPATH TIMINGCHECK %token SETUP HOLD SETUPHOLD RECOVERY REMOVAL RECREM WIDTH PERIOD SKEW NOCHANGE %token POSEDGE NEGEDGE COND CONDELSE -%token QSTRING ID FNUMBER DNUMBER EXPR_OPEN_IOPATH EXPR_OPEN EXPR_ID_CLOSE - -%type FNUMBER NUMBER -%type DNUMBER -%type number_opt -%type value triple -%type delval_list -%type QSTRING ID path port port_instance -%type EXPR_OPEN_IOPATH EXPR_OPEN EXPR_ID_CLOSE -%type port_spec port_tchk -%type port_transition -%type hchar - -// Used by error recovery. -%destructor { delete $$; } QSTRING + +%type NUMBER +%type number_opt +%type value triple +%type delval_list +%type path port port_instance +%type port_spec port_tchk +%type port_transition +%type hchar %start file @@ -107,17 +111,17 @@ header: // technically the ordering of these statements is fixed by the spec header_stmt: - '(' SDFVERSION QSTRING ')' { delete $3; } -| '(' DESIGN QSTRING ')' { delete $3; } -| '(' DATE QSTRING ')' { delete $3; } -| '(' VENDOR QSTRING ')' { delete $3; } -| '(' PROGRAM QSTRING ')' { delete $3; } -| '(' PVERSION QSTRING ')' { delete $3; } + '(' SDFVERSION QSTRING ')' +| '(' DESIGN QSTRING ')' +| '(' DATE QSTRING ')' +| '(' VENDOR QSTRING ')' +| '(' PROGRAM QSTRING ')' +| '(' PVERSION QSTRING ')' | '(' DIVIDER hchar ')' { reader->setDivider($3); } | '(' VOLTAGE triple ')' { reader->deleteTriple($3); } | '(' VOLTAGE NUMBER ')' | '(' VOLTAGE ')' // Illegal SDF (from OC). -| '(' PROCESS QSTRING ')' { delete $3; } +| '(' PROCESS QSTRING ')' | '(' PROCESS ')' // Illegal SDF (from OC). | '(' TEMPERATURE NUMBER ')' | '(' TEMPERATURE triple ')' { reader->deleteTriple($3); } @@ -153,7 +157,7 @@ celltype: cell_instance: '(' INSTANCE ')' - { reader->setInstance(nullptr); } + { reader->setInstance(); } | '(' INSTANCE '*' ')' { reader->setInstanceWildcard(); } | '(' INSTANCE path ')' @@ -196,10 +200,10 @@ path: del_def: '(' IOPATH port_spec port_instance retains delval_list ')' - { reader->iopath($3, $4, $6, nullptr, false); } + { reader->iopath($3, $4, $6, "", false); } | '(' CONDELSE '(' IOPATH port_spec port_instance retains delval_list ')' ')' - { reader->iopath($5, $6, $8, nullptr, true); } + { reader->iopath($5, $6, $8, "", true); } | '(' COND EXPR_OPEN_IOPATH port_spec port_instance retains delval_list ')' ')' { reader->iopath($4, $5, $7, $3, false); } @@ -297,15 +301,16 @@ port: port_instance: port + { $$ = $1; } | path hchar port { $$ = reader->makePath($1, $3); } ; port_spec: port_instance - { $$=reader->makePortSpec(sta::Transition::riseFall(),$1,nullptr); } + { $$ = reader->makePortSpec(sta::Transition::riseFall(), $1); } | '(' port_transition port_instance ')' - { $$ = reader->makePortSpec($2, $3, nullptr); } + { $$ = reader->makePortSpec($2, $3); } ; port_transition: @@ -315,6 +320,7 @@ port_transition: port_tchk: port_spec + { $$ = $1; } | '(' COND EXPR_ID_CLOSE { $$ = reader->makeCondPortSpec($3); } | '(' COND EXPR_OPEN port_transition port_instance ')' ')' @@ -353,6 +359,7 @@ triple: NUMBER: FNUMBER + { $$ = $1; } | DNUMBER { $$ = static_cast($1); } | '-' DNUMBER diff --git a/sdf/SdfReader.cc b/sdf/SdfReader.cc index 00279e498..007de2ba8 100644 --- a/sdf/SdfReader.cc +++ b/sdf/SdfReader.cc @@ -1,59 +1,58 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "sdf/SdfReader.hh" -#include #include +#include +#include +#include -#include "Zlib.hh" -#include "Error.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" -#include "Stats.hh" -#include "Report.hh" +#include "Error.hh" +#include "Graph.hh" #include "MinMax.hh" -#include "TimingArc.hh" #include "Network.hh" -#include "SdcNetwork.hh" -#include "Graph.hh" -#include "Corner.hh" -#include "DcalcAnalysisPt.hh" +#include "Report.hh" +#include "Scene.hh" #include "Sdc.hh" +#include "SdcNetwork.hh" +#include "Stats.hh" +#include "TimingArc.hh" +#include "Zlib.hh" #include "sdf/SdfReaderPvt.hh" #include "sdf/SdfScanner.hh" namespace sta { -using std::string; -using std::to_string; - class SdfTriple { public: SdfTriple(float *min, - float *typ, - float *max); + float *typ, + float *max); ~SdfTriple(); float **values() { return values_; } bool hasValue() const; @@ -66,66 +65,54 @@ class SdfPortSpec { public: SdfPortSpec(const Transition *tr, - const std::string *port, - const std::string *cond); - ~SdfPortSpec(); - const string *port() const { return port_; } + std::string_view port, + std::string_view cond); + std::string_view port() const { return port_; } const Transition *transition() const { return tr_; } - const string *cond() const { return cond_; } + std::string_view cond() const { return cond_; } private: const Transition *tr_; - const string *port_; - const string *cond_; // timing checks only + const std::string port_; + const std::string cond_; // timing checks only }; bool -readSdf(const char *filename, - const char *path, - Corner *corner, +readSdf(std::string_view filename, + std::string_view path, + Scene *scene, bool unescaped_dividers, bool incremental_only, MinMaxAll *cond_use, StaState *sta) { - int arc_min_index = corner->findDcalcAnalysisPt(MinMax::min())->index(); - int arc_max_index = corner->findDcalcAnalysisPt(MinMax::max())->index(); - SdfReader reader(filename, path, - arc_min_index, arc_max_index, - sta->sdc()->analysisType(), - unescaped_dividers, incremental_only, - cond_use, sta); + int arc_min_index = scene->dcalcAnalysisPtIndex(MinMax::min()); + int arc_max_index = scene->dcalcAnalysisPtIndex(MinMax::max()); + SdfReader reader(filename, path, arc_min_index, arc_max_index, + scene->sdc()->analysisType(), unescaped_dividers, + incremental_only, cond_use, sta); bool success = reader.read(); return success; } -SdfReader::SdfReader(const char *filename, - const char *path, +SdfReader::SdfReader(std::string_view filename, + std::string_view path, int arc_min_index, - int arc_max_index, - AnalysisType analysis_type, - bool unescaped_dividers, - bool is_incremental_only, + int arc_max_index, + AnalysisType analysis_type, + bool unescaped_dividers, + bool is_incremental_only, MinMaxAll *cond_use, - StaState *sta) : + StaState *sta) : StaState(sta), filename_(filename), path_(path), - triple_min_index_(0), - triple_max_index_(2), arc_delay_min_index_(arc_min_index), arc_delay_max_index_(arc_max_index), analysis_type_(analysis_type), unescaped_dividers_(unescaped_dividers), is_incremental_only_(is_incremental_only), - cond_use_(cond_use), - divider_('/'), - escape_('\\'), - instance_(nullptr), - cell_name_(nullptr), - in_timing_check_(false), - in_incremental_(false), - timescale_(1.0E-9F) // default units of ns + cond_use_(cond_use) { if (unescaped_dividers) network_ = makeSdcNetwork(network_); @@ -140,7 +127,7 @@ SdfReader::~SdfReader() bool SdfReader::read() { - gzstream::igzstream stream(filename_.c_str()); + gzstream::igzstream stream(std::string(filename_).c_str()); if (stream.is_open()) { Stats stats(debug_, report_); SdfScanner scanner(&stream, filename_, this, report_); @@ -151,7 +138,7 @@ SdfReader::read() return success; } else - throw FileNotReadable(filename_.c_str()); + throw FileNotReadable(filename_); } void @@ -162,29 +149,26 @@ SdfReader::setDivider(char divider) void SdfReader::setTimescale(float multiplier, - const string *units) + std::string_view units) { - if (multiplier == 1.0 - || multiplier == 10.0 - || multiplier == 100.0) { - if (*units == "us") + if (multiplier == 1.0 || multiplier == 10.0 || multiplier == 100.0) { + if (units == "us") timescale_ = multiplier * 1E-6F; - else if (*units == "ns") + else if (units == "ns") timescale_ = multiplier * 1E-9F; - else if (*units == "ps") + else if (units == "ps") timescale_ = multiplier * 1E-12F; else - sdfError(180, "TIMESCALE units not us, ns, or ps."); + error(180, "TIMESCALE units not us, ns, or ps."); } else - sdfError(181, "TIMESCALE multiplier not 1, 10, or 100."); - delete units; + error(181, "TIMESCALE multiplier not 1, 10, or 100."); } void -SdfReader::interconnect(const string *from_pin_name, - const string *to_pin_name, - SdfTripleSeq *triples) +SdfReader::interconnect(std::string_view from_pin_name, + std::string_view to_pin_name, + SdfTripleSeq *triples) { // Ignore non-incremental annotations in incremental only mode. if (!(is_incremental_only_ && !in_incremental_)) { @@ -194,64 +178,58 @@ SdfReader::interconnect(const string *from_pin_name, // Assume the pins are non-hierarchical and on the same net. Edge *edge = findWireEdge(from_pin, to_pin); if (edge) - setEdgeDelays(edge, triples, "INTERCONNECT"); + setEdgeDelays(edge, triples, "INTERCONNECT"); else { - bool from_is_hier = network_->isHierarchical(from_pin); - bool to_is_hier = network_->isHierarchical(to_pin); - if (from_is_hier || to_is_hier) { - if (from_is_hier) - sdfError(182, "pin %s is a hierarchical pin.", - from_pin_name->c_str()); - if (to_is_hier) - sdfError(183, "pin %s is a hierarchical pin.", - to_pin_name->c_str()); - } - else - sdfWarn(184, "INTERCONNECT from %s to %s not found.", - from_pin_name->c_str(), - to_pin_name->c_str()); + bool from_is_hier = network_->isHierarchical(from_pin); + bool to_is_hier = network_->isHierarchical(to_pin); + if (from_is_hier || to_is_hier) { + if (from_is_hier) + error(182, "pin {} is a hierarchical pin.", from_pin_name); + if (to_is_hier) + error(183, "pin {} is a hierarchical pin.", to_pin_name); + } + else + warn(184, "INTERCONNECT from {} to {} not found.", + from_pin_name, to_pin_name); } } else { if (from_pin == nullptr) - sdfWarn(185, "pin %s not found.", from_pin_name->c_str()); + warn(185, "pin {} not found.", from_pin_name); if (to_pin == nullptr) - sdfWarn(186, "pin %s not found.", to_pin_name->c_str()); + warn(186, "pin {} not found.", to_pin_name); } } - delete from_pin_name; - delete to_pin_name; deleteTripleSeq(triples); } void -SdfReader::port(const string *to_pin_name, - SdfTripleSeq *triples) +SdfReader::port(std::string_view to_pin_name, + SdfTripleSeq *triples) { // Ignore non-incremental annotations in incremental only mode. if (!(is_incremental_only_ && !in_incremental_)) { Pin *to_pin = (instance_) - ? network_->findPinRelative(instance_, to_pin_name->c_str()) - : network_->findPin(to_pin_name->c_str()); + ? network_->findPinRelative(instance_, to_pin_name) + : network_->findPin(to_pin_name); if (to_pin == nullptr) - sdfWarn(187, "pin %s not found.", to_pin_name->c_str()); + warn(187, "pin {} not found.", to_pin_name); else { Vertex *vertex = graph_->pinLoadVertex(to_pin); VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->role()->sdfRole()->isWire()) - setEdgeDelays(edge, triples, "PORT"); + Edge *edge = edge_iter.next(); + if (edge->role()->sdfRole()->isWire()) + setEdgeDelays(edge, triples, "PORT"); } } } - delete to_pin_name; deleteTripleSeq(triples); } Edge * SdfReader::findWireEdge(Pin *from_pin, - Pin *to_pin) + Pin *to_pin) { Vertex *to_vertex, *to_vertex_bidirect_drvr; graph_->pinVertices(to_pin, to_vertex, to_vertex_bidirect_drvr); @@ -261,8 +239,7 @@ SdfReader::findWireEdge(Pin *from_pin, while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); const TimingRole *edge_role = edge->role(); - if (edge->from(graph_)->pin() == from_pin - && edge_role->sdfRole()->isWire()) + if (edge->from(graph_)->pin() == from_pin && edge_role->sdfRole()->isWire()) return edge; } } @@ -271,86 +248,85 @@ SdfReader::findWireEdge(Pin *from_pin, void SdfReader::setEdgeDelays(Edge *edge, - SdfTripleSeq *triples, - const char *sdf_cmd) + SdfTripleSeq *triples, + std::string_view sdf_cmd) { // Rise/fall triples. size_t triple_count = triples->size(); - if (triple_count == 1 - || triple_count == 2) { + if (triple_count == 1 || triple_count == 2) { TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *arc : arc_set->arcs()) { size_t triple_index; if (triple_count == 1) - triple_index = 0; + triple_index = 0; else - triple_index = arc->toEdge()->sdfTripleIndex(); + triple_index = arc->toEdge()->sdfTripleIndex(); SdfTriple *triple = (*triples)[triple_index]; setEdgeArcDelays(edge, arc, triple); } } else if (triple_count == 0) - sdfError(188, "%s with no triples.", sdf_cmd); + error(188, "{} with no triples.", sdf_cmd); else - sdfError(189, "%s with more than 2 triples.", sdf_cmd); + error(189, "{} with more than 2 triples.", sdf_cmd); } void -SdfReader::setCell(const string *cell_name) +SdfReader::setCell(std::string_view cell_name) { cell_name_ = cell_name; } void -SdfReader::setInstance(const string *instance_name) +SdfReader::setInstance() { - if (instance_name) { - if (*instance_name == "*") { - notSupported("INSTANCE wildcards"); - instance_ = nullptr; - } - else { - instance_ = findInstance(instance_name); - if (instance_) { - Cell *inst_cell = network_->cell(instance_); - const char *inst_cell_name = network_->name(inst_cell); - if (cell_name_ && !stringEq(inst_cell_name, cell_name_->c_str())) - sdfWarn(190, "instance %s cell %s does not match enclosing cell %s.", - instance_name->c_str(), - inst_cell_name, - cell_name_->c_str()); - } + instance_ = nullptr; +} + +void +SdfReader::setInstance(std::string_view instance_name) +{ + if (instance_name == "*") { + warn(193, "INSTANCE wildcards not supported."); + instance_ = nullptr; + } + else { + instance_ = findInstance(instance_name); + if (instance_) { + Cell *inst_cell = network_->cell(instance_); + std::string inst_cell_name(network_->name(inst_cell)); + if (inst_cell_name != cell_name_) + warn(190, "instance {} cell {} does not match enclosing cell {}.", + instance_name, + inst_cell_name, + cell_name_); } } - else - instance_ = nullptr; - delete instance_name; } void SdfReader::setInstanceWildcard() { - notSupported("INSTANCE wildcards"); + warn(172, "INSTANCE wildcards not supported."); instance_ = nullptr; } void SdfReader::cellFinish() { - delete cell_name_; - cell_name_ = nullptr; + cell_name_.clear(); instance_ = nullptr; } void SdfReader::iopath(SdfPortSpec *from_edge, - const string *to_port_name, - SdfTripleSeq *triples, - const string *cond, - bool condelse) + std::string_view to_port_name, + SdfTripleSeq *triples, + std::string_view cond, + bool condelse) { if (instance_) { - const string *from_port_name = from_edge->port(); + std::string_view from_port_name = from_edge->port(); Cell *cell = network_->cell(instance_); Port *from_port = findPort(cell, from_port_name); Port *to_port = findPort(cell, to_port_name); @@ -360,8 +336,8 @@ SdfReader::iopath(SdfPortSpec *from_edge, // Do not report an error if the pin is not found because the // instance may not have the pin. if (from_pin && to_pin) { - Vertex *to_vertex = graph_->pinDrvrVertex(to_pin); - if (to_vertex) { + Vertex *to_vertex = graph_->pinDrvrVertex(to_pin); + if (to_vertex) { size_t triple_count = triples->size(); bool matched = false; // Fanin < fanout, so search for driver from load. @@ -371,16 +347,16 @@ SdfReader::iopath(SdfPortSpec *from_edge, while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); TimingArcSet *arc_set = edge->timingArcSet(); - const char *lib_cond = arc_set->sdfCond(); + const std::string &lib_cond = arc_set->sdfCond(); const TimingRole *edge_role = arc_set->role(); - bool cond_use_flag = cond_use_ && cond && lib_cond == nullptr + bool cond_use_flag = cond_use_ && !cond.empty() && lib_cond.empty() && !(!is_incremental_only_ && in_incremental_); if (edge->from(graph_)->pin() == from_pin && edge_role->sdfRole() == TimingRole::sdfIopath() && (cond_use_flag || (!condelse && condMatch(cond, lib_cond)) // condelse matches the default (unconditional) arc. - || (condelse && lib_cond == nullptr))) { + || (condelse && lib_cond.empty()))) { matched = true; for (TimingArc *arc : arc_set->arcs()) { if ((from_edge->transition() == Transition::riseFall()) @@ -404,41 +380,38 @@ SdfReader::iopath(SdfPortSpec *from_edge, } } if (!matched) - sdfWarn(191, "cell %s IOPATH %s -> %s not found.", - network_->cellName(instance_), - from_port_name->c_str(), - to_port_name->c_str()); + warn(191, "cell {} IOPATH {} -> {} not found.", + network_->cellName(instance_), + from_port_name, + to_port_name); } } } } - delete to_port_name; delete from_edge; deleteTripleSeq(triples); - delete cond; } Port * SdfReader::findPort(const Cell *cell, - const string *port_name) + std::string_view port_name) { - Port *port = network_->findPort(cell, port_name->c_str()); + Port *port = network_->findPort(cell, port_name); if (port == nullptr) - sdfWarn(194, "instance %s port %s not found.", - network_->pathName(instance_), - port_name->c_str()); + warn(194, "instance {} port {} not found.", network_->pathName(instance_), + port_name); return port; } void SdfReader::timingCheck(const TimingRole *role, SdfPortSpec *data_edge, - SdfPortSpec *clk_edge, + SdfPortSpec *clk_edge, SdfTriple *triple) { if (instance_) { - const string *data_port_name = data_edge->port(); - const string *clk_port_name = clk_edge->port(); + std::string_view data_port_name = data_edge->port(); + std::string_view clk_port_name = clk_edge->port(); Cell *cell = network_->cell(instance_); Port *data_port = findPort(cell, data_port_name); Port *clk_port = findPort(cell, clk_port_name); @@ -453,14 +426,13 @@ SdfReader::timingCheck(const TimingRole *role, void SdfReader::timingCheck1(const TimingRole *role, Port *data_port, - SdfPortSpec *data_edge, + SdfPortSpec *data_edge, Port *clk_port, - SdfPortSpec *clk_edge, - SdfTriple *triple) + SdfPortSpec *clk_edge, + SdfTriple *triple) { // Ignore non-incremental annotations in incremental only mode. - if (!(is_incremental_only_ && !in_incremental_) - && instance_) { + if (!(is_incremental_only_ && !in_incremental_) && instance_) { Pin *data_pin = network_->findPin(instance_, data_port); Pin *clk_pin = network_->findPin(instance_, clk_port); if (data_pin && clk_pin) { @@ -470,36 +442,32 @@ SdfReader::timingCheck1(const TimingRole *role, float *value_max = values[triple_max_index_]; if (value_min && value_max) { switch (analysis_type_) { - case AnalysisType::single: - break; - case AnalysisType::bc_wc: - if (role->genericRole() == TimingRole::setup()) + case AnalysisType::single: + break; + case AnalysisType::bc_wc: + if (role->genericRole() == TimingRole::setup()) + *value_min = *value_max; + else + *value_max = *value_min; + break; + case AnalysisType::ocv: *value_min = *value_max; - else - *value_max = *value_min; - break; - case AnalysisType::ocv: - *value_min = *value_max; - break; + break; } } - bool matched = annotateCheckEdges(data_pin, data_edge, - clk_pin, clk_edge, role, + bool matched = annotateCheckEdges(data_pin, data_edge, clk_pin, clk_edge, role, triple, false); // Liberty setup/hold checks on preset/clear pins can be translated // into recovery/removal checks, so be flexible about matching. if (!matched) - matched = annotateCheckEdges(data_pin, data_edge, - clk_pin, clk_edge, role, + matched = annotateCheckEdges(data_pin, data_edge, clk_pin, clk_edge, role, triple, true); if (!matched // Only warn when non-null values are present. && triple->hasValue()) - sdfWarn(192, "cell %s %s -> %s %s check not found.", - network_->cellName(instance_), - network_->name(data_port), - network_->name(clk_port), - role->to_string().c_str()); + warn(192, "cell {} {} -> {} {} check not found.", + network_->cellName(instance_), network_->name(data_port), + network_->name(clk_port), role->to_string()); } } } @@ -507,16 +475,16 @@ SdfReader::timingCheck1(const TimingRole *role, // Return true if matched. bool SdfReader::annotateCheckEdges(Pin *data_pin, - SdfPortSpec *data_edge, - Pin *clk_pin, - SdfPortSpec *clk_edge, - const TimingRole *sdf_role, - SdfTriple *triple, - bool match_generic) + SdfPortSpec *data_edge, + Pin *clk_pin, + SdfPortSpec *clk_edge, + const TimingRole *sdf_role, + SdfTriple *triple, + bool match_generic) { bool matched = false; - const string *cond_start = data_edge->cond(); - const string *cond_end = clk_edge->cond(); + std::string_view cond_start = data_edge->cond(); + std::string_view cond_end = clk_edge->cond(); // Timing check graph edges from clk to data. Vertex *to_vertex = graph_->pinLoadVertex(data_pin); // Fanin < fanout, so search for driver from load. @@ -526,24 +494,23 @@ SdfReader::annotateCheckEdges(Pin *data_pin, if (edge->from(graph_)->pin() == clk_pin) { TimingArcSet *arc_set = edge->timingArcSet(); const TimingRole *edge_role = arc_set->role(); - const char *lib_cond_start = arc_set->sdfCondStart(); - const char *lib_cond_end = arc_set->sdfCondEnd(); - bool cond_matches = condMatch(cond_start, lib_cond_start) - && condMatch(cond_end, lib_cond_end); + std::string_view lib_cond_start = arc_set->sdfCondStart(); + std::string_view lib_cond_end = arc_set->sdfCondEnd(); + bool cond_matches = + condMatch(cond_start, lib_cond_start) && condMatch(cond_end, lib_cond_end); if (((!match_generic && edge_role->sdfRole() == sdf_role) - || (match_generic - && edge_role->genericRole() == sdf_role->genericRole())) - && cond_matches) { - TimingArcSet *arc_set = edge->timingArcSet(); + || (match_generic && edge_role->genericRole() == sdf_role->genericRole())) + && cond_matches) { + TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *arc : arc_set->arcs()) { - if (((data_edge->transition() == Transition::riseFall()) - || (arc->toEdge() == data_edge->transition())) - && ((clk_edge->transition() == Transition::riseFall()) - || (arc->fromEdge() == clk_edge->transition()))) { - setEdgeArcDelays(edge, arc, triple); - } - } - matched = true; + if (((data_edge->transition() == Transition::riseFall()) + || (arc->toEdge() == data_edge->transition())) + && ((clk_edge->transition() == Transition::riseFall()) + || (arc->fromEdge() == clk_edge->transition()))) { + setEdgeArcDelays(edge, arc, triple); + } + } + matched = true; } } } @@ -552,16 +519,15 @@ SdfReader::annotateCheckEdges(Pin *data_pin, void SdfReader::timingCheckWidth(SdfPortSpec *edge, - SdfTriple *triple) + SdfTriple *triple) { // Ignore non-incremental annotations in incremental only mode. - if (!(is_incremental_only_ && !in_incremental_) - && instance_) { - const string *port_name = edge->port(); + if (!(is_incremental_only_ && !in_incremental_) && instance_) { + std::string_view port_name = edge->port(); Cell *cell = network_->cell(instance_); Port *port = findPort(cell, port_name); if (port) { - Pin *pin = network_->findPin(instance_, port_name->c_str()); + Pin *pin = network_->findPin(instance_, port_name); if (pin) { const RiseFall *rf = edge->transition()->asRiseFall(); Edge *edge; @@ -578,9 +544,9 @@ SdfReader::timingCheckWidth(SdfPortSpec *edge, void SdfReader::timingCheckSetupHold(SdfPortSpec *data_edge, - SdfPortSpec *clk_edge, - SdfTriple *setup_triple, - SdfTriple *hold_triple) + SdfPortSpec *clk_edge, + SdfTriple *setup_triple, + SdfTriple *hold_triple) { timingCheckSetupHold1(data_edge, clk_edge, setup_triple, hold_triple, TimingRole::setup(), TimingRole::hold()); @@ -588,9 +554,9 @@ SdfReader::timingCheckSetupHold(SdfPortSpec *data_edge, void SdfReader::timingCheckRecRem(SdfPortSpec *data_edge, - SdfPortSpec *clk_edge, - SdfTriple *rec_triple, - SdfTriple *rem_triple) + SdfPortSpec *clk_edge, + SdfTriple *rec_triple, + SdfTriple *rem_triple) { timingCheckSetupHold1(data_edge, clk_edge, rec_triple, rem_triple, TimingRole::recovery(), TimingRole::removal()); @@ -604,8 +570,8 @@ SdfReader::timingCheckSetupHold1(SdfPortSpec *data_edge, const TimingRole *setup_role, const TimingRole *hold_role) { - const string *data_port_name = data_edge->port(); - const string *clk_port_name = clk_edge->port(); + std::string_view data_port_name = data_edge->port(); + std::string_view clk_port_name = clk_edge->port(); Cell *cell = network_->cell(instance_); Port *data_port = findPort(cell, data_port_name); Port *clk_port = findPort(cell, clk_port_name); @@ -621,31 +587,30 @@ SdfReader::timingCheckSetupHold1(SdfPortSpec *data_edge, void SdfReader::timingCheckPeriod(SdfPortSpec *edge, - SdfTriple *triple) + SdfTriple *triple) { // Ignore non-incremental annotations in incremental only mode. - if (!(is_incremental_only_ && !in_incremental_) - && instance_) { - const string *port_name = edge->port(); + if (!(is_incremental_only_ && !in_incremental_) && instance_) { + std::string_view port_name = edge->port(); Cell *cell = network_->cell(instance_); Port *port = findPort(cell, port_name); if (port) { // Edge specifier is ignored for period checks. - Pin *pin = network_->findPin(instance_, port_name->c_str()); + Pin *pin = network_->findPin(instance_, port_name); if (pin) { - float **values = triple->values(); - float *value_ptr = values[triple_min_index_]; - if (value_ptr) { - float value = *value_ptr; - graph_->setPeriodCheckAnnotation(pin, arc_delay_min_index_, value); - } - if (triple_max_index_ != null_index_) { - value_ptr = values[triple_max_index_]; - if (value_ptr) { - float value = *value_ptr; - graph_->setPeriodCheckAnnotation(pin, arc_delay_max_index_, value); - } - } + float **values = triple->values(); + float *value_ptr = values[triple_min_index_]; + if (value_ptr) { + float value = *value_ptr; + graph_->setPeriodCheckAnnotation(pin, arc_delay_min_index_, value); + } + if (triple_max_index_ != null_index_) { + value_ptr = values[triple_max_index_]; + if (value_ptr) { + float value = *value_ptr; + graph_->setPeriodCheckAnnotation(pin, arc_delay_max_index_, value); + } + } } } } @@ -655,11 +620,11 @@ SdfReader::timingCheckPeriod(SdfPortSpec *edge, void SdfReader::timingCheckNochange(SdfPortSpec *data_edge, - SdfPortSpec *clk_edge, - SdfTriple *before_triple, - SdfTriple *after_triple) + SdfPortSpec *clk_edge, + SdfTriple *before_triple, + SdfTriple *after_triple) { - notSupported("NOCHANGE"); + warn(173, "NOCHANGE not supported."); delete data_edge; delete clk_edge; deleteTriple(before_triple); @@ -670,8 +635,7 @@ void SdfReader::device(SdfTripleSeq *triples) { // Ignore non-incremental annotations in incremental only mode. - if (!(is_incremental_only_ && !in_incremental_) - && instance_) { + if (!(is_incremental_only_ && !in_incremental_) && instance_) { InstancePinIterator *pin_iter = network_->pinIterator(instance_); while (pin_iter->hasNext()) { Pin *to_pin = pin_iter->next(); @@ -683,26 +647,24 @@ SdfReader::device(SdfTripleSeq *triples) } void -SdfReader::device(const string *to_port_name, - SdfTripleSeq *triples) +SdfReader::device(std::string_view to_port_name, + SdfTripleSeq *triples) { // Ignore non-incremental annotations in incremental only mode. - if (!(is_incremental_only_ && !in_incremental_) - && instance_) { + if (!(is_incremental_only_ && !in_incremental_) && instance_) { Cell *cell = network_->cell(instance_); Port *to_port = findPort(cell, to_port_name); if (to_port) { - Pin *to_pin = network_->findPin(instance_, to_port_name->c_str()); + Pin *to_pin = network_->findPin(instance_, to_port_name); setDevicePinDelays(to_pin, triples); } } - delete to_port_name; deleteTripleSeq(triples); } void SdfReader::setDevicePinDelays(Pin *to_pin, - SdfTripleSeq *triples) + SdfTripleSeq *triples) { Vertex *vertex = graph_->pinDrvrVertex(to_pin); if (vertex) { @@ -717,8 +679,8 @@ SdfReader::setDevicePinDelays(Pin *to_pin, void SdfReader::setEdgeArcDelays(Edge *edge, - TimingArc *arc, - SdfTriple *triple) + TimingArc *arc, + SdfTriple *triple) { setEdgeArcDelays(edge, arc, triple, triple_min_index_, arc_delay_min_index_); setEdgeArcDelays(edge, arc, triple, triple_max_index_, arc_delay_max_index_); @@ -726,10 +688,10 @@ SdfReader::setEdgeArcDelays(Edge *edge, void SdfReader::setEdgeArcDelays(Edge *edge, - TimingArc *arc, - SdfTriple *triple, - int triple_index, - int arc_delay_index) + TimingArc *arc, + SdfTriple *triple, + int triple_index, + int arc_delay_index) { if (triple_index != null_index_) { float **values = triple->values(); @@ -737,9 +699,9 @@ SdfReader::setEdgeArcDelays(Edge *edge, if (value_ptr) { ArcDelay delay; if (in_incremental_) - delay = *value_ptr + graph_->arcDelay(edge, arc, arc_delay_index); + delay = delaySum(graph_->arcDelay(edge, arc, arc_delay_index), *value_ptr, this); else - delay = *value_ptr; + delay = *value_ptr; graph_->setArcDelay(edge, arc, arc_delay_index, delay); graph_->setArcDelayAnnotated(edge, arc, arc_delay_index, true); edge->setDelayAnnotationIsIncremental(is_incremental_only_); @@ -749,8 +711,8 @@ SdfReader::setEdgeArcDelays(Edge *edge, void SdfReader::setEdgeArcDelaysCondUse(Edge *edge, - TimingArc *arc, - SdfTriple *triple) + TimingArc *arc, + SdfTriple *triple) { float **values = triple->values(); float *value_min = values[triple_min_index_]; @@ -769,28 +731,27 @@ SdfReader::setEdgeArcDelaysCondUse(Edge *edge, max = MinMax::max(); } setEdgeArcDelaysCondUse(edge, arc, value_min, triple_min_index_, - arc_delay_min_index_, min); + arc_delay_min_index_, min); setEdgeArcDelaysCondUse(edge, arc, value_max, triple_max_index_, - arc_delay_max_index_, max); + arc_delay_max_index_, max); } void SdfReader::setEdgeArcDelaysCondUse(Edge *edge, - TimingArc *arc, - float *value, - int triple_index, - int arc_delay_index, - const MinMax *min_max) + TimingArc *arc, + const float *value, + int triple_index, + int arc_delay_index, + const MinMax *min_max) { - if (value - && triple_index != null_index_) { + if (value && triple_index != null_index_) { ArcDelay delay(*value); if (!is_incremental_only_ && in_incremental_) - delay = graph_->arcDelay(edge, arc, arc_delay_index) + *value; + delay = delaySum(graph_->arcDelay(edge, arc, arc_delay_index), *value, this); else if (graph_->arcDelayAnnotated(edge, arc, arc_delay_index)) { ArcDelay prev_value = graph_->arcDelay(edge, arc, arc_delay_index); if (delayGreater(prev_value, delay, min_max, this)) - delay = prev_value; + delay = prev_value; } graph_->setArcDelay(edge, arc, arc_delay_index, delay); graph_->setArcDelayAnnotated(edge, arc, arc_delay_index, true); @@ -799,28 +760,31 @@ SdfReader::setEdgeArcDelaysCondUse(Edge *edge, } bool -SdfReader::condMatch(const string *sdf_cond, - const char *lib_cond) +SdfReader::condMatch(std::string_view sdf_cond, + std::string_view lib_cond) { // If the sdf is not conditional it matches any library condition. - if (sdf_cond == nullptr) + if (sdf_cond.empty()) return true; - else if (sdf_cond && lib_cond) { + else if (!sdf_cond.empty() && !lib_cond.empty()) { // Match sdf_cond and lib_cond ignoring blanks. - const char *c1 = sdf_cond->c_str(); - const char *c2 = lib_cond; - char ch1, ch2; - do { - ch1 = *c1++; - ch2 = *c2++; - while (ch1 && isspace(ch1)) - ch1 = *c1++; - while (ch2 && isspace(ch2)) - ch2 = *c2++; + size_t c1 = 0; + size_t c2 = 0; + while (c1 < sdf_cond.size() + && c2 < lib_cond.size()) { + char ch1 = sdf_cond[c1++]; + char ch2 = lib_cond[c2++]; + while (c1 < sdf_cond.size() + && isspace(ch1)) + ch1 = sdf_cond[c1++]; + while (c2 < lib_cond.size() + && isspace(ch2)) + ch2 = lib_cond[c2++]; if (ch1 != ch2) - return false; - } while (ch1 && ch2); - return (ch1 == '\0' && ch2 == '\0'); + return false; + } + return c1 == sdf_cond.size() + && c2 == lib_cond.size(); } else return false; @@ -828,32 +792,34 @@ SdfReader::condMatch(const string *sdf_cond, SdfPortSpec * SdfReader::makePortSpec(const Transition *tr, - const string *port, - const string *cond) + std::string_view port) +{ + return new SdfPortSpec(tr, port, ""); +} + +SdfPortSpec * +SdfReader::makePortSpec(const Transition *tr, + std::string_view port, + std::string_view cond) { return new SdfPortSpec(tr, port, cond); } SdfPortSpec * -SdfReader::makeCondPortSpec(const string *cond_port) +SdfReader::makeCondPortSpec(std::string_view cond_port) { // Search from end to find port name because condition may contain spaces. - string cond_port1(*cond_port); + std::string cond_port1(cond_port); trimRight(cond_port1); - auto port_idx = cond_port1.find_last_of(" "); - if (port_idx != cond_port1.npos) { - string *port1 = new string(cond_port1.substr(port_idx + 1)); - auto cond_end = cond_port1.find_last_not_of(" ", port_idx); - if (cond_end != cond_port1.npos) { - string *cond1 = new string(cond_port1.substr(0, cond_end + 1)); - SdfPortSpec *port_spec = new SdfPortSpec(Transition::riseFall(), - port1, - cond1); - delete cond_port; - return port_spec; + auto port_idx = cond_port1.find_last_of(' '); + if (port_idx != std::string::npos) { + std::string port1 = cond_port1.substr(port_idx + 1); + size_t cond_end = cond_port1.find_last_not_of(' ', port_idx); + if (cond_end != std::string::npos) { + std::string cond1 = cond_port1.substr(0, cond_end + 1); + return new SdfPortSpec(Transition::riseFall(), port1, cond1); } } - delete cond_port; return nullptr; } @@ -866,18 +832,14 @@ SdfReader::makeTripleSeq() void SdfReader::deleteTripleSeq(SdfTripleSeq *triples) { - SdfTripleSeq::Iterator iter(triples); - while (iter.hasNext()) { - SdfTriple *triple = iter.next(); - delete triple; - } + deleteContents(triples); delete triples; } SdfTriple * SdfReader::makeTriple() { - return new SdfTriple(0, 0, 0); + return new SdfTriple(nullptr, nullptr, nullptr); } SdfTriple * @@ -890,12 +852,15 @@ SdfReader::makeTriple(float value) SdfTriple * SdfReader::makeTriple(float *min, - float *typ, - float *max) + float *typ, + float *max) { - if (min) *min *= timescale_; - if (typ) *typ *= timescale_; - if (max) *max *= timescale_; + if (min) + *min *= timescale_; + if (typ) + *typ *= timescale_; + if (max) + *max *= timescale_; return new SdfTriple(min, typ, max); } @@ -917,151 +882,118 @@ SdfReader::setInIncremental(bool incr) in_incremental_ = incr; } -string * -SdfReader::unescaped(const string *token) +std::string +SdfReader::unescaped(std::string_view token) { char path_escape = network_->pathEscape(); char path_divider = network_->pathDivider(); - size_t token_length = token->size(); - string *unescaped = new string; + size_t token_length = token.size(); + std::string result; for (size_t i = 0; i < token_length; i++) { - char ch = (*token)[i]; + char ch = token[i]; if (ch == escape_) { - char next_ch = (*token)[i + 1]; + char next_ch = token[i + 1]; if (next_ch == divider_) { // Escaped divider. - // Translate sdf escape to network escape. - *unescaped += path_escape; - // Translate sdf divider to network divider. - *unescaped += path_divider; + // Translate sdf escape to network escape. + result += path_escape; + // Translate sdf divider to network divider. + result += path_divider; } - else if (next_ch == '[' - || next_ch == ']' - || next_ch == escape_) { - // Escaped bus bracket or escape. - // Translate sdf escape to network escape. - *unescaped += path_escape; - *unescaped += next_ch; + else if (next_ch == '[' || next_ch == ']' || next_ch == escape_) { + // Escaped bus bracket or escape. + // Translate sdf escape to network escape. + result += path_escape; + result += next_ch; } else // Escaped non-divider character. - *unescaped += next_ch; + result += next_ch; i++; } else // Just the normal noises. - *unescaped += ch; + result += ch; } - debugPrint(debug_, "sdf_name", 1, "unescape %s -> %s", - token->c_str(), - unescaped->c_str()); - delete token; - return unescaped; + debugPrint(debug_, "sdf_name", 1, "unescape {} -> {}", token, + result); + return result; } -string * -SdfReader::makePath(const string *head, - const string *tail) +std::string +SdfReader::makePath(std::string_view head, + std::string_view tail) { - string *path = new string(*head); - *path += network_->pathDivider(); - *path += *tail; - delete head; - delete tail; + std::string path(head); + path += network_->pathDivider(); + path += tail; return path; } -string * -SdfReader::makeBusName(string *base_name, +std::string +SdfReader::makeBusName(std::string_view base_name, int index) { - string *bus_name = unescaped(base_name); - *bus_name += '['; - *bus_name += to_string(index); - *bus_name += ']'; - delete base_name; + std::string bus_name = unescaped(base_name); + bus_name += '['; + bus_name += std::to_string(index); + bus_name += ']'; return bus_name; } -void -SdfReader::notSupported(const char *feature) +int +SdfReader::sdfLine() const { - sdfError(193, "%s not supported.", feature); -} - -void -SdfReader::sdfWarn(int id, - const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - report_->vfileWarn(id, filename_.c_str(), scanner_->lineno(), fmt, args); - va_end(args); -} - -void -SdfReader::sdfError(int id, - const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - report_->vfileError(id, filename_.c_str(), scanner_->lineno(), fmt, args); - va_end(args); + return scanner_->lineno(); } Pin * -SdfReader::findPin(const string *name) +SdfReader::findPin(std::string_view name) { - if (path_) { - string path_name(path_); + if (!path_.empty()) { + std::string path_name(path_); path_name += divider_; - path_name += *name; - Pin *pin = network_->findPin(path_name.c_str()); + path_name += name; + Pin *pin = network_->findPin(path_name); return pin; } else - return network_->findPin(name->c_str()); + return network_->findPin(name); } Instance * -SdfReader::findInstance(const string *name) +SdfReader::findInstance(std::string_view name) { - string inst_name; - if (path_) { + std::string inst_name; + if (!path_.empty()) { inst_name = path_; inst_name += divider_; - inst_name += *name; + inst_name += name; } else - inst_name = *name; - Instance *inst = network_->findInstance(inst_name.c_str()); + inst_name = name; + Instance *inst = network_->findInstance(inst_name); if (inst == nullptr) - sdfWarn(195, "instance %s not found.", inst_name.c_str()); + warn(195, "instance {} not found.", inst_name); return inst; } //////////////////////////////////////////////////////////////// SdfPortSpec::SdfPortSpec(const Transition *tr, - const string *port, - const string *cond) : + std::string_view port, + std::string_view cond) : tr_(tr), port_(port), cond_(cond) { } -SdfPortSpec::~SdfPortSpec() -{ - delete port_; - delete cond_; -} - //////////////////////////////////////////////////////////////// SdfTriple::SdfTriple(float *min, - float *typ, - float *max) + float *typ, + float *max) { values_[0] = min; values_[1] = typ; @@ -1073,9 +1005,12 @@ SdfTriple::~SdfTriple() if (values_[0] == values_[1] && values_[0] == values_[2]) delete values_[0]; else { - if (values_[0]) delete values_[0]; - if (values_[1]) delete values_[1]; - if (values_[2]) delete values_[2]; + if (values_[0]) + delete values_[0]; + if (values_[1]) + delete values_[1]; + if (values_[2]) + delete values_[2]; } } @@ -1088,7 +1023,7 @@ SdfTriple::hasValue() const //////////////////////////////////////////////////////////////// SdfScanner::SdfScanner(std::istream *stream, - const string &filename, + std::string_view filename, SdfReader *reader, Report *report) : yyFlexLexer(stream), @@ -1099,9 +1034,9 @@ SdfScanner::SdfScanner(std::istream *stream, } void -SdfScanner::error(const char *msg) +SdfScanner::error(std::string_view msg) { - report_->fileError(1869, filename_.c_str(), lineno(), "%s", msg); + report_->fileError(196, filename_, lineno(), "{}", msg); } -} // namespace +} // namespace sta diff --git a/sdf/SdfReader.hh b/sdf/SdfReader.hh index a2b4e064f..5bb2e8e58 100644 --- a/sdf/SdfReader.hh +++ b/sdf/SdfReader.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,10 +24,13 @@ #pragma once +#include +#include + namespace sta { class MinMaxAll; -class Corner; +class Scene; class StaState; // If unescaped_dividers is true, path names in the SDF do not have to @@ -40,7 +43,7 @@ class StaState; // If incremental_only is true non-incremental annoatations are ignored. // // path is a hierararchial path prefix for instances and pins in the -// sdf file. Pass 0 (nullptr) to specify no path. +// sdf file. Pass a null string to specify no path. // // The cond_use option is used when the SDF file contains conditional // delays and the library does not have conditional delay arcs. If @@ -52,12 +55,12 @@ class StaState; // maximum operating conditions. bool -readSdf(const char *filename, - const char *path, - Corner *corner, +readSdf(std::string_view filename, + std::string_view path, + Scene *scene, bool unescaped_dividers, bool incremental_only, MinMaxAll *cond_use, StaState *sta); -} // namespace +} // namespace sta diff --git a/sdf/SdfReaderPvt.hh b/sdf/SdfReaderPvt.hh index 067493915..3a53e391d 100644 --- a/sdf/SdfReaderPvt.hh +++ b/sdf/SdfReaderPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,14 +24,18 @@ #pragma once -#include "Vector.hh" -#include "TimingRole.hh" -#include "Transition.hh" +#include +#include +#include + +#include "GraphClass.hh" #include "LibertyClass.hh" #include "NetworkClass.hh" -#include "GraphClass.hh" +#include "Report.hh" #include "SdcClass.hh" #include "StaState.hh" +#include "TimingRole.hh" +#include "Transition.hh" namespace sta { @@ -40,74 +44,75 @@ class SdfTriple; class SdfPortSpec; class SdfScanner; -typedef Vector SdfTripleSeq; +using SdfTripleSeq = std::vector; class SdfReader : public StaState { public: - SdfReader(const char *filename, - const char *path, - int arc_min_index, - int arc_max_index, - AnalysisType analysis_type, - bool unescaped_dividers, - bool is_incremental_only, + SdfReader(std::string_view filename, + std::string_view path, + int arc_min_index, + int arc_max_index, + AnalysisType analysis_type, + bool unescaped_dividers, + bool is_incremental_only, MinMaxAll *cond_use, - StaState *sta); - ~SdfReader(); + StaState *sta); + ~SdfReader() override; bool read(); void setDivider(char divider); void setTimescale(float multiplier, - const std::string *units); + std::string_view units); void setPortDeviceDelay(Edge *edge, - SdfTripleSeq *triples, - bool from_trans); + SdfTripleSeq *triples, + bool from_trans); void setEdgeArcDelays(Edge *edge, - TimingArc *arc, - SdfTriple *triple); + TimingArc *arc, + SdfTriple *triple); void setEdgeArcDelays(Edge *edge, - TimingArc *arc, - SdfTriple *triple, - int triple_index, - int arc_delay_index); + TimingArc *arc, + SdfTriple *triple, + int triple_index, + int arc_delay_index); void setEdgeArcDelaysCondUse(Edge *edge, - TimingArc *arc, - SdfTriple *triple); + TimingArc *arc, + SdfTriple *triple); void setEdgeArcDelaysCondUse(Edge *edge, - TimingArc *arc, - float *value, - int triple_index, - int arc_delay_index, - const MinMax *min_max); - void setInstance(const std::string *instance_name); + TimingArc *arc, + const float *value, + int triple_index, + int arc_delay_index, + const MinMax *min_max); + void setInstance(); + void setInstance(std::string_view instance_name); void setInstanceWildcard(); void cellFinish(); - void setCell(const std::string *cell_name); - void interconnect(const std::string *from_pin_name, - const std::string *to_pin_name, - SdfTripleSeq *triples); + void setCell(std::string_view cell_name); + void interconnect(std::string_view from_pin_name, + std::string_view to_pin_name, + SdfTripleSeq *triples); void iopath(SdfPortSpec *from_edge, - const std::string *to_port_name, - SdfTripleSeq *triples, - const std::string *cond, - bool condelse); + std::string_view to_port_name, + SdfTripleSeq *triples, + std::string_view cond, + bool condelse); void timingCheck(const TimingRole *role, - SdfPortSpec *data_edge, - SdfPortSpec *clk_edge, - SdfTriple *triple); + SdfPortSpec *data_edge, + SdfPortSpec *clk_edge, + SdfTriple *triple); void timingCheckWidth(SdfPortSpec *edge, - SdfTriple *triple); + SdfTriple *triple); void timingCheckPeriod(SdfPortSpec *edge, - SdfTriple *triple); + SdfTriple *triple); void timingCheckSetupHold(SdfPortSpec *data_edge, - SdfPortSpec *clk_edge, - SdfTriple *setup_triple, - SdfTriple *hold_triple); + SdfPortSpec *clk_edge, + SdfTriple *setup_triple, + SdfTriple *hold_triple); void timingCheckRecRem(SdfPortSpec *data_edge, - SdfPortSpec *clk_edge, - SdfTriple *rec_triple, - SdfTriple *rem_triple); + SdfPortSpec *clk_edge, + SdfTriple *rec_triple, + SdfTriple *rem_triple); void timingCheckSetupHold1(SdfPortSpec *data_edge, SdfPortSpec *clk_edge, SdfTriple *setup_triple, @@ -115,58 +120,68 @@ public: const TimingRole *setup_role, const TimingRole *hold_role); void timingCheckNochange(SdfPortSpec *data_edge, - SdfPortSpec *clk_edge, - SdfTriple *before_triple, - SdfTriple *after_triple); - void port(const std::string *to_pin_name, - SdfTripleSeq *triples); + SdfPortSpec *clk_edge, + SdfTriple *before_triple, + SdfTriple *after_triple); + void port(std::string_view o_pin_name, + SdfTripleSeq *triples); void device(SdfTripleSeq *triples); - void device(const std::string *to_pin_name, - SdfTripleSeq *triples); + void device(std::string_view to_port_name, + SdfTripleSeq *triples); SdfTriple *makeTriple(); SdfTriple *makeTriple(float value); SdfTriple *makeTriple(float *min, - float *typ, - float *max); + float *typ, + float *max); void deleteTriple(SdfTriple *triple); SdfTripleSeq *makeTripleSeq(); void deleteTripleSeq(SdfTripleSeq *triples); SdfPortSpec *makePortSpec(const Transition *tr, - const std::string *port, - const std::string *cond); - SdfPortSpec *makeCondPortSpec(const std::string *cond_port); - std::string *unescaped(const std::string *token); - std::string *makePath(const std::string *head, - const std::string *tail); + std::string_view port); + SdfPortSpec *makePortSpec(const Transition *tr, + std::string_view port, + std::string_view cond); + SdfPortSpec *makeCondPortSpec(std::string_view cond_port); + std::string unescaped(std::string_view token); + std::string makePath(std::string_view head, + std::string_view tail); // Parser state used to control lexer for COND handling. bool inTimingCheck() { return in_timing_check_; } void setInTimingCheck(bool in); bool inIncremental() const { return in_incremental_; } void setInIncremental(bool incr); - std::string *makeBusName(std::string *bus_name, - int index); - const std::string &filename() const { return filename_; } - void sdfWarn(int id, - const char *fmt, ...); - void sdfError(int id, - const char *fmt, - ...); - void notSupported(const char *feature); + std::string makeBusName(std::string_view base_name, + int index); + std::string_view filename() const { return filename_; } + int sdfLine() const; + template + void warn(int id, + std::string_view fmt, + Args &&...args) + { + report_->fileWarn(id, filename_, sdfLine(), fmt, + std::forward(args)...); + } + template + void error(int id, + std::string_view fmt, + Args &&...args) + { + report_->fileError(id, filename_, sdfLine(), fmt, + std::forward(args)...); + } private: - int readSdfFile1(Network *network, - Graph *graph, - const char *filename); Edge *findCheckEdge(Pin *from_pin, - Pin *to_pin, - const TimingRole *sdf_role, - const std::string *cond_start, - const std::string *cond_end); + Pin *to_pin, + const TimingRole *sdf_role, + const std::string *cond_start, + const std::string *cond_end); Edge *findWireEdge(Pin *from_pin, - Pin *to_pin); - bool condMatch(const std::string *sdf_cond, - const char *lib_cond); + Pin *to_pin); + bool condMatch(std::string_view sdf_cond, + std::string_view lib_cond); void timingCheck1(const TimingRole *role, Port *data_port, SdfPortSpec *data_edge, @@ -174,28 +189,28 @@ private: SdfPortSpec *clk_edge, SdfTriple *triple); bool annotateCheckEdges(Pin *data_pin, - SdfPortSpec *data_edge, - Pin *clk_pin, - SdfPortSpec *clk_edge, - const TimingRole *sdf_role, - SdfTriple *triple, - bool match_generic); - Pin *findPin(const std::string *name); - Instance *findInstance(const std::string *name); + SdfPortSpec *data_edge, + Pin *clk_pin, + SdfPortSpec *clk_edge, + const TimingRole *sdf_role, + SdfTriple *triple, + bool match_generic); + Pin *findPin(std::string_view name); + Instance *findInstance(std::string_view name); void setEdgeDelays(Edge *edge, - SdfTripleSeq *triples, - const char *sdf_cmd); + SdfTripleSeq *triples, + std::string_view sdf_cmd); void setDevicePinDelays(Pin *to_pin, - SdfTripleSeq *triples); + SdfTripleSeq *triples); Port *findPort(const Cell *cell, - const std::string *port_name); + std::string_view port_name); - std::string filename_; + std::string_view filename_; SdfScanner *scanner_; - const char *path_; + std::string_view path_; // Which values to pull out of the sdf triples. - int triple_min_index_; - int triple_max_index_; + int triple_min_index_{0}; + int triple_max_index_{2}; // Which arc delay value to deposit the sdf values into. int arc_delay_min_index_; int arc_delay_max_index_; @@ -204,15 +219,15 @@ private: bool is_incremental_only_; MinMaxAll *cond_use_; - char divider_; - char escape_; - Instance *instance_; - const std::string *cell_name_; - bool in_timing_check_; - bool in_incremental_; - float timescale_; + char divider_{'/'}; + char escape_{'\\'}; + Instance *instance_{nullptr}; + std::string cell_name_; + bool in_timing_check_{false}; + bool in_incremental_{false}; + float timescale_{1.0E-9F}; // default units of ns static const int null_index_ = -1; }; -} // namespace +} // namespace sta diff --git a/sdf/SdfScanner.hh b/sdf/SdfScanner.hh index e66e8593b..8d0d5b43f 100644 --- a/sdf/SdfScanner.hh +++ b/sdf/SdfScanner.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,8 @@ #pragma once +#include + #include "SdfLocation.hh" #include "SdfParse.hh" @@ -41,26 +43,24 @@ class SdfScanner : public SdfFlexLexer { public: SdfScanner(std::istream *stream, - const std::string &filename, + std::string_view filename, SdfReader *reader, Report *report); - virtual ~SdfScanner() {} - - virtual int lex(SdfParse::semantic_type *const yylval, + virtual int lex(SdfParse::semantic_type *yylval, SdfParse::location_type *yylloc); // YY_DECL defined in SdfLex.ll // Method body created by flex in SdfLex.cc - void error(const char *msg); + void error(std::string_view msg); // Get rid of override virtual function warning. using FlexLexer::yylex; private: - std::string filename_; + std::string_view filename_; SdfReader *reader_; Report *report_; std::string token_; }; -} // namespace +} // namespace sta diff --git a/sdf/SdfWriter.cc b/sdf/SdfWriter.cc index 2c2bfa82f..79750e88e 100644 --- a/sdf/SdfWriter.cc +++ b/sdf/SdfWriter.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,47 +26,45 @@ #include #include +#include +#include -#include "Zlib.hh" -#include "StaConfig.hh" // STA_VERSION +#include "Format.hh" #include "Fuzzy.hh" -#include "StringUtil.hh" -#include "Units.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" +#include "Graph.hh" +#include "GraphDelayCalc.hh" #include "Liberty.hh" -#include "Sdc.hh" #include "MinMaxValues.hh" #include "Network.hh" -#include "Graph.hh" -#include "DcalcAnalysisPt.hh" -#include "GraphDelayCalc.hh" +#include "Scene.hh" +#include "Sdc.hh" +#include "StaConfig.hh" // STA_VERSION #include "StaState.hh" -#include "Corner.hh" -#include "PathAnalysisPt.hh" +#include "StringUtil.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Units.hh" +#include "Zlib.hh" namespace sta { -using std::string; - class SdfWriter : public StaState { public: SdfWriter(StaState *sta); - ~SdfWriter(); - void write(const char *filename, - const Corner *corner, - char sdf_divider, - bool include_typ, + void write(std::string_view filename, + const Scene *scene, + char sdf_divider, + bool include_typ, int digits, - bool gzip, - bool no_timestamp, - bool no_version); + bool gzip, + bool no_timestamp, + bool no_version); protected: void writeHeader(LibertyLibrary *default_lib, - bool no_timestamp, - bool no_version); + bool no_timestamp, + bool no_version); void writeTrailer(); void writeInterconnects(); void writeInstInterconnects(Instance *inst); @@ -76,120 +74,104 @@ class SdfWriter : public StaState void writeInstHeader(const Instance *inst); void writeInstTrailer(); void writeIopaths(const Instance *inst, - bool &inst_header); + bool &inst_header); void writeIopathHeader(); void writeIopathTrailer(); void writeTimingChecks(const Instance *inst, - bool &inst_header); + bool &inst_header); void ensureTimingCheckheaders(bool &check_header, - const Instance *inst, - bool &inst_header); + const Instance *inst, + bool &inst_header); void writeCheck(Edge *edge, - const char *sdf_check); + std::string_view sdf_check); void writeCheck(Edge *edge, - TimingArc *arc, - const char *sdf_check, - bool use_data_edge, - bool use_clk_edge); + TimingArc *arc, + std::string_view sdf_check, + bool use_data_edge, + bool use_clk_edge); void writeEdgeCheck(Edge *edge, - const char *sdf_check, - int clk_rf_index, - TimingArc *arcs[RiseFall::index_count][RiseFall::index_count]); + std::string_view sdf_check, + int clk_rf_index, + TimingArc *arcs[RiseFall::index_count][RiseFall::index_count]); void writeTimingCheckHeader(); void writeTimingCheckTrailer(); void writeWidthCheck(const Pin *pin, - const RiseFall *hi_low, - float min_width, - float max_width); + const RiseFall *hi_low, + float min_width, + float max_width); void writePeriodCheck(const Pin *pin, - float min_period); - const char *sdfEdge(const Transition *tr); + float min_period); + std::string_view sdfEdge(const Transition *tr); void writeArcDelays(Edge *edge); void writeSdfTriple(RiseFallMinMax &delays, const RiseFall *rf); void writeSdfTriple(float min, float max); void writeSdfDelay(double delay); - string sdfPortName(const Pin *pin); - string sdfPathName(const Pin *pin); - string sdfPathName(const Instance *inst); - string sdfName(const Instance *inst); + std::string sdfPortName(const Pin *pin); + std::string sdfPathName(const Pin *pin); + std::string sdfPathName(const Instance *inst); + std::string sdfName(const Instance *inst); private: char sdf_divider_; bool include_typ_; float timescale_; - char sdf_escape_; + char sdf_escape_{'\\'}; char network_escape_; - char *delay_format_; + int digits_; gzFile stream_; - const Corner *corner_; + const Scene *scene_; int arc_delay_min_index_; int arc_delay_max_index_; }; void -writeSdf(const char *filename, - const Corner *corner, - char sdf_divider, +writeSdf(std::string_view filename, + const Scene *scene, + char sdf_divider, bool include_typ, - int digits, - bool gzip, - bool no_timestamp, - bool no_version, - StaState *sta) + int digits, + bool gzip, + bool no_timestamp, + bool no_version, + StaState *sta) { SdfWriter writer(sta); - writer.write(filename, corner, sdf_divider, include_typ, digits, gzip, - no_timestamp, no_version); + writer.write(filename, scene, sdf_divider, include_typ, digits, gzip, + no_timestamp, no_version); } SdfWriter::SdfWriter(StaState *sta) : StaState(sta), - sdf_escape_('\\'), - network_escape_(network_->pathEscape()), - delay_format_(nullptr) -{ -} - -SdfWriter::~SdfWriter() + network_escape_(network_->pathEscape()) { - stringDelete(delay_format_); } void -SdfWriter::write(const char *filename, - const Corner *corner, - char sdf_divider, +SdfWriter::write(std::string_view filename, + const Scene *scene, + char sdf_divider, bool include_typ, - int digits, - bool gzip, - bool no_timestamp, - bool no_version) + int digits, + bool gzip, + bool no_timestamp, + bool no_version) { sdf_divider_ = sdf_divider; include_typ_ = include_typ; - if (delay_format_ == nullptr) - delay_format_ = stringPrint("%%.%df", digits); + digits_ = digits; LibertyLibrary *default_lib = network_->defaultLibertyLibrary(); timescale_ = default_lib->units()->timeUnit()->scale(); - corner_ = corner; - const MinMax *min_max; - const DcalcAnalysisPt *dcalc_ap; - - min_max = MinMax::min(); - dcalc_ap = corner_->findDcalcAnalysisPt(min_max); - arc_delay_min_index_ = dcalc_ap->index(); - - min_max = MinMax::max(); - dcalc_ap = corner_->findDcalcAnalysisPt(min_max); - arc_delay_max_index_ = dcalc_ap->index(); + scene_ = scene; + arc_delay_min_index_ = scene->dcalcAnalysisPtIndex(MinMax::min()); + arc_delay_max_index_ = scene->dcalcAnalysisPtIndex(MinMax::max()); - stream_ = gzopen(filename, gzip ? "wb" : "wT"); + stream_ = gzopen(std::string(filename).c_str(), gzip ? "wb" : "wT"); if (stream_ == nullptr) throw FileNotWritable(filename); @@ -204,50 +186,50 @@ SdfWriter::write(const char *filename, void SdfWriter::writeHeader(LibertyLibrary *default_lib, - bool no_timestamp, - bool no_version) -{ - gzprintf(stream_, "(DELAYFILE\n"); - gzprintf(stream_, " (SDFVERSION \"3.0\")\n"); - gzprintf(stream_, " (DESIGN \"%s\")\n", - network_->cellName(network_->topInstance())); - + bool no_timestamp, + bool no_version) +{ + sta::print(stream_, "(DELAYFILE\n"); + sta::print(stream_, " (SDFVERSION \"3.0\")\n"); + sta::print(stream_, " (DESIGN \"{}\")\n", + network_->cellName(network_->topInstance())); + if (!no_timestamp) { time_t now; time(&now); char *time_str = ctime(&now); // Remove trailing \n. time_str[strlen(time_str) - 1] = '\0'; - gzprintf(stream_, " (DATE \"%s\")\n", time_str); + sta::print(stream_, " (DATE \"{}\")\n", time_str); } - gzprintf(stream_, " (VENDOR \"Parallax\")\n"); - gzprintf(stream_, " (PROGRAM \"STA\")\n"); + sta::print(stream_, " (VENDOR \"Parallax\")\n"); + sta::print(stream_, " (PROGRAM \"STA\")\n"); if (!no_version) - gzprintf(stream_, " (VERSION \"%s\")\n", STA_VERSION); - gzprintf(stream_, " (DIVIDER %c)\n", sdf_divider_); + sta::print(stream_, " (VERSION \"{}\")\n", STA_VERSION); + sta::print(stream_, " (DIVIDER {:c})\n", sdf_divider_); LibertyLibrary *lib_min = default_lib; - const LibertySeq &libs_min = corner_->libertyLibraries(MinMax::min()); + const LibertySeq &libs_min = scene_->libertyLibraries(MinMax::min()); if (!libs_min.empty()) lib_min = libs_min[0]; LibertyLibrary *lib_max = default_lib; - const LibertySeq &libs_max = corner_->libertyLibraries(MinMax::max()); + const LibertySeq &libs_max = scene_->libertyLibraries(MinMax::max()); if (!libs_max.empty()) lib_max = libs_max[0]; OperatingConditions *cond_min = lib_min->defaultOperatingConditions(); OperatingConditions *cond_max = lib_max->defaultOperatingConditions(); if (cond_min && cond_max) { - gzprintf(stream_, " (VOLTAGE %.3f::%.3f)\n", - cond_min->voltage(), - cond_max->voltage()); - gzprintf(stream_, " (PROCESS \"%.3f::%.3f\")\n", - cond_min->process(), - cond_max->process()); - gzprintf(stream_, " (TEMPERATURE %.3f::%.3f)\n", - cond_min->temperature(), - cond_max->temperature()); + sta::print(stream_, " (VOLTAGE {:.3f}::{:.3f})\n", + cond_min->voltage(), + cond_max->voltage()); + sta::print(stream_, " (PROCESS \"{:.3f}::{:.3f}\")\n", + cond_min->process(), + cond_max->process()); + sta::print(stream_, " (TEMPERATURE {:.3f}::{:.3f})\n", + cond_min->temperature(), + cond_max->temperature()); } const char *sdf_timescale = nullptr; @@ -270,24 +252,24 @@ SdfWriter::writeHeader(LibertyLibrary *default_lib, else if (fuzzyEqual(timescale_, 100e-12)) sdf_timescale = "100ps"; if (sdf_timescale) - gzprintf(stream_, " (TIMESCALE %s)\n", sdf_timescale); + sta::print(stream_, " (TIMESCALE {})\n", sdf_timescale); } void SdfWriter::writeTrailer() { - gzprintf(stream_, ")\n"); + sta::print(stream_, ")\n"); } void SdfWriter::writeInterconnects() { - gzprintf(stream_, " (CELL\n"); - gzprintf(stream_, " (CELLTYPE \"%s\")\n", - network_->cellName(network_->topInstance())); - gzprintf(stream_, " (INSTANCE)\n"); - gzprintf(stream_, " (DELAY\n"); - gzprintf(stream_, " (ABSOLUTE\n"); + sta::print(stream_, " (CELL\n"); + sta::print(stream_, " (CELLTYPE \"{}\")\n", + network_->cellName(network_->topInstance())); + sta::print(stream_, " (INSTANCE)\n"); + sta::print(stream_, " (DELAY\n"); + sta::print(stream_, " (ABSOLUTE\n"); writeInstInterconnects(network_->topInstance()); @@ -298,9 +280,9 @@ SdfWriter::writeInterconnects() } delete inst_iter; - gzprintf(stream_, " )\n"); - gzprintf(stream_, " )\n"); - gzprintf(stream_, " )\n"); + sta::print(stream_, " )\n"); + sta::print(stream_, " )\n"); + sta::print(stream_, " )\n"); } void @@ -325,13 +307,13 @@ SdfWriter::writeInterconnectFromPin(Pin *drvr_pin) Edge *edge = edge_iter.next(); if (edge->isWire()) { Pin *load_pin = edge->to(graph_)->pin(); - string drvr_pin_name = sdfPathName(drvr_pin); - string load_pin_name = sdfPathName(load_pin); - gzprintf(stream_, " (INTERCONNECT %s %s ", - drvr_pin_name.c_str(), - load_pin_name.c_str()); + std::string drvr_pin_name = sdfPathName(drvr_pin); + std::string load_pin_name = sdfPathName(load_pin); + sta::print(stream_, " (INTERCONNECT {} {} ", + drvr_pin_name, + load_pin_name); writeArcDelays(edge); - gzprintf(stream_, ")\n"); + sta::print(stream_, ")\n"); } } } @@ -355,21 +337,21 @@ SdfWriter::writeInstances() void SdfWriter::writeInstHeader(const Instance *inst) { - gzprintf(stream_, " (CELL\n"); - gzprintf(stream_, " (CELLTYPE \"%s\")\n", network_->cellName(inst)); - string inst_name = sdfPathName(inst); - gzprintf(stream_, " (INSTANCE %s)\n", inst_name.c_str()); + sta::print(stream_, " (CELL\n"); + sta::print(stream_, " (CELLTYPE \"{}\")\n", network_->cellName(inst)); + std::string inst_name = sdfPathName(inst); + sta::print(stream_, " (INSTANCE {})\n", inst_name); } void SdfWriter::writeInstTrailer() { - gzprintf(stream_, " )\n"); + sta::print(stream_, " )\n"); } void SdfWriter::writeIopaths(const Instance *inst, - bool &inst_header) + bool &inst_header) { bool iopath_header = false; InstancePinIterator *pin_iter = network_->pinIterator(inst); @@ -379,39 +361,39 @@ SdfWriter::writeIopaths(const Instance *inst, Vertex *from_vertex = graph_->pinLoadVertex(from_pin); VertexOutEdgeIterator edge_iter(from_vertex, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - const TimingRole *role = edge->role(); - if (role == TimingRole::combinational() - || role == TimingRole::tristateEnable() - || role == TimingRole::regClkToQ() - || role == TimingRole::regSetClr() - || role == TimingRole::latchEnToQ() - || role == TimingRole::latchDtoQ()) { - Vertex *to_vertex = edge->to(graph_); - Pin *to_pin = to_vertex->pin(); - if (!inst_header) { - writeInstHeader(inst); - inst_header = true; - } - if (!iopath_header) { - writeIopathHeader(); - iopath_header = true; - } - const char *sdf_cond = edge->timingArcSet()->sdfCond(); - if (sdf_cond) { - gzprintf(stream_, " (COND %s\n", sdf_cond); - gzprintf(stream_, " "); - } - string from_pin_name = sdfPortName(from_pin); - string to_pin_name = sdfPortName(to_pin); - gzprintf(stream_, " (IOPATH %s %s ", - from_pin_name.c_str(), - to_pin_name.c_str()); - writeArcDelays(edge); - if (sdf_cond) - gzprintf(stream_, ")"); - gzprintf(stream_, ")\n"); - } + Edge *edge = edge_iter.next(); + const TimingRole *role = edge->role(); + if (role == TimingRole::combinational() + || role == TimingRole::tristateEnable() + || role == TimingRole::regClkToQ() + || role == TimingRole::regSetClr() + || role == TimingRole::latchEnToQ() + || role == TimingRole::latchDtoQ()) { + Vertex *to_vertex = edge->to(graph_); + Pin *to_pin = to_vertex->pin(); + if (!inst_header) { + writeInstHeader(inst); + inst_header = true; + } + if (!iopath_header) { + writeIopathHeader(); + iopath_header = true; + } + const std::string &sdf_cond = edge->timingArcSet()->sdfCond(); + if (!sdf_cond.empty()) { + sta::print(stream_, " (COND {}\n", sdf_cond); + sta::print(stream_, " "); + } + std::string from_pin_name = sdfPortName(from_pin); + std::string to_pin_name = sdfPortName(to_pin); + sta::print(stream_, " (IOPATH {} {} ", + from_pin_name, + to_pin_name); + writeArcDelays(edge); + if (!sdf_cond.empty()) + sta::print(stream_, ")"); + sta::print(stream_, ")\n"); + } } } } @@ -424,15 +406,15 @@ SdfWriter::writeIopaths(const Instance *inst, void SdfWriter::writeIopathHeader() { - gzprintf(stream_, " (DELAY\n"); - gzprintf(stream_, " (ABSOLUTE\n"); + sta::print(stream_, " (DELAY\n"); + sta::print(stream_, " (ABSOLUTE\n"); } void SdfWriter::writeIopathTrailer() { - gzprintf(stream_, " )\n"); - gzprintf(stream_, " )\n"); + sta::print(stream_, " )\n"); + sta::print(stream_, " )\n"); } void @@ -442,11 +424,11 @@ SdfWriter::writeArcDelays(Edge *edge) TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *arc : arc_set->arcs()) { const RiseFall *rf = arc->toEdge()->asRiseFall(); - ArcDelay min_delay = graph_->arcDelay(edge, arc, arc_delay_min_index_); - delays.setValue(rf, MinMax::min(), delayAsFloat(min_delay)); + const ArcDelay &min_delay = graph_->arcDelay(edge, arc, arc_delay_min_index_); + delays.setValue(rf, MinMax::min(), delayAsFloat(min_delay, MinMax::min(), this)); - ArcDelay max_delay = graph_->arcDelay(edge, arc, arc_delay_max_index_); - delays.setValue(rf, MinMax::max(), delayAsFloat(max_delay)); + const ArcDelay &max_delay = graph_->arcDelay(edge, arc, arc_delay_max_index_); + delays.setValue(rf, MinMax::max(), delayAsFloat(max_delay, MinMax::max(), this)); } if (delays.hasValue(RiseFall::rise(), MinMax::min()) @@ -455,10 +437,10 @@ SdfWriter::writeArcDelays(Edge *edge) writeSdfTriple(delays, RiseFall::rise()); // Merge rise/fall values if they are the same. if (!(fuzzyEqual(delays.value(RiseFall::rise(), MinMax::min()), - delays.value(RiseFall::fall(), MinMax::min())) - && fuzzyEqual(delays.value(RiseFall::rise(), MinMax::max()), - delays.value(RiseFall::fall(),MinMax::max())))) { - gzprintf(stream_, " "); + delays.value(RiseFall::fall(), MinMax::min())) + && fuzzyEqual(delays.value(RiseFall::rise(), MinMax::max()), + delays.value(RiseFall::fall(),MinMax::max())))) { + sta::print(stream_, " "); writeSdfTriple(delays, RiseFall::fall()); } } @@ -467,7 +449,7 @@ SdfWriter::writeArcDelays(Edge *edge) writeSdfTriple(delays, RiseFall::rise()); else if (delays.hasValue(RiseFall::fall(), MinMax::min())) { // Fall only. - gzprintf(stream_, "() "); + sta::print(stream_, "() "); writeSdfTriple(delays, RiseFall::fall()); } } @@ -485,28 +467,29 @@ void SdfWriter::writeSdfTriple(float min, float max) { - gzprintf(stream_, "("); + sta::print(stream_, "("); writeSdfDelay(min); if (include_typ_) { - gzprintf(stream_, ":"); + sta::print(stream_, ":"); writeSdfDelay((min + max) / 2.0); - gzprintf(stream_, ":"); + sta::print(stream_, ":"); } else - gzprintf(stream_, "::"); + sta::print(stream_, "::"); writeSdfDelay(max); - gzprintf(stream_, ")"); + sta::print(stream_, ")"); } void SdfWriter::writeSdfDelay(double delay) { - gzprintf(stream_, delay_format_, delay / timescale_); + std::string str = sta::formatRuntime("{:.{}f}", delay / timescale_, digits_); + sta::print(stream_, "{}", str); } void SdfWriter::writeTimingChecks(const Instance *inst, - bool &inst_header) + bool &inst_header) { bool check_header = false; @@ -517,40 +500,42 @@ SdfWriter::writeTimingChecks(const Instance *inst, if (vertex) { VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - const TimingRole *role = edge->role(); - const char *sdf_check = nullptr; - if (role == TimingRole::setup()) - sdf_check = "SETUP"; - else if (role == TimingRole::hold()) - sdf_check = "HOLD"; - else if (role == TimingRole::recovery()) - sdf_check = "RECOVERY"; - else if (role == TimingRole::removal()) - sdf_check = "REMOVAL"; - if (sdf_check) { - ensureTimingCheckheaders(check_header, inst, inst_header); - writeCheck(edge, sdf_check); - } + Edge *edge = edge_iter.next(); + const TimingRole *role = edge->role(); + const char *sdf_check = nullptr; + if (role == TimingRole::setup()) + sdf_check = "SETUP"; + else if (role == TimingRole::hold()) + sdf_check = "HOLD"; + else if (role == TimingRole::recovery()) + sdf_check = "RECOVERY"; + else if (role == TimingRole::removal()) + sdf_check = "REMOVAL"; + if (sdf_check) { + ensureTimingCheckheaders(check_header, inst, inst_header); + writeCheck(edge, sdf_check); + } } for (auto hi_low : RiseFall::range()) { - float min_width, max_width; - Edge *edge; - TimingArc *arc; - graph_->minPulseWidthArc(vertex, hi_low, edge, arc); - if (edge) { - min_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_min_index_)); - max_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_max_index_)); - ensureTimingCheckheaders(check_header, inst, inst_header); - writeWidthCheck(pin, hi_low, min_width, max_width); - } + float min_width, max_width; + Edge *edge; + TimingArc *arc; + graph_->minPulseWidthArc(vertex, hi_low, edge, arc); + if (edge) { + min_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_min_index_), + MinMax::min(), this); + max_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_max_index_), + MinMax::max(), this); + ensureTimingCheckheaders(check_header, inst, inst_header); + writeWidthCheck(pin, hi_low, min_width, max_width); + } } float min_period; bool exists; - graph_delay_calc_->minPeriod(pin, corner_, min_period, exists); + graph_delay_calc_->minPeriod(pin, scene_, min_period, exists); if (exists) { - ensureTimingCheckheaders(check_header, inst, inst_header); - writePeriodCheck(pin, min_period); + ensureTimingCheckheaders(check_header, inst, inst_header); + writePeriodCheck(pin, min_period); } } } @@ -562,8 +547,8 @@ SdfWriter::writeTimingChecks(const Instance *inst, void SdfWriter::ensureTimingCheckheaders(bool &check_header, - const Instance *inst, - bool &inst_header) + const Instance *inst, + bool &inst_header) { if (!inst_header) { writeInstHeader(inst); @@ -578,18 +563,18 @@ SdfWriter::ensureTimingCheckheaders(bool &check_header, void SdfWriter::writeTimingCheckHeader() { - gzprintf(stream_, " (TIMINGCHECK\n"); + sta::print(stream_, " (TIMINGCHECK\n"); } void SdfWriter::writeTimingCheckTrailer() { - gzprintf(stream_, " )\n"); + sta::print(stream_, " )\n"); } void SdfWriter::writeCheck(Edge *edge, - const char *sdf_check) + std::string_view sdf_check) { TimingArcSet *arc_set = edge->timingArcSet(); // Examine the arcs to see if the check requires clk or data edge specifiers. @@ -605,7 +590,7 @@ SdfWriter::writeCheck(Edge *edge, && arcs[RiseFall::fallIndex()][RiseFall::fallIndex()] == nullptr) writeEdgeCheck(edge, sdf_check, RiseFall::riseIndex(), arcs); else if (arcs[RiseFall::riseIndex()][RiseFall::riseIndex()] == nullptr - && arcs[RiseFall::riseIndex()][RiseFall::fallIndex()] == nullptr) + && arcs[RiseFall::riseIndex()][RiseFall::fallIndex()] == nullptr) writeEdgeCheck(edge, sdf_check, RiseFall::fallIndex(), arcs); else { // No special case; write all the checks with data and clock edge specifiers. @@ -616,9 +601,9 @@ SdfWriter::writeCheck(Edge *edge, void SdfWriter::writeEdgeCheck(Edge *edge, - const char *sdf_check, - int clk_rf_index, - TimingArc *arcs[RiseFall::index_count][RiseFall::index_count]) + std::string_view sdf_check, + int clk_rf_index, + TimingArc *arcs[RiseFall::index_count][RiseFall::index_count]) { // SDF requires edge specifiers on the data port to define separate // rise/fall check values. @@ -627,133 +612,142 @@ SdfWriter::writeEdgeCheck(Edge *edge, if (arcs[clk_rf_index][RiseFall::riseIndex()] && arcs[clk_rf_index][RiseFall::fallIndex()] && arcs[clk_rf_index][RiseFall::riseIndex()] - && arcs[clk_rf_index][RiseFall::fallIndex()] - && delayEqual(graph_->arcDelay(edge, - arcs[clk_rf_index][RiseFall::riseIndex()], - arc_delay_min_index_), - graph_->arcDelay(edge, - arcs[clk_rf_index][RiseFall::fallIndex()], - arc_delay_min_index_)) - && delayEqual(graph_->arcDelay(edge, - arcs[clk_rf_index][RiseFall::riseIndex()], - arc_delay_max_index_), - graph_->arcDelay(edge, - arcs[clk_rf_index][RiseFall::fallIndex()], - arc_delay_max_index_))) - // Rise/fall margins are the same, so no data edge specifier is required. - writeCheck(edge, arcs[clk_rf_index][RiseFall::riseIndex()], - sdf_check, false, true); - else { - if (arcs[clk_rf_index][RiseFall::riseIndex()]) - writeCheck(edge, arcs[clk_rf_index][RiseFall::riseIndex()], - sdf_check, true, true); - if (arcs[clk_rf_index][RiseFall::fallIndex()]) - writeCheck(edge, arcs[clk_rf_index][RiseFall::fallIndex()], - sdf_check, true, true); + && arcs[clk_rf_index][RiseFall::fallIndex()]) { + float rise_min=delayAsFloat(graph_->arcDelay(edge, + arcs[clk_rf_index][RiseFall::riseIndex()], + arc_delay_min_index_), + MinMax::min(), this); + float fall_min=delayAsFloat(graph_->arcDelay(edge, + arcs[clk_rf_index][RiseFall::fallIndex()], + arc_delay_min_index_), + MinMax::min(), this); + float rise_max=delayAsFloat(graph_->arcDelay(edge, + arcs[clk_rf_index][RiseFall::riseIndex()], + arc_delay_max_index_), + MinMax::max(), this); + float fall_max=delayAsFloat(graph_->arcDelay(edge, + arcs[clk_rf_index][RiseFall::fallIndex()], + arc_delay_max_index_), + MinMax::max(), this); + if (fuzzyEqual(rise_min, fall_min) + && fuzzyEqual(rise_max, fall_max)) { + // Rise/fall margins are the same, so no data edge specifier is required. + writeCheck(edge, arcs[clk_rf_index][RiseFall::riseIndex()], + sdf_check, false, true); + return; + } } + if (arcs[clk_rf_index][RiseFall::riseIndex()]) + writeCheck(edge, arcs[clk_rf_index][RiseFall::riseIndex()], + sdf_check, true, true); + if (arcs[clk_rf_index][RiseFall::fallIndex()]) + writeCheck(edge, arcs[clk_rf_index][RiseFall::fallIndex()], + sdf_check, true, true); } void SdfWriter::writeCheck(Edge *edge, - TimingArc *arc, - const char *sdf_check, - bool use_data_edge, - bool use_clk_edge) + TimingArc *arc, + std::string_view sdf_check, + bool use_data_edge, + bool use_clk_edge) { TimingArcSet *arc_set = edge->timingArcSet(); Pin *from_pin = edge->from(graph_)->pin(); Pin *to_pin = edge->to(graph_)->pin(); - const char *sdf_cond_start = arc_set->sdfCondStart(); - const char *sdf_cond_end = arc_set->sdfCondEnd(); + const std::string &sdf_cond_start = arc_set->sdfCondStart(); + const std::string &sdf_cond_end = arc_set->sdfCondEnd(); - gzprintf(stream_, " (%s ", sdf_check); + sta::print(stream_, " ({} ", sdf_check); - if (sdf_cond_start) - gzprintf(stream_, "(COND %s ", sdf_cond_start); + if (!sdf_cond_start.empty()) + sta::print(stream_, "(COND {} ", sdf_cond_start); - string to_pin_name = sdfPortName(to_pin); + std::string to_pin_name = sdfPortName(to_pin); if (use_data_edge) { - gzprintf(stream_, "(%s %s)", - sdfEdge(arc->toEdge()), - to_pin_name.c_str()); + sta::print(stream_, "({} {})", + sdfEdge(arc->toEdge()), + to_pin_name); } else - gzprintf(stream_, "%s", to_pin_name.c_str()); + sta::print(stream_, "{}", to_pin_name); - if (sdf_cond_start) - gzprintf(stream_, ")"); + if (!sdf_cond_start.empty()) + sta::print(stream_, ")"); - gzprintf(stream_, " "); + sta::print(stream_, " "); - if (sdf_cond_end) - gzprintf(stream_, "(COND %s ", sdf_cond_end); + if (!sdf_cond_end.empty()) + sta::print(stream_, "(COND {} ", sdf_cond_end); - string from_pin_name = sdfPortName(from_pin); + std::string from_pin_name = sdfPortName(from_pin); if (use_clk_edge) - gzprintf(stream_, "(%s %s)", - sdfEdge(arc->fromEdge()), - from_pin_name.c_str()); + sta::print(stream_, "({} {})", + sdfEdge(arc->fromEdge()), + from_pin_name); else - gzprintf(stream_, "%s", from_pin_name.c_str()); + sta::print(stream_, "{}", from_pin_name); - if (sdf_cond_end) - gzprintf(stream_, ")"); + if (!sdf_cond_end.empty()) + sta::print(stream_, ")"); - gzprintf(stream_, " "); + sta::print(stream_, " "); - ArcDelay min_delay = graph_->arcDelay(edge, arc, arc_delay_min_index_); - ArcDelay max_delay = graph_->arcDelay(edge, arc, arc_delay_max_index_); - writeSdfTriple(delayAsFloat(min_delay), delayAsFloat(max_delay)); + float min_delay = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_min_index_), + MinMax::min(), this); + float max_delay = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_max_index_), + MinMax::max(), this); + writeSdfTriple(min_delay, max_delay); - gzprintf(stream_, ")\n"); + sta::print(stream_, ")\n"); } void SdfWriter::writeWidthCheck(const Pin *pin, - const RiseFall *hi_low, - float min_width, - float max_width) -{ - string pin_name = sdfPortName(pin); - gzprintf(stream_, " (WIDTH (%s %s) ", - sdfEdge(hi_low->asTransition()), - pin_name.c_str()); + const RiseFall *hi_low, + float min_width, + float max_width) +{ + std::string pin_name = sdfPortName(pin); + sta::print(stream_, " (WIDTH ({} {}) ", + sdfEdge(hi_low->asTransition()), + pin_name); writeSdfTriple(min_width, max_width); - gzprintf(stream_, ")\n"); + sta::print(stream_, ")\n"); } void SdfWriter::writePeriodCheck(const Pin *pin, - float min_period) + float min_period) { - string pin_name = sdfPortName(pin); - gzprintf(stream_, " (PERIOD %s ", pin_name.c_str()); + std::string pin_name = sdfPortName(pin); + sta::print(stream_, " (PERIOD {} ", pin_name); writeSdfTriple(min_period, min_period); - gzprintf(stream_, ")\n"); + sta::print(stream_, ")\n"); } -const char * +std::string_view SdfWriter::sdfEdge(const Transition *tr) { if (tr == Transition::rise()) return "posedge"; else if (tr == Transition::fall()) return "negedge"; - return nullptr; + return {}; } //////////////////////////////////////////////////////////////// -string +std::string SdfWriter::sdfPathName(const Pin *pin) { Instance *inst = network_->instance(pin); if (network_->isTopInstance(inst)) return sdfPortName(pin); else { - string inst_path = sdfPathName(inst); - string port_name = sdfPortName(pin); - string sdf_name = inst_path; + std::string inst_path = sdfPathName(inst); + std::string port_name = sdfPortName(pin); + std::string sdf_name = inst_path; sdf_name += sdf_divider_; sdf_name += port_name; return sdf_name; @@ -761,17 +755,15 @@ SdfWriter::sdfPathName(const Pin *pin) } // Based on Network::pathName. -string +std::string SdfWriter::sdfPathName(const Instance *instance) { InstanceSeq inst_path; network_->path(instance, inst_path); - InstanceSeq::Iterator path_iter1(inst_path); - string path_name; + std::string path_name; while (!inst_path.empty()) { const Instance *inst = inst_path.back(); - string inst_name = sdfName(inst); - path_name += inst_name; + path_name += sdfName(inst); inst_path.pop_back(); if (!inst_path.empty()) path_name += sdf_divider_; @@ -780,41 +772,38 @@ SdfWriter::sdfPathName(const Instance *instance) } // Escape for non-alpha numeric characters. -string +std::string SdfWriter::sdfName(const Instance *inst) { - const char *name = network_->name(inst); - string sdf_name; - const char *p = name; - while (*p) { - char ch = *p; + const std::string &name = network_->name(inst); + std::string sdf_name; + for (char ch : name) { // Ignore sta escapes. if (ch != network_escape_) { if (!(isalnum(ch) || ch == '_')) - // Insert escape. - sdf_name += sdf_escape_; + // Insert escape. + sdf_name += sdf_escape_; sdf_name += ch; } - p++; } return sdf_name; } -string +std::string SdfWriter::sdfPortName(const Pin *pin) { - const char *name = network_->portName(pin); - size_t name_length = strlen(name); - string sdf_name; + const std::string &name = network_->portName(pin); + size_t name_length = name.size(); + std::string sdf_name; constexpr char bus_brkt_left = '['; constexpr char bus_brkt_right = ']'; size_t bus_index = name_length; if (name_length >= 4 && name[name_length - 1] == bus_brkt_right) { - const char *left = strrchr(name, bus_brkt_left); - if (left) - bus_index = left - name; + size_t left = name.rfind(bus_brkt_left); + if (left != std::string_view::npos) + bus_index = left; } for (size_t i = 0; i < name_length; i++) { char ch = name[i]; @@ -834,4 +823,4 @@ SdfWriter::sdfPortName(const Pin *pin) return sdf_name; } -} // namespace +} // namespace sta diff --git a/sdf/SdfWriter.hh b/sdf/SdfWriter.hh index 382faa71b..4cec60d92 100644 --- a/sdf/SdfWriter.hh +++ b/sdf/SdfWriter.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,20 +24,22 @@ #pragma once +#include + namespace sta { class StaState; -class Corner; +class Scene; void -writeSdf(const char *filename, - const Corner *corner, - char divider, +writeSdf(std::string_view filename, + const Scene *scene, + char divider, bool include_typ, - int digits, - bool gzip, - bool no_timestamp, - bool no_version, - StaState *sta); + int digits, + bool gzip, + bool no_timestamp, + bool no_version, + StaState *sta); -} // namespace +} // namespace sta diff --git a/search/Bdd.cc b/search/Bdd.cc index 0845c24c6..0e1ff7262 100644 --- a/search/Bdd.cc +++ b/search/Bdd.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,10 +24,10 @@ #include "Bdd.hh" -#include "cudd.h" -#include "StaConfig.hh" -#include "Report.hh" #include "FuncExpr.hh" +#include "Report.hh" +#include "StaConfig.hh" +#include "cudd.h" namespace sta { @@ -49,17 +49,18 @@ Bdd::funcBdd(const FuncExpr *expr) DdNode *right = nullptr; DdNode *result = nullptr; switch (expr->op()) { - case FuncExpr::op_port: { + case FuncExpr::Op::port: { LibertyPort *port = expr->port(); result = ensureNode(port); break; } - case FuncExpr::op_not: + case FuncExpr::Op::not_: left = funcBdd(expr->left()); if (left) + // NOLINTNEXTLINE(performance-no-int-to-ptr) result = Cudd_Not(left); break; - case FuncExpr::op_or: + case FuncExpr::Op::or_: left = funcBdd(expr->left()); right = funcBdd(expr->right()); if (left && right) @@ -69,7 +70,7 @@ Bdd::funcBdd(const FuncExpr *expr) else if (right) result = right; break; - case FuncExpr::op_and: + case FuncExpr::Op::and_: left = funcBdd(expr->left()); right = funcBdd(expr->right()); if (left && right) @@ -79,7 +80,7 @@ Bdd::funcBdd(const FuncExpr *expr) else if (right) result = right; break; - case FuncExpr::op_xor: + case FuncExpr::Op::xor_: left = funcBdd(expr->left()); right = funcBdd(expr->right()); if (left && right) @@ -89,10 +90,10 @@ Bdd::funcBdd(const FuncExpr *expr) else if (right) result = right; break; - case FuncExpr::op_one: + case FuncExpr::Op::one: result = Cudd_ReadOne(cudd_mgr_); break; - case FuncExpr::op_zero: + case FuncExpr::Op::zero: result = Cudd_ReadLogicZero(cudd_mgr_); break; default: @@ -161,4 +162,4 @@ Bdd::clearVarMap() bdd_var_idx_port_map_.clear(); } -} // namespace +} // namespace sta diff --git a/search/Bfs.cc b/search/Bfs.cc index ebea9fdbf..69b9c28b5 100644 --- a/search/Bfs.cc +++ b/search/Bfs.cc @@ -1,46 +1,46 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Bfs.hh" -#include "Report.hh" #include "Debug.hh" -#include "Mutex.hh" #include "DispatchQueue.hh" -#include "Network.hh" #include "Graph.hh" -#include "Sdc.hh" #include "Levelize.hh" +#include "Mutex.hh" +#include "Network.hh" +#include "Report.hh" +#include "Sdc.hh" #include "SearchPred.hh" namespace sta { BfsIterator::BfsIterator(BfsIndex bfs_index, - Level level_min, - Level level_max, - SearchPred *search_pred, - StaState *sta) : + Level level_min, + Level level_max, + SearchPred *search_pred, + StaState *sta) : StaState(sta), bfs_index_(bfs_index), level_min_(level_min), @@ -68,10 +68,6 @@ BfsIterator::ensureSize() } } -BfsIterator::~BfsIterator() -{ -} - void BfsIterator::clear() { @@ -80,7 +76,7 @@ BfsIterator::clear() VertexSeq &level_vertices = queue_[level]; for (Vertex *vertex : level_vertices) { if (vertex) - vertex->setBfsInQueue(bfs_index_, false); + vertex->setBfsInQueue(bfs_index_, false); } level_vertices.clear(); incrLevel(level); @@ -91,18 +87,18 @@ BfsIterator::clear() void BfsIterator::reportEntries() const { - for (Level level=first_level_; levelLessOrEqual(level, last_level_);incrLevel(level)){ + for (Level level = first_level_; levelLessOrEqual(level, last_level_); + incrLevel(level)) { const VertexSeq &level_vertices = queue_[level]; if (!level_vertices.empty()) { - report_->reportLine("Level %d", level); + report_->report("Level {}", level); for (Vertex *vertex : level_vertices) - report_->reportLine(" %s", - vertex ? vertex->to_string(this).c_str() : "NULL"); + report_->report(" {}", vertex ? vertex->to_string(this) : "NULL"); } } } -void +void BfsIterator::deleteEntries(Level level) { VertexSeq &level_vertices = queue_[level]; @@ -122,30 +118,23 @@ BfsIterator::empty() const void BfsIterator::enqueueAdjacentVertices(Vertex *vertex) { - enqueueAdjacentVertices(vertex, search_pred_, level_max_); -} - -void -BfsIterator::enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred) -{ - enqueueAdjacentVertices(vertex, search_pred, level_max_); + enqueueAdjacentVertices(vertex, search_pred_); } void BfsIterator::enqueueAdjacentVertices(Vertex *vertex, - Level to_level) + const Mode *mode) { - enqueueAdjacentVertices(vertex, search_pred_, to_level); + enqueueAdjacentVertices(vertex, search_pred_, mode); } int BfsIterator::visit(Level to_level, - VertexVisitor *visitor) + VertexVisitor *visitor) { int visit_count = 0; while (levelLessOrEqual(first_level_, last_level_) - && levelLessOrEqual(first_level_, to_level)) { + && levelLessOrEqual(first_level_, to_level)) { Level level = first_level_; VertexSeq &level_vertices = queue_[level]; incrLevel(first_level_); @@ -162,14 +151,13 @@ BfsIterator::visit(Level to_level, } } level_vertices.clear(); - visitor->levelFinished(); } return visit_count; } int BfsIterator::visitParallel(Level to_level, - VertexVisitor *visitor) + VertexVisitor *visitor) { size_t thread_count = thread_count_; int visit_count = 0; @@ -177,15 +165,16 @@ BfsIterator::visitParallel(Level to_level, if (thread_count == 1) visit_count = visit(to_level, visitor); else { - std::vector visitors; + std::vector visitors; + visitors.reserve(thread_count_); for (int k = 0; k < thread_count_; k++) - visitors.push_back(visitor->copy()); + visitors.push_back(visitor->copy()); while (levelLessOrEqual(first_level_, last_level_) - && levelLessOrEqual(first_level_, to_level)) { - VertexSeq &level_vertices = queue_[first_level_]; + && levelLessOrEqual(first_level_, to_level)) { + VertexSeq &level_vertices = queue_[first_level_]; Level level = first_level_; - incrLevel(first_level_); - if (!level_vertices.empty()) { + incrLevel(first_level_); + if (!level_vertices.empty()) { size_t vertex_count = level_vertices.size(); if (vertex_count < thread_count) { for (Vertex *vertex : level_vertices) { @@ -203,7 +192,7 @@ BfsIterator::visitParallel(Level to_level, for (size_t k = 0; k < thread_count; k++) { // Last thread gets the left overs. size_t to = (k == thread_count - 1) ? vertex_count : from + chunk_size; - dispatch_queue_->dispatch( [=](int) { + dispatch_queue_->dispatch([=, this](size_t) { for (size_t i = from; i < to; i++) { Vertex *vertex = level_vertices[i]; if (vertex) { @@ -217,13 +206,12 @@ BfsIterator::visitParallel(Level to_level, } dispatch_queue_->finishTasks(); } - visitor->levelFinished(); - level_vertices.clear(); + level_vertices.clear(); visit_count += vertex_count; - } + } } for (VertexVisitor *visitor : visitors) - delete visitor; + delete visitor; } } return visit_count; @@ -240,7 +228,7 @@ BfsIterator::hasNext(Level to_level) { findNext(to_level); return levelLessOrEqual(first_level_, last_level_) - && !queue_[first_level_].empty(); + && !queue_[first_level_].empty(); } Vertex * @@ -257,16 +245,16 @@ void BfsIterator::findNext(Level to_level) { while (levelLessOrEqual(first_level_, last_level_) - && levelLessOrEqual(first_level_, to_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(); + level_vertices.pop_back(); else { checkLevel(vertex, first_level_); - return; + return; } } incrLevel(first_level_); @@ -276,8 +264,7 @@ BfsIterator::findNext(Level to_level) void BfsIterator::enqueue(Vertex *vertex) { - debugPrint(debug_, "bfs", 2, "enqueue %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "bfs", 2, "enqueue {}", vertex->to_string(this)); if (!vertex->bfsInQueue(bfs_index_)) { Level level = vertex->level(); LockGuard lock(queue_lock_); @@ -286,9 +273,9 @@ BfsIterator::enqueue(Vertex *vertex) queue_[level].push_back(vertex); if (levelLess(last_level_, level)) - last_level_ = level; + last_level_ = level; if (levelLess(level, first_level_)) - first_level_ = level; + first_level_ = level; } } } @@ -304,20 +291,18 @@ void BfsIterator::checkInQueue(Vertex *vertex) { Level level = vertex->level(); - if (static_cast(queue_.size()) > level) { + if (std::cmp_greater(queue_.size(), level)) { for (Vertex *v : queue_[level]) { if (v == vertex) { - if (vertex->bfsInQueue(bfs_index_)) - return; - else - debugPrint(debug_, "bfs", 1, "extra %s", - vertex->to_string(this).c_str()); + if (vertex->bfsInQueue(bfs_index_)) + return; + else + debugPrint(debug_, "bfs", 1, "extra {}", vertex->to_string(this)); } } } if (vertex->bfsInQueue(bfs_index_)) - debugPrint(debug_, "brs", 1, "missing %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "brs", 1, "missing {}", vertex->to_string(this)); } void @@ -325,10 +310,8 @@ BfsIterator::checkLevel(Vertex *vertex, Level level) { if (vertex->level() != level) - report_->error(2300, "vertex %s level %d != bfs level %d", - vertex->to_string(this).c_str(), - vertex->level(), - level); + report_->error(2300, "vertex {} level {} != bfs level {}", + vertex->to_string(this), vertex->level(), level); } void @@ -343,14 +326,12 @@ BfsIterator::remove(Vertex *vertex) { // If the iterator has not been inited the queue will be empty. Level level = vertex->level(); - if (vertex->bfsInQueue(bfs_index_) - && static_cast(queue_.size()) > level) { - debugPrint(debug_, "bfs", 2, "remove %s", - vertex->to_string(this).c_str()); + if (vertex->bfsInQueue(bfs_index_) && std::cmp_greater(queue_.size(), level)) { + debugPrint(debug_, "bfs", 2, "remove {}", vertex->to_string(this)); for (Vertex *&v : queue_[level]) { if (v == vertex) { - v = nullptr; - vertex->setBfsInQueue(bfs_index_, false); + v = nullptr; + vertex->setBfsInQueue(bfs_index_, false); break; } } @@ -360,9 +341,13 @@ BfsIterator::remove(Vertex *vertex) //////////////////////////////////////////////////////////////// BfsFwdIterator::BfsFwdIterator(BfsIndex bfs_index, - SearchPred *search_pred, - StaState *sta) : - BfsIterator(bfs_index, 0, level_max, search_pred, sta) + SearchPred *search_pred, + StaState *sta) : + BfsIterator(bfs_index, + 0, + level_max, + search_pred, + sta) { } @@ -381,32 +366,46 @@ BfsFwdIterator::incrLevel(Level &level) const bool BfsFwdIterator::levelLessOrEqual(Level level1, - Level level2) const + Level level2) const { return level1 <= level2; } bool BfsFwdIterator::levelLess(Level level1, - Level level2) const + Level level2) const { return level1 < level2; } void BfsFwdIterator::enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred, - Level to_level) + SearchPred *search_pred) { 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 (to_vertex->level() <= to_level - && search_pred->searchThru(edge) - && search_pred->searchTo(to_vertex)) - enqueue(to_vertex); + if (search_pred->searchThru(edge) && search_pred->searchTo(to_vertex)) + enqueue(to_vertex); + } + } +} + +void +BfsFwdIterator::enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred, + 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); } } } @@ -414,9 +413,13 @@ BfsFwdIterator::enqueueAdjacentVertices(Vertex *vertex, //////////////////////////////////////////////////////////////// BfsBkwdIterator::BfsBkwdIterator(BfsIndex bfs_index, - SearchPred *search_pred, - StaState *sta) : - BfsIterator(bfs_index, level_max, 0, search_pred, sta) + SearchPred *search_pred, + StaState *sta) : + BfsIterator(bfs_index, + level_max, + 0, + search_pred, + sta) { } @@ -435,34 +438,48 @@ BfsBkwdIterator::incrLevel(Level &level) const bool BfsBkwdIterator::levelLessOrEqual(Level level1, - Level level2) const + Level level2) const { return level1 >= level2; } bool BfsBkwdIterator::levelLess(Level level1, - Level level2) const + Level level2) const { return level1 > level2; } void BfsBkwdIterator::enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred, - Level to_level) + SearchPred *search_pred) { 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 (from_vertex->level() >= to_level - && search_pred->searchFrom(from_vertex) - && search_pred->searchThru(edge)) - enqueue(from_vertex); + if (search_pred->searchFrom(from_vertex) && search_pred->searchThru(edge)) + enqueue(from_vertex); + } + } +} + +void +BfsBkwdIterator::enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred, + 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)) + enqueue(from_vertex); } } } -} // namespace +} // namespace sta diff --git a/search/CheckCapacitanceLimits.cc b/search/CheckCapacitanceLimits.cc deleted file mode 100644 index 391ecda13..000000000 --- a/search/CheckCapacitanceLimits.cc +++ /dev/null @@ -1,356 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#include "CheckCapacitanceLimits.hh" - -#include "Fuzzy.hh" -#include "Liberty.hh" -#include "Network.hh" -#include "Sdc.hh" -#include "InputDrive.hh" -#include "DcalcAnalysisPt.hh" -#include "GraphDelayCalc.hh" -#include "StaState.hh" -#include "Corner.hh" -#include "PortDirection.hh" -#include "Sim.hh" -#include "Graph.hh" -#include "GraphDelayCalc.hh" - -namespace sta { - -class PinCapacitanceLimitSlackLess -{ -public: - PinCapacitanceLimitSlackLess(const Corner *corner, - const MinMax *min_max, - CheckCapacitanceLimits *check_capacitance_limit, - const StaState *sta); - bool operator()(const Pin *pin1, - const Pin *pin2) const; - -private: - const Corner *corner_; - const MinMax *min_max_; - CheckCapacitanceLimits *check_capacitance_limit_; - const StaState *sta_; - -}; - -PinCapacitanceLimitSlackLess::PinCapacitanceLimitSlackLess(const Corner *corner, - const MinMax *min_max, - CheckCapacitanceLimits *check_capacitance_limit, - const StaState *sta) : - corner_(corner), - min_max_(min_max), - check_capacitance_limit_(check_capacitance_limit), - sta_(sta) -{ -} - -bool -PinCapacitanceLimitSlackLess::operator()(const Pin *pin1, - const Pin *pin2) const -{ - const Corner *corner1, *corner2; - const RiseFall *rf1, *rf2; - float capacitance1, capacitance2; - float limit1, limit2, slack1, slack2; - check_capacitance_limit_->checkCapacitance(pin1, corner_, min_max_, - corner1, rf1, capacitance1, - limit1, slack1); - check_capacitance_limit_->checkCapacitance(pin2, corner_, min_max_, - corner2, rf2, capacitance2, - limit2, slack2); - return fuzzyLess(slack1, slack2) - || (fuzzyEqual(slack1, slack2) - // Break ties for the sake of regression stability. - && sta_->network()->pinLess(pin1, pin2)); -} - -//////////////////////////////////////////////////////////////// - -CheckCapacitanceLimits::CheckCapacitanceLimits(const Sta *sta) : - sta_(sta) -{ -} - -void -CheckCapacitanceLimits::checkCapacitance(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - float &capacitance1, - float &limit1, - float &slack1) const -{ - corner1 = nullptr; - rf1 = nullptr; - capacitance1 = 0.0; - limit1 = 0.0; - slack1 = MinMax::min()->initValue(); - if (corner) - checkCapacitance1(pin, corner, min_max, - corner1, rf1, capacitance1, limit1, slack1); - else { - for (auto corner : *sta_->corners()) { - checkCapacitance1(pin, corner, min_max, - corner1, rf1, capacitance1, limit1, slack1); - } - } -} - -void -CheckCapacitanceLimits::checkCapacitance1(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - float &capacitance1, - float &limit1, - float &slack1) const -{ - float limit; - bool limit_exists; - findLimit(pin, corner, min_max, limit, limit_exists); - if (limit_exists) { - for (auto rf : RiseFall::range()) { - checkCapacitance(pin, corner, min_max, rf, limit, - corner1, rf1, capacitance1, slack1, limit1); - } - } -} - -// Return the tightest limit. -void -CheckCapacitanceLimits::findLimit(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const -{ - const Network *network = sta_->network(); - Sdc *sdc = sta_->sdc(); - - // Default to top ("design") limit. - Cell *top_cell = network->cell(network->topInstance()); - sdc->capacitanceLimit(top_cell, min_max, - limit, exists); - - float limit1; - bool exists1; - if (network->isTopLevelPort(pin)) { - Port *port = network->port(pin); - sdc->capacitanceLimit(port, min_max, limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - InputDrive *drive = sdc->findInputDrive(port); - if (drive) { - for (auto rf : RiseFall::range()) { - const LibertyCell *cell; - const LibertyPort *from_port; - float *from_slews; - const LibertyPort *to_port; - drive->driveCell(rf, min_max, cell, from_port, from_slews, to_port); - if (to_port) { - const LibertyPort *corner_port = to_port->cornerPort(corner, min_max); - corner_port->capacitanceLimit(min_max, limit1, exists1); - if (!exists1 - && corner_port->direction()->isAnyOutput() - && min_max == MinMax::max()) - corner_port->libertyLibrary()->defaultMaxCapacitance(limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - } - } - } - } - else { - Cell *cell = network->cell(network->instance(pin)); - sdc->capacitanceLimit(cell, min_max, - limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - LibertyPort *port = network->libertyPort(pin); - if (port) { - LibertyPort *corner_port = port->cornerPort(corner, min_max); - corner_port->capacitanceLimit(min_max, limit1, exists1); - if (!exists1 - && port->direction()->isAnyOutput()) - corner_port->libertyLibrary()->defaultMaxCapacitance(limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - } - } -} - -void -CheckCapacitanceLimits::checkCapacitance(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - const RiseFall *rf, - float limit, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - float &capacitance1, - float &slack1, - float &limit1) const -{ - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - GraphDelayCalc *dcalc = sta_->graphDelayCalc(); - float cap = dcalc->loadCap(pin, dcalc_ap); - - float slack = (min_max == MinMax::max()) - ? limit - cap : cap - limit; - if (slack < slack1 - // Break ties for the sake of regression stability. - || (fuzzyEqual(slack, slack1) - && rf->index() < rf1->index())) { - corner1 = corner; - rf1 = rf; - capacitance1 = cap; - slack1 = slack; - limit1 = limit; - } -} - -//////////////////////////////////////////////////////////////// - -PinSeq -CheckCapacitanceLimits::checkCapacitanceLimits(const Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max) -{ - const Network *network = sta_->network(); - PinSeq cap_pins; - float min_slack = MinMax::min()->initValue(); - if (net) { - NetPinIterator *pin_iter = network->pinIterator(net); - while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - checkCapLimits(pin, violators, corner, min_max, cap_pins, min_slack); - } - delete pin_iter; - } - else { - LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); - while (inst_iter->hasNext()) { - Instance *inst = inst_iter->next(); - checkCapLimits(inst, violators, corner, min_max, cap_pins, min_slack); - } - delete inst_iter; - // Check top level ports. - checkCapLimits(network->topInstance(), violators, corner, min_max, - cap_pins, min_slack); - } - sort(cap_pins, PinCapacitanceLimitSlackLess(corner, min_max, this, sta_)); - // Keep the min slack pin unless all violators or net pins. - if (!cap_pins.empty() && !violators && net == nullptr) - cap_pins.resize(1); - return cap_pins; -} - -void -CheckCapacitanceLimits::checkCapLimits(const Instance *inst, - bool violators, - const Corner *corner, - const MinMax *min_max, - PinSeq &cap_pins, - float &min_slack) -{ - const Network *network = sta_->network(); - InstancePinIterator *pin_iter = network->pinIterator(inst); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - checkCapLimits(pin, violators, corner, min_max, cap_pins, min_slack); - } - delete pin_iter; -} - -void -CheckCapacitanceLimits::checkCapLimits(const Pin *pin, - bool violators, - const Corner *corner, - const MinMax *min_max, - PinSeq &cap_pins, - float &min_slack) -{ - if (checkPin(pin)) { - const Corner *corner1; - const RiseFall *rf; - float capacitance, limit, slack; - checkCapacitance(pin, corner, min_max, corner1, rf, capacitance, limit, slack); - if (!fuzzyInf(slack)) { - if (violators) { - if (slack < 0.0) - cap_pins.push_back(pin); - } - else { - if (cap_pins.empty() - || slack < min_slack) { - cap_pins.push_back(pin); - min_slack = slack; - } - } - } - } -} - -bool -CheckCapacitanceLimits::checkPin(const Pin *pin) -{ - const Network *network = sta_->network(); - const Sim *sim = sta_->sim(); - const Sdc *sdc = sta_->sdc(); - const Graph *graph = sta_->graph(); - Vertex *vertex = graph->pinLoadVertex(pin); - return network->isDriver(pin) - && !sim->logicZeroOne(pin) - && !sdc->isDisabled(pin) - && !(vertex && sta_->isIdealClock(pin)); -} - -} // namespace diff --git a/search/CheckCapacitanceLimits.hh b/search/CheckCapacitanceLimits.hh deleted file mode 100644 index cae737748..000000000 --- a/search/CheckCapacitanceLimits.hh +++ /dev/null @@ -1,105 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include "MinMax.hh" -#include "Transition.hh" -#include "NetworkClass.hh" -#include "SdcClass.hh" -#include "Sta.hh" - -namespace sta { - -class StaState; -class Corner; - -class CheckCapacitanceLimits -{ -public: - CheckCapacitanceLimits(const Sta *sta); - // corner=nullptr checks all corners. - void checkCapacitance(const Pin *pin, - const Corner *corner1, - const MinMax *min_max, - // Return values. - // Corner is nullptr for no capacitance limit. - const Corner *&corner, - const RiseFall *&rf, - float &capacitance, - float &limit, - float &slack) const; - // Return pins with the min/max cap limit slack. - // net=null check all nets - // corner=nullptr checks all corners. - PinSeq checkCapacitanceLimits(const Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max); - -protected: - void checkCapacitance(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - const RiseFall *rf, - float limit, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - float &capacitance1, - float &slack1, - float &limit1) const; - void checkCapacitance1(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - float &capacitance1, - float &limit1, - float &slack1) const; - void findLimit(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &limit, - bool &limit_exists) const; - void checkCapLimits(const Instance *inst, - bool violators, - const Corner *corner, - const MinMax *min_max, - PinSeq &cap_pins, - float &min_slack); - void checkCapLimits(const Pin *pin, - bool violators, - const Corner *corner, - const MinMax *min_max, - PinSeq &cap_pins, - float &min_slack); - bool checkPin(const Pin *pin); - - const Sta *sta_; -}; - -} // namespace diff --git a/search/CheckCapacitances.cc b/search/CheckCapacitances.cc new file mode 100644 index 000000000..24983ba5b --- /dev/null +++ b/search/CheckCapacitances.cc @@ -0,0 +1,371 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#include "CheckCapacitances.hh" + +#include + +#include "ClkNetwork.hh" +#include "ContainerHelpers.hh" +#include "Fuzzy.hh" +#include "Graph.hh" +#include "GraphDelayCalc.hh" +#include "InputDrive.hh" +#include "Liberty.hh" +#include "MinMax.hh" +#include "Mode.hh" +#include "Network.hh" +#include "NetworkClass.hh" +#include "PortDirection.hh" +#include "Scene.hh" +#include "Sdc.hh" +#include "Sim.hh" +#include "StaState.hh" +#include "Transition.hh" + +namespace sta { + +class CapacitanceCheckSlackLess +{ +public: + CapacitanceCheckSlackLess(const StaState *sta); + bool operator()(const CapacitanceCheck &check1, + const CapacitanceCheck &check2) const; + +private: + const StaState *sta_; +}; + +CapacitanceCheckSlackLess::CapacitanceCheckSlackLess(const StaState *sta) : + sta_(sta) +{ +} + +bool +CapacitanceCheckSlackLess::operator()(const CapacitanceCheck &check1, + const CapacitanceCheck &check2) const +{ + return fuzzyLess(check1.slack(), check2.slack()) + || (fuzzyEqual(check1.slack(), check2.slack()) + // Break ties for the sake of regression stability. + && sta_->network()->pinLess(check1.pin(), check2.pin())); +} + +//////////////////////////////////////////////////////////////// + +CheckCapacitances::CheckCapacitances(const StaState *sta) : + sta_(sta) +{ +} + +void +CheckCapacitances::clear() +{ + checks_.clear(); +} + +CapacitanceCheck +CheckCapacitances::check(const Pin *pin, + const SceneSeq &scenes, + const MinMax *min_max) const +{ + return check(pin, false, scenes, min_max); +} + +CapacitanceCheck +CheckCapacitances::check(const Pin *pin, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max) const +{ + CapacitanceCheck min_slack_check(nullptr, 0.0, min_max->initValue(), + MinMax::min()->initValue(), nullptr, nullptr); + GraphDelayCalc *dcalc = sta_->graphDelayCalc(); + + for (const Scene *scene : scenes) { + if (checkPin(pin, scene)) { + float limit; + bool limit_exists; + findLimit(pin, scene, min_max, limit, limit_exists); + if (limit_exists) { + for (const RiseFall *rf : RiseFall::range()) { + float cap = dcalc->loadCap(pin, scene, min_max); + float slack = (min_max == MinMax::max()) + ? limit - cap : cap - limit; + if ((!violators || fuzzyLess(slack, 0.0)) + && (min_slack_check.pin() == nullptr + || fuzzyLess(slack, min_slack_check.slack()) + // Break ties for the sake of regression stability. + || (fuzzyEqual(slack, min_slack_check.slack()) + && rf->index() < min_slack_check.rf()->index()))) + min_slack_check = CapacitanceCheck(pin, cap, limit, slack, scene, rf); + } + } + } + } + return min_slack_check; +} + +// Return the tightest limit. +void +CheckCapacitances::findLimit(const Pin *pin, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &limit, + bool &exists) const +{ + const Network *network = sta_->network(); + Sdc *sdc = scene->sdc(); + + // Default to top ("design") limit. + Cell *top_cell = network->cell(network->topInstance()); + sdc->capacitanceLimit(top_cell, min_max, + limit, exists); + + float limit1; + bool exists1; + if (network->isTopLevelPort(pin)) { + Port *port = network->port(pin); + sdc->capacitanceLimit(port, min_max, limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + InputDrive *drive = sdc->findInputDrive(port); + if (drive) { + for (auto rf : RiseFall::range()) { + const LibertyCell *cell; + const LibertyPort *from_port; + const DriveCellSlews *from_slews; + const LibertyPort *to_port; + drive->driveCell(rf, min_max, cell, from_port, from_slews, to_port); + if (to_port) { + const LibertyPort *scene_port = to_port->scenePort(scene, min_max); + scene_port->capacitanceLimit(min_max, limit1, exists1); + if (!exists1 + && scene_port->direction()->isAnyOutput() + && min_max == MinMax::max()) + scene_port->libertyLibrary()->defaultMaxCapacitance(limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + } + } + } + } + else { + Cell *cell = network->cell(network->instance(pin)); + sdc->capacitanceLimit(cell, min_max, + limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + LibertyPort *port = network->libertyPort(pin); + if (port) { + LibertyPort *scene_port = port->scenePort(scene, min_max); + scene_port->capacitanceLimit(min_max, limit1, exists1); + if (!exists1 + && port->direction()->isAnyOutput()) + scene_port->libertyLibrary()->defaultMaxCapacitance(limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + } + } +} + +//////////////////////////////////////////////////////////////// + +CapacitanceCheckSeq & +CheckCapacitances::check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max) +{ + clear(); + if (violators) + return checkViolators(net, scenes, min_max); + else + return checkMaxCount(net, max_count, scenes, min_max); +} + +CapacitanceCheckSeq & +CheckCapacitances::checkViolators(const Net *net, + const SceneSeq &scenes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + if (net) { + NetPinIterator *pin_iter = network->pinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + CapacitanceCheck cap_check = check(pin, true, scenes, min_max); + if (!cap_check.isNull()) + checks_.push_back(cap_check); + } + delete pin_iter; + } + else { + LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); + while (inst_iter->hasNext()) { + Instance *inst = inst_iter->next(); + checkCapLimits(inst, true, scenes, min_max); + } + delete inst_iter; + // Check top level ports. + checkCapLimits(network->topInstance(), true, scenes, min_max); + } + + sort(checks_, CapacitanceCheckSlackLess(sta_)); + return checks_; +} + +CapacitanceCheckSeq & +CheckCapacitances::checkMaxCount(const Net *net, + size_t max_count, + const SceneSeq &scenes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + CapacitanceCheckHeap heap(max_count, CapacitanceCheckSlackLess(sta_)); + + if (net) { + NetPinIterator *pin_iter = network->pinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + check(pin, scenes, min_max, heap); + } + delete pin_iter; + } + else { + LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); + while (inst_iter->hasNext()) { + Instance *inst = inst_iter->next(); + checkCapLimits(inst, scenes, min_max, heap); + } + delete inst_iter; + // Check top level ports. + checkCapLimits(network->topInstance(), scenes, min_max, heap); + } + + checks_ = heap.extract(); + return checks_; +} + +void +CheckCapacitances::checkCapLimits(const Instance *inst, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + CapacitanceCheck cap_check = check(pin, violators, scenes, min_max); + if (!cap_check.isNull()) + checks_.push_back(cap_check); + } + delete pin_iter; +} + +void +CheckCapacitances::checkCapLimits(const Instance *inst, + const SceneSeq &scenes, + const MinMax *min_max, + CapacitanceCheckHeap &heap) +{ + const Network *network = sta_->network(); + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + check(pin, scenes, min_max, heap); + } + delete pin_iter; +} + +void +CheckCapacitances::check(const Pin *pin, + const SceneSeq &scenes, + const MinMax *min_max, + CapacitanceCheckHeap &heap) +{ + CapacitanceCheck cap_check = check(pin, false, scenes, min_max); + if (!cap_check.isNull()) + heap.insert(cap_check); +} + +bool +CheckCapacitances::checkPin(const Pin *pin, + const Scene *scene) const +{ + const Network *network = sta_->network(); + const Mode *mode = scene->mode(); + return network->isDriver(pin) + && !mode->sim()->isConstant(pin) + && !mode->sdc()->isDisabledConstraint(pin) + && !mode->clkNetwork()->isIdealClock(pin); +} + +//////////////////////////////////////////////////////////////// + +CapacitanceCheck::CapacitanceCheck() : + pin_(nullptr), + capacitance_(0.0), + limit_(INF), + slack_(-INF), + scene_(nullptr), + rf_(nullptr) +{ +} + +CapacitanceCheck::CapacitanceCheck(const Pin *pin, + float capacitance, + float limit, + float slack, + const Scene *scene, + const RiseFall *rf) : + pin_(pin), + capacitance_(capacitance), + limit_(limit), + slack_(slack), + scene_(scene), + rf_(rf) +{ +} + +} // namespace sta diff --git a/search/CheckCapacitances.hh b/search/CheckCapacitances.hh new file mode 100644 index 000000000..fc105dfaf --- /dev/null +++ b/search/CheckCapacitances.hh @@ -0,0 +1,130 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include +#include + +#include "BoundedHeap.hh" +#include "MinMax.hh" +#include "NetworkClass.hh" +#include "Scene.hh" +#include "SdcClass.hh" +#include "StaState.hh" +#include "Transition.hh" + +namespace sta { + +class StaState; +class Scene; +class RiseFall; +class CapacitanceCheckSlackLess; + +class CapacitanceCheck +{ +public: + CapacitanceCheck(); + CapacitanceCheck(const Pin *pin, + float capacitance, + float limit, + float slack, + const Scene *scene, + const RiseFall *rf); + bool isNull() { return pin_ == nullptr; } + const Pin *pin() const { return pin_; } + float capacitance() const { return capacitance_; } + float limit() const { return limit_; } + float slack() const { return slack_; } + const Scene *scene() const { return scene_; } + const RiseFall *rf() const { return rf_; } + +private: + const Pin *pin_; + float capacitance_; + float limit_; + float slack_; + const Scene *scene_; + const RiseFall *rf_; +}; + +using CapacitanceCheckSeq = std::vector; +using CapacitanceCheckHeap = BoundedHeap; + +class CheckCapacitances +{ +public: + CheckCapacitances(const StaState *sta); + void clear(); + // Return pins with the min/max cap limit slack. + // net=null check all nets + CapacitanceCheckSeq &check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max); + // Return min slack check across scenes. + CapacitanceCheck check(const Pin *pin, + const SceneSeq &scenes, + const MinMax *min_max) const; + +protected: + CapacitanceCheck check(const Pin *pin, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max) const; + void findLimit(const Pin *pin, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &limit, + bool &limit_exists) const; + void checkCapLimits(const Instance *inst, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max); + void checkCapLimits(const Instance *inst, + const SceneSeq &scenes, + const MinMax *min_max, + CapacitanceCheckHeap &heap); + void check(const Pin *pin, + const SceneSeq &scenes, + const MinMax *min_max, + CapacitanceCheckHeap &heap); + CapacitanceCheckSeq &checkViolators(const Net *net, + const SceneSeq &scenes, + const MinMax *min_max); + CapacitanceCheckSeq &checkMaxCount(const Net *net, + size_t max_count, + const SceneSeq &scenes, + const MinMax *min_max); + bool checkPin(const Pin *pin, + const Scene *scene) const; + + const StaState *sta_; + CapacitanceCheckSeq checks_; +}; + +} // namespace sta + diff --git a/search/CheckFanoutLimits.cc b/search/CheckFanoutLimits.cc deleted file mode 100644 index 726fbc31e..000000000 --- a/search/CheckFanoutLimits.cc +++ /dev/null @@ -1,332 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#include "CheckFanoutLimits.hh" - -#include "Fuzzy.hh" -#include "Liberty.hh" -#include "Network.hh" -#include "Sdc.hh" -#include "InputDrive.hh" -#include "Sim.hh" -#include "PortDirection.hh" -#include "Graph.hh" -#include "Search.hh" - -namespace sta { - -class PinFanoutLimitSlackLess -{ -public: - PinFanoutLimitSlackLess(const MinMax *min_max, - CheckFanoutLimits *check_fanout_limit, - const StaState *sta); - bool operator()(const Pin *pin1, - const Pin *pin2) const; - -private: - const MinMax *min_max_; - CheckFanoutLimits *check_fanout_limit_; - const StaState *sta_; - -}; - -PinFanoutLimitSlackLess::PinFanoutLimitSlackLess(const MinMax *min_max, - CheckFanoutLimits *check_fanout_limit, - const StaState *sta) : - min_max_(min_max), - check_fanout_limit_(check_fanout_limit), - sta_(sta) -{ -} - -bool -PinFanoutLimitSlackLess::operator()(const Pin *pin1, - const Pin *pin2) const -{ - float fanout1, fanout2; - float limit1, limit2, slack1, slack2; - check_fanout_limit_->checkFanout(pin1, min_max_, - fanout1, limit1, slack1); - check_fanout_limit_->checkFanout(pin2, min_max_, - fanout2, limit2, slack2); - return fuzzyLess(slack1, slack2) - || (fuzzyEqual(slack1, slack2) - // Break ties for the sake of regression stability. - && sta_->network()->pinLess(pin1, pin2)); -} - -//////////////////////////////////////////////////////////////// - -CheckFanoutLimits::CheckFanoutLimits(const Sta *sta) : - sta_(sta) -{ -} - -void -CheckFanoutLimits::checkFanout(const Pin *pin, - const MinMax *min_max, - // Return values. - float &fanout, - float &limit, - float &slack) const -{ - fanout = 0.0; - limit = min_max->initValue(); - slack = MinMax::min()->initValue(); - - float limit1; - bool limit1_exists; - findLimit(pin, min_max, limit1, limit1_exists); - if (limit1_exists) - checkFanout(pin, min_max, limit1, - fanout, limit, slack); -} - -// return the tightest limit. -void -CheckFanoutLimits::findLimit(const Pin *pin, - const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const -{ - const Network *network = sta_->network(); - Sdc *sdc = sta_->sdc(); - - limit = min_max->initValue(); - exists = false; - - // Default to top ("design") limit. - // Applies to input ports as well as instance outputs. - Cell *top_cell = network->cell(network->topInstance()); - sdc->fanoutLimit(top_cell, min_max, - limit, exists); - - float limit1; - bool exists1; - if (network->isTopLevelPort(pin)) { - Port *port = network->port(pin); - sdc->fanoutLimit(port, min_max, limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - InputDrive *drive = sdc->findInputDrive(port); - if (drive) { - for (auto rf : RiseFall::range()) { - const LibertyCell *cell; - const LibertyPort *from_port; - float *from_slews; - const LibertyPort *to_port; - drive->driveCell(rf, min_max, cell, from_port, from_slews, to_port); - if (to_port) { - to_port->fanoutLimit(min_max, limit1, exists1); - if (!exists1 - && min_max == MinMax::max() - && to_port->direction()->isAnyOutput()) - to_port->libertyLibrary()->defaultMaxFanout(limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - } - } - } - } - else { - Cell *cell = network->cell(network->instance(pin)); - sdc->fanoutLimit(cell, min_max, - limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - LibertyPort *port = network->libertyPort(pin); - if (port) { - port->fanoutLimit(min_max, limit1, exists1); - if (!exists1 - && min_max == MinMax::max() - && port->direction()->isAnyOutput()) - port->libertyLibrary()->defaultMaxFanout(limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - } - } -} - -void -CheckFanoutLimits::checkFanout(const Pin *pin, - const MinMax *min_max, - float limit1, - // Return values. - float &fanout, - float &limit, - float &slack) const -{ - float fanout1 = fanoutLoad(pin); - float slack1 = (min_max == MinMax::max()) - ? limit1 - fanout1 - : fanout1 - limit1; - if (fuzzyLessEqual(slack1, slack)) { - fanout = fanout1; - slack = slack1; - limit = limit1; - } -} - -float -CheckFanoutLimits::fanoutLoad(const Pin *pin) const -{ - float fanout = 0; - const Network *network = sta_->network(); - NetConnectedPinIterator *pin_iter = network->connectedPinIterator(pin); - while (pin_iter->hasNext()) { - const Pin *fanout_pin = pin_iter->next(); - if (network->isLoad(fanout_pin) - && !network->isTopLevelPort(fanout_pin)) { - LibertyPort *port = network->libertyPort(fanout_pin); - if (port) { - float fanout_load; - bool exists; - port->fanoutLoad(fanout_load, exists); - if (!exists) { - LibertyLibrary *lib = port->libertyLibrary(); - lib->defaultFanoutLoad(fanout_load, exists); - } - if (exists) - fanout += fanout_load; - } - else - fanout += 1; - } - } - delete pin_iter; - return fanout; -} - -//////////////////////////////////////////////////////////////// - -PinSeq -CheckFanoutLimits::checkFanoutLimits(const Net *net, - bool violators, - const MinMax *min_max) -{ - const Network *network = sta_->network(); - PinSeq fanout_pins; - float min_slack = MinMax::min()->initValue(); - if (net) { - NetPinIterator *pin_iter = network->pinIterator(net); - while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - checkFanoutLimits(pin, violators, min_max, fanout_pins, min_slack); - } - delete pin_iter; - } - else { - LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); - while (inst_iter->hasNext()) { - const Instance *inst = inst_iter->next(); - checkFanoutLimits(inst, violators, min_max, fanout_pins, min_slack); - } - delete inst_iter; - // Check top level ports. - checkFanoutLimits(network->topInstance(), violators, min_max, - fanout_pins, min_slack); - } - sort(fanout_pins, PinFanoutLimitSlackLess(min_max, this, sta_)); - // Keep the min slack pin unless all violators or net pins. - if (!fanout_pins.empty() && !violators && net == nullptr) - fanout_pins.resize(1); - return fanout_pins; -} - -void -CheckFanoutLimits::checkFanoutLimits(const Instance *inst, - bool violators, - const MinMax *min_max, - PinSeq &fanout_pins, - float &min_slack) -{ - const Network *network = sta_->network(); - InstancePinIterator *pin_iter = network->pinIterator(inst); - while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - checkFanoutLimits(pin, violators, min_max, fanout_pins, min_slack); - } - delete pin_iter; -} - -void -CheckFanoutLimits::checkFanoutLimits(const Pin *pin, - bool violators, - const MinMax *min_max, - PinSeq &fanout_pins, - float &min_slack) -{ - if (checkPin(pin)) { - float fanout; - float limit, slack; - checkFanout(pin, min_max, fanout, limit, slack); - if (!fuzzyInf(slack)) { - if (violators) { - if (slack < 0.0) - fanout_pins.push_back(pin); - } - else { - if (fanout_pins.empty() - || slack < min_slack) { - fanout_pins.push_back(pin); - min_slack = slack; - } - } - } - } -} - -bool -CheckFanoutLimits::checkPin(const Pin *pin) -{ - const Network *network = sta_->network(); - const Sim *sim = sta_->sim(); - const Sdc *sdc = sta_->sdc(); - const Graph *graph = sta_->graph(); - Vertex *vertex = graph->pinDrvrVertex(pin); - return network->isDriver(pin) - && !sim->logicZeroOne(pin) - && !sdc->isDisabled(pin) - && !(vertex && sta_->isIdealClock(pin)); -} - -} // namespace diff --git a/search/CheckFanoutLimits.hh b/search/CheckFanoutLimits.hh deleted file mode 100644 index 0af63f3e3..000000000 --- a/search/CheckFanoutLimits.hh +++ /dev/null @@ -1,82 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include "MinMax.hh" -#include "NetworkClass.hh" -#include "SdcClass.hh" -#include "Sta.hh" - -namespace sta { - -class StaState; - -class CheckFanoutLimits -{ -public: - CheckFanoutLimits(const Sta *sta); - void checkFanout(const Pin *pin, - const MinMax *min_max, - // Return values. - float &fanout, - float &limit, - float &slack) const; - // Return pins with the min/max fanout limit slack. - // net=null check all nets - // corner=nullptr checks all corners. - PinSeq checkFanoutLimits(const Net *net, - bool violators, - const MinMax *min_max); - -protected: - void checkFanout(const Pin *pin, - const MinMax *min_max, - float limit1, - // Return values. - float &fanout, - float &limit, - float &slack) const; - void findLimit(const Pin *pin, - const MinMax *min_max, - // Return values. - float &limit, - bool &limit_exists) const; - float fanoutLoad(const Pin *pin) const; - void checkFanoutLimits(const Instance *inst, - bool violators, - const MinMax *min_max, - PinSeq &fanout_pins, - float &min_slack); - void checkFanoutLimits(const Pin *pin, - bool violators, - const MinMax *min_max, - PinSeq &fanout_pins, - float &min_slack); - bool checkPin(const Pin *pin); - - const Sta *sta_; -}; - -} // namespace diff --git a/search/CheckFanouts.cc b/search/CheckFanouts.cc new file mode 100644 index 000000000..b5ffae22b --- /dev/null +++ b/search/CheckFanouts.cc @@ -0,0 +1,339 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#include "CheckFanouts.hh" + +#include + +#include "ClkNetwork.hh" +#include "ContainerHelpers.hh" +#include "Fuzzy.hh" +#include "Graph.hh" +#include "InputDrive.hh" +#include "Liberty.hh" +#include "MinMax.hh" +#include "Mode.hh" +#include "Network.hh" +#include "NetworkClass.hh" +#include "PortDirection.hh" +#include "Scene.hh" +#include "Sdc.hh" +#include "Sim.hh" +#include "Transition.hh" + +namespace sta { + +CheckFanouts::CheckFanouts(const Sta *sta) : + sta_(sta), + heap_(0, FanoutCheckSlackLess(sta)) +{ +} + +void +CheckFanouts::clear() +{ + checks_.clear(); + heap_.clear(); +} + +FanoutCheck +CheckFanouts::check(const Pin *pin, + const Mode *mode, + const MinMax *min_max) const +{ + FanoutCheck min_slack_check; + float fanout = fanoutLoad(pin); + if (checkPin(pin, mode)) { + float limit; + bool limit_exists; + findLimit(pin, mode->sdc(), min_max, limit, limit_exists); + if (limit_exists) { + float slack = (min_max == MinMax::max()) + ? limit - fanout + : fanout - limit; + return FanoutCheck(pin, fanout, limit, slack, mode); + } + } + return FanoutCheck(); +} + +// return the tightest limit. +void +CheckFanouts::findLimit(const Pin *pin, + const Sdc *sdc, + const MinMax *min_max, + // Return values. + float &limit, + bool &exists) const +{ + const Network *network = sta_->network(); + + limit = min_max->initValue(); + exists = false; + + // Default to top ("design") limit. + // Applies to input ports as well as instance outputs. + Cell *top_cell = network->cell(network->topInstance()); + sdc->fanoutLimit(top_cell, min_max, + limit, exists); + + float limit1; + bool exists1; + if (network->isTopLevelPort(pin)) { + Port *port = network->port(pin); + sdc->fanoutLimit(port, min_max, limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + InputDrive *drive = sdc->findInputDrive(port); + if (drive) { + for (const RiseFall *rf : RiseFall::range()) { + const LibertyCell *cell; + const LibertyPort *from_port; + const DriveCellSlews *from_slews; + const LibertyPort *to_port; + drive->driveCell(rf, min_max, cell, from_port, from_slews, to_port); + if (to_port) { + to_port->fanoutLimit(min_max, limit1, exists1); + if (!exists1 + && min_max == MinMax::max() + && to_port->direction()->isAnyOutput()) + to_port->libertyLibrary()->defaultMaxFanout(limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + } + } + } + } + else { + Cell *cell = network->cell(network->instance(pin)); + sdc->fanoutLimit(cell, min_max, + limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + LibertyPort *port = network->libertyPort(pin); + if (port) { + port->fanoutLimit(min_max, limit1, exists1); + if (!exists1 + && min_max == MinMax::max() + && port->direction()->isAnyOutput()) + port->libertyLibrary()->defaultMaxFanout(limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + } + } +} + +float +CheckFanouts::fanoutLoad(const Pin *pin) const +{ + float fanout = 0; + const Network *network = sta_->network(); + NetConnectedPinIterator *pin_iter = network->connectedPinIterator(pin); + while (pin_iter->hasNext()) { + const Pin *fanout_pin = pin_iter->next(); + if (network->isLoad(fanout_pin) + && !network->isTopLevelPort(fanout_pin)) { + LibertyPort *port = network->libertyPort(fanout_pin); + if (port) { + float fanout_load; + bool exists; + port->fanoutLoad(fanout_load, exists); + if (!exists) { + LibertyLibrary *lib = port->libertyLibrary(); + lib->defaultFanoutLoad(fanout_load, exists); + } + if (exists) + fanout += fanout_load; + } + else + fanout += 1; + } + } + delete pin_iter; + return fanout; +} + +//////////////////////////////////////////////////////////////// + +FanoutCheckSeq & +CheckFanouts::check(const Net *net, + size_t max_count, + bool violators, + const ModeSeq &modes, + const MinMax *min_max) +{ + clear(); + if (!violators) + heap_.setMaxSize(max_count); + + if (net) + checkNet(net, violators, modes, min_max); + else + checkAll(violators, modes, min_max); + + if (violators) + sort(checks_, FanoutCheckSlackLess(sta_)); + else + checks_ = heap_.extract(); + return checks_; +} + +void +CheckFanouts::checkNet(const Net *net, + bool violators, + const ModeSeq &modes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + if (net) { + NetPinIterator *pin_iter = network->pinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + checkPin(pin, violators, modes, min_max); + } + delete pin_iter; + } +} + +void +CheckFanouts::checkAll(bool violators, + const ModeSeq &modes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); + while (inst_iter->hasNext()) { + const Instance *inst = inst_iter->next(); + checkInst(inst, violators, modes, min_max); + } + delete inst_iter; + // Check top level ports. + checkInst(network->topInstance(), violators, modes, min_max); +} + +void +CheckFanouts::checkInst(const Instance *inst, + bool violators, + const ModeSeq &modes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + checkPin(pin, violators, modes, min_max); + } + delete pin_iter; +} + +void +CheckFanouts::checkPin(const Pin *pin, + bool violators, + const ModeSeq &modes, + const MinMax *min_max) +{ + for (const Mode *mode : modes) { + if (checkPin(pin, mode)) { + FanoutCheck fanout_check = check(pin, mode, min_max); + if (!fanout_check.isNull()) { + if (violators) { + if (fanout_check.slack() < 0.0) + checks_.push_back(fanout_check); + } + else + heap_.insert(fanout_check); + } + } + } +} + +bool +CheckFanouts::checkPin(const Pin *pin, + const Mode *mode) const +{ + const Network *network = sta_->network(); + return network->isDriver(pin) + && !mode->sim()->isConstant(pin) + && !mode->sdc()->isDisabledConstraint(pin) + && !mode->clkNetwork()->isIdealClock(pin); +} + +//////////////////////////////////////////////////////////////// + +FanoutCheck::FanoutCheck() : + pin_(nullptr), + fanout_(0.0), + limit_(INF), + slack_(INF), + mode_(nullptr) +{ +} + +FanoutCheck::FanoutCheck(const Pin *pin, + float fanout, + float limit, + float slack, + const Mode *mode) : + pin_(pin), + fanout_(fanout), + limit_(limit), + slack_(slack), + mode_(mode) +{ +} + +//////////////////////////////////////////////////////////////// + +FanoutCheckSlackLess::FanoutCheckSlackLess(const StaState *sta) : + sta_(sta) +{ +} + +bool +FanoutCheckSlackLess::operator()(const FanoutCheck &check1, + const FanoutCheck &check2) const +{ + return fuzzyLess(check1.slack(), check2.slack()) + || (fuzzyEqual(check1.slack(), check2.slack()) + // Break ties for the sake of regression stability. + && sta_->network()->pinLess(check1.pin(), check2.pin())); +} + +} // namespace sta diff --git a/search/CheckFanouts.hh b/search/CheckFanouts.hh new file mode 100644 index 000000000..a4b8e7abc --- /dev/null +++ b/search/CheckFanouts.hh @@ -0,0 +1,129 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include +#include + +#include "BoundedHeap.hh" +#include "MinMax.hh" +#include "Mode.hh" +#include "NetworkClass.hh" +#include "SdcClass.hh" +#include "Sta.hh" +#include "StaState.hh" + +namespace sta { + +class StaState; + +class FanoutCheck +{ +public: + FanoutCheck(); + FanoutCheck(const Pin *pin, + float fanout, + float limit, + float slack, + const Mode *mode); + bool isNull() { return pin_ == nullptr; } + const Pin *pin() const { return pin_; } + float fanout() const { return fanout_; } + float limit() const { return limit_; } + float slack() const { return slack_; } + const Mode *mode() const { return mode_; } + +private: + const Pin *pin_; + float fanout_; + float limit_; + float slack_; + const Mode *mode_; +}; + +class FanoutCheckSlackLess +{ +public: + FanoutCheckSlackLess(const StaState *sta); + bool operator()(const FanoutCheck &check1, + const FanoutCheck &check2) const; + +private: + const StaState *sta_; +}; + +using FanoutCheckSeq = std::vector; +using FanoutCheckHeap = BoundedHeap; + +class CheckFanouts +{ +public: + CheckFanouts(const Sta *sta); + void clear(); + // Return pins with the min/max fanout limit slack. + // net=null check all nets + FanoutCheckSeq &check(const Net *net, + size_t max_count, + bool violators, + const ModeSeq &modes, + const MinMax *min_max); + FanoutCheck check(const Pin *pin, + const Mode*mode, + const MinMax *min_max) const; + +protected: + void checkNet(const Net *net, + bool violators, + const ModeSeq &modes, + const MinMax *min_max); + void checkAll(bool violators, + const ModeSeq &modes, + const MinMax *min_max); + void checkInst(const Instance *inst, + bool violators, + const ModeSeq &modes, + const MinMax *min_max); + void checkPin(const Pin *pin, + bool violators, + const ModeSeq &modes, + const MinMax *min_max); + bool checkPin(const Pin *pin, + const Mode *mode) const; + + void findLimit(const Pin *pin, + const Sdc *sdc, + const MinMax *min_max, + // Return values. + float &limit, + bool &limit_exists) const; + float fanoutLoad(const Pin *pin) const; + + const Sta *sta_; + FanoutCheckSeq checks_; + FanoutCheckHeap heap_; +}; + +} // namespace sta + diff --git a/search/CheckMaxSkews.cc b/search/CheckMaxSkews.cc index 3496fb0c3..663694b33 100644 --- a/search/CheckMaxSkews.cc +++ b/search/CheckMaxSkews.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,170 +24,76 @@ #include "CheckMaxSkews.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" +#include + +#include "ContainerHelpers.hh" +#include "Delay.hh" +#include "Graph.hh" #include "Liberty.hh" #include "Network.hh" -#include "Graph.hh" -#include "Clock.hh" +#include "NetworkClass.hh" #include "Path.hh" -#include "PathAnalysisPt.hh" #include "Search.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Transition.hh" namespace sta { -// Abstract base class. -class MaxSkewCheckVisitor -{ -public: - MaxSkewCheckVisitor() {} - virtual ~MaxSkewCheckVisitor() {} - virtual void visit(MaxSkewCheck &check, - const StaState *sta) = 0; -}; - CheckMaxSkews::CheckMaxSkews(StaState *sta) : sta_(sta) { } -CheckMaxSkews::~CheckMaxSkews() -{ - checks_.deleteContents(); -} - void CheckMaxSkews::clear() { - checks_.deleteContentsClear(); -} - -class MaxSkewChecksVisitor : public MaxSkewCheckVisitor -{ -public: - explicit MaxSkewChecksVisitor(MaxSkewCheckSeq &checks); - virtual void visit(MaxSkewCheck &check, - const StaState *sta); - -private: - MaxSkewCheckSeq &checks_; -}; - -MaxSkewChecksVisitor::MaxSkewChecksVisitor(MaxSkewCheckSeq &checks) : - MaxSkewCheckVisitor(), - checks_(checks) -{ -} - -void -MaxSkewChecksVisitor::visit(MaxSkewCheck &check, - const StaState *) -{ - checks_.push_back(new MaxSkewCheck(check)); -} - -class MaxSkewViolatorsVisititor : public MaxSkewCheckVisitor -{ -public: - explicit MaxSkewViolatorsVisititor(MaxSkewCheckSeq &checks); - virtual void visit(MaxSkewCheck &check, - const StaState *sta); - -private: - MaxSkewCheckSeq &checks_; -}; - -MaxSkewViolatorsVisititor:: -MaxSkewViolatorsVisititor(MaxSkewCheckSeq &checks) : - MaxSkewCheckVisitor(), - checks_(checks) -{ -} - -void -MaxSkewViolatorsVisititor::visit(MaxSkewCheck &check, - const StaState *sta) -{ - if (delayLess(check.slack(sta), 0.0, sta)) - checks_.push_back(new MaxSkewCheck(check)); + checks_.clear(); } MaxSkewCheckSeq & -CheckMaxSkews::violations() +CheckMaxSkews::check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes) { clear(); - MaxSkewViolatorsVisititor visitor(checks_); - visitMaxSkewChecks(&visitor); - sort(checks_, MaxSkewSlackLess(sta_)); - return checks_; -} - -class MaxSkewSlackVisitor : public MaxSkewCheckVisitor -{ -public: - MaxSkewSlackVisitor(); - virtual void visit(MaxSkewCheck &check, - const StaState *sta); - MaxSkewCheck *minSlackCheck(); - -private: - MaxSkewCheck *min_slack_check_; -}; - -MaxSkewSlackVisitor::MaxSkewSlackVisitor() : - MaxSkewCheckVisitor(), - min_slack_check_(nullptr) -{ -} - -void -MaxSkewSlackVisitor::visit(MaxSkewCheck &check, - const StaState *sta) -{ - MaxSkewSlackLess slack_less(sta); - if (min_slack_check_ == nullptr - || slack_less(&check, min_slack_check_)) { - delete min_slack_check_; - min_slack_check_ = new MaxSkewCheck(check); - } -} - -MaxSkewCheck * -MaxSkewSlackVisitor::minSlackCheck() -{ - return min_slack_check_; -} - -MaxSkewCheck * -CheckMaxSkews::minSlackCheck() -{ - clear(); - MaxSkewSlackVisitor visitor; - visitMaxSkewChecks(&visitor); - MaxSkewCheck *check = visitor.minSlackCheck(); - // Save check for cleanup. - checks_.push_back(check); - return check; -} + scenes_ = Scene::sceneSet(scenes); -void -CheckMaxSkews::visitMaxSkewChecks(MaxSkewCheckVisitor *visitor) -{ Graph *graph = sta_->graph(); - VertexIterator vertex_iter(graph); - while (vertex_iter.hasNext()) { - Vertex *vertex = vertex_iter.next(); - visitMaxSkewChecks(vertex, visitor); + const Network *network = sta_->network(); + if (net) { + NetPinIterator *pin_iter = network->pinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + Vertex *vertex = graph->pinLoadVertex(pin); + check(vertex, violators); + } + delete pin_iter; } + else { + VertexIterator vertex_iter(graph); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + check(vertex, violators); + } + } + + // Sort checks by slack + sort(checks_, MaxSkewSlackLess(sta_)); + if (!violators && checks_.size() > max_count) + checks_.resize(max_count); + return checks_; } void -CheckMaxSkews:: visitMaxSkewChecks(Vertex *vertex, - MaxSkewCheckVisitor *visitor) +CheckMaxSkews::check(Vertex *vertex, + bool violators) { Graph *graph = sta_->graph(); Search *search = sta_->search(); const MinMax *clk_min_max = MinMax::max(); + MaxSkewCheck min_slack_check; VertexInEdgeIterator edge_iter(vertex, graph); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); @@ -195,35 +101,53 @@ CheckMaxSkews:: visitMaxSkewChecks(Vertex *vertex, Vertex *ref_vertex = edge->from(graph); TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *arc : arc_set->arcs()) { - const RiseFall *clk_rf = arc->fromEdge()->asRiseFall(); - const RiseFall *ref_rf = arc->toEdge()->asRiseFall(); - VertexPathIterator clk_path_iter(vertex, clk_rf, clk_min_max, search); - while (clk_path_iter.hasNext()) { - Path *clk_path = clk_path_iter.next(); - if (clk_path->isClock(search)) { - const PathAnalysisPt *clk_ap = clk_path->pathAnalysisPt(sta_); - PathAnalysisPt *ref_ap = clk_ap->tgtClkAnalysisPt(); - VertexPathIterator ref_path_iter(ref_vertex, ref_rf, ref_ap, sta_); - while (ref_path_iter.hasNext()) { - Path *ref_path = ref_path_iter.next(); - if (ref_path->isClock(search)) { - MaxSkewCheck check(clk_path, ref_path, arc, edge); - visitor->visit(check, sta_); - } - } - } - } + const RiseFall *clk_rf = arc->fromEdge()->asRiseFall(); + const RiseFall *ref_rf = arc->toEdge()->asRiseFall(); + VertexPathIterator clk_path_iter(vertex, clk_rf, clk_min_max, search); + while (clk_path_iter.hasNext()) { + Path *clk_path = clk_path_iter.next(); + if (clk_path->isClock(search)) { + const Scene *scene = clk_path->scene(sta_); + if (scenes_.contains(scene)) { + const MinMax *ref_min_max = clk_path->tgtClkMinMax(sta_); + VertexPathIterator ref_path_iter(ref_vertex, scene, ref_min_max, + ref_rf, sta_); + while (ref_path_iter.hasNext()) { + Path *ref_path = ref_path_iter.next(); + if (ref_path->isClock(search)) { + MaxSkewCheck skew_check(clk_path, ref_path, arc, edge); + Slack slack = skew_check.slack(sta_); + if ((min_slack_check.isNull() + || delayLess(slack, min_slack_check.slack(sta_), sta_)) + && (!violators || + delayLess(slack, 0.0, sta_))) + min_slack_check = skew_check; + } + } + } + } + } } } } + if (!min_slack_check.isNull()) + checks_.push_back(min_slack_check); } //////////////////////////////////////////////////////////////// +MaxSkewCheck::MaxSkewCheck() : + clk_path_(nullptr), + ref_path_(nullptr), + check_arc_(nullptr), + check_edge_(nullptr) +{ +} + MaxSkewCheck::MaxSkewCheck(Path *clk_path, - Path *ref_path, - TimingArc *check_arc, - Edge *check_edge) : + Path *ref_path, + TimingArc *check_arc, + Edge *check_edge) : clk_path_(clk_path), ref_path_(ref_path), check_arc_(check_arc), @@ -248,20 +172,22 @@ MaxSkewCheck::maxSkew(const StaState *sta) const { Search *search = sta->search(); return search->deratedDelay(ref_path_->vertex(sta), - check_arc_, check_edge_, false, - clk_path_->pathAnalysisPt(sta)); + check_arc_, check_edge_, false, + clk_path_->minMax(sta), + clk_path_->dcalcAnalysisPtIndex(sta), + ref_path_->scene(sta)->sdc()); } Delay -MaxSkewCheck::skew() const +MaxSkewCheck::skew(const StaState *sta) const { - return Delay(clk_path_->arrival() - ref_path_->arrival()); + return delayDiff(clk_path_->arrival(), ref_path_->arrival(), sta); } Slack MaxSkewCheck::slack(const StaState *sta) const { - return maxSkew(sta) - skew(); + return delayDiff(maxSkew(sta), skew(sta), sta); } //////////////////////////////////////////////////////////////// @@ -272,15 +198,15 @@ MaxSkewSlackLess::MaxSkewSlackLess(const StaState *sta) : } bool -MaxSkewSlackLess::operator()(const MaxSkewCheck *check1, - const MaxSkewCheck *check2) const +MaxSkewSlackLess::operator()(const MaxSkewCheck &check1, + const MaxSkewCheck &check2) const { - Slack slack1 = check1->slack(sta_); - Slack slack2 = check2->slack(sta_); + Slack slack1 = check1.slack(sta_); + Slack slack2 = check2.slack(sta_); return delayLess(slack1, slack2, sta_) - || (delayEqual(slack1, slack2) - // Break ties based on constrained pin names. - && sta_->network()->pinLess(check1->clkPin(sta_),check2->clkPin(sta_))); + || (delayEqual(slack1, slack2, sta_) + // Break ties based on constrained pin names. + && sta_->network()->pinLess(check1.clkPin(sta_), check2.clkPin(sta_))); } -} // namespace +} // namespace sta diff --git a/search/CheckMaxSkews.hh b/search/CheckMaxSkews.hh index 4f40ffbbc..2d23a9e7c 100644 --- a/search/CheckMaxSkews.hh +++ b/search/CheckMaxSkews.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,48 +24,34 @@ #pragma once -#include "GraphClass.hh" +#include +#include + #include "Delay.hh" -#include "StaState.hh" -#include "SearchClass.hh" +#include "GraphClass.hh" +#include "LibertyClass.hh" +#include "NetworkClass.hh" #include "Path.hh" +#include "Scene.hh" +#include "SearchClass.hh" +#include "StaState.hh" namespace sta { -class MaxSkewCheckVisitor; - -class CheckMaxSkews -{ -public: - explicit CheckMaxSkews(StaState *sta); - ~CheckMaxSkews(); - void clear(); - // All violating max skew checks. - MaxSkewCheckSeq &violations(); - // Max skew check with the least slack. - MaxSkewCheck *minSlackCheck(); - -protected: - void visitMaxSkewChecks(MaxSkewCheckVisitor *visitor); - void visitMaxSkewChecks(Vertex *vertex, - MaxSkewCheckVisitor *visitor); - - MaxSkewCheckSeq checks_; - StaState *sta_; -}; - class MaxSkewCheck { public: + MaxSkewCheck(); MaxSkewCheck(Path *clk_path, - Path *ref_path, - TimingArc *check_arc, - Edge *check_edge); + Path *ref_path, + TimingArc *check_arc, + Edge *check_edge); + bool isNull() const { return clk_path_ == nullptr; } const Path *clkPath() const { return clk_path_; } Pin *clkPin(const StaState *sta) const; const Path *refPath() const { return ref_path_; } Pin *refPin(const StaState *sta) const; - Delay skew() const; + Delay skew(const StaState *sta) const; ArcDelay maxSkew(const StaState *sta) const; Slack slack(const StaState *sta) const; TimingArc *checkArc() const { return check_arc_; } @@ -77,15 +63,38 @@ private: Edge *check_edge_; }; +using MaxSkewCheckSeq = std::vector; + +class CheckMaxSkews +{ +public: + CheckMaxSkews(StaState *sta); + void clear(); + // Return max skew checks. + // net=null check all nets + MaxSkewCheckSeq &check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes); + +protected: + void check(Vertex *vertex, + bool violators); + + SceneSet scenes_; + MaxSkewCheckSeq checks_; + StaState *sta_; +}; + class MaxSkewSlackLess { public: - explicit MaxSkewSlackLess(const StaState *sta); - bool operator()(const MaxSkewCheck *check1, - const MaxSkewCheck *check2) const; + MaxSkewSlackLess(const StaState *sta); + bool operator()(const MaxSkewCheck &check1, + const MaxSkewCheck &check2) const; protected: const StaState *sta_; }; -} // namespace +} // namespace sta diff --git a/search/CheckMinPeriods.cc b/search/CheckMinPeriods.cc index 063583823..ef32b9724 100644 --- a/search/CheckMinPeriods.cc +++ b/search/CheckMinPeriods.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,189 +24,147 @@ #include "CheckMinPeriods.hh" -#include "Liberty.hh" -#include "Network.hh" -#include "Sdc.hh" +#include + #include "Clock.hh" +#include "ContainerHelpers.hh" +#include "Delay.hh" #include "Graph.hh" -#include "DcalcAnalysisPt.hh" #include "GraphDelayCalc.hh" +#include "Liberty.hh" +#include "Mode.hh" +#include "Network.hh" +#include "NetworkClass.hh" +#include "Sdc.hh" +#include "SdcClass.hh" #include "Search.hh" +#include "SearchPred.hh" namespace sta { -// Abstract base class. -class MinPeriodCheckVisitor -{ -public: - MinPeriodCheckVisitor(const Corner *corner); - virtual ~MinPeriodCheckVisitor() {} - virtual void visit(MinPeriodCheck &check, - StaState *sta) = 0; - const Corner *corner() { return corner_; } - -protected: - const Corner *corner_; -}; - -MinPeriodCheckVisitor::MinPeriodCheckVisitor(const Corner *corner) : - corner_(corner) -{ -} - CheckMinPeriods::CheckMinPeriods(StaState *sta) : + heap_(0, MinPeriodSlackLess(sta)), sta_(sta) { } -CheckMinPeriods::~CheckMinPeriods() -{ - checks_.deleteContents(); -} - void CheckMinPeriods::clear() { - checks_.deleteContentsClear(); + checks_.clear(); + heap_.clear(); } -class MinPeriodViolatorsVisitor : public MinPeriodCheckVisitor -{ -public: - MinPeriodViolatorsVisitor(const Corner *corner, - MinPeriodCheckSeq &checks); - virtual void visit(MinPeriodCheck &check, - StaState *sta); - -private: - MinPeriodCheckSeq &checks_; -}; - -MinPeriodViolatorsVisitor::MinPeriodViolatorsVisitor(const Corner *corner, - MinPeriodCheckSeq &checks): - MinPeriodCheckVisitor(corner), - checks_(checks) +MinPeriodCheckSeq & +CheckMinPeriods::check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes) { + clear(); + if (!violators) + heap_.setMaxSize(max_count); + + if (net) + checkNet(net, violators, scenes); + else + checkAll(violators, scenes); + + if (violators) + sort(checks_, MinPeriodSlackLess(sta_)); + else + checks_ = heap_.extract(); + return checks_; } void -MinPeriodViolatorsVisitor::visit(MinPeriodCheck &check, - StaState *sta) +CheckMinPeriods::checkNet(const Net *net, + bool violators, + const SceneSeq &scenes) { - if (delayLess(check.slack(sta), 0.0, sta)) - checks_.push_back(check.copy()); -} - -MinPeriodCheckSeq & -CheckMinPeriods::violations(const Corner *corner) -{ - clear(); - MinPeriodViolatorsVisitor visitor(corner, checks_); - visitMinPeriodChecks(&visitor); - sort(checks_, MinPeriodSlackLess(sta_)); - return checks_; + Graph *graph = sta_->graph(); + NetPinIterator *pin_iter = sta_->network()->pinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + Vertex *vertex = graph->pinLoadVertex(pin); + checkVertex(vertex, violators, scenes); + } + delete pin_iter; } void -CheckMinPeriods::visitMinPeriodChecks(MinPeriodCheckVisitor *visitor) +CheckMinPeriods::checkAll(bool violators, + const SceneSeq &scenes) { Graph *graph = sta_->graph(); VertexIterator vertex_iter(graph); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); - if (isClkEnd(vertex, graph)) - visitMinPeriodChecks(vertex, visitor); + checkVertex(vertex, violators, scenes); } } void -CheckMinPeriods::visitMinPeriodChecks(Vertex *vertex, - MinPeriodCheckVisitor *visitor) -{ - Search *search = sta_->search(); - GraphDelayCalc *graph_dcalc = sta_->graphDelayCalc(); - const Corner *corner = visitor->corner(); - Pin *pin = vertex->pin(); - float min_period; - bool exists; - graph_dcalc->minPeriod(pin, corner, min_period, exists); - if (exists) { - const ClockSet clks = search->clocks(vertex); - ClockSet::ConstIterator clk_iter(clks); - while (clk_iter.hasNext()) { - Clock *clk = clk_iter.next(); - MinPeriodCheck check(pin, clk, corner); - visitor->visit(check, sta_); +CheckMinPeriods::checkVertex(Vertex *vertex, + bool violators, + const SceneSeq &scenes) +{ + MinPeriodCheck min_check = check(vertex, scenes); + if (!min_check.isNull()) { + if (violators) { + if (delayLess(min_check.slack(sta_), 0.0, sta_)) + checks_.push_back(min_check); } + else + heap_.insert(min_check); } } -//////////////////////////////////////////////////////////////// - -class MinPeriodSlackVisitor : public MinPeriodCheckVisitor -{ -public: - MinPeriodSlackVisitor(const Corner *corner); - void visit(MinPeriodCheck &check, - StaState *sta) override; - MinPeriodCheck *minSlackCheck(); - -private: - MinPeriodCheck *min_slack_check_; -}; - -MinPeriodSlackVisitor::MinPeriodSlackVisitor(const Corner *corner) : - MinPeriodCheckVisitor(corner), - min_slack_check_(nullptr) -{ -} - -void -MinPeriodSlackVisitor::visit(MinPeriodCheck &check, - StaState *sta) +MinPeriodCheck +CheckMinPeriods::check(Vertex *vertex, + const SceneSeq &scenes) { - MinPeriodSlackLess slack_less(sta); - if (min_slack_check_ == nullptr) - min_slack_check_ = check.copy(); - else if (slack_less(&check, min_slack_check_)) { - delete min_slack_check_; - min_slack_check_ = check.copy(); + Search *search = sta_->search(); + GraphDelayCalc *graph_dcalc = sta_->graphDelayCalc(); + MinPeriodCheck min_slack_check; + Pin *pin = vertex->pin(); + for (const Scene *scene : scenes) { + const Mode *mode = scene->mode(); + if (isClkEnd(vertex, mode)) { + float min_period; + bool exists; + graph_dcalc->minPeriod(pin, scene, min_period, exists); + if (exists) { + const ClockSet clks = search->clocks(vertex, mode); + for (Clock *clk : clks) { + MinPeriodCheck check(pin, clk, scene); + Slack slack = check.slack(sta_); + if (min_slack_check.isNull() + || delayLess(slack, min_slack_check.slack(sta_), sta_)) + min_slack_check = check; + } + } + } } -} - -MinPeriodCheck * -MinPeriodSlackVisitor::minSlackCheck() -{ - return min_slack_check_; -} - -MinPeriodCheck * -CheckMinPeriods::minSlackCheck(const Corner *corner) -{ - clear(); - MinPeriodSlackVisitor visitor(corner); - visitMinPeriodChecks(&visitor); - MinPeriodCheck *check = visitor.minSlackCheck(); - // Save check for cleanup. - checks_.push_back(check); - return check; + return min_slack_check; } //////////////////////////////////////////////////////////////// MinPeriodCheck::MinPeriodCheck(Pin *pin, - Clock *clk, - const Corner *corner) : + Clock *clk, + const Scene *scene) : pin_(pin), clk_(clk), - corner_(corner) + scene_(scene) { } -MinPeriodCheck * -MinPeriodCheck::copy() +MinPeriodCheck::MinPeriodCheck() : + pin_(nullptr), + clk_(nullptr), + scene_(nullptr) { - return new MinPeriodCheck(pin_, clk_, corner_); } float @@ -221,7 +179,7 @@ MinPeriodCheck::minPeriod(const StaState *sta) const GraphDelayCalc *graph_dcalc = sta->graphDelayCalc(); float min_period; bool exists; - graph_dcalc->minPeriod(pin_, corner_, min_period, exists); + graph_dcalc->minPeriod(pin_, scene_, min_period, exists); return min_period; } @@ -233,26 +191,26 @@ MinPeriodCheck::slack(const StaState *sta) const //////////////////////////////////////////////////////////////// -MinPeriodSlackLess::MinPeriodSlackLess(StaState *sta) : +MinPeriodSlackLess::MinPeriodSlackLess(const StaState *sta) : sta_(sta) { } bool -MinPeriodSlackLess::operator()(const MinPeriodCheck *check1, - const MinPeriodCheck *check2) const +MinPeriodSlackLess::operator()(const MinPeriodCheck &check1, + const MinPeriodCheck &check2) const { - Slack slack1 = check1->slack(sta_); - Slack slack2 = check2->slack(sta_); - const Pin *pin1 = check1->pin(); - const Pin *pin2 = check2->pin(); + Slack slack1 = check1.slack(sta_); + Slack slack2 = check2.slack(sta_); + const Pin *pin1 = check1.pin(); + const Pin *pin2 = check2.pin(); return delayLess(slack1, slack2, sta_) // Break ties based on pin and clock names. - || (delayEqual(slack1, slack2) - && (sta_->network()->pinLess(pin1, pin2) - || (pin1 == pin2 - && ClockNameLess()(check1->clk(), - check2->clk())))); + || (delayEqual(slack1, slack2, sta_) + && (sta_->network()->pinLess(pin1, pin2) + || (pin1 == pin2 + && ClockNameLess()(check1.clk(), + check2.clk())))); } -} // namespace +} // namespace sta diff --git a/search/CheckMinPeriods.hh b/search/CheckMinPeriods.hh index bcba3218b..87596108a 100644 --- a/search/CheckMinPeriods.hh +++ b/search/CheckMinPeriods.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,43 +24,41 @@ #pragma once -#include "NetworkClass.hh" -#include "GraphClass.hh" +#include +#include + +#include "BoundedHeap.hh" #include "Delay.hh" +#include "GraphClass.hh" +#include "NetworkClass.hh" +#include "Scene.hh" #include "SdcClass.hh" #include "SearchClass.hh" #include "StaState.hh" namespace sta { -class MinPeriodCheckVisitor; - -class CheckMinPeriods +class MinPeriodSlackLess { public: - CheckMinPeriods(StaState *sta); - ~CheckMinPeriods(); - void clear(); - MinPeriodCheckSeq &violations(const Corner *corner); - // Min period check with the least slack. - MinPeriodCheck *minSlackCheck(const Corner *corner); + MinPeriodSlackLess(const StaState *sta); + bool operator()(const MinPeriodCheck &check1, + const MinPeriodCheck &check2) const; -protected: - void visitMinPeriodChecks(MinPeriodCheckVisitor *visitor); - void visitMinPeriodChecks(Vertex *vertex, - MinPeriodCheckVisitor *visitor); - - MinPeriodCheckSeq checks_; - StaState *sta_; +private: + const StaState *sta_; }; +using MinPeriodHeap = BoundedHeap; + class MinPeriodCheck { public: + MinPeriodCheck(); MinPeriodCheck(Pin *pin, - Clock *clk, - const Corner *corner); - MinPeriodCheck *copy(); + Clock *clk, + const Scene *scene); + bool isNull() { return pin_ == nullptr; } Pin *pin() const { return pin_; } Clock *clk() const { return clk_; } float period() const; @@ -70,18 +68,36 @@ public: private: Pin *pin_; Clock *clk_; - const Corner *corner_; + const Scene *scene_; }; -class MinPeriodSlackLess +using MinPeriodCheckSeq = std::vector; + +class CheckMinPeriods { public: - MinPeriodSlackLess(StaState *sta); - bool operator()(const MinPeriodCheck *check1, - const MinPeriodCheck *check2) const; + CheckMinPeriods(StaState *sta); + void clear(); + MinPeriodCheckSeq &check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes); -private: - const StaState *sta_; +protected: + void checkNet(const Net *net, + bool violators, + const SceneSeq &scenes); + void checkAll(bool violators, + const SceneSeq &scenes); + void checkVertex(Vertex *vertex, + bool violators, + const SceneSeq &scenes); + MinPeriodCheck check(Vertex *vertex, + const SceneSeq &scenes); + + MinPeriodCheckSeq checks_; + MinPeriodHeap heap_; + StaState *sta_; }; -} // namespace +} // namespace sta diff --git a/search/CheckMinPulseWidths.cc b/search/CheckMinPulseWidths.cc index 395327ea4..bd3ae5f8d 100644 --- a/search/CheckMinPulseWidths.cc +++ b/search/CheckMinPulseWidths.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,261 +24,139 @@ #include "CheckMinPulseWidths.hh" +#include +#include + +#include "ClkInfo.hh" +#include "Clock.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" -#include "TimingRole.hh" +#include "Delay.hh" +#include "Graph.hh" +#include "GraphClass.hh" #include "Liberty.hh" #include "Network.hh" -#include "Graph.hh" -#include "Clock.hh" -#include "Sdc.hh" -#include "DcalcAnalysisPt.hh" -#include "GraphDelayCalc.hh" -#include "ClkInfo.hh" -#include "Tag.hh" +#include "NetworkClass.hh" #include "Path.hh" -#include "Corner.hh" -#include "PathAnalysisPt.hh" -#include "SearchPred.hh" #include "PathEnd.hh" +#include "Scene.hh" +#include "Sdc.hh" #include "Search.hh" +#include "SearchClass.hh" +#include "SearchPred.hh" +#include "Tag.hh" +#include "TimingArc.hh" #include "search/Crpr.hh" namespace sta { static void minPulseWidth(const Path *path, - const StaState *sta, - // Return values. - float &min_width, - bool &exists); - -// Abstract base class. -class MinPulseWidthCheckVisitor -{ -public: - MinPulseWidthCheckVisitor() {} - virtual ~MinPulseWidthCheckVisitor() {} - virtual void visit(MinPulseWidthCheck &check, - const StaState *sta) = 0; -}; + const StaState *sta, + // Return values. + float &min_width, + bool &exists); CheckMinPulseWidths::CheckMinPulseWidths(StaState *sta) : + heap_(0, MinPulseWidthSlackLess(sta)), sta_(sta) { } -CheckMinPulseWidths::~CheckMinPulseWidths() -{ - checks_.deleteContents(); -} - void CheckMinPulseWidths::clear() { - checks_.deleteContentsClear(); -} - -//////////////////////////////////////////////////////////////// - -class MinPulseWidthChecksVisitor : public MinPulseWidthCheckVisitor -{ -public: - explicit MinPulseWidthChecksVisitor(const Corner *corner, - MinPulseWidthCheckSeq &checks); - virtual void visit(MinPulseWidthCheck &check, - const StaState *sta); - -private: - const Corner *corner_; - MinPulseWidthCheckSeq &checks_; -}; - -MinPulseWidthChecksVisitor:: -MinPulseWidthChecksVisitor(const Corner *corner, - MinPulseWidthCheckSeq &checks) : - corner_(corner), - checks_(checks) -{ -} - -void -MinPulseWidthChecksVisitor::visit(MinPulseWidthCheck &check, - const StaState *sta) -{ - if (corner_ == nullptr - || check.corner(sta) == corner_) { - MinPulseWidthCheck *copy = new MinPulseWidthCheck(check.openPath()); - checks_.push_back(copy); - } + checks_.clear(); + heap_.clear(); } MinPulseWidthCheckSeq & -CheckMinPulseWidths::check(const Corner *corner) +CheckMinPulseWidths::check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes) { clear(); - MinPulseWidthChecksVisitor visitor(corner, checks_); - visitMinPulseWidthChecks(&visitor); - sort(checks_, MinPulseWidthSlackLess(sta_)); - return checks_; -} - -MinPulseWidthCheckSeq & -CheckMinPulseWidths::check(PinSeq *pins, - const Corner *corner) -{ - clear(); - Graph *graph = sta_->graph(); - MinPulseWidthChecksVisitor visitor(corner, checks_); - PinSeq::Iterator pin_iter(pins); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - Vertex *vertex = graph->pinLoadVertex(pin); - visitMinPulseWidthChecks(vertex, &visitor); - } - sort(checks_, MinPulseWidthSlackLess(sta_)); - return checks_; -} - -//////////////////////////////////////////////////////////////// + if (!violators) + heap_.setMaxSize(max_count); -class MinPulseWidthViolatorsVisitor : public MinPulseWidthCheckVisitor -{ -public: - explicit MinPulseWidthViolatorsVisitor(const Corner *corner, - MinPulseWidthCheckSeq &checks); - virtual void visit(MinPulseWidthCheck &check, - const StaState *sta); - -private: - const Corner *corner_; - MinPulseWidthCheckSeq &checks_; -}; - -MinPulseWidthViolatorsVisitor:: -MinPulseWidthViolatorsVisitor(const Corner *corner, - MinPulseWidthCheckSeq &checks) : - corner_(corner), - checks_(checks) -{ -} - -void -MinPulseWidthViolatorsVisitor::visit(MinPulseWidthCheck &check, - const StaState *sta) -{ - if (delayLess(check.slack(sta), 0.0, sta) - && (corner_ == nullptr - || check.corner(sta) == corner_)) { - MinPulseWidthCheck *copy = new MinPulseWidthCheck(check.openPath()); - checks_.push_back(copy); - } -} + if (net) + checkNet(net, violators, scenes); + else + checkAll(violators, scenes); -MinPulseWidthCheckSeq & -CheckMinPulseWidths::violations(const Corner *corner) -{ - clear(); - MinPulseWidthViolatorsVisitor visitor(corner, checks_); - visitMinPulseWidthChecks(&visitor); - sort(checks_, MinPulseWidthSlackLess(sta_)); + if (violators) + sort(checks_, MinPulseWidthSlackLess(sta_)); + else + checks_ = heap_.extract(); return checks_; } -//////////////////////////////////////////////////////////////// - -class MinPulseWidthSlackVisitor : public MinPulseWidthCheckVisitor -{ -public: - MinPulseWidthSlackVisitor(const Corner *corner); - virtual void visit(MinPulseWidthCheck &check, - const StaState *sta); - MinPulseWidthCheck *minSlackCheck(); - -private: - const Corner *corner_; - MinPulseWidthCheck *min_slack_check_; -}; - -MinPulseWidthSlackVisitor::MinPulseWidthSlackVisitor(const Corner *corner) : - corner_(corner), - min_slack_check_(nullptr) -{ -} - void -MinPulseWidthSlackVisitor::visit(MinPulseWidthCheck &check, - const StaState *sta) +CheckMinPulseWidths::checkNet(const Net *net, + bool violators, + const SceneSeq &scenes) { - MinPulseWidthSlackLess slack_less(sta); - if (corner_ == nullptr - || check.corner(sta) == corner_) { - if (min_slack_check_ == nullptr) - min_slack_check_ = check.copy(); - else if (slack_less(&check, min_slack_check_)) { - delete min_slack_check_; - min_slack_check_ = check.copy(); - } + Graph *graph = sta_->graph(); + NetPinIterator *pin_iter = sta_->network()->pinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + Vertex *vertex = graph->pinLoadVertex(pin); + checkVertex(vertex, violators, scenes); } -} - -MinPulseWidthCheck * -MinPulseWidthSlackVisitor::minSlackCheck() -{ - return min_slack_check_; -} - -MinPulseWidthCheck * -CheckMinPulseWidths::minSlackCheck(const Corner *corner) -{ - clear(); - MinPulseWidthSlackVisitor visitor(corner); - visitMinPulseWidthChecks(&visitor); - MinPulseWidthCheck *check = visitor.minSlackCheck(); - // Save check for cleanup. - checks_.push_back(check); - return check; + delete pin_iter; } void -CheckMinPulseWidths:: -visitMinPulseWidthChecks(MinPulseWidthCheckVisitor *visitor) +CheckMinPulseWidths::checkAll(bool violators, + const SceneSeq &scenes) { Graph *graph = sta_->graph(); - Debug *debug = sta_->debug(); VertexIterator vertex_iter(graph); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); - if (isClkEnd(vertex, graph)) { - debugPrint(debug, "mpw", 1, "check mpw %s", - vertex->to_string(sta_).c_str()); - visitMinPulseWidthChecks(vertex, visitor); - } + checkVertex(vertex, violators, scenes); } } void -CheckMinPulseWidths:: -visitMinPulseWidthChecks(Vertex *vertex, - MinPulseWidthCheckVisitor *visitor) +CheckMinPulseWidths::checkVertex(Vertex *vertex, + bool violators, + const SceneSeq &scenes) { Search *search = sta_->search(); + Debug *debug = sta_->debug(); const MinMax *min_max = MinMax::max(); + SceneSet scene_set = Scene::sceneSet(scenes); VertexPathIterator path_iter(vertex, search); while (path_iter.hasNext()) { Path *path = path_iter.next(); - if (path->isClock(search) - && !path->tag(sta_)->clkInfo()->isGenClkSrcPath()) { - if (path->minMax(sta_) == min_max) { - float min_width; - bool exists; - minPulseWidth(path, sta_, min_width, exists); - if (exists) { - MinPulseWidthCheck check(path); - Path *close_path = check.closePath(sta_); - // Don't bother visiting if nobody is home. - if (close_path) - visitor->visit(check, sta_); - } + Vertex *path_vertex = path->vertex(sta_); + const Mode *mode = path->mode(sta_); + if (isClkEnd(path_vertex, mode) + && path->isClock(search) + && !path->tag(sta_)->clkInfo()->isGenClkSrcPath() + && scene_set.contains(path->scene(sta_)) + && path->minMax(sta_) == min_max) { + float min_width; + bool exists; + minPulseWidth(path, sta_, min_width, exists); + if (exists) { + MinPulseWidthCheck check(path); + Path *close_path = check.closePath(sta_); + // Don't bother visiting if nobody is home. + if (close_path) { + debugPrint(debug, "mpw", 2, "{} {} {}", + path_vertex->to_string(sta_), + path->transition(sta_) == RiseFall::rise() ? "(high)" : "(low)", + delayAsString(check.slack(sta_), sta_)); + if (violators) { + if (delayLess(check.slack(sta_), 0.0, sta_)) + checks_.push_back(check); + } + else + heap_.insert(check); + } } } } @@ -296,10 +174,13 @@ MinPulseWidthCheck::MinPulseWidthCheck(Path *open_path) : { } -MinPulseWidthCheck * -MinPulseWidthCheck::copy() +std::string +MinPulseWidthCheck::to_string(const StaState *sta) { - return new MinPulseWidthCheck(open_path_); + std::string result(sta->network()->pathName(pin(sta))); + result += " "; + result += (openTransition(sta) == RiseFall::rise()) ? "(high)" : "(low)"; + return result; } Pin * @@ -317,42 +198,44 @@ MinPulseWidthCheck::openTransition(const StaState *sta) const Path * MinPulseWidthCheck::closePath(const StaState *sta) const { - PathAnalysisPt *open_ap = open_path_->pathAnalysisPt(sta); - PathAnalysisPt *close_ap = open_ap->tgtClkAnalysisPt(); + Scene *scene = open_path_->scene(sta); + const MinMax *close_min_max = open_path_->tgtClkMinMax(sta); const RiseFall *open_rf = open_path_->transition(sta); const RiseFall *close_rf = open_rf->opposite(); Tag *open_tag = open_path_->tag(sta); const ClkInfo *open_clk_info = open_tag->clkInfo(); - const ClkInfo close_clk_info(open_clk_info->clkEdge()->opposite(), - open_clk_info->clkSrc(), - open_clk_info->isPropagated(), - open_clk_info->genClkSrc(), - open_clk_info->isGenClkSrcPath(), - open_clk_info->pulseClkSense(), - delay_zero, 0.0, nullptr, - open_clk_info->pathAPIndex(), - open_clk_info->crprClkPath(sta), - sta); - Tag close_tag(0, - close_rf->index(), - close_ap->index(), - &close_clk_info, - open_tag->isClock(), - open_tag->inputDelay(), - open_tag->isSegmentStart(), - open_tag->states(), - false, sta); - debugPrint(sta->debug(), "mpw", 3, " open %s", - open_tag->to_string(sta).c_str()); - debugPrint(sta->debug(), "mpw", 3, " close %s", - close_tag.to_string(sta).c_str()); - VertexPathIterator close_iter(open_path_->vertex(sta), close_rf, - close_ap, sta); + const ClkInfo close_clk_info(scene, + open_clk_info->clkEdge()->opposite(), + open_clk_info->clkSrc(), + open_clk_info->isPropagated(), + open_clk_info->genClkSrc(), + open_clk_info->isGenClkSrcPath(), + open_clk_info->pulseClkSense(), + delay_zero, 0.0, nullptr, + open_clk_info->minMax(), + open_clk_info->crprClkPath(sta), + sta); + Tag close_tag(scene, + 0, + close_rf, + close_min_max, + &close_clk_info, + open_tag->isClock(), + open_tag->inputDelay(), + open_tag->isSegmentStart(), + open_tag->states(), + false); + debugPrint(sta->debug(), "mpw", 3, " open {}", + open_tag->to_string(sta)); + debugPrint(sta->debug(), "mpw", 3, " close {}", + close_tag.to_string(sta)); + VertexPathIterator close_iter(open_path_->vertex(sta), scene, close_min_max, + close_rf, sta); while (close_iter.hasNext()) { Path *close_path = close_iter.next(); if (Tag::matchNoPathAp(close_path->tag(sta), &close_tag)) { - debugPrint(sta->debug(), "mpw", 3, " match %s", - close_path->tag(sta)->to_string(sta).c_str()); + debugPrint(sta->debug(), "mpw", 3, " match {}", + close_path->tag(sta)->to_string(sta)); return close_path; } } @@ -375,13 +258,13 @@ MinPulseWidthCheck::closeArrival(const StaState *sta) const Arrival MinPulseWidthCheck::openDelay(const StaState *sta) const { - return openArrival(sta) - openClkEdge(sta)->time(); + return delayDiff(openArrival(sta), openClkEdge(sta)->time(), sta); } Arrival MinPulseWidthCheck::closeDelay(const StaState *sta) const { - return closeArrival(sta) - closeClkEdge(sta)->time(); + return delayDiff(closeArrival(sta), closeClkEdge(sta)->time(), sta); } const ClockEdge * @@ -412,9 +295,11 @@ MinPulseWidthCheck::closeOffset(const StaState *sta) const Arrival MinPulseWidthCheck::width(const StaState *sta) const { - return closeArrival(sta) + closeOffset(sta) - - open_path_->arrival() - + checkCrpr(sta); + Arrival close_with_offset = delaySum(closeArrival(sta), + closeOffset(sta), + sta); + Arrival minus_open = delayDiff(close_with_offset, open_path_->arrival(), sta); + return delaySum(minus_open, checkCrpr(sta), sta); } float @@ -434,27 +319,27 @@ MinPulseWidthCheck::minWidth(const StaState *sta) const // min_pulse_width timing group static void minPulseWidth(const Path *path, - const StaState *sta, - // Return values. - float &min_width, - bool &exists) + const StaState *sta, + // Return values. + float &min_width, + bool &exists) { Pin *pin = path->pin(sta); const Clock *clk = path->clock(sta); const RiseFall *rf = path->transition(sta); - Sdc *sdc = sta->sdc(); + const Sdc *sdc = path->sdc(sta); // set_min_pulse_width command. sdc->minPulseWidth(pin, clk, rf, min_width, exists); if (!exists) { - const PathAnalysisPt *path_ap = path->pathAnalysisPt(sta); - const DcalcAnalysisPt *dcalc_ap = path_ap->dcalcAnalysisPt(); + const MinMax *min_max = path->minMax(sta); + DcalcAPIndex dcalc_ap = path->dcalcAnalysisPtIndex(sta); Vertex *vertex = path->vertex(sta); Graph *graph = sta->graph(); Edge *edge; TimingArc *arc; graph->minPulseWidthArc(vertex, rf, edge, arc); if (edge) { - min_width = delayAsFloat(graph->arcDelay(edge, arc, dcalc_ap->index())); + min_width = delayAsFloat(graph->arcDelay(edge, arc, dcalc_ap), min_max, sta); exists = true; } } @@ -474,13 +359,13 @@ MinPulseWidthCheck::checkCrpr(const StaState *sta) const Slack MinPulseWidthCheck::slack(const StaState *sta) const { - return width(sta) - minWidth(sta); + return delayDiff(width(sta), minWidth(sta), sta); } -Corner * -MinPulseWidthCheck::corner(const StaState *sta) const +Scene * +MinPulseWidthCheck::scene(const StaState *sta) const { - return open_path_->pathAnalysisPt(sta)->corner(); + return open_path_->scene(sta); } //////////////////////////////////////////////////////////////// @@ -491,20 +376,20 @@ MinPulseWidthSlackLess::MinPulseWidthSlackLess(const StaState *sta) : } bool -MinPulseWidthSlackLess::operator()(const MinPulseWidthCheck *check1, - const MinPulseWidthCheck *check2) const +MinPulseWidthSlackLess::operator()(const MinPulseWidthCheck &check1, + const MinPulseWidthCheck &check2) const { - Slack slack1 = check1->slack(sta_); - Slack slack2 = check2->slack(sta_); - const Pin *pin1 = check1->pin(sta_); - const Pin *pin2 = check2->pin(sta_); + Slack slack1 = check1.slack(sta_); + Slack slack2 = check2.slack(sta_); + const Pin *pin1 = check1.pin(sta_); + const Pin *pin2 = check2.pin(sta_); return delayLess(slack1, slack2, sta_) - || (delayEqual(slack1, slack2) - // Break ties for the sake of regression stability. - && (sta_->network()->pinLess(pin1, pin2) - || (pin1 == pin2 - && check1->openPath()->rfIndex(sta_) - < check2->openPath()->rfIndex(sta_)))); + || (delayEqual(slack1, slack2, sta_) + // Break ties for the sake of regression stability. + && (sta_->network()->pinLess(pin1, pin2) + || (pin1 == pin2 + && check1.openPath()->rfIndex(sta_) + < check2.openPath()->rfIndex(sta_)))); } -} // namespace +} // namespace sta diff --git a/search/CheckMinPulseWidths.hh b/search/CheckMinPulseWidths.hh index d250cc203..5cd503cf3 100644 --- a/search/CheckMinPulseWidths.hh +++ b/search/CheckMinPulseWidths.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,59 +24,49 @@ #pragma once +#include +#include +#include + +#include "BoundedHeap.hh" +#include "Delay.hh" +#include "GraphClass.hh" +#include "NetworkClass.hh" +#include "Path.hh" +#include "Scene.hh" #include "SdcClass.hh" #include "SearchClass.hh" #include "StaState.hh" -#include "Path.hh" namespace sta { class RiseFall; -class MinPulseWidthCheck; -class MinPulseWidthCheckVisitor; -class CheckMinPulseWidths +class MinPulseWidthSlackLess { public: - CheckMinPulseWidths(StaState *sta); - ~CheckMinPulseWidths(); - void clear(); - // Min pulse width checks for pins. - // corner=nullptr checks all corners. - MinPulseWidthCheckSeq &check(PinSeq *pins, - const Corner *corner); - // All min pulse width checks. - // corner=nullptr checks all corners. - MinPulseWidthCheckSeq &check(const Corner *corner); - // All violating min pulse width checks. - // corner=nullptr checks all corners. - MinPulseWidthCheckSeq &violations(const Corner *corner); - // Min pulse width check with the least slack. - // corner=nullptr checks all corners. - MinPulseWidthCheck *minSlackCheck(const Corner *corner); - -protected: - void visitMinPulseWidthChecks(MinPulseWidthCheckVisitor *visitor); - void visitMinPulseWidthChecks(Vertex *vertex, - MinPulseWidthCheckVisitor *visitor); + MinPulseWidthSlackLess(const StaState *sta); + bool operator()(const MinPulseWidthCheck &check1, + const MinPulseWidthCheck &check2) const; - MinPulseWidthCheckSeq checks_; - StaState *sta_; +private: + const StaState *sta_; }; class MinPulseWidthCheck { public: - explicit MinPulseWidthCheck(); + MinPulseWidthCheck(); MinPulseWidthCheck(Path *open_path); - MinPulseWidthCheck *copy(); + std::string to_string(const StaState *sta); + bool isNull() const { return open_path_ == nullptr; } Pin *pin(const StaState *sta) const; const RiseFall *openTransition(const StaState *sta) const; Arrival width(const StaState *sta) const; float minWidth(const StaState *sta) const; Slack slack(const StaState *sta) const; Path *openPath() { return open_path_; } - Corner *corner(const StaState *sta) const; + Scene *scene(const StaState *sta) const; const Path *openPath() const { return open_path_; } Arrival openArrival(const StaState *sta) const; Path *closePath(const StaState *sta) const; @@ -93,15 +83,32 @@ protected: Path *open_path_; }; -class MinPulseWidthSlackLess +using MinPulseWidthCheckSeq = std::vector; +using MinPulseWidthCheckHeap = BoundedHeap; + +class CheckMinPulseWidths { public: - MinPulseWidthSlackLess(const StaState *sta); - bool operator()(const MinPulseWidthCheck *check1, - const MinPulseWidthCheck *check2) const; + CheckMinPulseWidths(StaState *sta); + void clear(); + MinPulseWidthCheckSeq &check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes); -private: - const StaState *sta_; +protected: + void checkNet(const Net *net, + bool violators, + const SceneSeq &scenes); + void checkAll(bool violators, + const SceneSeq &scenes); + void checkVertex(Vertex *vertex, + bool violators, + const SceneSeq &scenes); + + MinPulseWidthCheckSeq checks_; + MinPulseWidthCheckHeap heap_; + StaState *sta_; }; -} // namespace +} // namespace sta diff --git a/search/CheckSlewLimits.cc b/search/CheckSlewLimits.cc deleted file mode 100644 index 2cc392ca2..000000000 --- a/search/CheckSlewLimits.cc +++ /dev/null @@ -1,422 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#include "CheckSlewLimits.hh" - -#include "Fuzzy.hh" -#include "Liberty.hh" -#include "Network.hh" -#include "Sdc.hh" -#include "InputDrive.hh" -#include "Graph.hh" -#include "DcalcAnalysisPt.hh" -#include "GraphDelayCalc.hh" -#include "StaState.hh" -#include "Corner.hh" -#include "Path.hh" -#include "PortDirection.hh" -#include "Search.hh" -#include "ClkNetwork.hh" - -namespace sta { - -class PinSlewLimitSlackLess -{ -public: - PinSlewLimitSlackLess(const Corner *corner, - const MinMax *min_max, - CheckSlewLimits *check_slew_limit, - const StaState *sta); - bool operator()(const Pin *pin1, - const Pin *pin2) const; - -private: - const Corner *corner_; - const MinMax *min_max_; - CheckSlewLimits *check_slew_limit_; - const StaState *sta_; - -}; - -PinSlewLimitSlackLess::PinSlewLimitSlackLess(const Corner *corner, - const MinMax *min_max, - CheckSlewLimits *check_slew_limit, - const StaState *sta) : - corner_(corner), - min_max_(min_max), - check_slew_limit_(check_slew_limit), - sta_(sta) -{ -} - -bool -PinSlewLimitSlackLess::operator()(const Pin *pin1, - const Pin *pin2) const -{ - const Corner *corner1, *corner2; - const RiseFall *rf1, *rf2; - Slew slew1, slew2; - float limit1, limit2, slack1, slack2; - check_slew_limit_->checkSlew(pin1, corner_, min_max_, true, - corner1, rf1, slew1, limit1, slack1); - check_slew_limit_->checkSlew(pin2, corner_, min_max_, true, - corner2, rf2, slew2, limit2, slack2); - return fuzzyLess(slack1, slack2) - || (fuzzyEqual(slack1, slack2) - // Break ties for the sake of regression stability. - && sta_->network()->pinLess(pin1, pin2)); -} - -//////////////////////////////////////////////////////////////// - -CheckSlewLimits::CheckSlewLimits(const StaState *sta) : - sta_(sta) -{ -} - -void -CheckSlewLimits::checkSlewLimits(const Pin *pin, - bool violators, - const Corner *corner, - const MinMax *min_max, - PinSeq &slew_pins, - float &min_slack) -{ - const Corner *corner1; - const RiseFall *rf; - Slew slew; - float limit, slack; - checkSlew(pin, corner, min_max, true, corner1, rf, slew, limit, slack); - if (!fuzzyInf(slack)) { - if (violators) { - if (slack < 0.0) - slew_pins.push_back(pin); - } - else { - if (slew_pins.empty() - || slack < min_slack) { - slew_pins.push_back(pin); - min_slack = slack; - } - } - } -} - -void -CheckSlewLimits::checkSlew(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - bool check_clks, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - Slew &slew1, - float &limit1, - float &slack1) const -{ - corner1 = nullptr; - rf1 = nullptr; - slew1 = 0.0; - limit1 = 0.0; - slack1 = MinMax::min()->initValue(); - - Vertex *vertex, *bidirect_drvr_vertex; - sta_->graph()->pinVertices(pin, vertex, bidirect_drvr_vertex); - if (vertex) - checkSlew1(pin, vertex, corner, min_max, check_clks, - corner1, rf1, slew1, limit1, slack1); - if (bidirect_drvr_vertex) - checkSlew1(pin, bidirect_drvr_vertex, corner, min_max, check_clks, - corner1, rf1, slew1, limit1, slack1); -} - -void -CheckSlewLimits::checkSlew1(const Pin *pin, - const Vertex *vertex, - const Corner *corner, - const MinMax *min_max, - bool check_clks, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - Slew &slew1, - float &limit1, - float &slack1) const -{ - if (!vertex->isDisabledConstraint() - && !vertex->isConstant() - && !sta_->clkNetwork()->isIdealClock(pin)) { - ClockSet clks; - if (check_clks) - clks = clockDomains(vertex); - if (corner) - checkSlew2(pin, vertex, corner, min_max, clks, - corner1, rf1, slew1, limit1, slack1); - else { - for (auto corner : *sta_->corners()) { - checkSlew2(pin, vertex, corner, min_max, clks, - corner1, rf1, slew1, limit1, slack1); - } - } - } -} - -void -CheckSlewLimits::checkSlew2(const Pin *pin, - const Vertex *vertex, - const Corner *corner, - const MinMax *min_max, - const ClockSet &clks, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - Slew &slew1, - float &limit1, - float &slack1) const -{ - for (const RiseFall *rf : RiseFall::range()) { - float limit; - bool exists; - findLimit(pin, corner, rf, min_max, clks, - limit, exists); - if (exists) { - checkSlew3(vertex, corner, rf, min_max, limit, - corner1, rf1, slew1, slack1, limit1); - } - } -} - -void -CheckSlewLimits::checkSlew3(const Vertex *vertex, - const Corner *corner, - const RiseFall *rf, - const MinMax *min_max, - float limit, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - Slew &slew1, - float &slack1, - float &limit1) const -{ - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - Slew slew = sta_->graph()->slew(vertex, rf, dcalc_ap->index()); - float slew2 = delayAsFloat(slew); - float slack = (min_max == MinMax::max()) - ? limit - slew2 : slew2 - limit; - if (corner1 == nullptr - || (slack < slack1 - // Break ties for the sake of regression stability. - || (fuzzyEqual(slack, slack1) - && rf->index() < rf1->index()))) { - corner1 = corner; - rf1 = rf; - slew1 = slew; - slack1 = slack; - limit1 = limit; - } -} - -// Return the tightest limit. -void -CheckSlewLimits::findLimit(const Pin *pin, - const Corner *corner, - const RiseFall *rf, - const MinMax *min_max, - const ClockSet &clks, - // Return values. - float &limit, - bool &exists) const -{ - const Network *network = sta_->network(); - Sdc *sdc = sta_->sdc(); - LibertyPort *port = network->libertyPort(pin); - findLimit(port, corner, min_max, - limit, exists); - - float limit1; - bool exists1; - if (!clks.empty()) { - // Look for clock slew limits. - bool is_clk = sta_->clkNetwork()->isIdealClock(pin); - for (Clock *clk : clks) { - PathClkOrData clk_data = is_clk ? PathClkOrData::clk : PathClkOrData::data; - sdc->slewLimit(clk, rf, clk_data, min_max, - limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - } - } - - if (network->isTopLevelPort(pin)) { - Port *port = network->port(pin); - sdc->slewLimit(port, min_max, limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - InputDrive *drive = sdc->findInputDrive(port); - if (drive) { - for (auto rf : RiseFall::range()) { - const LibertyCell *cell; - const LibertyPort *from_port; - float *from_slews; - const LibertyPort *to_port; - drive->driveCell(rf, min_max, cell, from_port, from_slews, to_port); - if (to_port) { - const LibertyPort *corner_port = to_port->cornerPort(corner, min_max); - corner_port->slewLimit(min_max, limit1, exists1); - if (!exists1 - && corner_port->direction()->isAnyOutput() - && min_max == MinMax::max()) - corner_port->libertyLibrary()->defaultMaxSlew(limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - } - } - } - } -} - -void -CheckSlewLimits::findLimit(const LibertyPort *port, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const -{ - limit = INF; - exists = false; - - const Network *network = sta_->network(); - Sdc *sdc = sta_->sdc(); - float limit1; - bool exists1; - - // Default to top ("design") limit. - Cell *top_cell = network->cell(network->topInstance()); - sdc->slewLimit(top_cell, min_max, - limit1, exists1); - if (exists1) { - limit = limit1; - exists = true; - } - - if (port) { - const LibertyPort *corner_port = port->cornerPort(corner, min_max); - corner_port->slewLimit(min_max, limit1, exists1); - if (!exists1 - // default_max_transition only applies to outputs. - && corner_port->direction()->isAnyOutput() - && min_max == MinMax::max()) - corner_port->libertyLibrary()->defaultMaxSlew(limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - } -} - -ClockSet -CheckSlewLimits::clockDomains(const Vertex *vertex) const -{ - ClockSet clks; - VertexPathIterator path_iter(const_cast(vertex), sta_); - while (path_iter.hasNext()) { - Path *path = path_iter.next(); - const Clock *clk = path->clock(sta_); - if (clk) - clks.insert(const_cast(clk)); - } - return clks; -} - -//////////////////////////////////////////////////////////////// - -PinSeq -CheckSlewLimits::checkSlewLimits(const Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max) -{ - const Network *network = sta_->network(); - PinSeq slew_pins; - float min_slack = MinMax::min()->initValue(); - if (net) { - NetPinIterator *pin_iter = network->pinIterator(net); - while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - checkSlewLimits(pin, violators, corner, min_max, slew_pins, min_slack); - } - delete pin_iter; - } - else { - LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); - while (inst_iter->hasNext()) { - const Instance *inst = inst_iter->next(); - checkSlewLimits(inst, violators,corner, min_max, slew_pins, min_slack); - } - delete inst_iter; - // Check top level ports. - checkSlewLimits(network->topInstance(), violators, corner, min_max, - slew_pins, min_slack); - } - sort(slew_pins, PinSlewLimitSlackLess(corner, min_max, this, sta_)); - // Keep the min slack pin unless all violators or net pins. - if (!slew_pins.empty() && !violators && net == nullptr) - slew_pins.resize(1); - return slew_pins; -} - -void -CheckSlewLimits::checkSlewLimits(const Instance *inst, - bool violators, - const Corner *corner, - const MinMax *min_max, - PinSeq &slew_pins, - float &min_slack) -{ - const Network *network = sta_->network(); - InstancePinIterator *pin_iter = network->pinIterator(inst); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - checkSlewLimits(pin, violators, corner, min_max, slew_pins, min_slack); - } - delete pin_iter; -} - -} // namespace diff --git a/search/CheckSlewLimits.hh b/search/CheckSlewLimits.hh deleted file mode 100644 index ba9071031..000000000 --- a/search/CheckSlewLimits.hh +++ /dev/null @@ -1,129 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include "MinMax.hh" -#include "Transition.hh" -#include "NetworkClass.hh" -#include "GraphClass.hh" -#include "Delay.hh" -#include "SdcClass.hh" - -namespace sta { - -class StaState; -class DcalcAnalysisPt; -class Corner; - -class CheckSlewLimits -{ -public: - CheckSlewLimits(const StaState *sta); - // Return pins with the min/max slew limit slack. - // net=null check all nets - // corner=nullptr checks all corners. - PinSeq checkSlewLimits(const Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max); - // corner=nullptr checks all corners. - void checkSlew(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - bool check_clks, - // Return values. - // Corner is nullptr for no slew limit. - const Corner *&corner1, - const RiseFall *&rf, - Slew &slew, - float &limit, - float &slack) const; - void findLimit(const LibertyPort *port, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const; - -protected: - void checkSlew1(const Pin *pin, - const Vertex *vertex, - const Corner *corner, - const MinMax *min_max, - bool check_clks, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - Slew &slew1, - float &limit1, - float &slack1) const; - void checkSlew2(const Pin *pin, - const Vertex *vertex, - const Corner *corner, - const MinMax *min_max, - const ClockSet &clks, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - Slew &slew1, - float &limit1, - float &slack1) const; - void checkSlew3(const Vertex *vertex, - const Corner *corner1, - const RiseFall *rf1, - const MinMax *min_max, - float limit1, - // Return values. - const Corner *&corner, - const RiseFall *&rf, - Slew &slew, - float &slack, - float &limit) const; - void findLimit(const Pin *pin, - const Corner *corner, - const RiseFall *rf, - const MinMax *min_max, - const ClockSet &clks, - // Return values. - float &limit, - bool &limit_exists) const; - void checkSlewLimits(const Instance *inst, - bool violators, - const Corner *corner, - const MinMax *min_max, - PinSeq &slew_pins, - float &min_slack); - void checkSlewLimits(const Pin *pin, - bool violators, - const Corner *corner, - const MinMax *min_max, - PinSeq &slew_pins, - float &min_slack); - ClockSet clockDomains(const Vertex *vertex) const; - - const StaState *sta_; -}; - -} // namespace diff --git a/search/CheckSlews.cc b/search/CheckSlews.cc new file mode 100644 index 000000000..748cc336f --- /dev/null +++ b/search/CheckSlews.cc @@ -0,0 +1,427 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#include "CheckSlews.hh" + +#include + +#include "ClkNetwork.hh" +#include "Clock.hh" +#include "ContainerHelpers.hh" +#include "Delay.hh" +#include "Fuzzy.hh" +#include "Graph.hh" +#include "GraphClass.hh" +#include "InputDrive.hh" +#include "Liberty.hh" +#include "MinMax.hh" +#include "Mode.hh" +#include "Network.hh" +#include "NetworkClass.hh" +#include "Path.hh" +#include "PortDirection.hh" +#include "Scene.hh" +#include "Sdc.hh" +#include "SdcClass.hh" +#include "StaState.hh" +#include "Transition.hh" + +namespace sta { + +CheckSlews::CheckSlews(const StaState *sta) : + heap_(0, SlewCheckSlackLess(sta)), + sta_(sta) +{ +} + +void +CheckSlews::clear() +{ + checks_.clear(); + heap_.clear(); +} + +SlewCheckSeq & +CheckSlews::check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max) +{ + clear(); + if (!violators) + heap_.setMaxSize(max_count); + + if (net) + checkNet(net, violators, scenes, min_max); + else + checkAll(violators, scenes, min_max); + + if (violators) + sort(checks_, SlewCheckSlackLess(sta_)); + else + checks_ = heap_.extract(); + return checks_; +} + +void +CheckSlews::checkNet(const Net *net, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + NetPinIterator *pin_iter = network->pinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + checkPin(pin, violators, scenes, min_max); + } + delete pin_iter; +} + +void +CheckSlews::checkAll(bool violators, + const SceneSeq &scenes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); + while (inst_iter->hasNext()) { + const Instance *inst = inst_iter->next(); + checkInst(inst, violators, scenes, min_max); + } + delete inst_iter; + // Check top level ports. + checkInst(network->topInstance(), violators, scenes, min_max); +} + +void +CheckSlews::checkInst(const Instance *inst, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + checkPin(pin, violators, scenes, min_max); + } + delete pin_iter; +} + +void +CheckSlews::checkPin(const Pin *pin, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max) +{ + const Scene *scene; + const RiseFall *rf; + Slew slew; + float limit, slack; + check(pin, scenes, min_max, true, slew, limit, slack, rf, scene); + if (scene) { + if (violators) { + if (slack < 0.0) + checks_.emplace_back(pin, rf, slew, limit, slack, scene); + } + else + heap_.insert(SlewCheck(pin, rf, slew, limit, slack, scene)); + } +} + +void +CheckSlews::check(const Pin *pin, + const SceneSeq &scenes, + const MinMax *min_max, + bool check_clks, + // Return values. + Slew &slew, + float &limit, + float &slack, + const RiseFall *&rf, + const Scene *&scene) const +{ + scene = nullptr; + rf = nullptr; + slew = 0.0; + limit = 0.0; + slack = MinMax::min()->initValue(); + + for (const Scene *scene1 : scenes) { + Vertex *vertex, *bidirect_drvr_vertex; + sta_->graph()->pinVertices(pin, vertex, bidirect_drvr_vertex); + if (vertex) + check2(vertex, scene1, min_max, check_clks, + scene, rf, slew, limit, slack); + if (bidirect_drvr_vertex) + check2(bidirect_drvr_vertex, scene1, min_max, check_clks, + scene, rf, slew, limit, slack); + } +} + +void +CheckSlews::check2(const Vertex *vertex, + const Scene *scene, + const MinMax *min_max, + bool check_clks, + // Return values. + const Scene *&scene1, + const RiseFall *&rf1, + Slew &slew1, + float &limit1, + float &slack1) const +{ + const Mode *mode = scene->mode(); + const Sdc *sdc = mode->sdc(); + const ClkNetwork *clk_network = mode->clkNetwork(); + const Pin *pin = vertex->pin(); + if (!sdc->isDisabledConstraint(pin) + && !clk_network->isIdealClock(pin)) { + ConstClockSet clks; + if (check_clks) + clks = clockDomains(vertex, scene); + for (const RiseFall *rf : RiseFall::range()) { + float limit; + bool exists; + findLimit(pin, scene, rf, min_max, clks, + limit, exists); + if (exists) { + check3(vertex, scene, rf, min_max, limit, + scene1, rf1, slew1, slack1, limit1); + } + } + } +} + +void +CheckSlews::check3(const Vertex *vertex, + const Scene *scene, + const RiseFall *rf, + const MinMax *min_max, + float limit, + // Return values. + const Scene *&scene1, + const RiseFall *&rf1, + Slew &slew1, + float &slack1, + float &limit1) const +{ + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + Slew slew = sta_->graph()->slew(vertex, rf, ap_index); + float slew2 = delayAsFloat(slew); + float slack = (min_max == MinMax::max()) + ? limit - slew2 : slew2 - limit; + if (scene1 == nullptr + || (slack < slack1 + // Break ties for the sake of regression stability. + || (fuzzyEqual(slack, slack1) + && rf->index() < rf1->index()))) { + scene1 = scene; + rf1 = rf; + slew1 = slew; + slack1 = slack; + limit1 = limit; + } +} + +// Return the tightest limit. +void +CheckSlews::findLimit(const Pin *pin, + const Scene *scene, + const RiseFall *rf, + const MinMax *min_max, + const ConstClockSet &clks, + // Return values. + float &limit, + bool &exists) const +{ + const Network *network = sta_->network(); + const Sdc *sdc = scene->sdc(); + LibertyPort *port = network->libertyPort(pin); + findLimit(port, scene, min_max, + limit, exists); + + float limit1; + bool exists1; + if (!clks.empty()) { + // Look for clock slew limits. + const ClkNetwork *clk_network = scene->mode()->clkNetwork(); + bool is_clk = clk_network->isIdealClock(pin); + for (const Clock *clk : clks) { + PathClkOrData clk_data = is_clk ? PathClkOrData::clk : PathClkOrData::data; + sdc->slewLimit(clk, rf, clk_data, min_max, + limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + } + } + + if (network->isTopLevelPort(pin)) { + Port *port = network->port(pin); + sdc->slewLimit(port, min_max, limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + InputDrive *drive = sdc->findInputDrive(port); + if (drive) { + for (auto rf : RiseFall::range()) { + const LibertyCell *cell; + const LibertyPort *from_port; + const DriveCellSlews *from_slews; + const LibertyPort *to_port; + drive->driveCell(rf, min_max, cell, from_port, from_slews, to_port); + if (to_port) { + const LibertyPort *scene_port = to_port->scenePort(scene, min_max); + scene_port->slewLimit(min_max, limit1, exists1); + if (!exists1 + && scene_port->direction()->isAnyOutput() + && min_max == MinMax::max()) + scene_port->libertyLibrary()->defaultMaxSlew(limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + } + } + } + } +} + +void +CheckSlews::findLimit(const LibertyPort *port, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &limit, + bool &exists) const +{ + limit = INF; + exists = false; + + const Network *network = sta_->network(); + const Sdc *sdc = scene->sdc(); + float limit1; + bool exists1; + + // Default to top ("design") limit. + Cell *top_cell = network->cell(network->topInstance()); + sdc->slewLimit(top_cell, min_max, + limit1, exists1); + if (exists1) { + limit = limit1; + exists = true; + } + + if (port) { + const LibertyPort *scene_port = port->scenePort(scene, min_max); + scene_port->slewLimit(min_max, limit1, exists1); + if (!exists1 + // default_max_transition only applies to outputs. + && scene_port->direction()->isAnyOutput() + && min_max == MinMax::max()) + scene_port->libertyLibrary()->defaultMaxSlew(limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + } +} + +ConstClockSet +CheckSlews::clockDomains(const Vertex *vertex, + const Scene *scene) const +{ + ConstClockSet clks; + VertexPathIterator path_iter(const_cast(vertex), sta_); + while (path_iter.hasNext()) { + Path *path = path_iter.next(); + if (path->scene(sta_) == scene) { + const Clock *clk = path->clock(sta_); + if (clk) + clks.insert(clk); + } + } + return clks; +} + +//////////////////////////////////////////////////////////////// + +SlewCheck::SlewCheck() : + pin_(nullptr), + rf_(nullptr), + slew_(0.0), + limit_(0.0), + slack_(0.0), + scene_(nullptr) +{ +} + +SlewCheck::SlewCheck(const Pin *pin, + const RiseFall *rf, + Slew &slew, + float limit, + float slack, + const Scene *scene) : + pin_(pin), + rf_(rf), + slew_(slew), + limit_(limit), + slack_(slack), + scene_(scene) +{ +} + +//////////////////////////////////////////////////////////////// + +SlewCheckSlackLess::SlewCheckSlackLess(const StaState *sta) : + sta_(sta) +{ +} + +bool +SlewCheckSlackLess::operator()(const SlewCheck &check1, + const SlewCheck &check2) const +{ + float slack1 = check1.slack(); + float slack2 = check2.slack(); + return fuzzyLess(slack1, slack2) + || (fuzzyEqual(slack1, slack2) + // Break ties for the sake of regression stability. + && sta_->network()->pinLess(check1.pin(), check2.pin())); +} + +} // namespace sta diff --git a/search/CheckSlews.hh b/search/CheckSlews.hh new file mode 100644 index 000000000..ca43bd5e2 --- /dev/null +++ b/search/CheckSlews.hh @@ -0,0 +1,172 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include +#include + +#include "BoundedHeap.hh" +#include "Delay.hh" +#include "GraphClass.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "NetworkClass.hh" +#include "Scene.hh" +#include "SdcClass.hh" +#include "StaState.hh" +#include "Transition.hh" + +namespace sta { + +class Scene; +class Mode; +class SlewCheckSlackLess; + +class SlewCheck +{ +public: + SlewCheck(); + SlewCheck(const Pin *pin, + const RiseFall *rf, + Slew &slew, + float limit, + float slack, + const Scene *scene); + bool isNull() { return pin_ == nullptr; } + const Pin *pin() const { return pin_; } + Slew slew() const { return slew_; } + const RiseFall *edge() const { return rf_; } + float limit() const { return limit_; } + float slack() const { return slack_; } + const Scene *scene() const { return scene_; } + +private: + const Pin *pin_; + const RiseFall *rf_; + Slew slew_; + float limit_; + float slack_; + const Scene *scene_; +}; + +class SlewCheckSlackLess +{ +public: + SlewCheckSlackLess(const StaState *sta); + bool operator()(const SlewCheck &check1, + const SlewCheck &check2) const; + +private: + const StaState *sta_; + +}; + +using SlewCheckHeap = BoundedHeap; +using SlewCheckSeq = std::vector; + +class CheckSlews +{ +public: + CheckSlews(const StaState *sta); + void clear(); + // net=null check all nets + SlewCheckSeq &check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max); + void check(const Pin *pin, + const SceneSeq &scenes, + const MinMax *min_max, + bool check_clks, + // Return values. + // Scene is nullptr for no slew limit. + Slew &slew, + float &limit, + float &slack, + const RiseFall *&rf, + const Scene *&scene) const; + void findLimit(const LibertyPort *port, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &limit, + bool &exists) const; + +protected: + void checkNet(const Net *net, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max); + void checkAll(bool violators, + const SceneSeq &scenes, + const MinMax *min_max); + void checkInst(const Instance *inst, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max); + void checkPin(const Pin *pin, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max); + void check2(const Vertex *vertex, + const Scene *scene, + const MinMax *min_max, + bool check_clks, + // Return values. + const Scene *&scene1, + const RiseFall *&rf, + Slew &slew1, + float &limit1, + float &slack1) const; + void check3(const Vertex *vertex, + const Scene *scene, + const RiseFall *rf, + const MinMax *min_max, + float limit, + // Return values. + const Scene *&scene1, + const RiseFall *&rf1, + Slew &slew1, + float &slack1, + float &limit1) const; + void findLimit(const Pin *pin, + const Scene *scene, + const RiseFall *rf, + const MinMax *min_max, + const ConstClockSet &clks, + // Return values. + float &limit, + bool &limit_exists) const; + ConstClockSet clockDomains(const Vertex *vertex, + const Scene *scene) const; + + SlewCheckSeq checks_; + SlewCheckHeap heap_; + const StaState *sta_; +}; + +} // namespace sta + diff --git a/search/CheckTiming.cc b/search/CheckTiming.cc index 76e11698e..f64975e5a 100644 --- a/search/CheckTiming.cc +++ b/search/CheckTiming.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,27 +24,28 @@ #include "CheckTiming.hh" -#include "Error.hh" -#include "TimingRole.hh" +#include "ClkNetwork.hh" +#include "ExceptionPath.hh" +#include "Format.hh" +#include "Genclks.hh" +#include "Graph.hh" +#include "GraphClass.hh" +#include "Levelize.hh" +#include "Mode.hh" #include "Network.hh" +#include "NetworkClass.hh" #include "NetworkCmp.hh" -#include "PortDirection.hh" -#include "Graph.hh" +#include "Path.hh" #include "PortDelay.hh" -#include "ExceptionPath.hh" +#include "PortDirection.hh" #include "Sdc.hh" -#include "SearchPred.hh" -#include "Levelize.hh" -#include "Bfs.hh" -#include "Search.hh" -#include "Genclks.hh" -#include "Path.hh" +#include "SdcClass.hh" #include "Sim.hh" +#include "StaState.hh" +#include "TimingRole.hh" namespace sta { -using std::string; - CheckTiming::CheckTiming(StaState *sta) : StaState(sta) { @@ -58,10 +59,7 @@ CheckTiming::~CheckTiming() void CheckTiming::deleteErrors() { - CheckErrorSeq::Iterator error_iter(errors_); - while (error_iter.hasNext()) { - CheckError *error = error_iter.next(); - deleteContents(error); + for (CheckError *error : errors_) { delete error; } } @@ -74,15 +72,21 @@ CheckTiming::clear() } CheckErrorSeq & -CheckTiming::check(bool no_input_delay, - bool no_output_delay, - bool reg_multiple_clks, - bool reg_no_clks, - bool unconstrained_endpoints, - bool loops, - bool generated_clks) +CheckTiming::check(const Mode *mode, + bool no_input_delay, + bool no_output_delay, + bool reg_multiple_clks, + bool reg_no_clks, + bool unconstrained_endpoints, + bool loops, + bool generated_clks) { clear(); + mode_ = mode; + sdc_ = mode->sdc(); + sim_ = mode->sim(); + clk_network_ = mode->clkNetwork(); + if (no_input_delay) checkNoInputDelay(); if (no_output_delay) @@ -110,14 +114,13 @@ CheckTiming::checkNoInputDelay() if (!sdc_->isClock(pin)) { PortDirection *dir = network_->direction(pin); if (dir->isAnyInput() - && !sdc_->hasInputDelay(pin) - && !sim_->logicZeroOne(pin)) - no_arrival.insert(pin); + && !sdc_->hasInputDelay(pin) + && !sim_->isConstant(pin)) + no_arrival.insert(pin); } } delete pin_iter; - pushPinErrors("Warning: There %is %d input port%s missing set_input_delay.", - no_arrival); + pushPinErrors("Warning: There {} {} input port{} missing set_input_delay.",no_arrival); } void @@ -125,8 +128,8 @@ CheckTiming::checkNoOutputDelay() { PinSet no_departure(network_); checkNoOutputDelay(no_departure); - pushPinErrors("Warning: There %is %d output port%s missing set_output_delay.", - no_departure); + pushPinErrors("Warning: There {} {} output port{} missing set_output_delay.", + no_departure); } void @@ -138,8 +141,8 @@ CheckTiming::checkNoOutputDelay(PinSet &no_departure) const Pin *pin = pin_iter->next(); PortDirection *dir = network_->direction(pin); if (dir->isAnyOutput() - && !sdc_->hasOutputDelay(pin) - && !sim_->logicZeroOne(pin)) + && !sdc_->hasOutputDelay(pin) + && !sim_->isConstant(pin)) no_departure.insert(pin); } delete pin_iter; @@ -152,31 +155,42 @@ CheckTiming::hasClkedCheck(Vertex *vertex) while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); if (edge->role() == TimingRole::setup() - && search_->isClock(edge->from(graph_))) + && clk_network_->isClock(edge->from(graph_))) return true; } return false; } -// Search incrementally maintains register/latch clock pins, so use it. void CheckTiming::checkRegClks(bool reg_multiple_clks, - bool reg_no_clks) + bool reg_no_clks) { PinSet no_clk_pins(network_); PinSet multiple_clk_pins(network_); - for (Vertex *vertex : *graph_->regClkVertices()) { + for (Vertex *vertex : graph_->regClkVertices()) { const Pin *pin = vertex->pin(); - ClockSet clks = search_->clocks(vertex); - if (reg_no_clks && clks.empty()) + const ClockSet *clks = clk_network_->clocks(pin); + if (reg_no_clks && clks == nullptr) no_clk_pins.insert(pin); - if (reg_multiple_clks && clks.size() > 1) + if (reg_multiple_clks && clks && clks->size() > 1) multiple_clk_pins.insert(pin); } - pushPinErrors("Warning: There %is %d unclocked register/latch pin%s.", - no_clk_pins); - pushPinErrors("Warning: There %is %d register/latch pin%s with multiple clocks.", - multiple_clk_pins); + pushPinErrors("Warning: There {} {} unclocked register/latch pin{}.", + no_clk_pins); + pushPinErrors("Warning: There {} {} register/latch pin{} with multiple clocks.", + multiple_clk_pins); +} + +static const char * +plurality(int n) +{ + return n == 1 ? "is" : "are"; +} + +static const char * +pluralSuffix(int n) +{ + return n == 1 ? "" : "s"; } void @@ -192,33 +206,27 @@ CheckTiming::checkLoops() loop_count++; } if (loop_count > 0) { - string error_msg; - errorMsgSubst("Warning: There %is %d combinational loop%s in the design.", - loop_count, error_msg); CheckError *error = new CheckError; - error->push_back(stringCopy(error_msg.c_str())); + error->push_back(sta::format("Warning: There {} {} combinational loop{} in the design.", + plurality(loop_count), + loop_count, + pluralSuffix(loop_count))); - GraphLoopSeq::Iterator loop_iter2(loops); - while (loop_iter2.hasNext()) { - GraphLoop *loop = loop_iter2.next(); + for (GraphLoop *loop : loops) { if (loop->isCombinational()) { - EdgeSeq::Iterator edge_iter(loop->edges()); - Edge *last_edge = nullptr; - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - Pin *pin = edge->from(graph_)->pin(); - const char *pin_name = stringCopy(sdc_network_->pathName(pin)); - error->push_back(pin_name); - last_edge = edge; - } + Edge *last_edge = nullptr; + for (Edge *edge : *loop->edges()) { + Pin *pin = edge->from(graph_)->pin(); + error->push_back(sdc_network_->pathName(pin)); + last_edge = edge; + } if (last_edge) { - error->push_back(stringCopy("| loop cut point")); + error->emplace_back("| loop cut point"); const Pin *pin = last_edge->to(graph_)->pin(); - const char *pin_name = stringCopy(sdc_network_->pathName(pin)); - error->push_back(pin_name); + error->push_back(sdc_network_->pathName(pin)); // Separator between loops. - error->push_back(stringCopy("--------------------------------")); + error->emplace_back("--------------------------------"); } } } @@ -232,8 +240,8 @@ CheckTiming::checkUnconstrainedEndpoints() PinSet unconstrained_ends(network_); checkUnconstrainedOutputs(unconstrained_ends); checkUnconstrainedSetups(unconstrained_ends); - pushPinErrors("Warning: There %is %d unconstrained endpoint%s.", - unconstrained_ends); + pushPinErrors("Warning: There {} {} unconstrained endpoint{}.", + unconstrained_ends); } void @@ -246,10 +254,10 @@ CheckTiming::checkUnconstrainedOutputs(PinSet &unconstrained_ends) PortDirection *dir = network_->direction(pin); Vertex *vertex = graph_->pinLoadVertex(pin); if (dir->isAnyOutput() - && !vertex->isConstant() + && !sim_->isConstant(pin) && !((hasClkedDepature(pin) - && hasClkedArrival(vertex)) - || hasMaxDelay(pin))) + && hasClkedArrival(vertex)) + || hasMaxDelay(pin))) unconstrained_ends.insert(pin); } delete pin_iter; @@ -262,8 +270,8 @@ CheckTiming::hasClkedDepature(Pin *pin) if (output_delays) { for (OutputDelay *output_delay : *output_delays) { if (output_delay->clkEdge() != nullptr - || output_delay->refPin() != nullptr) - return true; + || output_delay->refPin() != nullptr) + return true; } } return false; @@ -273,13 +281,13 @@ CheckTiming::hasClkedDepature(Pin *pin) bool CheckTiming::hasMaxDelay(Pin *pin) { - for (ExceptionPath *exception : sdc_->exceptions()) { + for (const ExceptionPath *exception : sdc_->exceptions()) { ExceptionTo *to = exception->to(); if (exception->isPathDelay() - && exception->minMax() == MinMaxAll::max() - && to - && to->hasPins() - && to->pins()->hasKey(pin)) + && exception->minMax() == MinMaxAll::max() + && to + && to->hasPins() + && to->pins()->contains(pin)) return true; } return false; @@ -291,12 +299,12 @@ CheckTiming::checkUnconstrainedSetups(PinSet &unconstrained_ends) VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); - if (!vertex->isConstant()) { + if (!sim_->isConstant(vertex)) { VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); if (edge->role() == TimingRole::setup() - && (!search_->isClock(edge->from(graph_)) + && (!clk_network_->isClock(edge->from(graph_)) || !hasClkedArrival(edge->to(graph_)))) { unconstrained_ends.insert(vertex->pin()); break; @@ -322,53 +330,42 @@ void CheckTiming::checkGeneratedClocks() { ClockSet gen_clk_errors; - for (auto clk : sdc_->clks()) { + for (auto clk : sdc_->clocks()) { if (clk->isGenerated()) { - search_->genclks()->checkMaster(clk); + mode_->genclks()->checkMaster(clk, sdc_); bool found_clk = false; - VertexSet src_vertices(graph_); + VertexSet src_vertices = makeVertexSet(this); clk->srcPinVertices(src_vertices, network_, graph_); - VertexSet::Iterator vertex_iter(src_vertices); - while (vertex_iter.hasNext()) { - Vertex *vertex = vertex_iter.next(); - if (search_->isClock(vertex)) { - found_clk = true; - break; - } + for (Vertex *vertex : src_vertices) { + if (clk_network_->isClock(vertex)) { + found_clk = true; + break; + } } if (!found_clk) - gen_clk_errors.insert(clk); + gen_clk_errors.insert(clk); } } - pushClkErrors("Warning: There %is %d generated clock%s that %is not connected to a clock source.", - gen_clk_errors); + pushClkErrors("Warning: There {} {} generated clock{} not connected to a clock source.", + gen_clk_errors); } // Report the "msg" error for each pin in "pins". -// -// Substitutions in msg are done as follows if the pin count is one -// or greater than one. -// %is - is/are -// %d - pin count -// %s - s/"" -// %a - a/"" void -CheckTiming::pushPinErrors(const char *msg, - PinSet &pins) +CheckTiming::pushPinErrors(std::string_view msg, + PinSet &pins) { if (!pins.empty()) { CheckError *error = new CheckError; - string error_msg; - errorMsgSubst(msg, pins.size(), error_msg); - // Copy the error strings because the error deletes them when it - // is deleted. - error->push_back(stringCopy(error_msg.c_str())); + error->push_back(sta::formatRuntime(msg, + plurality(pins.size()), + pins.size(), + pluralSuffix(pins.size()))); // Sort the error pins so the output is independent of the order // the the errors are discovered. PinSeq pins1 = sortByPathName(&pins, network_); for (const Pin *pin : pins1) { - const char *pin_name = stringCopy(sdc_network_->pathName(pin)); - error->push_back(pin_name); + error->push_back(sdc_network_->pathName(pin)); } errors_.push_back(error); } @@ -376,67 +373,22 @@ CheckTiming::pushPinErrors(const char *msg, void CheckTiming::pushClkErrors(const char *msg, - ClockSet &clks) + ClockSet &clks) { if (!clks.empty()) { CheckError *error = new CheckError; - string error_msg; - errorMsgSubst(msg, clks.size(), error_msg); - // Copy the error strings because the error deletes them when it - // is deleted. - error->push_back(stringCopy(error_msg.c_str())); + error->push_back(sta::formatRuntime(msg, + plurality(clks.size()), + clks.size(), + pluralSuffix(clks.size()))); // Sort the error clks so the output is independent of the order // the the errors are discovered. ClockSeq clks1 = sortByName(&clks); for (const Clock *clk : clks1) { - const char *clk_name = stringCopy(clk->name()); - error->push_back(clk_name); + error->push_back(clk->name()); } errors_.push_back(error); } } -// Copy msg making substitutions for singular/plurals. -void -CheckTiming::errorMsgSubst(const char *msg, - int obj_count, - string &error_msg) -{ - for (const char *s = msg; *s; s++) { - char ch = *s; - if (ch == '%') { - char flag = s[1]; - if (flag == 'i') { - if (obj_count > 1) - error_msg += "are"; - else - error_msg += "is"; - s += 2; - } - else if (flag == 'a') { - if (obj_count == 1) { - error_msg += 'a'; - s++; - } - else - // Skip space after %a. - s += 2; - } - else if (flag == 's') { - if (obj_count > 1) - error_msg += 's'; - s++; - } - else if (flag == 'd') { - error_msg += std::to_string(obj_count); - s++; - } - else - criticalError(245, "unknown print flag"); - } - else - error_msg += ch; - } -} - -} // namespace +} // namespace sta diff --git a/search/CheckTiming.hh b/search/CheckTiming.hh index 1bbc6dd19..ca7b98862 100644 --- a/search/CheckTiming.hh +++ b/search/CheckTiming.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,30 +24,36 @@ #pragma once -#include "Vector.hh" -#include "StringSeq.hh" -#include "NetworkClass.hh" +#include +#include + #include "GraphClass.hh" +#include "Mode.hh" +#include "NetworkClass.hh" #include "SdcClass.hh" #include "StaState.hh" +#include "StringUtil.hh" namespace sta { -typedef StringSeq CheckError; -typedef Vector CheckErrorSeq; +class ClkNetwork; + +using CheckError = StringSeq; +using CheckErrorSeq = std::vector; class CheckTiming : public StaState { public: - explicit CheckTiming(StaState *sta); - ~CheckTiming(); - CheckErrorSeq &check(bool no_input_delay, - bool no_output_delay, - bool reg_multiple_clks, - bool reg_no_clks, - bool unconstrained_endpoints, - bool loops, - bool generated_clks); + CheckTiming(StaState *sta); + ~CheckTiming() override; + CheckErrorSeq &check(const Mode *mode, + bool no_input_delay, + bool no_output_delay, + bool reg_multiple_clks, + bool reg_no_clks, + bool unconstrained_endpoints, + bool loops, + bool generated_clks); protected: void clear(); @@ -55,10 +61,10 @@ protected: void checkNoInputDelay(); void checkNoOutputDelay(); void checkRegClks(bool reg_multiple_clks, - bool reg_no_clks); + bool reg_no_clks); void checkUnconstrainedEndpoints(); bool hasClkedArrival(Vertex *vertex); - void checkNoOutputDelay(PinSet &ends); + void checkNoOutputDelay(PinSet &no_departure); void checkUnconstrainedOutputs(PinSet &unconstrained_ends); void checkUnconstrainedSetups(PinSet &unconstrained_ends); void checkLoops(); @@ -66,15 +72,16 @@ protected: bool hasClkedCheck(Vertex *vertex); bool hasMaxDelay(Pin *pin); void checkGeneratedClocks(); - void pushPinErrors(const char *msg, - PinSet &pins); + void pushPinErrors(std::string_view msg, + PinSet &pins); void pushClkErrors(const char *msg, - ClockSet &clks); - void errorMsgSubst(const char *msg, - int count, - std::string &error_msg); + ClockSet &clks); CheckErrorSeq errors_; + const Mode *mode_{nullptr}; + const Sdc *sdc_{nullptr}; + const Sim *sim_{nullptr}; + const ClkNetwork *clk_network_{nullptr}; }; -} // namespace +} // namespace sta diff --git a/search/ClkDelays.hh b/search/ClkDelays.hh index 1b394914c..5c410a9e9 100644 --- a/search/ClkDelays.hh +++ b/search/ClkDelays.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,10 +24,11 @@ #pragma once +#include "Delay.hh" #include "MinMax.hh" +#include "Path.hh" #include "StaState.hh" #include "Transition.hh" -#include "Path.hh" namespace sta { @@ -41,7 +42,7 @@ public: // Return values. Delay &insertion, Delay &delay, - float &internal_latency, + float &lib_clk_delay, Delay &latency, Path &path, bool &exists) const; @@ -49,7 +50,7 @@ public: const RiseFall *end_rf, const MinMax *min_max, // Return values. - Delay &delay, + Delay &latency, bool &exists) const; static Delay latency(Path *clk_path, StaState *sta); @@ -61,9 +62,9 @@ public: StaState *sta); private: - static float insertionDelay(Path *clk_path, + static Delay insertionDelay(Path *clk_path, StaState *sta); - static float delay(Path *clk_path, + static Delay delay(Path *clk_path, StaState *sta); static float clkTreeDelay(Path *clk_path, StaState *sta); @@ -76,4 +77,4 @@ private: bool exists_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count]; }; -} // namespace +} // namespace sta diff --git a/search/ClkInfo.cc b/search/ClkInfo.cc index a86791016..535267995 100644 --- a/search/ClkInfo.cc +++ b/search/ClkInfo.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,29 +26,30 @@ #include -#include "Units.hh" -#include "Network.hh" #include "Graph.hh" +#include "Network.hh" +#include "Scene.hh" #include "Sdc.hh" -#include "Corner.hh" #include "Search.hh" #include "Tag.hh" -#include "PathAnalysisPt.hh" +#include "Units.hh" namespace sta { -ClkInfo::ClkInfo(const ClockEdge *clk_edge, - const Pin *clk_src, - bool is_propagated, +ClkInfo::ClkInfo(Scene *scene, + const ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, const Pin *gen_clk_src, - bool is_gen_clk_src_path, - const RiseFall *pulse_clk_sense, - Arrival insertion, - float latency, - ClockUncertainties *uncertainties, - PathAPIndex path_ap_index, - const Path *crpr_clk_path, - const StaState *sta) : + bool is_gen_clk_src_path, + const RiseFall *pulse_clk_sense, + Arrival insertion, + float latency, + const ClockUncertainties *uncertainties, + const MinMax *min_max, + const Path *crpr_clk_path, + const StaState *sta) : + scene_(scene), clk_edge_(clk_edge), clk_src_(clk_src), gen_clk_src_(gen_clk_src), @@ -61,15 +62,11 @@ ClkInfo::ClkInfo(const ClockEdge *clk_edge, crpr_path_refs_filter_(crpr_clk_path ? crpr_clk_path->tag(sta)->isFilter() : false), is_pulse_clk_(pulse_clk_sense != nullptr), pulse_clk_sense_(pulse_clk_sense ? pulse_clk_sense->index() : 0), - path_ap_index_(path_ap_index) + min_max_index_(min_max->index()) { findHash(sta); } -ClkInfo::~ClkInfo() -{ -} - void ClkInfo::findHash(const StaState *sta) { @@ -101,12 +98,18 @@ ClkInfo::findHash(const StaState *sta) hashIncr(hash_, hash_float(uncertainty)); } hashIncr(hash_, hash_float(latency_)); - hashIncr(hash_, hash_float(delayAsFloat(insertion_))); + hashIncr(hash_, hash_float(insertion_.mean())); hashIncr(hash_, is_propagated_); hashIncr(hash_, is_gen_clk_src_path_); hashIncr(hash_, is_pulse_clk_); hashIncr(hash_, pulse_clk_sense_); - hashIncr(hash_, path_ap_index_); + hashIncr(hash_, min_max_index_); +} + +const MinMax * +ClkInfo::minMax() const +{ + return MinMax::find(min_max_index_); } VertexId @@ -143,15 +146,14 @@ std::string ClkInfo::to_string(const StaState *sta) const { Network *network = sta->network(); - Corners *corners = sta->corners(); std::string result; - PathAnalysisPt *path_ap = corners->findPathAnalysisPt(path_ap_index_); - result += path_ap->pathMinMax()->to_string(); + const MinMax *min_max = minMax(); + result += scene_->name(); result += "/"; - result += std::to_string(path_ap_index_); - + result += min_max->to_string(); result += " "; + if (clk_edge_) result += clk_edge_->name(); else @@ -181,7 +183,7 @@ ClkInfo::to_string(const StaState *sta) const if (delayGreater(insertion_, 0.0, sta)) { result += " insert"; - result += delayAsString(insertion_, sta); + result += delayAsString(insertion_, min_max, sta); } if (uncertainties_) { @@ -235,15 +237,15 @@ ClkInfoEqual::ClkInfoEqual(const StaState *sta) : bool ClkInfoEqual::operator()(const ClkInfo *clk_info1, - const ClkInfo *clk_info2) const + const ClkInfo *clk_info2) const { return ClkInfo::equal(clk_info1, clk_info2, sta_); } bool ClkInfo::equal(const ClkInfo *clk_info1, - const ClkInfo *clk_info2, - const StaState *sta) + const ClkInfo *clk_info2, + const StaState *sta) { return ClkInfo::cmp(clk_info1, clk_info2, sta) == 0; } @@ -257,16 +259,23 @@ ClkInfoLess::ClkInfoLess(const StaState *sta) : bool ClkInfoLess::operator()(const ClkInfo *clk_info1, - const ClkInfo *clk_info2) const + const ClkInfo *clk_info2) const { return ClkInfo::cmp(clk_info1, clk_info2, sta_) < 0; } int ClkInfo::cmp(const ClkInfo *clk_info1, - const ClkInfo *clk_info2, - const StaState *sta) + const ClkInfo *clk_info2, + const StaState *sta) { + size_t scene_index1 = clk_info1->scene()->index(); + size_t scene_index2 = clk_info2->scene()->index(); + if (scene_index1 < scene_index2) + return -1; + if (scene_index1 > scene_index2) + return 1; + const ClockEdge *clk_edge1 = clk_info1->clkEdge(); const ClockEdge *clk_edge2 = clk_info2->clkEdge(); int edge_index1 = clk_edge1 ? clk_edge1->index() : -1; @@ -276,11 +285,11 @@ ClkInfo::cmp(const ClkInfo *clk_info1, if (edge_index1 > edge_index2) return 1; - PathAPIndex path_ap_index1 = clk_info1->pathAPIndex(); - PathAPIndex path_ap_index2 = clk_info2->pathAPIndex(); - if (path_ap_index1 < path_ap_index2) + int mm_index1 = clk_info1->minMaxIndex(); + int mm_index2 = clk_info2->minMaxIndex(); + if (mm_index1 < mm_index2) return -1; - if (path_ap_index1 > path_ap_index2) + if (mm_index1 > mm_index2) return 1; const Network *network = sta->network(); @@ -302,14 +311,11 @@ ClkInfo::cmp(const ClkInfo *clk_info1, if (gen_clk_src_id1 > gen_clk_src_id2) return 1; - bool crpr_on = sta->crprActive(); - if (crpr_on) { - const Path *crpr_path1 = clk_info1->crprClkPathRaw(); - const Path *crpr_path2 = clk_info2->crprClkPathRaw(); - int path_cmp = Path::cmp(crpr_path1, crpr_path2, sta); - if (path_cmp != 0) - return path_cmp; - } + const Path *crpr_path1 = clk_info1->crprClkPathRaw(); + const Path *crpr_path2 = clk_info2->crprClkPathRaw(); + int path_cmp = Path::cmp(crpr_path1, crpr_path2, sta); + if (path_cmp != 0) + return path_cmp; const ClockUncertainties *uncertainties1 = clk_info1->uncertainties(); const ClockUncertainties *uncertainties2 = clk_info2->uncertainties(); @@ -367,4 +373,4 @@ ClkInfo::cmp(const ClkInfo *clk_info1, return 0; } -} // namespace +} // namespace sta diff --git a/search/ClkInfo.hh b/search/ClkInfo.hh index 09d2c0e0c..a83529d5f 100644 --- a/search/ClkInfo.hh +++ b/search/ClkInfo.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,10 +24,19 @@ #pragma once -#include "Transition.hh" -#include "SearchClass.hh" -#include "Sdc.hh" +#include +#include + +#include "Clock.hh" +#include "Delay.hh" +#include "GraphClass.hh" +#include "MinMax.hh" +#include "NetworkClass.hh" #include "Path.hh" +#include "Sdc.hh" +#include "SearchClass.hh" +#include "StaState.hh" +#include "Transition.hh" namespace sta { @@ -36,20 +45,23 @@ class Path; class ClkInfo { public: - ClkInfo(const ClockEdge *clk_edge, - const Pin *clk_src, - bool is_propagated, - const Pin *gen_clk_src, - bool is_gen_clk_src_path, - const RiseFall *pulse_clk_sense, - Arrival insertion, - float latency, - ClockUncertainties *uncertainties, - PathAPIndex path_ap_index, - const Path *crpr_clk_path, - const StaState *sta); - ~ClkInfo(); + ClkInfo(Scene *scene, + const ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, + const Pin *gen_clk_src, + bool is_gen_clk_src_path, + const RiseFall *pulse_clk_sense, + Arrival insertion, + float latency, + const ClockUncertainties *uncertainties, + const MinMax *min_max, + const Path *crpr_clk_path, + const StaState *sta); std::string to_string(const StaState *sta) const; + Scene *scene() const { return scene_; } + const MinMax *minMax() const; + int minMaxIndex() const { return min_max_index_; } const ClockEdge *clkEdge() const { return clk_edge_; } const Clock *clock() const; const Pin *clkSrc() const { return clk_src_; } @@ -61,8 +73,7 @@ public: float latency() const { return latency_; } Arrival &insertion() { return insertion_; } const Arrival &insertion() const { return insertion_; } - ClockUncertainties *uncertainties() const { return uncertainties_; } - PathAPIndex pathAPIndex() const { return path_ap_index_; } + const ClockUncertainties *uncertainties() const { return uncertainties_; } // Clock path used for crpr resolution. // Null for clocks because the path cannot point to itself. Path *crprClkPath(const StaState *sta); @@ -76,20 +87,21 @@ public: const Path *crprClkPathRaw() const; static int cmp(const ClkInfo *clk_info1, - const ClkInfo *clk_info2, - const StaState *sta); + const ClkInfo *clk_info2, + const StaState *sta); static bool equal(const ClkInfo *clk_info1, - const ClkInfo *clk_info2, - const StaState *sta); + const ClkInfo *clk_info2, + const StaState *sta); protected: void findHash(const StaState *sta); private: + Scene *scene_; const ClockEdge *clk_edge_; const Pin *clk_src_; const Pin *gen_clk_src_; Path crpr_clk_path_; - ClockUncertainties *uncertainties_; + const ClockUncertainties *uncertainties_; Arrival insertion_; float latency_; size_t hash_; @@ -100,16 +112,15 @@ private: bool crpr_path_refs_filter_:1; bool is_pulse_clk_:1; unsigned int pulse_clk_sense_:RiseFall::index_bit_count; - unsigned int path_ap_index_:path_ap_index_bit_count; + unsigned int min_max_index_:MinMax::index_bit_count; }; class ClkInfoLess { public: - explicit ClkInfoLess(const StaState *sta); - ~ClkInfoLess() {} + ClkInfoLess(const StaState *sta); bool operator()(const ClkInfo *clk_info1, - const ClkInfo *clk_info2) const; + const ClkInfo *clk_info2) const; protected: const StaState *sta_; @@ -126,10 +137,10 @@ class ClkInfoEqual public: ClkInfoEqual(const StaState *sta); bool operator()(const ClkInfo *clk_info1, - const ClkInfo *clk_info2) const; + const ClkInfo *clk_info2) const; protected: const StaState *sta_; }; -} // namespace +} // namespace sta diff --git a/search/ClkLatency.cc b/search/ClkLatency.cc index d9b4a68f4..1e972abc4 100644 --- a/search/ClkLatency.cc +++ b/search/ClkLatency.cc @@ -1,43 +1,43 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "ClkLatency.hh" #include -#include "Report.hh" +#include "ClkInfo.hh" +#include "Clock.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" -#include "Units.hh" +#include "Graph.hh" #include "Liberty.hh" #include "Network.hh" -#include "Clock.hh" -#include "Graph.hh" #include "Path.hh" -#include "StaState.hh" +#include "Report.hh" #include "Search.hh" -#include "PathAnalysisPt.hh" -#include "ClkInfo.hh" +#include "StaState.hh" +#include "Units.hh" namespace sta { @@ -48,29 +48,31 @@ ClkLatency::ClkLatency(StaState *sta) : ClkDelays ClkLatency::findClkDelays(const Clock *clk, - const Corner *corner, + const Scene *scene, bool include_internal_latency) { ConstClockSeq clks; clks.push_back(clk); - ClkDelayMap clk_delay_map = findClkDelays(clks, corner, - include_internal_latency); + SceneSet scenes; + scenes.insert(scene); + ClkDelayMap clk_delay_map = findClkDelays(clks, scenes, include_internal_latency); return clk_delay_map[clk]; } void ClkLatency::reportClkLatency(ConstClockSeq &clks, - const Corner *corner, + const SceneSeq &scenes, bool include_internal_latency, int digits) { - ClkDelayMap clk_delay_map = findClkDelays(clks, corner, include_internal_latency); + const SceneSet scenes1 = Scene::sceneSet(scenes); + ClkDelayMap clk_delay_map = findClkDelays(clks, scenes1, include_internal_latency); // Sort the clocks to report in a stable order. ConstClockSeq sorted_clks; for (const Clock *clk : clks) sorted_clks.push_back(clk); - std::sort(sorted_clks.begin(), sorted_clks.end(), ClkNameLess()); + sort(sorted_clks, ClockNameLess()); for (const Clock *clk : sorted_clks) { ClkDelays clk_delays = clk_delay_map[clk]; @@ -85,7 +87,7 @@ ClkLatency::reportClkLatency(const Clock *clk, int digits) { Unit *time_unit = units_->timeUnit(); - report_->reportLine("Clock %s", clk->name()); + report_->report("Clock {}", clk->name()); for (const RiseFall *src_rf : RiseFall::range()) { for (const RiseFall *end_rf : RiseFall::range()) { Path path_min; @@ -94,47 +96,41 @@ ClkLatency::reportClkLatency(const Clock *clk, float internal_latency_min; Delay latency_min; bool exists_min; - clk_delays.delay(src_rf, end_rf, MinMax::min(), insertion_min, - delay_min, internal_latency_min, latency_min, - path_min, exists_min); + clk_delays.delay(src_rf, end_rf, MinMax::min(), insertion_min, delay_min, + internal_latency_min, latency_min, path_min, exists_min); Path path_max; Delay insertion_max; Delay delay_max; float internal_latency_max; Delay latency_max; bool exists_max; - clk_delays.delay(src_rf, end_rf, MinMax::max(), insertion_max, - delay_max, internal_latency_max, latency_max, - path_max, exists_max); + clk_delays.delay(src_rf, end_rf, MinMax::max(), insertion_max, delay_max, + internal_latency_max, latency_max, path_max, exists_max); if (exists_min & exists_max) { - report_->reportLine("%s -> %s", - src_rf->name(), - end_rf->name()); - report_->reportLine(" min max"); - - report_->reportLine("%7s %7s source latency", - delayAsString(insertion_min, this, digits), - delayAsString(insertion_max, this, digits)); - report_->reportLine("%7s %7s network latency %s", - delayAsString(delay_min, this, digits), - "", - sdc_network_->pathName(path_min.pin(this))); - report_->reportLine("%7s %7s network latency %s", - "", - delayAsString(delay_max, this, digits), - sdc_network_->pathName(path_max.pin(this))); - if (internal_latency_min != 0.0 - || internal_latency_max != 0.0) - report_->reportLine("%7s %7s internal clock latency", - time_unit->asString(internal_latency_min, digits), - time_unit->asString(internal_latency_max, digits)); - report_->reportLine("---------------"); - report_->reportLine("%7s %7s latency", - delayAsString(latency_min, this, digits), - delayAsString(latency_max, this, digits)); - Delay skew = latency_max - latency_min; - report_->reportLine(" %7s skew", - delayAsString(skew, this, digits)); + report_->report("{} -> {}", src_rf->name(), end_rf->name()); + report_->report(" min max"); + report_->report("{:>7} {:>7} source latency", + delayAsString(insertion_min, MinMax::min(), digits, this), + delayAsString(insertion_max, MinMax::max(), digits, this)); + report_->report("{:>7} {:>7} network latency {}", + delayAsString(delay_min, MinMax::min(), digits, this), + "", + sdc_network_->pathName(path_min.pin(this))); + report_->report("{:>7} {:>7} network latency {}", + "", + delayAsString(delay_max, MinMax::max(), digits, this), + sdc_network_->pathName(path_max.pin(this))); + if (internal_latency_min != 0.0 || internal_latency_max != 0.0) + report_->report("{:>7} {:>7} internal clock latency", + time_unit->asString(internal_latency_min, digits), + time_unit->asString(internal_latency_max, digits)); + report_->report("---------------"); + report_->report("{:>7} {:>7} latency", + delayAsString(latency_min, MinMax::min(), digits, this), + delayAsString(latency_max, MinMax::max(), digits, this)); + Delay skew = delayDiff(latency_max, latency_min, this); + report_->report(" {:>7} skew", + delayAsString(skew, MinMax::max(), digits, this)); report_->reportBlankLine(); } } @@ -143,25 +139,28 @@ ClkLatency::reportClkLatency(const Clock *clk, ClkDelayMap ClkLatency::findClkDelays(ConstClockSeq &clks, - const Corner *corner, + const SceneSet &scenes, bool include_internal_latency) { + ConstClockSet clk_set; + for (const Clock *clk : clks) + clk_set.insert(clk); + ClkDelayMap clk_delay_map; // Make entries for the relevant clocks to filter path clocks. for (const Clock *clk : clks) clk_delay_map[clk]; - for (Vertex *clk_vertex : *graph_->regClkVertices()) { + + for (Vertex *clk_vertex : graph_->regClkVertices()) { VertexPathIterator path_iter(clk_vertex, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); - const ClockEdge *path_clk_edge = path->clkEdge(this); - const PathAnalysisPt *path_ap = path->pathAnalysisPt(this); - if (path_clk_edge - && (corner == nullptr - || path_ap->corner() == corner)) { - const Clock *path_clk = path_clk_edge->clock(); + const Scene *path_scene = path->scene(this); + const Clock *path_clk = path->clock(this); + if (path_clk && scenes.contains(path_scene) && clk_set.contains(path_clk)) { auto delays_itr = clk_delay_map.find(path_clk); if (delays_itr != clk_delay_map.end()) { + const ClockEdge *path_clk_edge = path->clkEdge(this); ClkDelays &clk_delays = delays_itr->second; const RiseFall *clk_rf = path_clk_edge->transition(); const MinMax *min_max = path->minMax(this); @@ -247,10 +246,10 @@ ClkDelays::setLatency(const RiseFall *src_rf, int end_rf_index = end_rf->index(); int mm_index = min_max->index(); - float insertion = insertionDelay(path, sta); + Delay insertion = insertionDelay(path, sta); insertion_[src_rf_index][end_rf_index][mm_index] = insertion; - float delay1 = delay(path, sta); + Delay delay1 = delay(path, sta); delay_[src_rf_index][end_rf_index][mm_index] = delay1; float internal_latency = 0.0; @@ -259,7 +258,7 @@ ClkDelays::setLatency(const RiseFall *src_rf, internal_latency_[src_rf_index][end_rf_index][mm_index] = internal_latency; } - float latency = insertion + delay1 + internal_latency; + Delay latency = delaySum(delay1, delaySum(insertion, internal_latency, sta), sta); latency_[src_rf_index][end_rf_index][mm_index] = latency; path_[src_rf_index][end_rf_index][mm_index] = *path; @@ -270,23 +269,22 @@ Delay ClkDelays::latency(Path *clk_path, StaState *sta) { - - float insertion = insertionDelay(clk_path, sta); - float delay1 = delay(clk_path, sta); + Delay insertion = insertionDelay(clk_path, sta); + Delay delay1 = delay(clk_path, sta); float lib_clk_delay = clkTreeDelay(clk_path, sta); - return insertion + delay1 + lib_clk_delay; + return delaySum(delay1, delaySum(insertion, lib_clk_delay, sta), sta); } -float +Delay ClkDelays::delay(Path *clk_path, StaState *sta) { Arrival arrival = clk_path->arrival(); const ClockEdge *path_clk_edge = clk_path->clkEdge(sta); - return delayAsFloat(arrival) - path_clk_edge->time(); + return delayDiff(arrival, path_clk_edge->time(), sta); } -float +Delay ClkDelays::insertionDelay(Path *clk_path, StaState *sta) { @@ -295,10 +293,9 @@ ClkDelays::insertionDelay(Path *clk_path, const RiseFall *clk_rf = clk_edge->transition(); const ClkInfo *clk_info = clk_path->clkInfo(sta); const Pin *src_pin = clk_info->clkSrc(); - const PathAnalysisPt *path_ap = clk_path->pathAnalysisPt(sta); const MinMax *min_max = clk_path->minMax(sta); - return delayAsFloat(sta->search()->clockInsertion(clk, src_pin, clk_rf, min_max, - min_max, path_ap)); + const Mode *mode = clk_path->mode(sta); + return sta->search()->clockInsertion(clk, src_pin, clk_rf, min_max, min_max, mode); } float @@ -310,8 +307,8 @@ ClkDelays::clkTreeDelay(Path *clk_path, const LibertyPort *port = sta->network()->libertyPort(pin); const MinMax *min_max = clk_path->minMax(sta); const RiseFall *rf = clk_path->transition(sta); - float slew = delayAsFloat(clk_path->slew(sta)); + float slew = delayAsFloat(clk_path->slew(sta), min_max, sta); return port->clkTreeDelay(slew, rf, min_max); } -} // namespace +} // namespace sta diff --git a/search/ClkLatency.hh b/search/ClkLatency.hh index 3a7d0385e..313ff8718 100644 --- a/search/ClkLatency.hh +++ b/search/ClkLatency.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,16 +26,15 @@ #include +#include "ClkDelays.hh" #include "SdcClass.hh" -#include "StaState.hh" -#include "Transition.hh" +#include "Scene.hh" #include "SearchClass.hh" -#include "Path.hh" -#include "ClkDelays.hh" +#include "StaState.hh" namespace sta { -typedef std::map ClkDelayMap; +using ClkDelayMap = std::map; // Find and report clock skews between source/target registers. class ClkLatency : public StaState @@ -44,20 +43,20 @@ public: ClkLatency(StaState *sta); // Report clk latency for clks. void reportClkLatency(ConstClockSeq &clks, - const Corner *corner, + const SceneSeq &scenes, bool include_internal_latency, int digits); ClkDelays findClkDelays(const Clock *clk, - const Corner *corner, + const Scene *scene, bool include_internal_latency); protected: ClkDelayMap findClkDelays(ConstClockSeq &clks, - const Corner *corner, + const SceneSet &scenes, bool include_internal_latency); void reportClkLatency(const Clock *clk, ClkDelays &clk_delays, int digits); }; -} // namespace +} // namespace sta diff --git a/search/ClkNetwork.cc b/search/ClkNetwork.cc index 663c350ca..93a294965 100644 --- a/search/ClkNetwork.cc +++ b/search/ClkNetwork.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,25 +24,27 @@ #include "ClkNetwork.hh" +#include "Bfs.hh" #include "Debug.hh" -#include "Network.hh" #include "Graph.hh" -#include "Bfs.hh" +#include "Mode.hh" +#include "Network.hh" #include "Sdc.hh" -#include "SearchPred.hh" #include "Search.hh" +#include "SearchPred.hh" namespace sta { -ClkNetwork::ClkNetwork(StaState *sta) : +ClkNetwork::ClkNetwork(Mode *mode, + StaState *sta) : StaState(sta), - clk_pins_valid_(false) + mode_(mode) { } ClkNetwork::~ClkNetwork() { - clk_pins_map_.deleteContentsClear(); + deleteContents(clk_pins_map_); } void @@ -57,7 +59,7 @@ ClkNetwork::clear() { clk_pins_valid_ = false; pin_clks_map_.clear(); - clk_pins_map_.deleteContentsClear(); + deleteContents(clk_pins_map_); pin_ideal_clks_map_.clear(); } @@ -85,7 +87,7 @@ ClkNetwork::disconnectPinBefore(const Pin *pin) void ClkNetwork::connectPinAfter(const Pin *pin) { - if (isClock(pin)) + if (network_->isRegClkPin(pin)) clkPinsInvalid(); } @@ -93,7 +95,8 @@ class ClkSearchPred : public ClkTreeSearchPred { public: ClkSearchPred(const StaState *sta); - virtual bool searchTo(const Vertex *to); + bool searchTo(const Vertex *to, + const Mode *mode) const override; }; ClkSearchPred::ClkSearchPred(const StaState *sta) : @@ -102,10 +105,10 @@ ClkSearchPred::ClkSearchPred(const StaState *sta) : } bool -ClkSearchPred::searchTo(const Vertex *to) +ClkSearchPred::searchTo(const Vertex *to, + const Mode *mode) const { - const Sdc *sdc = sta_->sdc(); - return !sdc->isLeafPinClock(to->pin()); + return !mode->sdc()->isLeafPinClock(to->pin()); } void @@ -120,38 +123,39 @@ ClkNetwork::findClkPins() void ClkNetwork::findClkPins(bool ideal_only, - PinClksMap &pin_clks_map) + PinClksMap &pin_clks_map) { + const Sdc *sdc = mode_->sdc(); ClkSearchPred srch_pred(this); BfsFwdIterator bfs(BfsIndex::other, &srch_pred, this); - for (Clock *clk : sdc_->clks()) { + for (Clock *clk : sdc->clocks()) { if (!ideal_only - || !clk->isPropagated()) { + || !clk->isPropagated()) { PinSet *clk_pins = clk_pins_map_[clk]; if (clk_pins == nullptr) { clk_pins = new PinSet(network_); clk_pins_map_[clk] = clk_pins; } 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); - } + 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); + } } while (bfs.hasNext()) { - Vertex *vertex = bfs.next(); - const Pin *pin = vertex->pin(); - if (!ideal_only - || !sdc_->isPropagatedClock(pin)) { - clk_pins->insert(pin); - ClockSet &pin_clks = pin_clks_map[pin]; + Vertex *vertex = bfs.next(); + 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); - } + bfs.enqueueAdjacentVertices(vertex); + } } } } @@ -160,8 +164,13 @@ ClkNetwork::findClkPins(bool ideal_only, bool ClkNetwork::isClock(const Pin *pin) const { - return network_->isRegClkPin(pin) - || pin_clks_map_.hasKey(pin); + return pin_clks_map_.contains(pin); +} + +bool +ClkNetwork::isClock(const Vertex *vertex) const +{ + return isClock(vertex->pin()); } bool @@ -183,30 +192,45 @@ ClkNetwork::isClock(const Net *net) const bool ClkNetwork::isIdealClock(const Pin *pin) const { - return pin_ideal_clks_map_.hasKey(pin); + return pin_ideal_clks_map_.contains(pin); +} + +bool +ClkNetwork::isIdealClock(const Vertex *vertex) const +{ + return isIdealClock(vertex->pin()); } bool ClkNetwork::isPropagatedClock(const Pin *pin) const { - return pin_clks_map_.hasKey(pin) - && !pin_ideal_clks_map_.hasKey(pin); + return pin_clks_map_.contains(pin) + && !pin_ideal_clks_map_.contains(pin); } const ClockSet * -ClkNetwork::clocks(const Pin *pin) +ClkNetwork::clocks(const Pin *pin) const { - if (pin_clks_map_.hasKey(pin)) - return &pin_clks_map_[pin]; + auto itr = pin_clks_map_.find(pin); + if (itr != pin_clks_map_.end()) + return &itr->second; else return nullptr; } + +const ClockSet * +ClkNetwork::clocks(const Vertex *vertex) const +{ + return clocks(vertex->pin()); +} + const ClockSet * -ClkNetwork::idealClocks(const Pin *pin) +ClkNetwork::idealClocks(const Pin *pin) const { - if (pin_ideal_clks_map_.hasKey(pin)) - return &pin_ideal_clks_map_[pin]; + auto itr = pin_ideal_clks_map_.find(pin); + if (itr != pin_ideal_clks_map_.end()) + return &itr->second; else return nullptr; } @@ -214,7 +238,7 @@ ClkNetwork::idealClocks(const Pin *pin) const PinSet * ClkNetwork::pins(const Clock *clk) { - if (clk_pins_map_.hasKey(clk)) + if (clk_pins_map_.contains(clk)) return clk_pins_map_[clk]; else return nullptr; @@ -223,14 +247,12 @@ ClkNetwork::pins(const Clock *clk) float ClkNetwork::idealClkSlew(const Pin *pin, const RiseFall *rf, - const MinMax *min_max) + const MinMax *min_max) const { - const ClockSet *clks = clk_network_->idealClocks(pin); + const ClockSet *clks = idealClocks(pin); if (clks && !clks->empty()) { float slew = min_max->initValue(); - ClockSet::ConstIterator clk_iter(clks); - while (clk_iter.hasNext()) { - Clock *clk = clk_iter.next(); + for (Clock *clk : *clks) { float clk_slew = clk->slew(rf, min_max); if (min_max->compare(clk_slew, slew)) slew = clk_slew; @@ -241,4 +263,4 @@ ClkNetwork::idealClkSlew(const Pin *pin, return 0.0; } -} // namespace +} // namespace sta diff --git a/search/ClkSkew.cc b/search/ClkSkew.cc index 33df2af64..83c3c1a5a 100644 --- a/search/ClkSkew.cc +++ b/search/ClkSkew.cc @@ -1,180 +1,58 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "ClkSkew.hh" -#include // abs #include +#include // abs +#include +#include +#include -#include "Fuzzy.hh" -#include "Report.hh" +#include "Bfs.hh" +#include "Crpr.hh" #include "Debug.hh" #include "DispatchQueue.hh" -#include "Units.hh" -#include "TimingArc.hh" +#include "Fuzzy.hh" +#include "Graph.hh" #include "Liberty.hh" #include "Network.hh" -#include "Graph.hh" -#include "Sdc.hh" -#include "Bfs.hh" #include "Path.hh" -#include "StaState.hh" -#include "PathAnalysisPt.hh" -#include "SearchPred.hh" -#include "Search.hh" -#include "Crpr.hh" #include "PathEnd.hh" +#include "Report.hh" +#include "Sdc.hh" +#include "Search.hh" +#include "SearchPred.hh" +#include "StaState.hh" +#include "TimingArc.hh" +#include "Units.hh" namespace sta { -using std::abs; - -ClkSkew::ClkSkew() : - src_path_(nullptr), - tgt_path_(nullptr), - include_internal_latency_(false), - skew_(0.0) -{ -} - -ClkSkew::ClkSkew(Path *src_path, - Path *tgt_path, - bool include_internal_latency, - StaState *sta) : - src_path_(src_path), - tgt_path_(tgt_path), - include_internal_latency_(include_internal_latency) -{ - skew_ = srcLatency(sta) - - tgtLatency(sta) - - delayAsFloat(crpr(sta)) - + uncertainty(sta); -} - -ClkSkew::ClkSkew(const ClkSkew &clk_skew) -{ - src_path_ = clk_skew.src_path_; - tgt_path_ = clk_skew.tgt_path_; - include_internal_latency_ = clk_skew.include_internal_latency_; - skew_ = clk_skew.skew_; -} - -void -ClkSkew::operator=(const ClkSkew &clk_skew) -{ - src_path_ = clk_skew.src_path_; - tgt_path_ = clk_skew.tgt_path_; - include_internal_latency_ = clk_skew.include_internal_latency_; - skew_ = clk_skew.skew_; -} - -float -ClkSkew::srcLatency(const StaState *sta) -{ - Arrival src_arrival = src_path_->arrival(); - return delayAsFloat(src_arrival) - src_path_->clkEdge(sta)->time() - + clkTreeDelay(src_path_, sta); -} - -float -ClkSkew::srcInternalClkLatency(const StaState *sta) -{ - return clkTreeDelay(src_path_, sta); -} - -float -ClkSkew::tgtLatency(const StaState *sta) -{ - Arrival tgt_arrival = tgt_path_->arrival(); - return delayAsFloat(tgt_arrival) - tgt_path_->clkEdge(sta)->time() - + clkTreeDelay(tgt_path_, sta); -} - -float -ClkSkew::tgtInternalClkLatency(const StaState *sta) -{ - return clkTreeDelay(tgt_path_, sta); -} - -float -ClkSkew::clkTreeDelay(Path *clk_path, - const StaState *sta) -{ - if (include_internal_latency_) { - const Vertex *vertex = clk_path->vertex(sta); - const Pin *pin = vertex->pin(); - const LibertyPort *port = sta->network()->libertyPort(pin); - const MinMax *min_max = clk_path->minMax(sta); - const RiseFall *rf = clk_path->transition(sta); - float slew = delayAsFloat(clk_path->slew(sta)); - return port->clkTreeDelay(slew, rf, min_max); - } - else - return 0.0; -} - -Crpr -ClkSkew::crpr(const StaState *sta) -{ - CheckCrpr *check_crpr = sta->search()->checkCrpr(); - return check_crpr->checkCrpr(src_path_, tgt_path_); -} - -float -ClkSkew::uncertainty(const StaState *sta) -{ - const TimingRole *check_role = (src_path_->minMax(sta) == SetupHold::max()) - ? TimingRole::setup() - : TimingRole::hold(); - // Uncertainty decreases slack, but increases skew. - return -PathEnd::checkTgtClkUncertainty(tgt_path_, tgt_path_->clkEdge(sta), - check_role, sta); -} - -bool -ClkSkew::srcTgtPathNameLess(ClkSkew &clk_skew1, - ClkSkew &clk_skew2, - const StaState *sta) -{ - Network *network = sta->sdcNetwork(); - const char *src_path1 = network->pathName(clk_skew1.srcPath()->pin(sta)); - const char *src_path2 = network->pathName(clk_skew2.srcPath()->pin(sta)); - const char *tgt_path1 = network->pathName(clk_skew1.tgtPath()->pin(sta)); - const char *tgt_path2 = network->pathName(clk_skew2.tgtPath()->pin(sta)); - return stringLess(src_path1, src_path2) - || (stringEqual(src_path1, src_path2) - && stringEqual(tgt_path1, tgt_path2)); -} - - -//////////////////////////////////////////////////////////////// - ClkSkews::ClkSkews(StaState *sta) : StaState(sta), - corner_(nullptr), - include_internal_latency_(true), - fanout_pred_(sta) + fanout_pred_(this) { } @@ -186,87 +64,107 @@ ClkSkews::clear() void ClkSkews::reportClkSkew(ConstClockSeq &clks, - const Corner *corner, - const SetupHold *setup_hold, + const SceneSeq &scenes, + const SetupHold *setup_hold, bool include_internal_latency, - int digits) + int digits) { - findClkSkew(clks, corner, include_internal_latency); + findClkSkew(clks, scenes, include_internal_latency); // Sort the clocks to report in a stable order. ConstClockSeq sorted_clks; for (const Clock *clk : clks) sorted_clks.push_back(clk); - std::sort(sorted_clks.begin(), sorted_clks.end(), ClkNameLess()); + sort(sorted_clks, ClockNameLess()); for (const Clock *clk : sorted_clks) { - report_->reportLine("Clock %s", clk->name()); + report_->report("Clock {}", clk->name()); auto skew_itr = skews_.find(clk); if (skew_itr != skews_.end()) reportClkSkew(skew_itr->second[setup_hold->index()], digits); else - report_->reportLine("No launch/capture paths found."); + report_->report("No launch/capture paths found."); report_->reportBlankLine(); } } void ClkSkews::reportClkSkew(ClkSkew &clk_skew, - int digits) + int digits) { Unit *time_unit = units_->timeUnit(); Path *src_path = clk_skew.srcPath(); Path *tgt_path = clk_skew.tgtPath(); - float src_latency = clk_skew.srcLatency(this); + const MinMax *src_min_max = src_path->minMax(this); + Arrival src_latency = clk_skew.srcLatency(this); float tgt_latency = clk_skew.tgtLatency(this); float src_internal_clk_latency = clk_skew.srcInternalClkLatency(this); float tgt_internal_clk_latency = clk_skew.tgtInternalClkLatency(this); float uncertainty = clk_skew.uncertainty(this); if (src_internal_clk_latency != 0.0) - src_latency -= src_internal_clk_latency; - report_->reportLine("%7s source latency %s %s", - time_unit->asString(src_latency, digits), - sdc_network_->pathName(src_path->pin(this)), - src_path->transition(this)->to_string().c_str()); + delayDecr(src_latency, src_internal_clk_latency, this); + report_->report("{:>7} source latency {} {}", + delayAsString(src_latency, src_min_max, digits, this), + sdc_network_->pathName(src_path->pin(this)), + src_path->transition(this)->shortName()); if (src_internal_clk_latency != 0.0) - report_->reportLine("%7s source internal clock delay", - time_unit->asString(src_internal_clk_latency, digits)); + report_->report("{:>7} source internal clock delay", + time_unit->asString(src_internal_clk_latency, digits)); if (tgt_internal_clk_latency != 0.0) tgt_latency -= tgt_internal_clk_latency; - report_->reportLine("%7s target latency %s %s", - time_unit->asString(-tgt_latency, digits), - sdc_network_->pathName(tgt_path->pin(this)), - tgt_path->transition(this)->to_string().c_str()); + report_->report("{:>7} target latency {} {}", + time_unit->asString(-tgt_latency, digits), + sdc_network_->pathName(tgt_path->pin(this)), + tgt_path->transition(this)->shortName()); if (tgt_internal_clk_latency != 0.0) - report_->reportLine("%7s target internal clock delay", - time_unit->asString(-tgt_internal_clk_latency, digits)); + report_->report("{:>7} target internal clock delay", + time_unit->asString(-tgt_internal_clk_latency, digits)); if (uncertainty != 0.0) - report_->reportLine("%7s clock uncertainty", - time_unit->asString(uncertainty, digits)); - report_->reportLine("%7s CRPR", - time_unit->asString(delayAsFloat(-clk_skew.crpr(this)), - digits)); - report_->reportLine("--------------"); - report_->reportLine("%7s %s skew", - time_unit->asString(clk_skew.skew(), digits), - src_path->minMax(this) == MinMax::max() ? "setup" : "hold"); + report_->report("{:>7} clock uncertainty", + time_unit->asString(uncertainty, digits)); + report_->report("{:>7} CRPR", + delayAsString(delayDiff(0.0, clk_skew.crpr(this), this), + MinMax::max(), digits, this)); + report_->report("--------------"); + report_->report("{:>7} {} skew", + delayAsString(clk_skew.skew(), MinMax::max(), digits, this), + src_path->minMax(this) == MinMax::max() ? "setup" : "hold"); } -float -ClkSkews::findWorstClkSkew(const Corner *corner, +static float +delayAbsMax(const Delay &delay, + const StaState *sta) +{ + float min = delayAsFloat(delay, MinMax::min(), sta); + float max = delayAsFloat(delay, MinMax::max(), sta); + return std::max(std::abs(min), std::abs(max)); +} + +static bool +delayAbsGreater(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return delayAbsMax(delay1, sta) > delayAbsMax(delay2, sta); +} + +Delay +ClkSkews::findWorstClkSkew(const SceneSeq &scenes, const SetupHold *setup_hold, bool include_internal_latency) { ConstClockSeq clks; - for (const Clock *clk : *sdc_->clocks()) - clks.push_back(clk); - findClkSkew(clks, corner, include_internal_latency); - float worst_skew = 0.0; + for (const Scene *scene : scenes_) { + for (const Clock *clk : scene->sdc()->clocks()) + clks.push_back(clk); + } + findClkSkew(clks, scenes, include_internal_latency); + Delay worst_skew = 0.0; for (const auto& [clk, clk_skews] : skews_) { - float skew = clk_skews[setup_hold->index()].skew(); - if (abs(skew) > abs(worst_skew)) + Delay skew = clk_skews[setup_hold->index()].skew(); + if (delayAbsGreater(skew, worst_skew, this)) worst_skew = skew; } return worst_skew; @@ -274,29 +172,30 @@ ClkSkews::findWorstClkSkew(const Corner *corner, void ClkSkews::findClkSkew(ConstClockSeq &clks, - const Corner *corner, + const SceneSeq &scenes, bool include_internal_latency) -{ - if (corner == corner_ - && include_internal_latency == include_internal_latency_ - && clks == clks_ - && !skews_.empty()) +{ + if (scenes == scenes_ && include_internal_latency == include_internal_latency_ + && clks == clks_ && !skews_.empty()) return; skews_.clear(); clks_ = clks; - corner_ = corner; + scenes_ = scenes; include_internal_latency_ = include_internal_latency; clk_set_.clear(); for (const Clock *clk : clks) clk_set_.insert(clk); + scenes_set_ = Scene::sceneSet(scenes); + // This sets modes_ for fanout_pred_ also. + modes_ = Scene::modes(scenes_); if (thread_count_ > 1) { std::vector partial_skews(thread_count_); - for (Vertex *src_vertex : *graph_->regClkVertices()) { + for (Vertex *src_vertex : graph_->regClkVertices()) { if (hasClkPaths(src_vertex)) { - dispatch_queue_->dispatch([this, src_vertex, &partial_skews](int i) { + dispatch_queue_->dispatch([this, src_vertex, &partial_skews](size_t i) { findClkSkewFrom(src_vertex, partial_skews[i]); }); } @@ -304,30 +203,34 @@ ClkSkews::findClkSkew(ConstClockSeq &clks, dispatch_queue_->finishTasks(); // Reduce skews from each register source. - for (size_t i = 0; i < partial_skews.size(); i++) { - for (auto& [clk, partial_skew] : partial_skews[i]) { + for (auto & i : partial_skews) { + for (auto &[clk, partial_skew] : i) { auto itr = skews_.find(clk); if (itr == skews_.end()) { // Insert new entry using emplace with piecewise_construct // This will default-construct the array, then we copy the elements - auto result = skews_.emplace(std::piecewise_construct, - std::forward_as_tuple(clk), - std::make_tuple()); + auto result = + skews_.emplace(std::piecewise_construct, std::forward_as_tuple(clk), + std::make_tuple()); itr = result.first; // Copy array elements for (int setup_hold_idx : SetupHold::rangeIndex()) itr->second[setup_hold_idx] = partial_skew[setup_hold_idx]; - } else { + } + else { // Update existing entry for (int setup_hold_idx : SetupHold::rangeIndex()) { ClkSkew &final_skew = itr->second[setup_hold_idx]; ClkSkew &partial_skew_val = partial_skew[setup_hold_idx]; - float partial_skew1 = partial_skew_val.skew(); - float final_skew1 = final_skew.skew(); - if (abs(partial_skew1) > abs(final_skew1) - || (fuzzyEqual(abs(partial_skew1), abs(final_skew1)) + Delay partial_skew1 = partial_skew_val.skew(); + Delay final_skew1 = final_skew.skew(); + float partial_skew_max = delayAbsMax(partial_skew1, this); + float final_skew_max = delayAbsMax(final_skew1, this); + if (partial_skew_max > final_skew_max + || (fuzzyEqual(partial_skew_max, final_skew_max) // Break ties based on source/target path names. - && ClkSkew::srcTgtPathNameLess(partial_skew_val, final_skew, this))) + && ClkSkew::srcTgtPathNameLess(partial_skew_val, final_skew, + this))) final_skew = partial_skew_val; } } @@ -335,7 +238,7 @@ ClkSkews::findClkSkew(ConstClockSeq &clks, } } else { - for (Vertex *src_vertex : *graph_->regClkVertices()) { + for (Vertex *src_vertex : graph_->regClkVertices()) { if (hasClkPaths(src_vertex)) findClkSkewFrom(src_vertex, skews_); } @@ -349,7 +252,7 @@ ClkSkews::hasClkPaths(Vertex *vertex) while (path_iter.hasNext()) { Path *path = path_iter.next(); const Clock *path_clk = path->clock(this); - if (clk_set_.find(path_clk) != clk_set_.end()) + if (clk_set_.contains(path_clk)) return true; } return false; @@ -365,9 +268,8 @@ ClkSkews::findClkSkewFrom(Vertex *src_vertex, if (edge->role()->genericRole() == TimingRole::regClkToQ()) { Vertex *q_vertex = edge->to(graph_); const RiseFall *rf = edge->timingArcSet()->isRisingFallingEdge(); - const RiseFallBoth *src_rf = rf - ? rf->asRiseFallBoth() - : RiseFallBoth::riseFall(); + const RiseFallBoth *src_rf = + rf ? rf->asRiseFallBoth() : RiseFallBoth::riseFall(); findClkSkewFrom(src_vertex, q_vertex, src_rf, skews); } } @@ -375,9 +277,9 @@ ClkSkews::findClkSkewFrom(Vertex *src_vertex, void ClkSkews::findClkSkewFrom(Vertex *src_vertex, - Vertex *q_vertex, - const RiseFallBoth *src_rf, - ClkSkewMap &skews) + Vertex *q_vertex, + const RiseFallBoth *src_rf, + ClkSkewMap &skews) { VertexSet endpoints = findFanout(q_vertex); for (Vertex *end : endpoints) { @@ -387,12 +289,11 @@ ClkSkews::findClkSkewFrom(Vertex *src_vertex, const TimingRole *role = edge->role(); if (role->genericRole() == TimingRole::setup() || role->genericRole() == TimingRole::hold()) { - Vertex *tgt_vertex = edge->from(graph_); - const RiseFall *tgt_rf1 = edge->timingArcSet()->isRisingFallingEdge(); - const RiseFallBoth *tgt_rf = tgt_rf1 - ? tgt_rf1->asRiseFallBoth() - : RiseFallBoth::riseFall(); - findClkSkew(src_vertex, src_rf, tgt_vertex, tgt_rf, skews); + Vertex *tgt_vertex = edge->from(graph_); + const RiseFall *tgt_rf1 = edge->timingArcSet()->isRisingFallingEdge(); + const RiseFallBoth *tgt_rf = + tgt_rf1 ? tgt_rf1->asRiseFallBoth() : RiseFallBoth::riseFall(); + findClkSkew(src_vertex, src_rf, tgt_vertex, tgt_rf, skews); } } } @@ -400,50 +301,48 @@ ClkSkews::findClkSkewFrom(Vertex *src_vertex, void ClkSkews::findClkSkew(Vertex *src_vertex, - const RiseFallBoth *src_rf, - Vertex *tgt_vertex, - const RiseFallBoth *tgt_rf, + const RiseFallBoth *src_rf, + Vertex *tgt_vertex, + const RiseFallBoth *tgt_rf, ClkSkewMap &skews) { Unit *time_unit = units_->timeUnit(); VertexPathIterator src_iter(src_vertex, this); while (src_iter.hasNext()) { Path *src_path = src_iter.next(); + Scene *src_scene = src_path->scene(this); const Clock *src_clk = src_path->clock(this); if (src_path->isClock(this) - && src_rf->matches(src_path->transition(this)) - && clk_set_.find(src_clk) != clk_set_.end()) { - Corner *src_corner = src_path->pathAnalysisPt(this)->corner(); - const MinMax *tgt_min_max = src_path->minMax(this)->opposite(); - if (corner_ == nullptr - || src_corner == corner_) { - VertexPathIterator tgt_iter(tgt_vertex, this); - while (tgt_iter.hasNext()) { - Path *tgt_path = tgt_iter.next(); - const Clock *tgt_clk = tgt_path->clock(this); - if (tgt_clk == src_clk - && tgt_path->isClock(this) - && tgt_rf->matches(tgt_path->transition(this)) - && tgt_path->minMax(this) == tgt_min_max - && tgt_path->pathAnalysisPt(this)->corner() == src_corner) { - ClkSkew probe(src_path, tgt_path, include_internal_latency_, this); - const SetupHold *setup_hold = src_path->minMax(this); - ClkSkew &clk_skew = skews[src_clk][setup_hold->index()]; - debugPrint(debug_, "clk_skew", 2, - "%s %s %s -> %s %s %s crpr = %s skew = %s", - network_->pathName(src_path->pin(this)), - src_path->transition(this)->to_string().c_str(), - time_unit->asString(probe.srcLatency(this)), - network_->pathName(tgt_path->pin(this)), - tgt_path->transition(this)->to_string().c_str(), - time_unit->asString(probe.tgtLatency(this)), - delayAsString(probe.crpr(this), this), - time_unit->asString(probe.skew())); - if (clk_skew.srcPath() == nullptr - || abs(probe.skew()) > abs(clk_skew.skew())) - clk_skew = probe; - } - } + && src_rf->matches(src_path->transition(this)) + && clk_set_.contains(src_clk) + && scenes_set_.contains(src_scene)) { + const MinMax *src_min_max = src_path->minMax(this); + const MinMax *tgt_min_max = src_min_max->opposite(); + VertexPathIterator tgt_iter(tgt_vertex, this); + while (tgt_iter.hasNext()) { + Path *tgt_path = tgt_iter.next(); + const Clock *tgt_clk = tgt_path->clock(this); + if (tgt_clk == src_clk && tgt_path->isClock(this) + && tgt_rf->matches(tgt_path->transition(this)) + && tgt_path->minMax(this) == tgt_min_max + && tgt_path->scene(this) == src_scene) { + ClkSkew probe(src_path, tgt_path, include_internal_latency_, this); + const SetupHold *setup_hold = src_path->minMax(this); + ClkSkew &clk_skew = skews[src_clk][setup_hold->index()]; + debugPrint(debug_, "clk_skew", 2, + "{} {} {} -> {} {} {} crpr = {} skew = {}", + network_->pathName(src_path->pin(this)), + src_path->transition(this)->shortName(), + delayAsString(probe.srcLatency(this), src_min_max, this), + network_->pathName(tgt_path->pin(this)), + tgt_path->transition(this)->shortName(), + time_unit->asString(probe.tgtLatency(this)), + delayAsString(probe.crpr(this), this), + delayAsString(probe.skew(), MinMax::max(), this)); + if (clk_skew.srcPath() == nullptr + || delayAbsGreater(probe.skew(), clk_skew.skew(), this)) + clk_skew = probe; + } } } } @@ -452,15 +351,15 @@ ClkSkews::findClkSkew(Vertex *src_vertex, VertexSet ClkSkews::findFanout(Vertex *from) { - VertexSet endpoints(graph_); - UnorderedSet visited; + VertexSet endpoints = makeVertexSet(this); + std::unordered_set visited; findFanout1(from, visited, endpoints); return endpoints; } void ClkSkews::findFanout1(Vertex *from, - UnorderedSet &visited, + std::unordered_set &visited, VertexSet &endpoints) { visited.insert(from); @@ -482,20 +381,128 @@ ClkSkews::findFanout1(Vertex *from, //////////////////////////////////////////////////////////////// +ClkSkew::ClkSkew() : + src_path_(nullptr), + tgt_path_(nullptr), + include_internal_latency_(false), + skew_(0.0) +{ +} + +ClkSkew::ClkSkew(Path *src_path, + Path *tgt_path, + bool include_internal_latency, + StaState *sta) : + src_path_(src_path), + tgt_path_(tgt_path), + include_internal_latency_(include_internal_latency) +{ + skew_ = delayDiff(delaySum(srcLatency(sta), uncertainty(sta), sta), + delaySum(tgtLatency(sta), crpr(sta), sta), sta); +} + +ClkSkew::ClkSkew(const ClkSkew &clk_skew) +{ + src_path_ = clk_skew.src_path_; + tgt_path_ = clk_skew.tgt_path_; + include_internal_latency_ = clk_skew.include_internal_latency_; + skew_ = clk_skew.skew_; +} + +Arrival +ClkSkew::srcLatency(const StaState *sta) +{ + return delayDiff(delaySum(src_path_->arrival(), clkTreeDelay(src_path_, sta), sta), + src_path_->clkEdge(sta)->time(), sta); +} + +float +ClkSkew::srcInternalClkLatency(const StaState *sta) +{ + return clkTreeDelay(src_path_, sta); +} + +float +ClkSkew::tgtLatency(const StaState *sta) +{ + Arrival tgt_arrival = tgt_path_->arrival(); + return delayAsFloat(delaySum(delayDiff(tgt_arrival, + tgt_path_->clkEdge(sta)->time(),sta), + clkTreeDelay(tgt_path_, sta), sta)); +} + +float +ClkSkew::tgtInternalClkLatency(const StaState *sta) +{ + return clkTreeDelay(tgt_path_, sta); +} + +float +ClkSkew::clkTreeDelay(Path *clk_path, + const StaState *sta) +{ + if (include_internal_latency_) { + const Vertex *vertex = clk_path->vertex(sta); + const Pin *pin = vertex->pin(); + const LibertyPort *port = sta->network()->libertyPort(pin); + const MinMax *min_max = clk_path->minMax(sta); + const RiseFall *rf = clk_path->transition(sta); + float slew = delayAsFloat(clk_path->slew(sta), min_max, sta); + return port->clkTreeDelay(slew, rf, min_max); + } + else + return 0.0; +} + +Crpr +ClkSkew::crpr(const StaState *sta) +{ + CheckCrpr *check_crpr = sta->search()->checkCrpr(); + return check_crpr->checkCrpr(src_path_, tgt_path_); +} + +float +ClkSkew::uncertainty(const StaState *sta) +{ + const TimingRole *check_role = (src_path_->minMax(sta) == SetupHold::max()) + ? TimingRole::setup() + : TimingRole::hold(); + // Uncertainty decreases slack, but increases skew. + return -PathEnd::checkTgtClkUncertainty(tgt_path_, tgt_path_->clkEdge(sta), + check_role, sta); +} + +bool +ClkSkew::srcTgtPathNameLess(ClkSkew &clk_skew1, + ClkSkew &clk_skew2, + const StaState *sta) +{ + Network *network = sta->sdcNetwork(); + std::string src_path1 = network->pathName(clk_skew1.srcPath()->pin(sta)); + std::string src_path2 = network->pathName(clk_skew2.srcPath()->pin(sta)); + std::string tgt_path1 = network->pathName(clk_skew1.tgtPath()->pin(sta)); + std::string tgt_path2 = network->pathName(clk_skew2.tgtPath()->pin(sta)); + return src_path1 < src_path2 + || (src_path1 == src_path2 + && tgt_path1 == tgt_path2); +} + +//////////////////////////////////////////////////////////////// + FanOutSrchPred::FanOutSrchPred(const StaState *sta) : SearchPred1(sta) { } bool -FanOutSrchPred::searchThru(Edge *edge) +FanOutSrchPred::searchThru(Edge *edge, + const Mode *mode) const { const TimingRole *role = edge->role(); - return SearchPred1::searchThru(edge) - && (role == TimingRole::wire() - || role == TimingRole::combinational() - || role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable()); + return SearchPred1::searchThru(edge, mode) + && (role == TimingRole::wire() || role == TimingRole::combinational() + || role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable()); } -} // namespace +} // namespace sta diff --git a/search/ClkSkew.hh b/search/ClkSkew.hh index f59d03dcd..1247f79bc 100644 --- a/search/ClkSkew.hh +++ b/search/ClkSkew.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,14 +25,19 @@ #pragma once #include +#include -#include "UnorderedSet.hh" +#include "Delay.hh" +#include "GraphClass.hh" +#include "MinMax.hh" +#include "Mode.hh" +#include "Path.hh" +#include "Scene.hh" #include "SdcClass.hh" -#include "StaState.hh" -#include "Transition.hh" #include "SearchClass.hh" #include "SearchPred.hh" -#include "Path.hh" +#include "StaState.hh" +#include "Transition.hh" namespace sta { @@ -44,20 +49,20 @@ class ClkSkew public: ClkSkew(); ClkSkew(Path *src_path, - Path *tgt_path, + Path *tgt_path, bool include_internal_latency, - StaState *sta); + StaState *sta); ClkSkew(const ClkSkew &clk_skew); - void operator=(const ClkSkew &clk_skew); + ClkSkew &operator=(const ClkSkew &clk_skew) = default; Path *srcPath() { return src_path_; } Path *tgtPath() { return tgt_path_; } - float srcLatency(const StaState *sta); + Arrival srcLatency(const StaState *sta); float tgtLatency(const StaState *sta); float srcInternalClkLatency(const StaState *sta); float tgtInternalClkLatency(const StaState *sta); Crpr crpr(const StaState *sta); float uncertainty(const StaState *sta); - float skew() const { return skew_; } + Delay skew() const { return skew_; } static bool srcTgtPathNameLess(ClkSkew &clk_skew1, ClkSkew &clk_skew2, const StaState *sta); @@ -69,16 +74,18 @@ private: Path *src_path_; Path *tgt_path_; bool include_internal_latency_; - float skew_; + Delay skew_; }; -typedef std::map ClkSkewMap; +using ClkSkewMap = std::map; class FanOutSrchPred : public SearchPred1 { public: FanOutSrchPred(const StaState *sta); - virtual bool searchThru(Edge *edge); + bool searchThru(Edge *edge, + const Mode *mode) const override; + using SearchPred1::searchThru; }; // Find and report clock skews between source/target registers. @@ -89,44 +96,45 @@ public: void clear(); // Report clk skews for clks. void reportClkSkew(ConstClockSeq &clks, - const Corner *corner, - const SetupHold *setup_hold, + const SceneSeq &scenes, + const SetupHold *setup_hold, bool include_internal_latency, - int digits); + int digits); // Find worst clock skew between src/target registers. - float findWorstClkSkew(const Corner *corner, + Delay findWorstClkSkew(const SceneSeq &scenes, const SetupHold *setup_hold, bool include_internal_latency); protected: void findClkSkew(ConstClockSeq &clks, - const Corner *corner, + const SceneSeq &scenes, bool include_internal_latency); bool hasClkPaths(Vertex *vertex); void findClkSkewFrom(Vertex *src_vertex, - ClkSkewMap &skews); + ClkSkewMap &skews); void findClkSkewFrom(Vertex *src_vertex, - Vertex *q_vertex, - const RiseFallBoth *src_rf, - ClkSkewMap &skews); + Vertex *q_vertex, + const RiseFallBoth *src_rf, + ClkSkewMap &skews); void findClkSkew(Vertex *src_vertex, - const RiseFallBoth *src_rf, - Vertex *tgt_vertex, - const RiseFallBoth *tgt_rf, - ClkSkewMap &skews); + const RiseFallBoth *src_rf, + Vertex *tgt_vertex, + const RiseFallBoth *tgt_rf, + ClkSkewMap &skews); VertexSet findFanout(Vertex *from); void findFanout1(Vertex *from, - UnorderedSet &visited, + std::unordered_set &visited, VertexSet &endpoints); void reportClkSkew(ClkSkew &clk_skew, int digits); + // Node StaState scenes_ and modes_ are reused there. ConstClockSeq clks_; ConstClockSet clk_set_; - const Corner *corner_; - bool include_internal_latency_; + SceneSet scenes_set_; + bool include_internal_latency_{true}; FanOutSrchPred fanout_pred_; ClkSkewMap skews_; }; -} // namespace +} // namespace sta diff --git a/search/Corner.cc b/search/Corner.cc index 19a7e5df2..e69de29bb 100644 --- a/search/Corner.cc +++ b/search/Corner.cc @@ -1,461 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#include "Corner.hh" - -#include "Sdc.hh" -#include "Parasitics.hh" -#include "DcalcAnalysisPt.hh" -#include "PathAnalysisPt.hh" - -namespace sta { - -Corners::Corners(StaState *sta) : - StaState(sta) -{ -} - -Corners::~Corners() -{ - clear(); -} - -void -Corners::clear() -{ - corners_.deleteContentsClear(); - corner_map_.clear(); - dcalc_analysis_pts_.deleteContentsClear(); - path_analysis_pts_.deleteContentsClear(); - parasitic_analysis_pts_.deleteContentsClear(); -} - -int -Corners::count() const -{ - return corners_.size(); -} - -bool -Corners::multiCorner() const -{ - return corners_.size() > 1; -} - -Corner * -Corners::findCorner(const char *corner_name) -{ - return corner_map_.findKey(corner_name); -} - -Corner * -Corners::findCorner(int corner_index) -{ - return corners_[corner_index]; -} - -void -Corners::analysisTypeChanged() -{ - makeAnalysisPts(); -} - -void -Corners::operatingConditionsChanged() -{ - for (DcalcAnalysisPt *dcalc_ap : dcalc_analysis_pts_) { - const MinMax *min_max = dcalc_ap->constraintMinMax(); - const OperatingConditions *op_cond = - sdc_->operatingConditions(min_max); - dcalc_ap->setOperatingConditions(op_cond); - } -} - -void -Corners::makeCorners(StringSet *corner_names) -{ - clear(); - int index = 0; - for (const char *name : *corner_names) { - Corner *corner = new Corner(name, index); - corners_.push_back(corner); - // Use the copied name in the map. - corner_map_[corner->name()] = corner; - index++; - } - makeAnalysisPts(); -} - -void -Corners::copy(Corners *corners) -{ - clear(); - int index = 0; - for (Corner *orig : corners->corners_) { - Corner *corner = new Corner(orig->name(), index); - corners_.push_back(corner); - // Use the copied name in the map. - corner_map_[corner->name()] = corner; - index++; - } - makeAnalysisPts(); - - for (ParasiticAnalysisPt *orig_ap : corners->parasitic_analysis_pts_) { - ParasiticAnalysisPt *ap = new ParasiticAnalysisPt(orig_ap->name(), - orig_ap->index(), - orig_ap->indexMax()); - parasitic_analysis_pts_.push_back(ap); - } - - for (size_t i = 0; i < corners->corners_.size(); i++) { - Corner *orig = corners->corners_[i]; - Corner *corner = corners_[i]; - corner->parasitic_analysis_pts_ = orig->parasitic_analysis_pts_; - } -} - -void -Corners::makeParasiticAnalysisPts(bool per_corner) -{ - parasitic_analysis_pts_.deleteContentsClear(); - if (per_corner) { - // per corner, per min/max - parasitic_analysis_pts_.resize(corners_.size() * MinMax::index_count); - for (Corner *corner : corners_) { - corner->setParasiticAnalysisPtcount(MinMax::index_count); - for (const MinMax *min_max : MinMax::range()) { - int mm_index = min_max->index(); - int ap_index = corner->index() * MinMax::index_count + mm_index; - int ap_index_max = corner->index() * MinMax::index_count - + MinMax::max()->index(); - std::string ap_name = corner->name(); - ap_name += "_"; - ap_name += min_max->to_string(); - ParasiticAnalysisPt *ap = new ParasiticAnalysisPt(ap_name.c_str(), - ap_index, ap_index_max); - parasitic_analysis_pts_[ap_index] = ap; - corner->setParasiticAP(ap, mm_index); - } - } - } - else { - // shared corner, per min/max - parasitic_analysis_pts_.resize(MinMax::index_count); - int ap_index_max = MinMax::max()->index(); - for (const MinMax *min_max : MinMax::range()) { - int mm_index = min_max->index(); - int ap_index = mm_index; - ParasiticAnalysisPt *ap = new ParasiticAnalysisPt(min_max->to_string().c_str(), - ap_index, - ap_index_max); - parasitic_analysis_pts_[ap_index] = ap; - for (Corner *corner : corners_) { - corner->setParasiticAnalysisPtcount(MinMax::index_count); - corner->setParasiticAP(ap, mm_index); - } - } - } -} - -void -Corners::makeAnalysisPts() -{ - dcalc_analysis_pts_.deleteContentsClear(); - path_analysis_pts_.deleteContentsClear(); - - for (Corner *corner : corners_) { - makeDcalcAnalysisPts(corner); - makePathAnalysisPts(corner); - } -} - -void -Corners::makeDcalcAnalysisPts(Corner *corner) -{ - DcalcAnalysisPt *min_ap, *max_ap; - switch (sdc_->analysisType()) { - case AnalysisType::single: - corner->setDcalcAnalysisPtcount(1); - max_ap = makeDcalcAnalysisPt(corner, MinMax::max(), MinMax::min()); - max_ap->setCheckClkSlewIndex(max_ap->index()); - break; - case AnalysisType::bc_wc: - corner->setDcalcAnalysisPtcount(2); - min_ap = makeDcalcAnalysisPt(corner, MinMax::min(), MinMax::min()); - max_ap = makeDcalcAnalysisPt(corner, MinMax::max(), MinMax::max()); - min_ap->setCheckClkSlewIndex(min_ap->index()); - max_ap->setCheckClkSlewIndex(max_ap->index()); - break; - case AnalysisType::ocv: - corner->setDcalcAnalysisPtcount(2); - min_ap = makeDcalcAnalysisPt(corner, MinMax::min(), MinMax::max()); - max_ap = makeDcalcAnalysisPt(corner, MinMax::max(), MinMax::min()); - min_ap->setCheckClkSlewIndex(max_ap->index()); - max_ap->setCheckClkSlewIndex(min_ap->index()); - break; - } -} - -DcalcAnalysisPt * -Corners::makeDcalcAnalysisPt(Corner *corner, - const MinMax *min_max, - const MinMax *check_clk_slew_min_max) -{ - OperatingConditions *op_cond = sdc_->operatingConditions(min_max); - DcalcAnalysisPt *dcalc_ap = new DcalcAnalysisPt(corner, - dcalc_analysis_pts_.size(), - op_cond, min_max, - check_clk_slew_min_max); - dcalc_analysis_pts_.push_back(dcalc_ap); - corner->addDcalcAP(dcalc_ap); - return dcalc_ap; -} - -// The clock insertion delay (source latency) required for setup and -// hold checks is: -// -// hold check -// report_timing -delay_type min -// path insertion pll_delay -// src clk min early max -// tgt clk max late min -// -// setup check -// report_timing -delay_type max -// path insertion pll_delay -// src clk max late min -// tgt clk min early max -// -// For analysis type single or bc_wc only one path is required, but as -// shown above both early and late insertion delays are required. -// To find propagated generated clock insertion delays both early and -// late clock network paths are required. Thus, analysis type single -// makes min and max analysis points. -// Only one of them is enabled to "report paths". -void -Corners::makePathAnalysisPts(Corner *corner) -{ - DcalcAnalysisPt *dcalc_ap_min = corner->findDcalcAnalysisPt(MinMax::min()); - DcalcAnalysisPt *dcalc_ap_max = corner->findDcalcAnalysisPt(MinMax::max()); - switch (sdc_->analysisType()) { - case AnalysisType::single: - case AnalysisType::bc_wc: - makePathAnalysisPts(corner, false, dcalc_ap_min, dcalc_ap_max); - break; - case AnalysisType::ocv: - makePathAnalysisPts(corner, true, dcalc_ap_min, dcalc_ap_max); - break; - } -} - - -void -Corners::makePathAnalysisPts(Corner *corner, - bool swap_clk_min_max, - DcalcAnalysisPt *dcalc_ap_min, - DcalcAnalysisPt *dcalc_ap_max) -{ - PathAnalysisPt *min_ap = new PathAnalysisPt(corner, - path_analysis_pts_.size(), - MinMax::min(), dcalc_ap_min); - path_analysis_pts_.push_back(min_ap); - corner->addPathAP(min_ap); - - PathAnalysisPt *max_ap = new PathAnalysisPt(corner, - path_analysis_pts_.size(), - MinMax::max(), dcalc_ap_max); - path_analysis_pts_.push_back(max_ap); - corner->addPathAP(max_ap); - - if (swap_clk_min_max) { - min_ap->setTgtClkAnalysisPt(max_ap); - max_ap->setTgtClkAnalysisPt(min_ap); - } - else { - min_ap->setTgtClkAnalysisPt(min_ap); - max_ap->setTgtClkAnalysisPt(max_ap); - } - - min_ap->setInsertionAnalysisPt(MinMax::min(), min_ap); - min_ap->setInsertionAnalysisPt(MinMax::max(), max_ap); - max_ap->setInsertionAnalysisPt(MinMax::min(), min_ap); - max_ap->setInsertionAnalysisPt(MinMax::max(), max_ap); -} - -int -Corners::parasiticAnalysisPtCount() const -{ - return parasitic_analysis_pts_.size(); -} - -ParasiticAnalysisPtSeq & -Corners::parasiticAnalysisPts() -{ - return parasitic_analysis_pts_; -} - -DcalcAPIndex -Corners::dcalcAnalysisPtCount() const -{ - return dcalc_analysis_pts_.size(); -} - -DcalcAnalysisPtSeq & -Corners::dcalcAnalysisPts() -{ - return dcalc_analysis_pts_; -} - -const DcalcAnalysisPtSeq & -Corners::dcalcAnalysisPts() const -{ - return dcalc_analysis_pts_; -} - -PathAPIndex -Corners::pathAnalysisPtCount() const -{ - return path_analysis_pts_.size(); -} - -PathAnalysisPtSeq & -Corners::pathAnalysisPts() -{ - return path_analysis_pts_; -} - -const PathAnalysisPtSeq & -Corners::pathAnalysisPts() const -{ - return path_analysis_pts_; -} - -PathAnalysisPt * -Corners::findPathAnalysisPt(PathAPIndex path_index) const -{ - return path_analysis_pts_[path_index]; -} - -//////////////////////////////////////////////////////////////// - -Corner::Corner(const char *name, - int index) : - name_(name), - index_(index), - path_analysis_pts_(MinMax::index_count) -{ -} - -ParasiticAnalysisPt * -Corner::findParasiticAnalysisPt(const MinMax *min_max) const -{ - int ap_count = parasitic_analysis_pts_.size(); - if (ap_count == 0) - return nullptr; - else if (ap_count == 1) - return parasitic_analysis_pts_[0]; - else if (ap_count == 2) - return parasitic_analysis_pts_[min_max->index()]; - else { - criticalError(246, "unknown parasitic analysis point count"); - return nullptr; - } -} - -void -Corner::setParasiticAnalysisPtcount(int ap_count) -{ - parasitic_analysis_pts_.resize(ap_count); -} - -void -Corner::setParasiticAP(ParasiticAnalysisPt *ap, - int mm_index) -{ - parasitic_analysis_pts_[mm_index] = ap; -} - -void -Corner::setDcalcAnalysisPtcount(DcalcAPIndex ap_count) -{ - dcalc_analysis_pts_.resize(ap_count); -} - -void -Corner::addDcalcAP(DcalcAnalysisPt *dcalc_ap) -{ - if (dcalc_analysis_pts_.size() == 1) - dcalc_analysis_pts_[0] = dcalc_ap; - else - dcalc_analysis_pts_[dcalc_ap->constraintMinMax()->index()] = dcalc_ap; -} - -DcalcAnalysisPt * -Corner::findDcalcAnalysisPt(const MinMax *min_max) const -{ - int ap_count = dcalc_analysis_pts_.size(); - if (ap_count == 0) - return nullptr; - else if (ap_count == 1) - return dcalc_analysis_pts_[0]; - else if (ap_count == 2) - return dcalc_analysis_pts_[min_max->index()]; - else { - criticalError(247, "unknown analysis point count"); - return nullptr; - } -} - -PathAnalysisPt * -Corner::findPathAnalysisPt(const MinMax *min_max) const -{ - return path_analysis_pts_[min_max->index()]; -} - -void -Corner::addPathAP(PathAnalysisPt *path_ap) -{ - path_analysis_pts_[path_ap->pathMinMax()->index()] = path_ap; -} - -void -Corner::addLiberty(LibertyLibrary *lib, - const MinMax *min_max) -{ - liberty_[min_max->index()].push_back(lib); -} - -const LibertySeq & -Corner::libertyLibraries(const MinMax *min_max) const -{ - return liberty_[min_max->index()]; -} - -int -Corner::libertyIndex(const MinMax *min_max) const -{ - return index_ * MinMax::index_count + min_max->index(); -} - -} // namespace diff --git a/search/Crpr.cc b/search/Crpr.cc index 5420f7202..105f3a5c3 100644 --- a/search/Crpr.cc +++ b/search/Crpr.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,30 +24,26 @@ #include "Crpr.hh" +#include #include // abs -#include +#include "ClkInfo.hh" #include "Debug.hh" -#include "Vector.hh" -#include "Network.hh" +#include "Genclks.hh" #include "Graph.hh" -#include "Sdc.hh" +#include "Mode.hh" +#include "Network.hh" #include "Path.hh" -#include "PathAnalysisPt.hh" -#include "ClkInfo.hh" -#include "Tag.hh" -#include "TagGroup.hh" -#include "VisitPathEnds.hh" #include "PathEnd.hh" +#include "Sdc.hh" #include "Search.hh" -#include "Genclks.hh" +#include "Tag.hh" +#include "TagGroup.hh" #include "Variables.hh" +#include "VisitPathEnds.hh" namespace sta { -using std::min; -using std::abs; - CheckCrpr::CheckCrpr(StaState *sta) : StaState(sta) { @@ -61,11 +57,10 @@ CheckCrpr::maxCrpr(const ClkInfo *clk_info) const Path *crpr_clk_path = clk_info->crprClkPath(this); if (crpr_clk_path) { Arrival other_arrival = otherMinMaxArrival(crpr_clk_path); - float crpr_diff = abs(delayAsFloat(crpr_clk_path->arrival(), - EarlyLate::late(), - this) - - delayAsFloat(other_arrival, EarlyLate::early(), - this)); + float crpr_diff = std::abs(delayAsFloat(crpr_clk_path->arrival(), + EarlyLate::late(), this) + - delayAsFloat(other_arrival, EarlyLate::early(), + this)); return crpr_diff; } return 0.0F; @@ -74,11 +69,12 @@ CheckCrpr::maxCrpr(const ClkInfo *clk_info) Arrival CheckCrpr::otherMinMaxArrival(const Path *path) { - PathAnalysisPt *other_ap = path->pathAnalysisPt(this)->tgtClkAnalysisPt(); + const MinMax *tgt_min_max = path->tgtClkMinMax(this); Tag *tag = path->tag(this); VertexPathIterator other_iter(path->vertex(this), - path->transition(this), - other_ap, this); + path->scene(this), tgt_min_max, + path->transition(this), + this); while (other_iter.hasNext()) { Path *other = other_iter.next(); if (Tag::matchCrpr(other->tag(this), tag)) @@ -91,7 +87,7 @@ CheckCrpr::otherMinMaxArrival(const Path *path) Crpr CheckCrpr::checkCrpr(const Path *src_path, - const Path *tgt_clk_path) + const Path *tgt_clk_path) { Crpr crpr; Pin *crpr_pin; @@ -101,15 +97,15 @@ CheckCrpr::checkCrpr(const Path *src_path, void CheckCrpr::checkCrpr(const Path *src_path, - const Path *tgt_clk_path, - // Return values. - Crpr &crpr, - Pin *&crpr_pin) + const Path *tgt_clk_path, + // Return values. + Crpr &crpr, + Pin *&crpr_pin) { crpr = 0.0; crpr_pin = nullptr; - if (crprActive() - && src_path && tgt_clk_path) { + if (src_path && tgt_clk_path + && crprActive(src_path->mode(this))) { bool same_pin = (variables_->crprMode() == CrprMode::same_pin); checkCrpr1(src_path, tgt_clk_path, same_pin, crpr, crpr_pin); } @@ -117,11 +113,11 @@ CheckCrpr::checkCrpr(const Path *src_path, void CheckCrpr::checkCrpr1(const Path *src_path, - const Path *tgt_clk_path, - bool same_pin, - // Return values. - Crpr &crpr, - Pin *&crpr_pin) + const Path *tgt_clk_path, + bool same_pin, + // Return values. + Crpr &crpr, + Pin *&crpr_pin) { crpr = 0.0; crpr_pin = nullptr; @@ -146,12 +142,13 @@ CheckCrpr::checkCrpr1(const Path *src_path, // is from the opposite min/max of the data. && src_clk_min_max != tgt_clk_path->minMax(this) && (src_clk_path - || src_clk->isGenerated())) { + || src_clk->isGenerated())) { // Src path from input port clk path can only be from generated clk path. if (src_clk_path == nullptr) { src_clk_path = portClkPath(src_clk_info->clkEdge(), src_clk_info->clkSrc(), - src_path->pathAnalysisPt(this)); + src_path->scene(this), + src_path->minMax(this)); } findCrpr(src_clk_path, tgt_clk_path, same_pin, crpr, crpr_pin); } @@ -160,16 +157,17 @@ CheckCrpr::checkCrpr1(const Path *src_path, // Find the clk path for an input/output port. Path * CheckCrpr::portClkPath(const ClockEdge *clk_edge, - const Pin *clk_src_pin, - const PathAnalysisPt *path_ap) + const Pin *clk_src_pin, + const Scene *scene, + const MinMax *min_max) { Vertex *clk_vertex = graph_->pinDrvrVertex(clk_src_pin); - VertexPathIterator path_iter(clk_vertex, clk_edge->transition(), - path_ap, this); + VertexPathIterator path_iter(clk_vertex, scene, min_max, + clk_edge->transition(), this); while (path_iter.hasNext()) { Path *path = path_iter.next(); if (path->clkEdge(this) == clk_edge - && path->isClock(this)) { + && path->isClock(this)) { return path; } } @@ -178,11 +176,11 @@ CheckCrpr::portClkPath(const ClockEdge *clk_edge, void CheckCrpr::findCrpr(const Path *src_clk_path, - const Path *tgt_clk_path, - bool same_pin, - // Return values. - Crpr &crpr, - Pin *&crpr_pin) + const Path *tgt_clk_path, + bool same_pin, + // Return values. + Crpr &crpr, + Pin *&crpr_pin) { crpr = 0.0; crpr_pin = nullptr; @@ -202,12 +200,12 @@ CheckCrpr::findCrpr(const Path *src_clk_path, const Path *src_path = src_gclk_paths[i]; const Path *tgt_path = tgt_gclk_paths[j]; if (src_path->clkInfo(this)->clkSrc() - == tgt_path->clkInfo(this)->clkSrc()) { - src_clk_path1 = src_gclk_paths[i]; - tgt_clk_path1 = tgt_gclk_paths[j]; + == tgt_path->clkInfo(this)->clkSrc()) { + src_clk_path1 = src_gclk_paths[i]; + tgt_clk_path1 = tgt_gclk_paths[j]; } else - break; + break; } } const Path *src_clk_path2 = src_clk_path1; @@ -222,14 +220,14 @@ CheckCrpr::findCrpr(const Path *src_clk_path, if (level_diff >= 0) { src_clk_path2 = src_clk_path2->prevPath(); if (src_clk_path2 == nullptr - || src_clk_path2->isNull()) + || src_clk_path2->isNull()) break; src_level = src_clk_path2->vertex(this)->level(); } if (level_diff <= 0) { tgt_clk_path2 = tgt_clk_path2->prevPath(); if (tgt_clk_path2 == nullptr - || tgt_clk_path2->isNull()) + || tgt_clk_path2->isNull()) break; tgt_level = tgt_clk_path2->vertex(this)->level(); } @@ -237,8 +235,8 @@ CheckCrpr::findCrpr(const Path *src_clk_path, if (src_clk_path2 && !src_clk_path2->isNull() && tgt_clk_path2 && !tgt_clk_path2->isNull() && (src_clk_path2->transition(this) == tgt_clk_path2->transition(this) - || same_pin)) { - debugPrint(debug_, "crpr", 2, "crpr pin %s", + || same_pin)) { + debugPrint(debug_, "crpr", 2, "crpr pin {}", network_->pathName(src_clk_path2->pin(this))); crpr = findCrpr1(src_clk_path2, tgt_clk_path2); crpr_pin = src_clk_path2->pin(this); @@ -252,11 +250,12 @@ CheckCrpr::genClkSrcPaths(const Path *path) const ClkInfo *clk_info = path->clkInfo(this); const ClockEdge *clk_edge = clk_info->clkEdge(); const Pin *clk_src = clk_info->clkSrc(); - PathAnalysisPt *path_ap = path->pathAnalysisPt(this); + const Mode *mode = path->mode(this); + const MinMax *min_max = path->minMax(this); gclk_paths.push_back(path); - Genclks *genclks = search_->genclks(); + Genclks *genclks = mode->genclks(); while (clk_edge->clock()->isGenerated()) { - const Path *genclk_path = genclks->srcPath(clk_edge, clk_src, path_ap); + const Path *genclk_path = genclks->srcPath(clk_edge, clk_src, min_max); if (genclk_path == nullptr) break; clk_info = genclk_path->clkInfo(this); @@ -269,36 +268,33 @@ CheckCrpr::genClkSrcPaths(const Path *path) Crpr CheckCrpr::findCrpr1(const Path *src_clk_path, - const Path *tgt_clk_path) + const Path *tgt_clk_path) { if (variables_->pocvEnabled()) { // Remove variation on the common path. // Note that the crpr sigma is negative to offset the - // sigma of the common clock path. - const EarlyLate *src_el = src_clk_path->minMax(this); - const EarlyLate *tgt_el = tgt_clk_path->minMax(this); - Arrival src_arrival = src_clk_path->arrival(); - Arrival tgt_arrival = tgt_clk_path->arrival(); + // std_dev of the common clock path. + const Arrival &src_arrival = src_clk_path->arrival(); + const Arrival &tgt_arrival = tgt_clk_path->arrival(); float src_clk_time = src_clk_path->clkEdge(this)->time(); float tgt_clk_time = tgt_clk_path->clkEdge(this)->time(); - float crpr_mean = abs(delayAsFloat(src_arrival) - src_clk_time - - (delayAsFloat(tgt_arrival) - tgt_clk_time)); + float crpr_mean = std::abs(src_arrival.mean() - src_clk_time + - (tgt_arrival.mean() - tgt_clk_time)); // Remove the sigma from both source and target path arrivals. - float crpr_sigma2 = delaySigma2(src_arrival, src_el) - + delaySigma2(tgt_arrival, tgt_el); - return makeDelay2(crpr_mean, -crpr_sigma2, -crpr_sigma2); + float crpr_sigma2 = src_arrival.stdDev2() + tgt_arrival.stdDev2(); + return makeDelay2(crpr_mean, -crpr_sigma2); } else { // The source and target edges are different so the crpr // is the min of the source and target max-min delay. float src_delta = crprArrivalDiff(src_clk_path); float tgt_delta = crprArrivalDiff(tgt_clk_path); - debugPrint(debug_, "crpr", 2, " src delta %s", + debugPrint(debug_, "crpr", 2, " src delta {}", delayAsString(src_delta, this)); - debugPrint(debug_, "crpr", 2, " tgt delta %s", + debugPrint(debug_, "crpr", 2, " tgt delta {}", delayAsString(tgt_delta, this)); - float common_delay = min(src_delta, tgt_delta); - debugPrint(debug_, "crpr", 2, " %s delta %s", + float common_delay = std::min(src_delta, tgt_delta); + debugPrint(debug_, "crpr", 2, " {} delta {}", network_->pathName(src_clk_path->pin(this)), delayAsString(common_delay, this)); return common_delay; @@ -309,14 +305,15 @@ float CheckCrpr::crprArrivalDiff(const Path *path) { Arrival other_arrival = otherMinMaxArrival(path); - float crpr_diff = abs(delayAsFloat(path->arrival()) - - delayAsFloat(other_arrival)); + const MinMax *min_max = path->minMax(this); + float crpr_diff = std::abs(delayAsFloat(path->arrival(), min_max, this) + - delayAsFloat(other_arrival, min_max->opposite(), this)); return crpr_diff; } Crpr CheckCrpr::outputDelayCrpr(const Path *src_clk_path, - const ClockEdge *tgt_clk_edge) + const ClockEdge *tgt_clk_edge) { Crpr crpr; Pin *crpr_pin; @@ -326,30 +323,32 @@ CheckCrpr::outputDelayCrpr(const Path *src_clk_path, void CheckCrpr::outputDelayCrpr(const Path *src_path, - const ClockEdge *tgt_clk_edge, - // Return values. - Crpr &crpr, - Pin *&crpr_pin) + const ClockEdge *tgt_clk_edge, + // Return values. + Crpr &crpr, + Pin *&crpr_pin) { crpr = 0.0; crpr_pin = nullptr; - if (crprActive()) { - const PathAnalysisPt *path_ap = src_path->pathAnalysisPt(this); - const PathAnalysisPt *tgt_path_ap = path_ap->tgtClkAnalysisPt(); + const Scene *scene = src_path->scene(this); + const Mode *mode = scene->mode(); + if (crprActive(mode)) { + const MinMax *tgt_min_max = src_path->tgtClkMinMax(this); bool same_pin = (variables_->crprMode() == CrprMode::same_pin); - outputDelayCrpr1(src_path,tgt_clk_edge,tgt_path_ap, same_pin, - crpr, crpr_pin); + outputDelayCrpr1(src_path, tgt_clk_edge, scene, tgt_min_max, + same_pin, crpr, crpr_pin); } } void CheckCrpr::outputDelayCrpr1(const Path *src_path, - const ClockEdge *tgt_clk_edge, - const PathAnalysisPt *tgt_path_ap, - bool same_pin, - // Return values. - Crpr &crpr, - Pin *&crpr_pin) + const ClockEdge *tgt_clk_edge, + const Scene *scene, + const MinMax *min_max, + bool same_pin, + // Return values. + Crpr &crpr, + Pin *&crpr_pin) { crpr = 0.0; crpr_pin = nullptr; @@ -363,7 +362,7 @@ CheckCrpr::outputDelayCrpr1(const Path *src_path, && crprPossible(src_clk, tgt_clk)) { Path *tgt_genclk_path = portClkPath(tgt_clk_edge, tgt_clk_edge->clock()->defaultPin(), - tgt_path_ap); + scene, min_max); const Path *src_clk_path = src_path->clkInfo(this)->crprClkPath(this); if (src_clk_path) findCrpr(src_clk_path, tgt_genclk_path, same_pin, crpr, crpr_pin); @@ -372,17 +371,17 @@ CheckCrpr::outputDelayCrpr1(const Path *src_path, bool CheckCrpr::crprPossible(const Clock *clk1, - const Clock *clk2) + const Clock *clk2) { return clk1 && clk2 && !clk1->isVirtual() && !clk2->isVirtual() // Generated clocks can have crpr in the source path. && (clk1 == clk2 - || clk1->isGenerated() - || clk2->isGenerated() - // Different non-generated clocks with the same source pins (using -add). - || PinSet::intersects(&clk1->pins(), &clk2->pins(), network_)); + || clk1->isGenerated() + || clk2->isGenerated() + // Different non-generated clocks with the same source pins (using -add). + || intersects(&clk1->pins(), &clk2->pins(), network_)); } -} // namespace +} // namespace sta diff --git a/search/Crpr.hh b/search/Crpr.hh index e26353aa5..64ac97b07 100644 --- a/search/Crpr.hh +++ b/search/Crpr.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,9 +24,13 @@ #pragma once +#include "Clock.hh" +#include "Delay.hh" +#include "NetworkClass.hh" +#include "Path.hh" #include "SdcClass.hh" -#include "StaState.hh" #include "SearchClass.hh" +#include "StaState.hh" namespace sta { @@ -36,59 +40,61 @@ class CrprPaths; class CheckCrpr : public StaState { public: - explicit CheckCrpr(StaState *sta); + CheckCrpr(StaState *sta); // Find the maximum possible crpr (clock min/max delta delay) for path. Arrival maxCrpr(const ClkInfo *clk_info); // Timing check CRPR. - Crpr checkCrpr(const Path *src_clk_path, - const Path *tgt_clk_path); + Crpr checkCrpr(const Path *src_path, + const Path *tgt_clk_path); void checkCrpr(const Path *src_path, - const Path *tgt_clk_path, - // Return values. - Crpr &crpr, - Pin *&crpr_pin); + const Path *tgt_clk_path, + // Return values. + Crpr &crpr, + Pin *&crpr_pin); // Output delay CRPR. Crpr outputDelayCrpr(const Path *src_clk_path, - const ClockEdge *tgt_clk_edge); - void outputDelayCrpr(const Path *src_clk_path, - const ClockEdge *tgt_clk_edge, - // Return values. - Crpr &crpr, - Pin *&crpr_pin); + const ClockEdge *tgt_clk_edge); + void outputDelayCrpr(const Path *src_path, + const ClockEdge *tgt_clk_edge, + // Return values. + Crpr &crpr, + Pin *&crpr_pin); private: void clkPathPrev(const Path *path, Path &prev); Arrival otherMinMaxArrival(const Path *path); void checkCrpr1(const Path *src_path, - const Path *tgt_clk_path, - bool same_pin, - // Return values. - Crpr &crpr, - Pin *&crpr_pin); + const Path *tgt_clk_path, + bool same_pin, + // Return values. + Crpr &crpr, + Pin *&crpr_pin); void outputDelayCrpr1(const Path *src_path, - const ClockEdge *tgt_clk_edge, - const PathAnalysisPt *tgt_path_ap, - bool same_pin, - // Return values. - Crpr &crpr, - Pin *&crpr_pin); + const ClockEdge *tgt_clk_edge, + const Scene *scene, + const MinMax *min_max, + bool same_pin, + // Return values. + Crpr &crpr, + Pin *&crpr_pin); bool crprPossible(const Clock *clk1, - const Clock *clk2); + const Clock *clk2); ConstPathSeq genClkSrcPaths(const Path *path); void findCrpr(const Path *src_clk_path, - const Path *tgt_clk_path, - bool same_pin, - // Return values. - Crpr &crpr, - Pin *&common_pin); + const Path *tgt_clk_path, + bool same_pin, + // Return values. + Crpr &crpr, + Pin *&crpr_pin); Path *portClkPath(const ClockEdge *clk_edge, const Pin *clk_src_pin, - const PathAnalysisPt *path_ap); + const Scene *scene, + const MinMax *min_max); Crpr findCrpr1(const Path *src_clk_path, - const Path *tgt_clk_path); + const Path *tgt_clk_path); float crprArrivalDiff(const Path *path); }; -} // namespace +} // namespace sta diff --git a/search/FindRegister.cc b/search/FindRegister.cc index 66f9b5539..bafcf79cc 100644 --- a/search/FindRegister.cc +++ b/search/FindRegister.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,167 +24,164 @@ #include "FindRegister.hh" -#include "TimingRole.hh" +#include "Clock.hh" #include "FuncExpr.hh" -#include "TimingArc.hh" -#include "Sequential.hh" +#include "Graph.hh" #include "Liberty.hh" +#include "Mode.hh" #include "Network.hh" -#include "Graph.hh" #include "Sdc.hh" -#include "Clock.hh" -#include "SearchPred.hh" #include "Search.hh" +#include "SearchPred.hh" +#include "Sequential.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" namespace sta { static TimingSense pathSenseThru(TimingSense from_sense, - TimingSense thru_sense); + TimingSense thru_sense); static bool hasMinPulseWidthCheck(LibertyPort *port); // Predicate used for searching from clocks to find registers. -class FindRegClkPred : public SearchPred1 +class FindRegClkPred : public ClkTreeSearchPred { public: FindRegClkPred(Clock *clk, - const StaState *sta); - virtual bool searchThru(Edge *edge); - virtual bool searchFrom(const Vertex *from_vertex); + const StaState *sta); + bool searchFrom(const Vertex *from_vertex, + const Mode *mode) const override; private: Clock *clk_; }; FindRegClkPred::FindRegClkPred(Clock *clk, - const StaState *sta) : - SearchPred1(sta), + const StaState *sta) : + ClkTreeSearchPred(sta), clk_(clk) { } bool -FindRegClkPred::searchFrom(const Vertex *from_vertex) +FindRegClkPred::searchFrom(const Vertex *from_vertex, + const Mode *mode) const { - const Sdc *sdc = sta_->sdc(); const Pin *from_pin = from_vertex->pin(); - return !sdc->clkStopPropagation(from_pin, clk_) - && SearchPred1::searchFrom(from_vertex); + return !mode->sdc()->clkStopPropagation(from_pin, clk_) + && ClkTreeSearchPred::searchFrom(from_vertex, mode); } -bool -FindRegClkPred::searchThru(Edge *edge) -{ - const TimingRole *role = edge->role(); - return (role->isWire() - || role == TimingRole::combinational()) - && SearchPred1::searchThru(edge); -} +//////////////////////////////////////////////////////////////// // Helper for "all_registers". // Visit all register instances. class FindRegVisitor : public StaState { public: - FindRegVisitor(StaState *sta); - virtual ~FindRegVisitor() {} + FindRegVisitor(const StaState *sta); + ~FindRegVisitor() override = default; void visitRegs(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches); + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode); private: void visitRegs(const Pin *clk_pin, - TimingSense clk_sense, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches); + TimingSense clk_sense, + const RiseFallBoth *clk_rf, + bool registers, + bool latches); virtual void visitReg(Instance *inst) = 0; virtual void visitSequential(Instance *inst, - Sequential *seq) = 0; + const Sequential *seq) = 0; void visitFanoutRegs(Vertex *from_vertex, - TimingSense from_sense, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - SearchPred &clk_pred, - VertexSet &visited_vertices); + TimingSense from_sense, + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + SearchPred &clk_pred, + VertexSet &visited_vertices, + const Mode *mode); void findSequential(const Pin *clk_pin, - Instance *inst, - LibertyCell *cell, - TimingSense clk_sense, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - bool &has_seqs, - bool &matches); + Instance *inst, + LibertyCell *cell, + TimingSense clk_sense, + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + // Return values. + bool &has_seqs, + bool &matches); bool findInferedSequential(LibertyCell *cell, - TimingSense clk_sense, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches); + TimingSense clk_sense, + const RiseFallBoth *clk_rf, + bool registers, + bool latches); bool hasTimingCheck(LibertyCell *cell, - LibertyPort *clk, - LibertyPort *d); + LibertyPort *clk, + LibertyPort *d); }; -FindRegVisitor::FindRegVisitor(StaState *sta) : +FindRegVisitor::FindRegVisitor(const StaState *sta) : StaState(sta) { } void FindRegVisitor::visitRegs(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches) + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode) { if (clks && !clks->empty()) { // Use DFS search to find all registers downstream of the clocks. - ClockSet::Iterator clk_iter(clks); - while (clk_iter.hasNext()) { - Clock *clk = clk_iter.next(); + for (Clock *clk : *clks) { FindRegClkPred clk_pred(clk, this); - VertexSet visited_vertices(graph_); + VertexSet visited_vertices = makeVertexSet(this); for (const Pin *pin : clk->leafPins()) { - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - visitFanoutRegs(vertex, TimingSense::positive_unate, - clk_rf, edge_triggered, - latches, clk_pred, - visited_vertices); - // Clocks defined on bidirect pins blow it out both ends. - if (bidirect_drvr_vertex) - visitFanoutRegs(bidirect_drvr_vertex, - TimingSense::positive_unate, - clk_rf, edge_triggered, - latches, clk_pred, - visited_vertices); + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + visitFanoutRegs(vertex, TimingSense::positive_unate, + clk_rf, registers, + latches, clk_pred, + visited_vertices, mode); + // Clocks defined on bidirect pins blow it out both ends. + if (bidirect_drvr_vertex) + visitFanoutRegs(bidirect_drvr_vertex, + TimingSense::positive_unate, + clk_rf, registers, + latches, clk_pred, + visited_vertices, mode); } } } else { - for (Vertex *vertex : *graph_->regClkVertices()) { + for (Vertex *vertex : graph_->regClkVertices()) { visitRegs(vertex->pin(), TimingSense::positive_unate, - RiseFallBoth::riseFall(), - edge_triggered, latches); + RiseFallBoth::riseFall(), + registers, latches); } } } void FindRegVisitor::visitFanoutRegs(Vertex *from_vertex, - TimingSense from_sense, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - SearchPred &clk_pred, - VertexSet &visited_vertices) -{ - if (!visited_vertices.hasKey(from_vertex) - && clk_pred.searchFrom(from_vertex)) { + TimingSense from_sense, + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + SearchPred &clk_pred, + VertexSet &visited_vertices, + const Mode *mode) +{ + if (!visited_vertices.contains(from_vertex) + && clk_pred.searchFrom(from_vertex, mode)) { visited_vertices.insert(from_vertex); VertexOutEdgeIterator edge_iter(from_vertex, graph_); while (edge_iter.hasNext()) { @@ -193,34 +190,34 @@ FindRegVisitor::visitFanoutRegs(Vertex *from_vertex, const Pin *to_pin = to_vertex->pin(); TimingSense to_sense = pathSenseThru(from_sense, edge->sense()); if (to_vertex->isRegClk()) - visitRegs(to_pin, to_sense, clk_rf, edge_triggered, latches); + visitRegs(to_pin, to_sense, clk_rf, registers, latches); // Even register clock pins can have combinational fanout arcs. - if (clk_pred.searchThru(edge) - && clk_pred.searchTo(to_vertex)) - visitFanoutRegs(to_vertex, to_sense, clk_rf, edge_triggered, latches, - clk_pred, visited_vertices); + if (clk_pred.searchThru(edge, mode) + && clk_pred.searchTo(to_vertex, mode)) + visitFanoutRegs(to_vertex, to_sense, clk_rf, registers, latches, + clk_pred, visited_vertices, mode); } } } void FindRegVisitor::visitRegs(const Pin *clk_pin, - TimingSense clk_sense, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches) + TimingSense clk_sense, + const RiseFallBoth *clk_rf, + bool registers, + bool latches) { Instance *inst = network_->instance(clk_pin); LibertyCell *cell = network_->libertyCell(inst); - if (!edge_triggered || !latches + if (!registers || !latches || clk_rf != RiseFallBoth::riseFall()) { bool matches, has_seqs; findSequential(clk_pin, inst, cell, clk_sense, clk_rf, - edge_triggered, latches, - has_seqs, matches); + registers, latches, + has_seqs, matches); if (!has_seqs) matches = findInferedSequential(cell, clk_sense, clk_rf, - edge_triggered, latches); + registers, latches); if (matches) visitReg(inst); } @@ -232,39 +229,40 @@ FindRegVisitor::visitRegs(const Pin *clk_pin, void FindRegVisitor::findSequential(const Pin *clk_pin, - Instance *inst, - LibertyCell *cell, - TimingSense clk_sense, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - bool &has_seqs, - bool &matches) + Instance *inst, + LibertyCell *cell, + TimingSense clk_sense, + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + // Return values. + bool &has_seqs, + bool &matches) { has_seqs = false; matches = false; - for (Sequential *seq : cell->sequentials()) { + for (const Sequential &seq : cell->sequentials()) { has_seqs = true; - if ((seq->isRegister() && edge_triggered) - || (seq->isLatch() && latches)) { + if ((seq.isRegister() && registers) + || (seq.isLatch() && latches)) { if (clk_rf == RiseFallBoth::riseFall()) { - visitSequential(inst, seq); - matches = true; - break; + visitSequential(inst, &seq); + matches = true; + break; } else { - FuncExpr *clk_func = seq->clock(); - LibertyPort *port = network_->libertyPort(clk_pin); - TimingSense port_sense = clk_func->portTimingSense(port); - TimingSense path_sense = pathSenseThru(clk_sense, port_sense); - if ((path_sense == TimingSense::positive_unate - && clk_rf == RiseFallBoth::rise()) - || (path_sense == TimingSense::negative_unate - && clk_rf == RiseFallBoth::fall())) { - visitSequential(inst, seq); - matches = true; - break; - } + FuncExpr *clk_func = seq.clock(); + LibertyPort *port = network_->libertyPort(clk_pin); + TimingSense port_sense = clk_func->portTimingSense(port); + TimingSense path_sense = pathSenseThru(clk_sense, port_sense); + if ((path_sense == TimingSense::positive_unate + && clk_rf == RiseFallBoth::rise()) + || (path_sense == TimingSense::negative_unate + && clk_rf == RiseFallBoth::fall())) { + visitSequential(inst, &seq); + matches = true; + break; + } } } } @@ -272,10 +270,10 @@ FindRegVisitor::findSequential(const Pin *clk_pin, bool FindRegVisitor::findInferedSequential(LibertyCell *cell, - TimingSense clk_sense, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches) + TimingSense clk_sense, + const RiseFallBoth *clk_rf, + bool registers, + bool latches) { bool matches = false; const RiseFall *clk_rf1 = clk_rf->asRiseFall(); @@ -283,16 +281,16 @@ FindRegVisitor::findInferedSequential(LibertyCell *cell, TimingArc *arc = *arc_set->arcs().begin(); const RiseFall *arc_clk_rf = arc->fromEdge()->asRiseFall(); bool tr_matches = (clk_rf == RiseFallBoth::riseFall() - || (arc_clk_rf == clk_rf1 - && clk_sense == TimingSense::positive_unate) - || (arc_clk_rf == clk_rf1->opposite() - && clk_sense == TimingSense::negative_unate)); + || (arc_clk_rf == clk_rf1 + && clk_sense == TimingSense::positive_unate) + || (arc_clk_rf == clk_rf1->opposite() + && clk_sense == TimingSense::negative_unate)); const TimingRole *role = arc_set->role(); if (tr_matches - && ((role == TimingRole::regClkToQ() - && edge_triggered) - || (role == TimingRole::latchEnToQ() - && latches))) { + && ((role == TimingRole::regClkToQ() + && registers) + || (role == TimingRole::latchEnToQ() + && latches))) { matches = true; break; } @@ -302,8 +300,8 @@ FindRegVisitor::findInferedSequential(LibertyCell *cell, bool FindRegVisitor::hasTimingCheck(LibertyCell *cell, - LibertyPort *clk, - LibertyPort *d) + LibertyPort *clk, + LibertyPort *d) { for (TimingArcSet *arc_set : cell->timingArcSets(clk, d)) { const TimingRole *role = arc_set->role(); @@ -316,21 +314,22 @@ FindRegVisitor::hasTimingCheck(LibertyCell *cell, class FindRegInstances : public FindRegVisitor { public: - explicit FindRegInstances(StaState *sta); + FindRegInstances(const StaState *sta); InstanceSet findRegs(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches); + bool registers, + bool latches, + const Mode *mode); private: - virtual void visitReg(Instance *inst); - virtual void visitSequential(Instance *inst, - Sequential *seq); + void visitReg(Instance *inst) override; + void visitSequential(Instance *inst, + const Sequential *seq) override; InstanceSet regs_; }; -FindRegInstances::FindRegInstances(StaState *sta) : +FindRegInstances::FindRegInstances(const StaState *sta) : FindRegVisitor(sta), regs_(network_) { @@ -338,17 +337,18 @@ FindRegInstances::FindRegInstances(StaState *sta) : InstanceSet FindRegInstances::findRegs(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches) + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode) { - visitRegs(clks, clk_rf, edge_triggered, latches); + visitRegs(clks, clk_rf, registers, latches, mode); return regs_; } void FindRegInstances::visitSequential(Instance *, - Sequential *) + const Sequential *) { } @@ -360,13 +360,14 @@ FindRegInstances::visitReg(Instance *inst) InstanceSet findRegInstances(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - StaState *sta) + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode, + const StaState *sta) { FindRegInstances find_regs(sta); - return find_regs.findRegs(clks, clk_rf, edge_triggered, latches); + return find_regs.findRegs(clks, clk_rf, registers, latches, mode); } //////////////////////////////////////////////////////////////// @@ -374,28 +375,30 @@ findRegInstances(ClockSet *clks, class FindRegPins : public FindRegVisitor { public: - FindRegPins(StaState *sta); + FindRegPins(const StaState *sta); PinSet findPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches); + bool registers, + bool latches, + const Mode *mode); +private: + void visitReg(Instance *inst) override; + void visitSequential(Instance *inst, + const Sequential *seq) override; protected: - virtual void visitReg(Instance *inst); - virtual void visitSequential(Instance *inst, - Sequential *seq); virtual bool matchPin(Pin *pin); void visitExpr(FuncExpr *expr, - Instance *inst, - Sequential *seq); + Instance *inst, + const Sequential *seq); // Sequential expressions to find instance pins. - virtual FuncExpr *seqExpr1(Sequential *seq) = 0; - virtual FuncExpr *seqExpr2(Sequential *seq) = 0; + virtual FuncExpr *seqExpr1(const Sequential *seq) = 0; + virtual FuncExpr *seqExpr2(const Sequential *seq) = 0; PinSet pins_; }; -FindRegPins::FindRegPins(StaState *sta) : +FindRegPins::FindRegPins(const StaState *sta) : FindRegVisitor(sta), pins_(network_) { @@ -403,17 +406,18 @@ FindRegPins::FindRegPins(StaState *sta) : PinSet FindRegPins::findPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches) + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode) { - visitRegs(clks, clk_rf, edge_triggered, latches); + visitRegs(clks, clk_rf, registers, latches, mode); return pins_; } void FindRegPins::visitSequential(Instance *inst, - Sequential *seq) + const Sequential *seq) { visitExpr(seqExpr1(seq), inst, seq); visitExpr(seqExpr2(seq), inst, seq); @@ -421,16 +425,15 @@ FindRegPins::visitSequential(Instance *inst, void FindRegPins::visitExpr(FuncExpr *expr, - Instance *inst, - Sequential *) + Instance *inst, + const Sequential *) { if (expr) { - FuncExprPortIterator port_iter(expr); - while (port_iter.hasNext()) { - LibertyPort *port = port_iter.next(); + LibertyPortSet ports = expr->ports(); + for (LibertyPort *port : ports) { Pin *pin = network_->findPin(inst, port); if (pin) - pins_.insert(pin); + pins_.insert(pin); } } } @@ -456,27 +459,27 @@ FindRegPins::matchPin(Pin *) class FindRegDataPins : public FindRegPins { public: - explicit FindRegDataPins(StaState *sta); + FindRegDataPins(const StaState *sta); -private: - virtual bool matchPin(Pin *pin); - virtual FuncExpr *seqExpr1(Sequential *seq); - virtual FuncExpr *seqExpr2(Sequential *seq); +protected: + bool matchPin(Pin *pin) override; + FuncExpr *seqExpr1(const Sequential *seq) override; + FuncExpr *seqExpr2(const Sequential *seq) override; }; -FindRegDataPins::FindRegDataPins(StaState *sta) : +FindRegDataPins::FindRegDataPins(const StaState *sta) : FindRegPins(sta) { } FuncExpr * -FindRegDataPins::seqExpr1(Sequential *seq) +FindRegDataPins::seqExpr1(const Sequential *seq) { return seq->data(); } FuncExpr * -FindRegDataPins::seqExpr2(Sequential *) +FindRegDataPins::seqExpr2(const Sequential *) { return nullptr; } @@ -508,13 +511,14 @@ hasMinPulseWidthCheck(LibertyPort *port) PinSet findRegDataPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - StaState *sta) + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode, + const StaState *sta) { FindRegDataPins find_regs(sta); - return find_regs.findPins(clks, clk_rf, edge_triggered, latches); + return find_regs.findPins(clks, clk_rf, registers, latches, mode); } //////////////////////////////////////////////////////////////// @@ -522,15 +526,15 @@ findRegDataPins(ClockSet *clks, class FindRegClkPins : public FindRegPins { public: - explicit FindRegClkPins(StaState *sta); + FindRegClkPins(const StaState *sta); -private: - virtual bool matchPin(Pin *pin); - virtual FuncExpr *seqExpr1(Sequential *seq); - virtual FuncExpr *seqExpr2(Sequential *seq); +protected: + bool matchPin(Pin *pin) override; + FuncExpr *seqExpr1(const Sequential *seq) override; + FuncExpr *seqExpr2(const Sequential *seq) override; }; -FindRegClkPins::FindRegClkPins(StaState *sta) : +FindRegClkPins::FindRegClkPins(const StaState *sta) : FindRegPins(sta) { } @@ -541,7 +545,7 @@ FindRegClkPins::matchPin(Pin *pin) // Liberty port clock attribute is not present in latches (for nlc18 anyway). LibertyPort *port = network_->libertyPort(pin); LibertyCell *cell = port->libertyCell(); - for (TimingArcSet *arc_set : cell->timingArcSets(port, nullptr)) { + for (TimingArcSet *arc_set : cell->timingArcSetsFrom(port)) { const TimingRole *role = arc_set->role(); if (role == TimingRole::regClkToQ() || role == TimingRole::latchEnToQ()) @@ -552,26 +556,27 @@ FindRegClkPins::matchPin(Pin *pin) FuncExpr * -FindRegClkPins::seqExpr1(Sequential *seq) +FindRegClkPins::seqExpr1(const Sequential *seq) { return seq->clock(); } FuncExpr * -FindRegClkPins::seqExpr2(Sequential *) +FindRegClkPins::seqExpr2(const Sequential *) { return nullptr; } PinSet findRegClkPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - StaState *sta) + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode, + const StaState *sta) { FindRegClkPins find_regs(sta); - return find_regs.findPins(clks, clk_rf, edge_triggered, latches); + return find_regs.findPins(clks, clk_rf, registers, latches, mode); } //////////////////////////////////////////////////////////////// @@ -579,15 +584,15 @@ findRegClkPins(ClockSet *clks, class FindRegAsyncPins : public FindRegPins { public: - explicit FindRegAsyncPins(StaState *sta); + FindRegAsyncPins(const StaState *sta); -private: - virtual bool matchPin(Pin *pin); - virtual FuncExpr *seqExpr1(Sequential *seq) { return seq->clear(); } - virtual FuncExpr *seqExpr2(Sequential *seq) { return seq->preset(); } +protected: + bool matchPin(Pin *pin) override; + FuncExpr *seqExpr1(const Sequential *seq) override { return seq->clear(); } + FuncExpr *seqExpr2(const Sequential *seq) override { return seq->preset(); } }; -FindRegAsyncPins::FindRegAsyncPins(StaState *sta) : +FindRegAsyncPins::FindRegAsyncPins(const StaState *sta) : FindRegPins(sta) { } @@ -597,7 +602,7 @@ FindRegAsyncPins::matchPin(Pin *pin) { LibertyPort *port = network_->libertyPort(pin); LibertyCell *cell = port->libertyCell(); - for (TimingArcSet *arc_set : cell->timingArcSets(port, nullptr)) { + for (TimingArcSet *arc_set : cell->timingArcSetsFrom(port)) { const TimingRole *role = arc_set->role(); if (role == TimingRole::regSetClr()) return true; @@ -607,13 +612,14 @@ FindRegAsyncPins::matchPin(Pin *pin) PinSet findRegAsyncPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - StaState *sta) + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode, + const StaState *sta) { FindRegAsyncPins find_regs(sta); - return find_regs.findPins(clks, clk_rf, edge_triggered, latches); + return find_regs.findPins(clks, clk_rf, registers, latches, mode); } //////////////////////////////////////////////////////////////// @@ -621,20 +627,22 @@ findRegAsyncPins(ClockSet *clks, class FindRegOutputPins : public FindRegPins { public: - explicit FindRegOutputPins(StaState *sta); + FindRegOutputPins(const StaState *sta); + +protected: + bool matchPin(Pin *pin) override; + // Unused. + FuncExpr *seqExpr1(const Sequential *seq) override; + FuncExpr *seqExpr2(const Sequential *seq) override; private: - virtual bool matchPin(Pin *pin); - virtual void visitSequential(Instance *inst, - Sequential *seq); + void visitSequential(Instance *inst, + const Sequential *seq) override; void visitOutput(LibertyPort *port, - Instance *inst); - // Unused. - virtual FuncExpr *seqExpr1(Sequential *seq); - virtual FuncExpr *seqExpr2(Sequential *seq); + Instance *inst); }; -FindRegOutputPins::FindRegOutputPins(StaState *sta) : +FindRegOutputPins::FindRegOutputPins(const StaState *sta) : FindRegPins(sta) { } @@ -644,11 +652,11 @@ FindRegOutputPins::matchPin(Pin *pin) { LibertyPort *port = network_->libertyPort(pin); LibertyCell *cell = port->libertyCell(); - for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, port)) { + for (TimingArcSet *arc_set : cell->timingArcSetsTo( port)) { const TimingRole *role = arc_set->role(); if (role == TimingRole::regClkToQ() - || role == TimingRole::latchEnToQ() - || role == TimingRole::latchDtoQ()) + || role == TimingRole::latchEnToQ() + || role == TimingRole::latchDtoQ()) return true; } return false; @@ -656,7 +664,7 @@ FindRegOutputPins::matchPin(Pin *pin) void FindRegOutputPins::visitSequential(Instance *inst, - Sequential *seq) + const Sequential *seq) { visitOutput(seq->output(), inst); visitOutput(seq->outputInv(), inst); @@ -664,7 +672,7 @@ FindRegOutputPins::visitSequential(Instance *inst, void FindRegOutputPins::visitOutput(LibertyPort *port, - Instance *inst) + Instance *inst) { if (port) { // Sequential outputs are internal ports. @@ -676,35 +684,36 @@ FindRegOutputPins::visitOutput(LibertyPort *port, LibertyPort *pin_port = network_->libertyPort(pin); FuncExpr *func = pin_port->function(); if (func - && func->port() - && func->port() == port) - pins_.insert(pin); + && func->port() + && func->port() == port) + pins_.insert(pin); } delete pin_iter; } } FuncExpr * -FindRegOutputPins::seqExpr1(Sequential *) +FindRegOutputPins::seqExpr1(const Sequential *) { return nullptr; } FuncExpr * -FindRegOutputPins::seqExpr2(Sequential *) +FindRegOutputPins::seqExpr2(const Sequential *) { return nullptr; } PinSet findRegOutputPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - StaState *sta) + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode, + const StaState *sta) { FindRegOutputPins find_regs(sta); - return find_regs.findPins(clks, clk_rf, edge_triggered, latches); + return find_regs.findPins(clks, clk_rf, registers, latches, mode); } //////////////////////////////////////////////////////////////// @@ -713,75 +722,75 @@ static TimingSense path_sense_thru[timing_sense_count][timing_sense_count]; static void initPathSenseThru1(TimingSense from, - TimingSense thru, - TimingSense to) + TimingSense thru, + TimingSense to) { - path_sense_thru[int(from)][int(thru)] = to; + path_sense_thru[static_cast(from)][static_cast(thru)] = to; } void initPathSenseThru() { initPathSenseThru1(TimingSense::positive_unate, TimingSense::positive_unate, - TimingSense::positive_unate); + TimingSense::positive_unate); initPathSenseThru1(TimingSense::positive_unate, TimingSense::negative_unate, - TimingSense::negative_unate); + TimingSense::negative_unate); initPathSenseThru1(TimingSense::positive_unate, TimingSense::non_unate, - TimingSense::non_unate); + TimingSense::non_unate); initPathSenseThru1(TimingSense::positive_unate, TimingSense::none, - TimingSense::none); + TimingSense::none); initPathSenseThru1(TimingSense::positive_unate, TimingSense::unknown, - TimingSense::unknown); + TimingSense::unknown); initPathSenseThru1(TimingSense::negative_unate, TimingSense::positive_unate, - TimingSense::negative_unate); + TimingSense::negative_unate); initPathSenseThru1(TimingSense::negative_unate, TimingSense::negative_unate, - TimingSense::positive_unate); + TimingSense::positive_unate); initPathSenseThru1(TimingSense::negative_unate, TimingSense::non_unate, - TimingSense::non_unate); + TimingSense::non_unate); initPathSenseThru1(TimingSense::negative_unate, TimingSense::none, - TimingSense::none); + TimingSense::none); initPathSenseThru1(TimingSense::negative_unate, TimingSense::unknown, - TimingSense::unknown); + TimingSense::unknown); initPathSenseThru1(TimingSense::non_unate, TimingSense::positive_unate, - TimingSense::non_unate); + TimingSense::non_unate); initPathSenseThru1(TimingSense::non_unate, TimingSense::negative_unate, - TimingSense::non_unate); + TimingSense::non_unate); initPathSenseThru1(TimingSense::non_unate, TimingSense::non_unate, - TimingSense::non_unate); + TimingSense::non_unate); initPathSenseThru1(TimingSense::non_unate, TimingSense::none, - TimingSense::none); + TimingSense::none); initPathSenseThru1(TimingSense::non_unate, TimingSense::unknown, - TimingSense::unknown); + TimingSense::unknown); initPathSenseThru1(TimingSense::none, TimingSense::positive_unate, - TimingSense::none); + TimingSense::none); initPathSenseThru1(TimingSense::none, TimingSense::negative_unate, - TimingSense::none); + TimingSense::none); initPathSenseThru1(TimingSense::none, TimingSense::non_unate, - TimingSense::none); + TimingSense::none); initPathSenseThru1(TimingSense::none, TimingSense::none, - TimingSense::none); + TimingSense::none); initPathSenseThru1(TimingSense::none, TimingSense::unknown, - TimingSense::unknown); + TimingSense::unknown); initPathSenseThru1(TimingSense::unknown, TimingSense::positive_unate, - TimingSense::unknown); + TimingSense::unknown); initPathSenseThru1(TimingSense::unknown, TimingSense::negative_unate, - TimingSense::unknown); + TimingSense::unknown); initPathSenseThru1(TimingSense::unknown, TimingSense::non_unate, - TimingSense::unknown); + TimingSense::unknown); initPathSenseThru1(TimingSense::unknown, TimingSense::none, - TimingSense::unknown); + TimingSense::unknown); initPathSenseThru1(TimingSense::unknown, TimingSense::unknown, - TimingSense::unknown); + TimingSense::unknown); } static TimingSense pathSenseThru(TimingSense from_sense, - TimingSense thru_sense) + TimingSense thru_sense) { - return path_sense_thru[int(from_sense)][int(thru_sense)]; + return path_sense_thru[static_cast(from_sense)][static_cast(thru_sense)]; } -} // namespace +} // namespace sta diff --git a/search/FindRegister.hh b/search/FindRegister.hh index f9820f9c7..36f9cd894 100644 --- a/search/FindRegister.hh +++ b/search/FindRegister.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,29 +25,51 @@ #pragma once #include "LibertyClass.hh" +#include "Mode.hh" #include "NetworkClass.hh" #include "SdcClass.hh" #include "StaState.hh" +#include "Transition.hh" namespace sta { InstanceSet -findRegInstances(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, bool latches, StaState *sta); +findRegInstances(ClockSet *clks, + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode, + const StaState *sta); PinSet -findRegDataPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, bool latches, StaState *sta); +findRegDataPins(ClockSet *clks, + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode, + const StaState *sta); PinSet -findRegClkPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, bool latches, StaState *sta); +findRegClkPins(ClockSet *clks, + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode, + const StaState *sta); PinSet -findRegAsyncPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, bool latches, StaState *sta); +findRegAsyncPins(ClockSet *clks, + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode, + const StaState *sta); PinSet -findRegOutputPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, bool latches, StaState *sta); +findRegOutputPins(ClockSet *clks, + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode, + const StaState *sta); void initPathSenseThru(); -} // namespace +} // namespace sta diff --git a/search/GatedClk.cc b/search/GatedClk.cc index 804cee9df..11880d025 100644 --- a/search/GatedClk.cc +++ b/search/GatedClk.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,11 +24,13 @@ #include "GatedClk.hh" +#include "ClkNetwork.hh" #include "FuncExpr.hh" +#include "Graph.hh" #include "Liberty.hh" -#include "PortDirection.hh" +#include "Mode.hh" #include "Network.hh" -#include "Graph.hh" +#include "PortDirection.hh" #include "Sdc.hh" #include "Search.hh" @@ -40,88 +42,87 @@ GatedClk::GatedClk(const StaState *sta) : } bool -GatedClk::isGatedClkEnable(Vertex *vertex) const +GatedClk::isGatedClkEnable(Vertex *vertex, + const Mode *mode) const { bool is_gated_clk_enable; const Pin *clk_pin; LogicValue logic_active_value; - isGatedClkEnable(vertex, - is_gated_clk_enable, clk_pin, logic_active_value); + isGatedClkEnable(vertex, mode, + is_gated_clk_enable, clk_pin, logic_active_value); return is_gated_clk_enable; } void GatedClk::isGatedClkEnable(Vertex *enable_vertex, - bool &is_gated_clk_enable, - const Pin *&clk_pin, - LogicValue &logic_active_value) const + const Mode *mode, + // Return values. + bool &is_gated_clk_enable, + const Pin *&clk_pin, + LogicValue &logic_active_value) const { is_gated_clk_enable = false; const Pin *enable_pin = enable_vertex->pin(); const Instance *inst = network_->instance(enable_pin); LibertyPort *enable_port = network_->libertyPort(enable_pin); EvalPred *eval_pred = search_->evalPred(); + ClkNetwork *clk_network = mode->clkNetwork(); if (enable_port + && !clk_network->isClock(enable_vertex) && enable_port->direction()->isInput() - && !sdc_->isDisableClockGatingCheck(enable_pin) - && !sdc_->isDisableClockGatingCheck(inst) - && eval_pred->searchFrom(enable_vertex)) { + && eval_pred->searchFrom(enable_vertex, mode)) { + const Sdc *sdc = mode->sdc(); FuncExpr *func = nullptr; Vertex *gclk_vertex = nullptr; VertexOutEdgeIterator edge_iter(enable_vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); gclk_vertex = edge->to(graph_); + const Pin *gclk_pin = gclk_vertex->pin(); if (edge->role() == TimingRole::combinational() - && eval_pred->searchTo(gclk_vertex) - && eval_pred->searchThru(edge)) { - const Pin *gclk_pin = gclk_vertex->pin(); - LibertyPort *gclk_port = network_->libertyPort(gclk_pin); - if (gclk_port) { - func = gclk_port->function(); - if (func) - break; - } - } - } - if (func - && search_->isClock(gclk_vertex) - && !search_->isClock(enable_vertex)) { - FuncExprPortIterator clk_port_iter(func); - while (clk_port_iter.hasNext()) { - LibertyPort *clk_port = clk_port_iter.next(); - if (clk_port != enable_port) { - bool is_clk_gate = false; - isClkGatingFunc(func, enable_port, clk_port, - is_clk_gate, logic_active_value); - if (is_clk_gate) { - clk_pin = network_->findPin(inst, clk_port); - if (clk_pin - && !sdc_->isDisableClockGatingCheck(clk_pin) - && search_->isClock(graph_->pinLoadVertex(clk_pin))) { - is_gated_clk_enable = true; - break; - } - } - } + && eval_pred->searchTo(gclk_vertex, mode) + && eval_pred->searchThru(edge, mode)) { + LibertyPort *gclk_port = network_->libertyPort(gclk_pin); + func = gclk_port->function(); + if (gclk_port + && func + && clk_network->isClock(gclk_vertex)) { + LibertyPortSet clk_ports = func->ports(); + for (LibertyPort *clk_port : clk_ports) { + if (clk_port != enable_port) { + bool is_clk_gate = false; + isClkGatingFunc(func, enable_port, clk_port, + is_clk_gate, logic_active_value); + if (is_clk_gate) { + clk_pin = network_->findPin(inst, clk_port); + if (clk_pin + && !sdc->isDisableClockGatingCheck(clk_pin) + && clk_network->isClock(graph_->pinLoadVertex(clk_pin))) { + is_gated_clk_enable = true; + return; + } + } + } + } + } } } + } } -void +PinSet GatedClk::gatedClkEnables(Vertex *clk_vertex, - // Return value. - PinSet &enable_pins) + const Mode *mode) { + PinSet enable_pins(network_); const Pin *clk_pin = clk_vertex->pin(); const Instance *inst = network_->instance(clk_pin); LibertyPort *clk_port = network_->libertyPort(clk_pin); EvalPred *eval_pred = search_->evalPred(); if (clk_port - && !sdc_->isDisableClockGatingCheck(clk_pin) - && !sdc_->isDisableClockGatingCheck(inst) - && eval_pred->searchFrom(clk_vertex)) { + && eval_pred->searchFrom(clk_vertex, mode)) { + ClkNetwork *clk_network = mode->clkNetwork(); FuncExpr *func = nullptr; Vertex *gclk_vertex = nullptr; VertexOutEdgeIterator edge_iter(clk_vertex, graph_); @@ -129,56 +130,55 @@ GatedClk::gatedClkEnables(Vertex *clk_vertex, Edge *edge = edge_iter.next(); gclk_vertex = edge->to(graph_); if (edge->role() == TimingRole::combinational() - && eval_pred->searchTo(gclk_vertex) - && eval_pred->searchThru(edge)) { - const Pin *gclk_pin = gclk_vertex->pin(); - LibertyPort *gclk_port = network_->libertyPort(gclk_pin); - if (gclk_port) { - func = gclk_port->function(); - if (func) { - if (search_->isClock(gclk_vertex)) { - FuncExprPortIterator enable_port_iter(func); - while (enable_port_iter.hasNext()) { - LibertyPort *enable_port = enable_port_iter.next(); - if (enable_port != clk_port) { - bool is_clk_gate; - LogicValue logic_value; - isClkGatingFunc(func, enable_port, clk_port, - is_clk_gate, logic_value); - if (is_clk_gate) { - Pin *enable_pin = network_->findPin(inst, enable_port); - if (enable_pin - && !sdc_->isDisableClockGatingCheck(enable_pin) - && !search_->isClock(graph_->pinLoadVertex(enable_pin))) { - enable_pins.insert(enable_pin); - } - } - } - } - } - break; - } - } + && eval_pred->searchTo(gclk_vertex, mode) + && eval_pred->searchThru(edge, mode)) { + const Pin *gclk_pin = gclk_vertex->pin(); + LibertyPort *gclk_port = network_->libertyPort(gclk_pin); + if (gclk_port) { + func = gclk_port->function(); + if (func) { + if (clk_network->isClock(gclk_vertex)) { + LibertyPortSet enable_ports = func->ports(); + for (LibertyPort *enable_port : enable_ports) { + if (enable_port != clk_port) { + bool is_clk_gate; + LogicValue logic_value; + isClkGatingFunc(func, enable_port, clk_port, + is_clk_gate, logic_value); + if (is_clk_gate) { + Pin *enable_pin = network_->findPin(inst, enable_port); + if (enable_pin + && !clk_network->isClock(graph_->pinLoadVertex(enable_pin))) { + enable_pins.insert(enable_pin); + } + } + } + } + } + break; + } + } } } } + return enable_pins; } void GatedClk::isClkGatingFunc(FuncExpr *func, - LibertyPort *enable_port, - LibertyPort *clk_port, - bool &is_clk_gate, - LogicValue &logic_value) const + LibertyPort *enable_port, + LibertyPort *clk_port, + bool &is_clk_gate, + LogicValue &logic_value) const { // The function should be in two-level SOP or POS form depending on "cost". // We need to apply literal cofactor if any input port is constant and // do "simple" logic minimization based on SOP and POS. - while (func->op() == FuncExpr::op_not) + while (func->op() == FuncExpr::Op::not_) func = func->left(); - if (func->op() == FuncExpr::op_and) + if (func->op() == FuncExpr::Op::and_) logic_value = LogicValue::one; - else if (func->op() == FuncExpr::op_or) + else if (func->op() == FuncExpr::Op::or_) logic_value = LogicValue::zero; else { is_clk_gate = false; @@ -190,35 +190,32 @@ GatedClk::isClkGatingFunc(FuncExpr *func, functionClkOperands(func, func->right(), funcs); bool need_gating_check = false; - FuncExprSet::Iterator expr_iter(funcs); - while (expr_iter.hasNext()) { - FuncExpr *expr = expr_iter.next(); - if (expr->op() == FuncExpr::op_not) { - if (expr->left()->op() == FuncExpr::op_port - && expr->left()->port() == clk_port) { - need_gating_check = true; - logic_value = (logic_value == LogicValue::one) ? LogicValue::zero : LogicValue::one; + for (FuncExpr *expr : funcs) { + if (expr->op() == FuncExpr::Op::not_) { + if (expr->left()->op() == FuncExpr::Op::port + && expr->left()->port() == clk_port) { + need_gating_check = true; + logic_value = (logic_value == LogicValue::one) + ? LogicValue::zero + : LogicValue::one; } } else { - if (expr->op() == FuncExpr::op_port - && expr->port() == clk_port) { - need_gating_check = true; + if (expr->op() == FuncExpr::Op::port + && expr->port() == clk_port) { + need_gating_check = true; } } } if (need_gating_check) { - FuncExprSet::Iterator expr_iter2(funcs); - while (expr_iter2.hasNext()) { - FuncExpr *expr = expr_iter2.next(); - FuncExprPortIterator en_port_iter(expr); - while (en_port_iter.hasNext()) { - LibertyPort *port = en_port_iter.next(); - if (port == enable_port) { - is_clk_gate = true; - return; - } + for (FuncExpr *expr : funcs) { + LibertyPortSet ports = expr->ports(); + for (LibertyPort *port : ports) { + if (port == enable_port) { + is_clk_gate = true; + return; + } } } } @@ -227,8 +224,8 @@ GatedClk::isClkGatingFunc(FuncExpr *func, void GatedClk::functionClkOperands(FuncExpr *root_expr, - FuncExpr *expr, - FuncExprSet &funcs) const + FuncExpr *expr, + FuncExprSet &funcs) const { if (expr->op() != root_expr->op()) funcs.insert(expr); @@ -240,7 +237,7 @@ GatedClk::functionClkOperands(FuncExpr *root_expr, const RiseFall * GatedClk::gatedClkActiveTrans(LogicValue active_value, - const MinMax *min_max) const + const MinMax *min_max) const { const RiseFall *leading_rf; switch (active_value) { @@ -262,4 +259,4 @@ GatedClk::gatedClkActiveTrans(LogicValue active_value, return leading_rf->opposite(); } -} // namespace +} // namespace sta diff --git a/search/GatedClk.hh b/search/GatedClk.hh index 8f63307d5..c6853dbc4 100644 --- a/search/GatedClk.hh +++ b/search/GatedClk.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,40 +24,48 @@ #pragma once -#include "SdcClass.hh" +#include + #include "GraphClass.hh" -#include "SearchClass.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "Mode.hh" +#include "NetworkClass.hh" +#include "SdcClass.hh" #include "StaState.hh" +#include "Transition.hh" namespace sta { -typedef Set FuncExprSet; +using FuncExprSet = std::set; class GatedClk : public StaState { public: GatedClk(const StaState *sta); - bool isGatedClkEnable(Vertex *vertex) const; + bool isGatedClkEnable(Vertex *vertexm, + const Mode *mode) const; void isGatedClkEnable(Vertex *enable_vertex, - bool &is_gated_clk_enable, - const Pin *&clk_pin, - LogicValue &logic_active_value) const; - void gatedClkEnables(Vertex *clk_vertex, - // Return value. - PinSet &enable_pins); + const Mode *mode, + // Return values. + bool &is_gated_clk_enable, + const Pin *&clk_pin, + LogicValue &logic_active_value) const; + PinSet gatedClkEnables(Vertex *clk_vertex, + const Mode *mode); const RiseFall *gatedClkActiveTrans(LogicValue active_value, const MinMax *min_max) const; protected: void isClkGatingFunc(FuncExpr *func, - LibertyPort *enable_port, - LibertyPort *clk_port, - bool &is_clk_gate, - LogicValue &logic_value) const; + LibertyPort *enable_port, + LibertyPort *clk_port, + bool &is_clk_gate, + LogicValue &logic_value) const; void functionClkOperands(FuncExpr *root_expr, - FuncExpr *curr_expr, - FuncExprSet &funcs) const; + FuncExpr *curr_expr, + FuncExprSet &funcs) const; }; -} // namespace +} // namespace sta diff --git a/search/Genclks.cc b/search/Genclks.cc index e002c4c78..2123ea1bb 100644 --- a/search/Genclks.cc +++ b/search/Genclks.cc @@ -1,103 +1,90 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Genclks.hh" -#include "Stats.hh" +#include + +#include "Bfs.hh" +#include "Clock.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" -#include "Report.hh" +#include "ExceptionPath.hh" +#include "Graph.hh" +#include "Levelize.hh" +#include "Mode.hh" #include "Network.hh" +#include "Path.hh" #include "PortDirection.hh" -#include "Graph.hh" +#include "Report.hh" +#include "Scene.hh" #include "Sdc.hh" -#include "ExceptionPath.hh" -#include "Clock.hh" -#include "StaState.hh" +#include "Search.hh" #include "SearchPred.hh" -#include "Bfs.hh" +#include "StaState.hh" +#include "Stats.hh" #include "TagGroup.hh" -#include "Corner.hh" -#include "PathAnalysisPt.hh" -#include "Levelize.hh" -#include "Path.hh" -#include "Search.hh" #include "Variables.hh" namespace sta { -using std::max; - class GenclkInfo { public: GenclkInfo(Clock *gclk, - Level gclk_level, - VertexSet *fanins, - FilterPath *src_filter); + Level gclk_level, + FilterPath *src_filter, + const StaState *sta); ~GenclkInfo(); - EdgeSet *fdbkEdges() const { return fdbk_edges_; } - VertexSet *fanins() const { return fanins_; } + EdgeSet &fdbkEdges() { return fdbk_edges_; } + VertexSet &fanins() { return fanins_; } Level gclkLevel() const { return gclk_level_; } FilterPath *srcFilter() const { return src_filter_; } - void setLatchFdbkEdges(EdgeSet *fdbk_edges); bool foundLatchFdbkEdges() const { return found_latch_fdbk_edges_; } void setFoundLatchFdbkEdges(bool found); protected: Clock *gclk_; Level gclk_level_; - VertexSet *fanins_; - EdgeSet *fdbk_edges_; - bool found_latch_fdbk_edges_; + VertexSet fanins_; + EdgeSet fdbk_edges_; + bool found_latch_fdbk_edges_{false}; FilterPath *src_filter_; }; GenclkInfo::GenclkInfo(Clock *gclk, - Level gclk_level, - VertexSet *fanins, - FilterPath *src_filter) : + Level gclk_level, + FilterPath *src_filter, + const StaState *sta) : gclk_(gclk), gclk_level_(gclk_level), - fanins_(fanins), - fdbk_edges_(nullptr), - found_latch_fdbk_edges_(false), + fanins_(makeVertexSet(sta->graph())), src_filter_(src_filter) { } -GenclkInfo::~GenclkInfo() -{ - delete fanins_; - delete fdbk_edges_; - delete src_filter_; -} - -void -GenclkInfo::setLatchFdbkEdges(EdgeSet *fdbk_edges) -{ - fdbk_edges_ = fdbk_edges; -} +GenclkInfo::~GenclkInfo() { delete src_filter_; } void GenclkInfo::setFoundLatchFdbkEdges(bool found) @@ -107,16 +94,17 @@ GenclkInfo::setFoundLatchFdbkEdges(bool found) //////////////////////////////////////////////////////////////// -Genclks::Genclks(StaState *sta) : +Genclks::Genclks(const Mode *mode, + StaState *sta) : StaState(sta), - found_insertion_delays_(false), + mode_(mode), vertex_src_paths_map_(graph_) { } Genclks::~Genclks() { - genclk_info_map_.deleteContentsClear(); + deleteContents(genclk_info_map_); clearSrcPaths(); } @@ -124,8 +112,7 @@ void Genclks::clear() { found_insertion_delays_ = false; - genclk_info_map_.deleteContentsClear(); - vertex_src_paths_map_.clear(); + deleteContents(genclk_info_map_); clearSrcPaths(); } @@ -134,7 +121,7 @@ Genclks::fanins(const Clock *clk) { GenclkInfo *genclk_info = genclkInfo(clk); if (genclk_info) - return genclk_info->fanins(); + return &genclk_info->fanins(); else return nullptr; } @@ -159,7 +146,7 @@ Genclks::clkPinMaxLevel(const Clock *clk) const Level max_level = 0; for (const Pin *pin : clk->leafPins()) { Vertex *vertex = srcPath(pin); - max_level = max(max_level, vertex->level()); + max_level = std::max(max_level, vertex->level()); } return max_level; } @@ -167,9 +154,9 @@ Genclks::clkPinMaxLevel(const Clock *clk) const class ClockPinMaxLevelLess { public: - explicit ClockPinMaxLevelLess(const Genclks *genclks); + ClockPinMaxLevelLess(const Genclks *genclks); bool operator()(Clock *clk1, - Clock *clk2) const; + Clock *clk2) const; protected: const Genclks *genclks_; @@ -182,7 +169,7 @@ ClockPinMaxLevelLess::ClockPinMaxLevelLess(const Genclks *genclks) : bool ClockPinMaxLevelLess::operator()(Clock *clk1, - Clock *clk2) const + Clock *clk2) const { return genclks_->clkPinMaxLevel(clk1) < genclks_->clkPinMaxLevel(clk2); } @@ -198,111 +185,83 @@ Genclks::ensureInsertionDelays() Stats stats(debug_, report_); debugPrint(debug_, "genclk", 1, "find generated clk insertion delays"); + clearSrcPaths(); + Sdc *sdc = mode_->sdc(); ClockSeq gclks; - for (auto clk : sdc_->clks()) { + for (Clock *clk : sdc->clocks()) { if (clk->isGenerated()) { - checkMaster(clk); - gclks.push_back(clk); + checkMaster(clk, sdc); + gclks.push_back(clk); } } - clearSrcPaths(); - // Generated clocks derived from a generated clock inherit its // insertion delay, so sort the clocks by source pin level. sort(gclks, ClockPinMaxLevelLess(this)); for (Clock *gclk : gclks) { if (gclk->masterClk()) { - findInsertionDelays(gclk); - recordSrcPaths(gclk); + findInsertionDelays(gclk); + recordSrcPaths(gclk); } } - stats.report("Find generated clk insertion delays"); found_insertion_delays_ = true; } } -// Similar to ClkTreeSearchPred but ignore constants. -class GenClkMasterSearchPred : public SearchPred +//////////////////////////////////////////////////////////////// + +class GenClkMasterSearchPred : public ClkTreeSearchPred { public: - explicit GenClkMasterSearchPred(const StaState *sta); - virtual bool searchFrom(const Vertex *from_vertex); - virtual bool searchThru(Edge *edge); - virtual bool searchTo(const Vertex *to_vertex); - -protected: - const StaState *sta_; + GenClkMasterSearchPred(const StaState *sta); + bool searchThruAllow(const TimingRole *role) const override; }; GenClkMasterSearchPred::GenClkMasterSearchPred(const StaState *sta) : - SearchPred(), - sta_(sta) -{ -} - -bool -GenClkMasterSearchPred::searchFrom(const Vertex *from_vertex) + ClkTreeSearchPred(sta) { - return !from_vertex->isDisabledConstraint(); } bool -GenClkMasterSearchPred::searchThru(Edge *edge) +GenClkMasterSearchPred::searchThruAllow(const TimingRole *role) const { - const Variables *variables = sta_->variables(); - const TimingRole *role = edge->role(); - // Propagate clocks through constants. - return !(edge->role()->isTimingCheck() - || edge->isDisabledLoop() - || edge->isDisabledConstraint() - // Constants disable edge cond expression. - || edge->isDisabledCond() - || sta_->isDisabledCondDefault(edge) - // Register/latch preset/clr edges are disabled by default. - || (!variables->presetClrArcsEnabled() - && role == TimingRole::regSetClr()) - || (edge->isBidirectInstPath() - && !variables->bidirectInstPathsEnabled()) - || (edge->isBidirectNetPath() - && !variables->bidirectNetPathsEnabled())); + return role->isWire() + || role == TimingRole::combinational() + || role == TimingRole::regClkToQ(); } -bool -GenClkMasterSearchPred::searchTo(const Vertex *) -{ - return true; -} +//////////////////////////////////////////////////////////////// void -Genclks::checkMaster(Clock *gclk) +Genclks::checkMaster(Clock *gclk, + const Sdc *sdc) { - ensureMaster(gclk); + ensureMaster(gclk, sdc); if (gclk->masterClk() == nullptr) - report_->warn(1060, "no master clock found for generated clock %s.", - gclk->name()); + report_->warn(1060, "no master clock found for generated clock {}.", + gclk->name()); } void -Genclks::ensureMaster(Clock *gclk) +Genclks::ensureMaster(Clock *gclk, + const Sdc *sdc) { Clock *master_clk = gclk->masterClk(); if (master_clk == nullptr) { int master_clk_count = 0; bool found_master = false; Pin *src_pin = gclk->srcPin(); - ClockSet *master_clks = sdc_->findClocks(src_pin); - ClockSet::Iterator master_iter(master_clks); - if (master_iter.hasNext()) { - while (master_iter.hasNext()) { - master_clk = master_iter.next(); + ClockSet *master_clks = sdc->findClocks(src_pin); + if (master_clks) { + ClockSet::iterator master_iter = master_clks->begin(); + while (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, " %s master clk %s", - gclk->name(), + debugPrint(debug_, "genclk", 2, " {} master clk {}", gclk->name(), master_clk->name()); found_master = true; master_clk_count++; @@ -317,17 +276,16 @@ Genclks::ensureMaster(Clock *gclk) while (iter.hasNext()) { Vertex *vertex = iter.next(); Pin *pin = vertex->pin(); - if (sdc_->isLeafPinClock(pin)) { - ClockSet *master_clks = sdc_->findLeafPinClocks(pin); + if (sdc->isLeafPinClock(pin)) { + ClockSet *master_clks = sdc->findLeafPinClocks(pin); if (master_clks) { - ClockSet::Iterator master_iter(master_clks); - if (master_iter.hasNext()) { - master_clk = master_iter.next(); + 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, " %s master clk %s", - gclk->name(), + debugPrint(debug_, "genclk", 2, " {} master clk {}", gclk->name(), master_clk->name()); master_clk_count++; break; @@ -335,22 +293,21 @@ Genclks::ensureMaster(Clock *gclk) } } } - iter.enqueueAdjacentVertices(vertex); + iter.enqueueAdjacentVertices(vertex, mode_); } } if (master_clk_count > 1) report_->warn(1061, - "generated clock %s pin %s is in the fanout of multiple clocks.", - gclk->name(), - network_->pathName(src_pin)); + "generated clock {} pin {} is in the fanout of multiple clocks.", + gclk->name(), network_->pathName(src_pin)); } } void Genclks::seedSrcPins(Clock *clk, - BfsBkwdIterator &iter) + BfsBkwdIterator &iter) { - VertexSet src_vertices(graph_); + VertexSet src_vertices = makeVertexSet(this); clk->srcPinVertices(src_vertices, network_, graph_); for (Vertex *vertex : src_vertices) iter.enqueue(vertex); @@ -359,55 +316,36 @@ Genclks::seedSrcPins(Clock *clk, //////////////////////////////////////////////////////////////// // Similar to ClkTreeSearchPred but -// search thru constants // respect generated clock combinational attribute -// search thru disabled loop arcs class GenClkFaninSrchPred : public GenClkMasterSearchPred { public: - explicit GenClkFaninSrchPred(Clock *gclk, - const StaState *sta); - virtual bool searchFrom(const Vertex *from_vertex); - virtual bool searchThru(Edge *edge); - virtual bool searchTo(const Vertex *to_vertex); + GenClkFaninSrchPred(Clock *gclk, + const StaState *sta); + bool searchThruAllow(const TimingRole *role) const override; private: bool combinational_; }; GenClkFaninSrchPred::GenClkFaninSrchPred(Clock *gclk, - const StaState *sta) : + const StaState *sta) : GenClkMasterSearchPred(sta), combinational_(gclk->combinational()) { } bool -GenClkFaninSrchPred::searchFrom(const Vertex *from_vertex) -{ - return !from_vertex->isDisabledConstraint(); -} - -bool -GenClkFaninSrchPred::searchThru(Edge *edge) +GenClkFaninSrchPred::searchThruAllow(const TimingRole *role) const { - const TimingRole *role = edge->role(); - return GenClkMasterSearchPred::searchThru(edge) - && (role == TimingRole::combinational() - || role == TimingRole::wire() - || !combinational_); -} - -bool -GenClkFaninSrchPred::searchTo(const Vertex *) -{ - return true; + return role == TimingRole::combinational() + || role == TimingRole::wire() + || !combinational_; } void Genclks::findFanin(Clock *gclk, - // Return value. - VertexSet *fanins) + VertexSet &fanins) { // Search backward from generated clock source pin to a clock pin. GenClkFaninSrchPred srch_pred(gclk, this); @@ -415,102 +353,97 @@ Genclks::findFanin(Clock *gclk, seedClkVertices(gclk, iter, fanins); while (iter.hasNext()) { Vertex *vertex = iter.next(); - if (!fanins->hasKey(vertex)) { - fanins->insert(vertex); - debugPrint(debug_, "genclk", 2, "gen clk %s fanin %s", - gclk->name(), vertex->to_string(this).c_str()); - iter.enqueueAdjacentVertices(vertex); + if (!fanins.contains(vertex)) { + fanins.insert(vertex); + debugPrint(debug_, "genclk", 2, "gen clk {} fanin {}", gclk->name(), + vertex->to_string(this)); + iter.enqueueAdjacentVertices(vertex, mode_); } } } void Genclks::seedClkVertices(Clock *clk, - BfsBkwdIterator &iter, - VertexSet *fanins) + BfsBkwdIterator &iter, + VertexSet &fanins) { for (const Pin *pin : clk->leafPins()) { Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - fanins->insert(vertex); - iter.enqueueAdjacentVertices(vertex); + fanins.insert(vertex); + iter.enqueueAdjacentVertices(vertex, mode_); if (bidirect_drvr_vertex) { - fanins->insert(bidirect_drvr_vertex); - iter.enqueueAdjacentVertices(bidirect_drvr_vertex); + fanins.insert(bidirect_drvr_vertex); + iter.enqueueAdjacentVertices(bidirect_drvr_vertex, mode_); } } } //////////////////////////////////////////////////////////////// -class GenClkInsertionSearchPred : public SearchPred0, public DynLoopSrchPred +class GenClkInsertionSearchPred : public SearchPred0 { public: GenClkInsertionSearchPred(Clock *gclk, - TagGroupBldr *tag_bldr, - GenclkInfo *genclk_info, - const StaState *sta); - virtual bool searchThru(Edge *edge); - virtual bool searchTo(const Vertex *to_vertex); + GenclkInfo *genclk_info, + const StaState *sta); + bool searchThru(Edge *edge, + const Mode *mode) const override; + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; private: - bool isNonGeneratedClkPin(const Pin *pin) const; + bool isNonGeneratedClkPin(const Pin *pin, + const Sdc *sdc) const; Clock *gclk_; GenclkInfo *genclk_info_; }; GenClkInsertionSearchPred::GenClkInsertionSearchPred(Clock *gclk, - TagGroupBldr *tag_bldr, - GenclkInfo *genclk_info, - const StaState *sta) : + GenclkInfo *genclk_info, + const StaState *sta) : SearchPred0(sta), - DynLoopSrchPred(tag_bldr), gclk_(gclk), genclk_info_(genclk_info) { } bool -GenClkInsertionSearchPred::searchThru(Edge *edge) +GenClkInsertionSearchPred::searchThru(Edge *edge, + const Mode *mode) const { - const Graph *graph = sta_->graph(); - const Sdc *sdc = sta_->sdc(); - Search *search = sta_->search(); const TimingRole *role = edge->role(); - EdgeSet *fdbk_edges = genclk_info_->fdbkEdges(); - return SearchPred0::searchThru(edge) - && !role->isTimingCheck() - && (sta_->variables()->clkThruTristateEnabled() - || !(role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable())) - && !(fdbk_edges && fdbk_edges->hasKey(edge)) - && loopEnabled(edge, sdc, graph, search); + EdgeSet &fdbk_edges = genclk_info_->fdbkEdges(); + return SearchPred0::searchThru(edge, mode) && !role->isTimingCheck() + && (sta_->variables()->clkThruTristateEnabled() + || !(role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable())) + && !fdbk_edges.contains(edge); } bool -GenClkInsertionSearchPred::searchTo(const Vertex *to_vertex) +GenClkInsertionSearchPred::searchTo(const Vertex *to_vertex, + const Mode *mode) const { Pin *to_pin = to_vertex->pin(); - return SearchPred0::searchTo(to_vertex) - // Propagate through other generated clock roots but not regular - // clock roots. - && !(!gclk_->leafPins().hasKey(to_pin) - && isNonGeneratedClkPin(to_pin)) - && genclk_info_->fanins()->hasKey(const_cast(to_vertex)); + return SearchPred0::searchTo(to_vertex, mode) + // Propagate through other generated clock roots but not regular + // clock roots. + && !(!gclk_->leafPins().contains(to_pin) + && isNonGeneratedClkPin(to_pin, mode->sdc())) + && genclk_info_->fanins().contains(const_cast(to_vertex)); } bool -GenClkInsertionSearchPred::isNonGeneratedClkPin(const Pin *pin) const +GenClkInsertionSearchPred::isNonGeneratedClkPin(const Pin *pin, + const Sdc *sdc) const { - const Sdc *sdc = sta_->sdc(); ClockSet *clks = sdc->findLeafPinClocks(pin); if (clks) { - ClockSet::Iterator clk_iter(clks); - while (clk_iter.hasNext()) { - const Clock *clk = clk_iter.next(); + for (const Clock *clk : *clks) { if (!clk->isGenerated()) - return true; + return true; } } return false; @@ -521,11 +454,10 @@ GenClkInsertionSearchPred::isNonGeneratedClkPin(const Pin *pin) const void Genclks::findInsertionDelays(Clock *gclk) { - debugPrint(debug_, "genclk", 2, "find gen clk %s insertion", - gclk->name()); + debugPrint(debug_, "genclk", 2, "find gen clk {} insertion", gclk->name()); GenclkInfo *genclk_info = makeGenclkInfo(gclk); FilterPath *src_filter = genclk_info->srcFilter(); - GenClkInsertionSearchPred srch_pred(gclk, nullptr, genclk_info, this); + GenClkInsertionSearchPred srch_pred(gclk, genclk_info, this); BfsFwdIterator insert_iter(BfsIndex::other, &srch_pred, this); seedSrcPins(gclk, src_filter, insert_iter); // Propagate arrivals to generated clk root pin level. @@ -533,54 +465,48 @@ Genclks::findInsertionDelays(Clock *gclk) // Unregister the filter so that it is not triggered by other searches. // The exception itself has to stick around because the source path // tags reference it. - sdc_->unrecordException(src_filter); + mode_->sdc()->unrecordException(src_filter); } GenclkInfo * Genclks::makeGenclkInfo(Clock *gclk) { - FilterPath *src_filter = makeSrcFilter(gclk); + FilterPath *src_filter = makeSrcFilter(gclk, mode_->sdc()); Level gclk_level = clkPinMaxLevel(gclk); - VertexSet *fanins = new VertexSet(graph_); - findFanin(gclk, fanins); - GenclkInfo *genclk_info = new GenclkInfo(gclk, gclk_level, fanins, - src_filter); - genclk_info_map_.insert(gclk, genclk_info); + GenclkInfo *genclk_info = new GenclkInfo(gclk, gclk_level, src_filter, this); + findFanin(gclk, genclk_info->fanins()); + genclk_info_map_[gclk] = genclk_info; return genclk_info; } GenclkInfo * Genclks::genclkInfo(const Clock *gclk) const { - return genclk_info_map_.findKey(const_cast(gclk)); + return findKey(genclk_info_map_, const_cast(gclk)); } FilterPath * Genclks::srcFilter(Clock *gclk) { - GenclkInfo *genclk_info = genclk_info_map_.findKey(gclk); + GenclkInfo *genclk_info = findKey(genclk_info_map_, gclk); if (genclk_info) return genclk_info->srcFilter(); else return nullptr; } -EdgeSet * +EdgeSet & Genclks::latchFdbkEdges(const Clock *clk) { GenclkInfo *genclk_info = genclkInfo(clk); - if (genclk_info) - return genclk_info->fdbkEdges(); - else - return nullptr; + return genclk_info->fdbkEdges(); } void Genclks::findLatchFdbkEdges(const Clock *clk) { GenclkInfo *genclk_info = genclkInfo(clk); - if (genclk_info - && !genclk_info->foundLatchFdbkEdges()) + if (genclk_info && !genclk_info->foundLatchFdbkEdges()) findLatchFdbkEdges(clk, genclk_info); } @@ -595,114 +521,114 @@ Genclks::findLatchFdbkEdges(const Clock *clk) // D to Q edge is encountered in the BFS arrival search. void Genclks::findLatchFdbkEdges(const Clock *gclk, - GenclkInfo *genclk_info) + GenclkInfo *genclk_info) { Level gclk_level = genclk_info->gclkLevel(); - EdgeSet *fdbk_edges = nullptr; + EdgeSet &fdbk_edges = genclk_info->fdbkEdges(); for (const Pin *pin : gclk->masterClk()->leafPins()) { Vertex *vertex = graph_->pinDrvrVertex(pin); - VertexSet path_vertices(graph_); - VertexSet visited_vertices(graph_); + VertexSet path_vertices = makeVertexSet(this); + VertexSet visited_vertices = makeVertexSet(this); SearchPred1 srch_pred(this); findLatchFdbkEdges(vertex, gclk_level, srch_pred, path_vertices, - visited_vertices, fdbk_edges); + visited_vertices, fdbk_edges); } - genclk_info->setLatchFdbkEdges(fdbk_edges); genclk_info->setFoundLatchFdbkEdges(true); } void Genclks::findLatchFdbkEdges(Vertex *from_vertex, - Level gclk_level, - SearchPred &srch_pred, - VertexSet &path_vertices, - VertexSet &visited_vertices, - EdgeSet *&fdbk_edges) + Level gclk_level, + SearchPred &srch_pred, + VertexSet &path_vertices, + VertexSet &visited_vertices, + EdgeSet &fdbk_edges) { - if (!visited_vertices.hasKey(from_vertex)) { + if (!visited_vertices.contains(from_vertex)) { visited_vertices.insert(from_vertex); path_vertices.insert(from_vertex); VertexOutEdgeIterator edge_iter(from_vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *to_vertex = edge->to(graph_); - if (path_vertices.hasKey(to_vertex)) { - debugPrint(debug_, "genclk", 2, " found feedback edge %s", - edge->to_string(this).c_str()); - if (fdbk_edges == nullptr) - fdbk_edges = new EdgeSet; - fdbk_edges->insert(edge); + if (path_vertices.contains(to_vertex)) { + debugPrint(debug_, "genclk", 2, " found feedback edge {}", + edge->to_string(this)); + fdbk_edges.insert(edge); } - else if (srch_pred.searchThru(edge) - && srch_pred.searchTo(to_vertex) - && to_vertex->level() <= gclk_level) - findLatchFdbkEdges(to_vertex, gclk_level, srch_pred, - path_vertices, visited_vertices, fdbk_edges); + else if (srch_pred.searchThru(edge, mode_) + && srch_pred.searchTo(to_vertex, mode_) + && to_vertex->level() <= gclk_level) + findLatchFdbkEdges(to_vertex, gclk_level, srch_pred, path_vertices, + visited_vertices, fdbk_edges); } path_vertices.erase(from_vertex); } } FilterPath * -Genclks::makeSrcFilter(Clock *gclk) +Genclks::makeSrcFilter(Clock *gclk, + Sdc *sdc) { ClockSet *from_clks = new ClockSet; from_clks->insert(gclk->masterClk()); const RiseFallBoth *rf = RiseFallBoth::riseFall(); - ExceptionFrom *from = sdc_->makeExceptionFrom(nullptr,from_clks,nullptr,rf); + ExceptionFrom *from = sdc->makeExceptionFrom(nullptr, from_clks, nullptr, rf); PinSet *thru_pins = new PinSet(network_); thru_pins->insert(gclk->srcPin()); - ExceptionThru *thru = sdc_->makeExceptionThru(thru_pins,nullptr,nullptr,rf); + ExceptionThru *thru = sdc->makeExceptionThru(thru_pins, nullptr, nullptr, rf); ExceptionThruSeq *thrus = new ExceptionThruSeq; thrus->push_back(thru); ClockSet *to_clks = new ClockSet; to_clks->insert(gclk); - ExceptionTo *to = sdc_->makeExceptionTo(nullptr, to_clks, nullptr, rf, rf); + ExceptionTo *to = sdc->makeExceptionTo(nullptr, to_clks, nullptr, rf, rf); - return sdc_->makeFilterPath(from, thrus, to); + return sdc->makeFilterPath(from, thrus, to); } void Genclks::seedSrcPins(Clock *gclk, - FilterPath *src_filter, - BfsFwdIterator &insert_iter) + FilterPath *src_filter, + BfsFwdIterator &insert_iter) { Clock *master_clk = gclk->masterClk(); for (const Pin *master_pin : master_clk->leafPins()) { Vertex *vertex = graph_->pinDrvrVertex(master_pin); if (vertex) { - debugPrint(debug_, "genclk", 2, " seed src pin %s", + debugPrint(debug_, "genclk", 2, " seed src pin {}", network_->pathName(master_pin)); TagGroupBldr tag_bldr(true, this); tag_bldr.init(vertex); copyGenClkSrcPaths(vertex, &tag_bldr); - for (auto path_ap : corners_->pathAnalysisPts()) { - const MinMax *min_max = path_ap->pathMinMax(); - const EarlyLate *early_late = min_max; - for (const RiseFall *rf : RiseFall::range()) { - Arrival insert = search_->clockInsertion(master_clk, master_pin, rf, - min_max, early_late, path_ap); - Tag *tag = makeTag(gclk, master_clk, master_pin, rf, - src_filter, insert, path_ap); - tag_bldr.setArrival(tag, insert); + for (Scene *scene : mode_->scenes()) { + for (const MinMax *min_max : MinMax::range()) { + const EarlyLate *early_late = min_max; + for (const RiseFall *rf : RiseFall::range()) { + Arrival insert = search_->clockInsertion(master_clk, master_pin, rf, + min_max, early_late, mode_); + Tag *tag = makeTag(gclk, master_clk, master_pin, rf, src_filter, insert, + scene, min_max); + tag_bldr.setArrival(tag, insert); + } } + search_->setVertexArrivals(vertex, &tag_bldr); + insert_iter.enqueueAdjacentVertices(vertex, mode_); } - search_->setVertexArrivals(vertex, &tag_bldr); - insert_iter.enqueueAdjacentVertices(vertex); } } } Tag * Genclks::makeTag(const Clock *gclk, - const Clock *master_clk, - const Pin *master_pin, - const RiseFall *master_rf, - FilterPath *src_filter, + const Clock *master_clk, + const Pin *master_pin, + const RiseFall *master_rf, + FilterPath *src_filter, Arrival insert, - const PathAnalysisPt *path_ap) + Scene *scene, + const MinMax *min_max) { ExceptionState *state = src_filter->firstState(); // If the src pin is one of the master pins the filter is active @@ -711,101 +637,112 @@ Genclks::makeTag(const Clock *gclk, state = state->nextState(); ExceptionStateSet *states = new ExceptionStateSet(); states->insert(state); - const ClkInfo *clk_info = search_->findClkInfo(master_clk->edge(master_rf), - master_pin, true, nullptr, true, - nullptr, insert, 0.0, nullptr, - path_ap, nullptr); - return search_->findTag(master_rf, path_ap, clk_info, false, - nullptr, false, states, true, nullptr); + const ClkInfo *clk_info = search_->findClkInfo( + scene, master_clk->edge(master_rf), master_pin, true, nullptr, true, nullptr, + insert, 0.0, nullptr, min_max, nullptr); + return search_->findTag(scene, master_rf, min_max, clk_info, false, nullptr, false, + states, true, nullptr); } class GenClkArrivalSearchPred : public EvalPred { public: GenClkArrivalSearchPred(Clock *gclk, - const StaState *sta); - bool searchThru(Edge *edge); - virtual bool searchTo(const Vertex *to_vertex); + const StaState *sta); + bool searchThru(Edge *edge, + const Mode *mode) const override; + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; private: bool combinational_; }; GenClkArrivalSearchPred::GenClkArrivalSearchPred(Clock *gclk, - const StaState *sta) : + const StaState *sta) : EvalPred(sta), combinational_(gclk->combinational()) { } bool -GenClkArrivalSearchPred::searchThru(Edge *edge) +GenClkArrivalSearchPred::searchThru(Edge *edge, + const Mode *mode) const { const TimingRole *role = edge->role(); - return EvalPred::searchThru(edge) - && (role == TimingRole::combinational() - || role->isWire() - || !combinational_) - && (sta_->variables()->clkThruTristateEnabled() - || !(role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable())); + return EvalPred::searchThru(edge, mode) + && (role == TimingRole::combinational() || role->isWire() || !combinational_) + && (sta_->variables()->clkThruTristateEnabled() + || !(role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable())); } // Override EvalPred::searchTo to search to generated clock pin. bool -GenClkArrivalSearchPred::searchTo(const Vertex *to_vertex) +GenClkArrivalSearchPred::searchTo(const Vertex *to_vertex, + const Mode *mode) const { - return SearchPred0::searchTo(to_vertex); + // NOLINTNEXTLINE(bugprone-parent-virtual-call) + return SearchPred0::searchTo(to_vertex, mode); } class GenclkSrcArrivalVisitor : public ArrivalVisitor { public: GenclkSrcArrivalVisitor(Clock *gclk, - BfsFwdIterator *insert_iter, - GenclkInfo *genclk_info, - const StaState *sta); - virtual VertexVisitor *copy() const; - virtual void visit(Vertex *vertex); + BfsFwdIterator *insert_iter, + GenclkInfo *genclk_info, + const Mode *mode); + VertexVisitor *copy() const override; + void visit(Vertex *vertex) override; protected: GenclkSrcArrivalVisitor(Clock *gclk, - BfsFwdIterator *insert_iter, - GenclkInfo *genclk_info, - bool always_to_endpoints, - SearchPred *pred, - const StaState *sta); + BfsFwdIterator *insert_iter, + GenclkInfo *genclk_info, + bool always_to_endpoints, + SearchPred *pred, + const Mode *mode); Clock *gclk_; BfsFwdIterator *insert_iter_; GenclkInfo *genclk_info_; GenClkInsertionSearchPred srch_pred_; + const Mode *mode_; + const Sdc *sdc_; + Genclks *genclks_; }; GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk, - BfsFwdIterator *insert_iter, - GenclkInfo *genclk_info, - const StaState *sta): - ArrivalVisitor(sta), + BfsFwdIterator *insert_iter, + GenclkInfo *genclk_info, + const Mode *mode) : + ArrivalVisitor(mode->sta()), gclk_(gclk), insert_iter_(insert_iter), genclk_info_(genclk_info), - srch_pred_(gclk_, tag_bldr_, genclk_info, sta) + srch_pred_(gclk_, genclk_info, mode->sta()), + mode_(mode), + sdc_(mode->sdc()), + genclks_(mode->genclks()) { } // Copy constructor. GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk, - BfsFwdIterator *insert_iter, - GenclkInfo *genclk_info, - bool always_to_endpoints, - SearchPred *pred, - const StaState *sta) : - ArrivalVisitor(always_to_endpoints, pred, sta), + BfsFwdIterator *insert_iter, + GenclkInfo *genclk_info, + bool always_to_endpoints, + SearchPred *pred, + const Mode *mode) : + ArrivalVisitor(always_to_endpoints, pred, this), gclk_(gclk), insert_iter_(insert_iter), genclk_info_(genclk_info), - srch_pred_(gclk, tag_bldr_, genclk_info, sta) + srch_pred_(gclk, genclk_info, this), + mode_(mode), + sdc_(mode->sdc()), + genclks_(mode->genclks()) { } @@ -813,33 +750,31 @@ VertexVisitor * GenclkSrcArrivalVisitor::copy() const { return new GenclkSrcArrivalVisitor(gclk_, insert_iter_, genclk_info_, - always_to_endpoints_, pred_, this); + always_to_endpoints_, pred_, mode_); } void GenclkSrcArrivalVisitor::visit(Vertex *vertex) { - Genclks *genclks = search_->genclks(); - debugPrint(debug_, "genclk", 2, "find gen clk insert arrival %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "genclk", 2, "find gen clk insert arrival {}", + vertex->to_string(this)); tag_bldr_->init(vertex); has_fanin_one_ = graph_->hasFaninOne(vertex); - genclks->copyGenClkSrcPaths(vertex, tag_bldr_); + genclks_->copyGenClkSrcPaths(vertex, tag_bldr_); visitFaninPaths(vertex); // Propagate beyond the clock tree to reach generated clk roots. - insert_iter_->enqueueAdjacentVertices(vertex, &srch_pred_); + insert_iter_->enqueueAdjacentVertices(vertex, &srch_pred_, mode_); search_->setVertexArrivals(vertex, tag_bldr_); } void Genclks::findSrcArrivals(Clock *gclk, - BfsFwdIterator &insert_iter, - GenclkInfo *genclk_info) + BfsFwdIterator &insert_iter, + GenclkInfo *genclk_info) { GenClkArrivalSearchPred eval_pred(gclk, this); - GenclkSrcArrivalVisitor arrival_visitor(gclk, &insert_iter, - genclk_info, this); - arrival_visitor.init(true, &eval_pred); + GenclkSrcArrivalVisitor arrival_visitor(gclk, &insert_iter, genclk_info, mode_); + arrival_visitor.init(true, false, &eval_pred); // This cannot restrict the search level because loops in the clock tree // can circle back to the generated clock src pin. // Parallel visit is slightly slower (at last check). @@ -849,11 +784,11 @@ Genclks::findSrcArrivals(Clock *gclk, // Copy generated clock source paths to tag_bldr. void Genclks::copyGenClkSrcPaths(Vertex *vertex, - TagGroupBldr *tag_bldr) + TagGroupBldr *tag_bldr) { auto itr = vertex_src_paths_map_.find(vertex); if (itr != vertex_src_paths_map_.end()) { - const std::vector &src_paths = itr->second; + const std::vector &src_paths = itr->second; for (const Path *path : src_paths) { Path src_path = *path; Path *prev_path = src_path.prevPath(); @@ -861,11 +796,11 @@ Genclks::copyGenClkSrcPaths(Vertex *vertex, Path *prev_vpath = Path::vertexPath(prev_path, this); src_path.setPrevPath(prev_vpath); } - debugPrint(debug_, "genclk", 3, "vertex %s insert genclk %s src path %s %ss", - src_path.vertex(this)->to_string(this).c_str(), - src_path.tag(this)->genClkSrcPathClk(this)->name(), - src_path.tag(this)->pathAnalysisPt(this)->pathMinMax()->to_string().c_str(), - src_path.tag(this)->to_string(true, false, this).c_str()); + debugPrint(debug_, "genclk", 3, "vertex {} insert genclk {} src path {} {}s", + src_path.vertex(this)->to_string(this), + src_path.tag(this)->genClkSrcPathClk()->name(), + src_path.tag(this)->minMax()->to_string(), + src_path.tag(this)->to_string(true, false, this)); tag_bldr->insertPath(src_path); } } @@ -876,107 +811,95 @@ Genclks::copyGenClkSrcPaths(Vertex *vertex, void Genclks::clearSrcPaths() { - for (auto const & [clk_pin, src_paths] : genclk_src_paths_) { - for (const Path &src_path : src_paths) - delete src_path.prevPath(); + for (const auto& [vertex, paths] : vertex_src_paths_map_) { + for (const Path *path : paths) + delete path; } + vertex_src_paths_map_.clear(); genclk_src_paths_.clear(); } size_t Genclks::srcPathIndex(const RiseFall *clk_rf, - const PathAnalysisPt *path_ap) const + const MinMax *min_max) const { - return path_ap->index() * RiseFall::index_count + clk_rf->index(); + return min_max->index() * RiseFall::index_count + clk_rf->index(); } void Genclks::recordSrcPaths(Clock *gclk) { - int path_count = RiseFall::index_count - * corners_->pathAnalysisPtCount(); + size_t path_count = RiseFall::index_count * MinMax::index_count; bool divide_by_1 = gclk->isDivideByOneCombinational(); bool invert = gclk->invert(); - bool has_edges = gclk->edges() != nullptr; + bool has_edges = !gclk->edges().empty(); for (const Pin *gclk_pin : gclk->leafPins()) { std::vector &src_paths = genclk_src_paths_[ClockPinPair(gclk, gclk_pin)]; src_paths.resize(path_count); Vertex *gclk_vertex = srcPath(gclk_pin); - bool found_src_paths = false; VertexPathIterator path_iter(gclk_vertex, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); const ClockEdge *src_clk_edge = path->clkEdge(this); - if (src_clk_edge - && matchesSrcFilter(path, gclk)) { - const EarlyLate *early_late = path->minMax(this); - const RiseFall *src_clk_rf = src_clk_edge->transition(); - const RiseFall *rf = path->transition(this); - bool inverting_path = (rf != src_clk_rf); - const PathAnalysisPt *path_ap = path->pathAnalysisPt(this); - size_t path_index = srcPathIndex(rf, path_ap); - Path &src_path = src_paths[path_index]; - if ((!divide_by_1 - || (inverting_path == invert)) - && (!has_edges - || src_clk_rf == gclk->masterClkEdgeTr(rf)) - && (src_path.isNull() - || delayGreater(path->arrival(), - src_path.arrival(), - early_late, - this))) { - debugPrint(debug_, "genclk", 2, " %s insertion %s %s %s", - network_->pathName(gclk_pin), - early_late->to_string().c_str(), - rf->to_string().c_str(), - delayAsString(path->arrival(), this)); - // If this path is replacing another one delete the previous one. - delete src_path.prevPath(); + if (src_clk_edge && matchesSrcFilter(path, gclk)) { + const EarlyLate *early_late = path->minMax(this); + const RiseFall *src_clk_rf = src_clk_edge->transition(); + const RiseFall *rf = path->transition(this); + bool inverting_path = (rf != src_clk_rf); + size_t path_index = srcPathIndex(rf, path->minMax(this)); + Path &src_path = src_paths[path_index]; + if ((!divide_by_1 || (inverting_path == invert)) + && (!has_edges || src_clk_rf == gclk->masterClkEdgeTr(rf)) + && (src_path.isNull() + || delayGreater(path->arrival(), src_path.arrival(), early_late, + this))) { + debugPrint(debug_, "genclk", 2, " {} insertion {} {} {}", + network_->pathName(gclk_pin), early_late->to_string(), + rf->shortName(), delayAsString(path->arrival(), this)); src_path = *path; - Path *prev_copy = &src_path; - Path *p = path->prevPath(); - while (p) { - Path *copy = new Path(p); - copy->setIsEnum(true); - prev_copy->setPrevPath(copy); - prev_copy = copy; - p = p->prevPath(); - } - found_src_paths = true; - } + } } } - if (found_src_paths) { - // Record vertex->genclk src paths. - for (const Path &path : src_paths) { - if (!path.isNull()) { - const Path *p = &path; - while (p && !p->isNull()) { - Vertex *vertex = p->vertex(this); - vertex_src_paths_map_[vertex].push_back(p); - p = p->prevPath(); - } + // Record vertex->genclk src paths. + bool found_src_paths = false; + for (size_t path_index = 0; path_index < path_count; path_index++) { + Path &src_path = src_paths[path_index]; + if (!src_path.isNull()) { + Path *prev_copy = &src_path; + const Path *p = src_path.prevPath(); + while (p) { + Path *copy = new Path(p); + copy->setIsEnum(true); + prev_copy->setPrevPath(copy); + prev_copy = copy; + + Vertex *vertex = p->vertex(this); + vertex_src_paths_map_[vertex].push_back(copy); + p = p->prevPath(); } + found_src_paths = true; } } // Don't warn if the master clock is ideal. - else if (gclk->masterClk() - && gclk->masterClk()->isPropagated()) - report_->warn(1062, "generated clock %s source pin %s missing paths from master clock %s.", - gclk->name(), - network_->pathName(gclk_pin), - gclk->masterClk()->name()); + if (!found_src_paths + && gclk->masterClk() + && gclk->masterClk()->isPropagated()) + report_->warn(1062, + "generated clock {} source pin {} missing paths from master clock {}.", + gclk->name(), + network_->pathName(gclk_pin), + gclk->masterClk()->name()); } deleteGenclkSrcPaths(gclk); } void -Genclks:: deleteGenclkSrcPaths(Clock *gclk) +Genclks::deleteGenclkSrcPaths(Clock *gclk) { GenclkInfo *genclk_info = genclkInfo(gclk); - GenClkInsertionSearchPred srch_pred(gclk, nullptr, genclk_info, this); + GenClkInsertionSearchPred srch_pred(gclk, genclk_info, mode_->sta()); BfsFwdIterator insert_iter(BfsIndex::other, &srch_pred, this); FilterPath *src_filter = genclk_info->srcFilter(); seedSrcPins(gclk, src_filter, insert_iter); @@ -984,27 +907,22 @@ Genclks:: deleteGenclkSrcPaths(Clock *gclk) while (insert_iter.hasNext()) { Vertex *vertex = insert_iter.next(); search_->deletePaths(vertex); - insert_iter.enqueueAdjacentVertices(vertex, &srch_pred); + insert_iter.enqueueAdjacentVertices(vertex, &srch_pred, mode_); } } bool Genclks::matchesSrcFilter(Path *path, - const Clock *gclk) const + const Clock *gclk) const { Tag *tag = path->tag(this); const ExceptionStateSet *states = tag->states(); - if (tag->isGenClkSrcPath() - && states) { - ExceptionStateSet::ConstIterator state_iter(states); - while (state_iter.hasNext()) { - ExceptionState *state = state_iter.next(); + if (tag->isGenClkSrcPath() && states) { + for (ExceptionState *state : *states) { ExceptionPath *except = state->exception(); - if (except->isFilter() - && state->nextThru() == nullptr - && except->to() - && except->to()->matches(gclk)) - return true; + if (except->isFilter() && state->nextThru() == nullptr && except->to() + && except->to()->matches(gclk)) + return true; } } return false; @@ -1015,32 +933,29 @@ Genclks::srcPath(const Path *clk_path) const { const Pin *src_pin = clk_path->pin(this); const ClockEdge *clk_edge = clk_path->clkEdge(this); - const PathAnalysisPt *path_ap = clk_path->pathAnalysisPt(this); const EarlyLate *early_late = clk_path->minMax(this); - PathAnalysisPt *insert_ap = path_ap->insertionAnalysisPt(early_late); - return srcPath(clk_edge->clock(), src_pin, clk_edge->transition(), - insert_ap); + return srcPath(clk_edge->clock(), src_pin, clk_edge->transition(), early_late); } const Path * Genclks::srcPath(const ClockEdge *clk_edge, - const Pin *src_pin, - const PathAnalysisPt *path_ap) const + const Pin *src_pin, + const MinMax *min_max) const { - return srcPath(clk_edge->clock(), src_pin, clk_edge->transition(), path_ap); + return srcPath(clk_edge->clock(), src_pin, clk_edge->transition(), min_max); } const Path * Genclks::srcPath(const Clock *gclk, - const Pin *src_pin, - const RiseFall *rf, - const PathAnalysisPt *path_ap) const + const Pin *src_pin, + const RiseFall *rf, + const MinMax *min_max) const { auto itr = genclk_src_paths_.find(ClockPinPair(gclk, src_pin)); if (itr != genclk_src_paths_.end()) { const std::vector &src_paths = itr->second; if (!src_paths.empty()) { - size_t path_index = srcPathIndex(rf, path_ap); + size_t path_index = srcPathIndex(rf, min_max); const Path *src_path = &src_paths[path_index]; if (!src_path->isNull()) return src_path; @@ -1051,13 +966,11 @@ Genclks::srcPath(const Clock *gclk, Arrival Genclks::insertionDelay(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap) const + const Pin *pin, + const RiseFall *rf, + const EarlyLate *early_late) const { - PathAnalysisPt *insert_ap = path_ap->insertionAnalysisPt(early_late); - const Path *src_path = srcPath(clk, pin, rf, insert_ap); + const Path *src_path = srcPath(clk, pin, rf, early_late); if (src_path) return src_path->arrival(); else @@ -1068,7 +981,7 @@ Genclks::insertionDelay(const Clock *clk, bool ClockPinPairLess::operator()(const ClockPinPair &pair1, - const ClockPinPair &pair2) const + const ClockPinPair &pair2) const { const Clock *clk1 = pair1.first; @@ -1077,9 +990,7 @@ ClockPinPairLess::operator()(const ClockPinPair &pair1, int clk_index2 = clk2->index(); const Pin *pin1 = pair1.second; const Pin *pin2 = pair2.second; - return (clk_index1 < clk_index2 - || (clk_index1 == clk_index2 - && pin1 < pin2)); + return (clk_index1 < clk_index2 || (clk_index1 == clk_index2 && pin1 < pin2)); } class ClockPinPairHash @@ -1107,16 +1018,15 @@ class ClockPinPairEqual { public: bool operator()(const ClockPinPair &pair1, - const ClockPinPair &pair2) const; + const ClockPinPair &pair2) const; }; bool ClockPinPairEqual::operator()(const ClockPinPair &pair1, - const ClockPinPair &pair2) const + const ClockPinPair &pair2) const { - return pair1.first == pair2.first - && pair1.second == pair2.second; + return pair1.first == pair2.first && pair1.second == pair2.second; } -} // namespace +} // namespace sta diff --git a/search/Genclks.hh b/search/Genclks.hh index 0f69f5f06..67cc55de0 100644 --- a/search/Genclks.hh +++ b/search/Genclks.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,13 +24,20 @@ #pragma once -#include "Map.hh" -#include "Transition.hh" -#include "NetworkClass.hh" +#include +#include +#include +#include + +#include "Clock.hh" #include "Graph.hh" +#include "MinMax.hh" +#include "Mode.hh" +#include "NetworkClass.hh" #include "SdcClass.hh" #include "SearchClass.hh" #include "StaState.hh" +#include "Transition.hh" namespace sta { @@ -40,51 +47,53 @@ class BfsBkwdIterator; class SearchPred; class TagGroupBldr; -typedef std::pair ClockPinPair; +using ClockPinPair = std::pair; class ClockPinPairLess { public: bool operator()(const ClockPinPair &pair1, - const ClockPinPair &pair2) const; + const ClockPinPair &pair2) const; }; -typedef Map GenclkInfoMap; -typedef Map, ClockPinPairLess> GenclkSrcPathMap; -typedef std::map, VertexIdLess> VertexGenclkSrcPathsMap; +using GenclkInfoMap = std::map; +using GenclkSrcPathMap = std::map, ClockPinPairLess>; +using VertexGenclkSrcPathsMap = std::map, VertexIdLess>; class Genclks : public StaState { public: - Genclks(StaState *sta); - ~Genclks(); + Genclks(const Mode *mode, + StaState *sta); + ~Genclks() override; void clear(); void ensureInsertionDelays(); VertexSet *fanins(const Clock *clk); void findLatchFdbkEdges(const Clock *clk); - EdgeSet *latchFdbkEdges(const Clock *clk); - void checkMaster(Clock *gclk); - void ensureMaster(Clock *gclk); + EdgeSet &latchFdbkEdges(const Clock *clk); + void checkMaster(Clock *gclk, + const Sdc *sdc); + void ensureMaster(Clock *gclk, + const Sdc *sdc); // Generated clock insertion delay. Arrival insertionDelay(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap) const; + const Pin *pin, + const RiseFall *rf, + const EarlyLate *early_late) const; // Generated clock source path for a clock path root. const Path *srcPath(const Path *clk_path) const; // Generated clock source path. const Path *srcPath(const ClockEdge *clk_edge, const Pin *src_pin, - const PathAnalysisPt *path_ap) const; + const MinMax *min_max) const; const Path *srcPath(const Clock *clk, const Pin *src_pin, const RiseFall *rf, - const PathAnalysisPt *path_ap) const; + const MinMax *min_max) const; Vertex *srcPath(const Pin *pin) const; Level clkPinMaxLevel(const Clock *clk) const; void copyGenClkSrcPaths(Vertex *vertex, - TagGroupBldr *tag_bldr); + TagGroupBldr *tag_bldr); private: void findInsertionDelays(); @@ -93,49 +102,51 @@ private: void recordSrcPaths(Clock *gclk); void findInsertionDelays(Clock *gclk); void seedClkVertices(Clock *clk, - BfsBkwdIterator &iter, - VertexSet *fanins); + BfsBkwdIterator &iter, + VertexSet &dfanins); size_t srcPathIndex(const RiseFall *clk_rf, - const PathAnalysisPt *path_ap) const; + const MinMax *min_max) const; bool matchesSrcFilter(Path *path, - const Clock *gclk) const; + const Clock *gclk) const; void seedSrcPins(Clock *gclk, - FilterPath *src_filter, - BfsFwdIterator &insert_iter); + FilterPath *src_filter, + BfsFwdIterator &insert_iter); void findSrcArrivals(Clock *gclk, - BfsFwdIterator &insert_iter, - GenclkInfo *genclk_info); - virtual FilterPath *makeSrcFilter(Clock *gclk); + BfsFwdIterator &insert_iter, + GenclkInfo *genclk_info); + FilterPath *makeSrcFilter(Clock *gclk, + Sdc *sdc); void deleteGenClkInfo(); virtual Tag *makeTag(const Clock *gclk, - const Clock *master_clk, - const Pin *master_pin, - const RiseFall *rf, - FilterPath *src_filter, + const Clock *master_clk, + const Pin *master_pin, + const RiseFall *rf, + FilterPath *src_filter, Arrival insert, - const PathAnalysisPt *path_ap); + Scene *scene, + const MinMax *min_max); void seedSrcPins(Clock *clk, - BfsBkwdIterator &iter); + BfsBkwdIterator &iter); void findInsertionDelay(Clock *gclk); GenclkInfo *makeGenclkInfo(Clock *gclk); FilterPath *srcFilter(Clock *gclk); void findFanin(Clock *gclk, - // Return value. - VertexSet *fanins); + VertexSet &fanins); void findLatchFdbkEdges(const Clock *clk, - GenclkInfo *genclk_info); + GenclkInfo *genclk_info); void findLatchFdbkEdges(Vertex *vertex, - Level gclk_level, - SearchPred &srch_pred, - VertexSet &path_vertices, - VertexSet &visited_vertices, - EdgeSet *&fdbk_edges); + Level gclk_level, + SearchPred &srch_pred, + VertexSet &path_vertices, + VertexSet &visited_vertices, + EdgeSet &fdbk_edges); void deleteGenclkSrcPaths(Clock *gclk); - bool found_insertion_delays_; + const Mode *mode_; + bool found_insertion_delays_{false}; GenclkSrcPathMap genclk_src_paths_; GenclkInfoMap genclk_info_map_; VertexGenclkSrcPathsMap vertex_src_paths_map_; }; -} // namespace +} // namespace sta diff --git a/search/Latches.cc b/search/Latches.cc index 73ffa95cf..f8d5b68ef 100644 --- a/search/Latches.cc +++ b/search/Latches.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,21 +24,21 @@ #include "Latches.hh" +#include "ClkInfo.hh" +#include "Crpr.hh" #include "Debug.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" +#include "ExceptionPath.hh" +#include "Graph.hh" #include "Liberty.hh" +#include "Mode.hh" #include "Network.hh" -#include "Graph.hh" -#include "ExceptionPath.hh" -#include "Sdc.hh" -#include "ClkInfo.hh" -#include "Tag.hh" -#include "Sim.hh" #include "PathEnd.hh" -#include "PathAnalysisPt.hh" +#include "Sdc.hh" #include "Search.hh" -#include "Crpr.hh" +#include "Sim.hh" +#include "Tag.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" namespace sta { @@ -49,18 +49,19 @@ Latches::Latches(StaState *sta) : void Latches::latchRequired(const Path *data_path, - const Path *enable_path, - const Path *disable_path, - const MultiCyclePath *mcp, - const PathDelay *path_delay, - Arrival src_clk_latency, - const ArcDelay &margin, - // Return values. - Required &required, - Arrival &borrow, - Arrival &adjusted_data_arrival, - Delay &time_given_to_startpoint) const + const Path *enable_path, + const Path *disable_path, + const MultiCyclePath *mcp, + const PathDelay *path_delay, + Arrival src_clk_latency, + const ArcDelay &margin, + // Return values. + Required &required, + Arrival &borrow, + Arrival &adjusted_data_arrival, + Delay &time_given_to_startpoint) const { + Sdc *sdc = data_path->sdc(this); const Arrival data_arrival = data_path->arrival(); float max_delay = 0.0; bool ignore_clk_latency = false; @@ -69,42 +70,44 @@ Latches::latchRequired(const Path *data_path, ignore_clk_latency = path_delay->ignoreClkLatency(); } if (ignore_clk_latency) { - required = max_delay + src_clk_latency; + required = delaySum(src_clk_latency, max_delay, this); borrow = 0.0; adjusted_data_arrival = data_arrival; time_given_to_startpoint = 0.0; } else if (enable_path && disable_path) { - debugPrint(debug_, "latch", 1, "latch %s", + debugPrint(debug_, "latch", 1, "latch {}", sdc_network_->pathName(data_path->pin(this))); Delay open_latency, latency_diff, max_borrow; float nom_pulse_width, open_uncertainty; Crpr open_crpr, crpr_diff; bool borrow_limit_exists; latchBorrowInfo(data_path, enable_path, disable_path, margin, - ignore_clk_latency, - nom_pulse_width, open_latency, latency_diff, - open_uncertainty, open_crpr, crpr_diff, max_borrow, - borrow_limit_exists); + ignore_clk_latency, + nom_pulse_width, open_latency, latency_diff, + open_uncertainty, open_crpr, crpr_diff, max_borrow, + borrow_limit_exists); const ClockEdge *data_clk_edge = data_path->clkEdge(this); const ClockEdge *enable_clk_edge = enable_path->clkEdge(this); const TimingRole *check_role = enable_path->clkInfo(this)->isPulseClk() ? TimingRole::setup() : TimingRole::latchSetup(); - CycleAccting *acct = sdc_->cycleAccting(data_clk_edge, - enable_clk_edge); + CycleAccting *acct = sdc->cycleAccting(data_clk_edge, + enable_clk_edge); // checkTgtClkTime float tgt_clk_time = path_delay ? 0.0 : acct->requiredTime(check_role); // checkTgtClkArrival broken down into components. - Arrival enable_arrival = max_delay - + tgt_clk_time - + open_latency - + open_uncertainty - + PathEnd::checkSetupMcpAdjustment(data_clk_edge, enable_clk_edge, mcp, - 1, sdc_) - + open_crpr; - debugPrint(debug_, "latch", 1, "data %s enable %s", + Arrival enable_arrival = delaySum(max_delay + + tgt_clk_time + + open_uncertainty + + PathEnd::checkSetupMcpAdjustment(data_clk_edge, + enable_clk_edge, + mcp, 1, sdc), + open_latency, + this); + enable_arrival = delaySum(enable_arrival, open_crpr, this); + debugPrint(debug_, "latch", 1, "data {} enable {}", delayAsString(data_arrival, this), delayAsString(enable_arrival, this)); if (delayLessEqual(data_arrival, enable_arrival, this)) { @@ -116,25 +119,31 @@ Latches::latchRequired(const Path *data_path, } else { // Data arrives while latch is transparent. - borrow = data_arrival - enable_arrival; + borrow = delayDiff(data_arrival, enable_arrival, this); if (delayLessEqual(borrow, max_borrow, this)) - required = data_arrival; + required = data_arrival; else { - borrow = max_borrow; - required = enable_arrival + max_borrow; + borrow = max_borrow; + required = delaySum(enable_arrival, max_borrow, this); } - time_given_to_startpoint = borrow + open_uncertainty + open_crpr; + time_given_to_startpoint = delaySum(delaySum(borrow, + open_uncertainty, + this), + open_crpr, this); // Cycle accounting for required time is with respect to the // data clock zeroth cycle. The data departs the latch // with respect to the enable clock zeroth cycle. float data_shift_to_enable_clk = acct->sourceTimeOffset(check_role) - - acct->targetTimeOffset(check_role); - adjusted_data_arrival = required + data_shift_to_enable_clk; + - acct->targetTimeOffset(check_role); + adjusted_data_arrival = delaySum(required, data_shift_to_enable_clk, this); } } else if (disable_path) { - required = max_delay + search_->clkPathArrival(disable_path) - margin; + required = delayDiff(delaySum(max_delay, + search_->clkPathArrival(disable_path), + this), + margin, this); // Borrow cannot be determined without enable path. borrow = 0.0; adjusted_data_arrival = data_arrival; @@ -146,38 +155,39 @@ Latches::latchRequired(const Path *data_path, adjusted_data_arrival = data_arrival; time_given_to_startpoint = 0.0; } - debugPrint(debug_, "latch", 2, "req %s borrow %s time_given %s adj_arrival %s", - delayAsString(required, this), - delayAsString(borrow, this), - delayAsString(time_given_to_startpoint, this), - delayAsString(adjusted_data_arrival, this)); + debugPrint(debug_, "latch", 2, "req {} borrow {} time_given {} adj_arrival {}", + delayAsString(required, this), + delayAsString(borrow, this), + delayAsString(time_given_to_startpoint, this), + delayAsString(adjusted_data_arrival, this)); } void Latches::latchBorrowInfo(const Path *data_path, - const Path *enable_path, - const Path *disable_path, - const ArcDelay &margin, - bool ignore_clk_latency, - // Return values. - float &nom_pulse_width, - Delay &open_latency, - Delay &latency_diff, - float &open_uncertainty, - Crpr &open_crpr, - Crpr &crpr_diff, - Delay &max_borrow, - bool &borrow_limit_exists) const + const Path *enable_path, + const Path *disable_path, + const ArcDelay &margin, + bool ignore_clk_latency, + // Return values. + float &nom_pulse_width, + Delay &open_latency, + Delay &latency_diff, + float &open_uncertainty, + Crpr &open_crpr, + Crpr &crpr_diff, + Delay &max_borrow, + bool &borrow_limit_exists) const { if (data_path && enable_path && disable_path) { + Sdc *sdc = data_path->sdc(this); const ClockEdge *data_clk_edge = data_path->clkEdge(this); const ClockEdge *enable_clk_edge = enable_path->clkEdge(this); const ClockEdge *disable_clk_edge = disable_path->clkEdge(this); bool is_pulse_clk = enable_path->clkInfo(this)->isPulseClk(); - nom_pulse_width = is_pulse_clk ? 0.0F : enable_clk_edge->pulseWidth(); + nom_pulse_width = is_pulse_clk ? 0.0 : enable_clk_edge->pulseWidth(); open_uncertainty = PathEnd::checkClkUncertainty(data_clk_edge, enable_clk_edge, enable_path, - TimingRole::latchSetup(), this); + TimingRole::latchSetup(), sdc); if (ignore_clk_latency) { open_latency = 0.0; latency_diff = 0.0; @@ -188,24 +198,25 @@ Latches::latchBorrowInfo(const Path *data_path, CheckCrpr *check_crpr = search_->checkCrpr(); open_crpr = check_crpr->checkCrpr(data_path, enable_path); Crpr close_crpr = check_crpr->checkCrpr(data_path, disable_path); - crpr_diff = open_crpr - close_crpr; + crpr_diff = delayDiff(open_crpr, close_crpr, this); open_latency = PathEnd::checkTgtClkDelay(enable_path, enable_clk_edge, TimingRole::setup(), this); Arrival close_latency = PathEnd::checkTgtClkDelay(disable_path, disable_clk_edge, TimingRole::latchSetup(), this); - latency_diff = open_latency - close_latency; + latency_diff = delayDiff(open_latency, close_latency, this); } float borrow_limit; - sdc_->latchBorrowLimit(data_path->pin(this), disable_path->pin(this), - enable_clk_edge->clock(), - borrow_limit, borrow_limit_exists); + sdc->latchBorrowLimit(data_path->pin(this), disable_path->pin(this), + enable_clk_edge->clock(), + borrow_limit, borrow_limit_exists); if (borrow_limit_exists) max_borrow = borrow_limit; else - max_borrow = nom_pulse_width - delayAsFloat(latency_diff) - - delayAsFloat(crpr_diff) - delayAsFloat(margin); + max_borrow = delayDiff(nom_pulse_width, + delaySum(delaySum(latency_diff, crpr_diff, this), + margin, this), this); } else { nom_pulse_width = 0.0; @@ -215,64 +226,65 @@ Latches::latchBorrowInfo(const Path *data_path, open_crpr = 0.0; crpr_diff = 0.0; } - debugPrint(debug_, "latch", 2, "nom_width %s open_lat %s lat_diff %s open_uncert %s", - delayAsString(nom_pulse_width, this), - delayAsString(open_latency, this), - delayAsString(latency_diff, this), - delayAsString(open_uncertainty, this)); - debugPrint(debug_, "latch", 2, "open_crpr %s crpr_diff %s open_uncert %s max_borrow %s", - delayAsString(open_crpr, this), - delayAsString(crpr_diff, this), - delayAsString(open_uncertainty, this), - borrow_limit_exists ? delayAsString(max_borrow, this) : "none"); + debugPrint(debug_, "latch", 2, "nom_width {} open_lat {} lat_diff {} open_uncert {}", + delayAsString(nom_pulse_width, this), + delayAsString(open_latency, this), + delayAsString(latency_diff, this), + delayAsString(open_uncertainty, this)); + debugPrint(debug_, "latch", 2, "open_crpr {} crpr_diff {} open_uncert {} max_borrow {}", + delayAsString(open_crpr, this), + delayAsString(crpr_diff, this), + delayAsString(open_uncertainty, this), + borrow_limit_exists ? delayAsString(max_borrow, this) : "none"); } void Latches::latchRequired(const Path *data_path, - const Path *enable_path, - const Path *disable_path, - const PathAnalysisPt *path_ap, - // Return values. - Required &required, - Arrival &borrow, - Arrival &adjusted_data_arrival, - Delay &time_given_to_startpoint) const + const Path *enable_path, + const Path *disable_path, + // Return values. + Required &required, + Arrival &borrow, + Arrival &adjusted_data_arrival, + Delay &time_given_to_startpoint) const { + Sdc *sdc = data_path->sdc(this); Vertex *data_vertex = data_path->vertex(this); const RiseFall *data_rf = data_path->transition(this); - ArcDelay setup = latchSetupMargin(data_vertex,data_rf,disable_path,path_ap); + ArcDelay setup = latchSetupMargin(data_vertex,data_rf,disable_path); ExceptionPath *excpt = search_->exceptionTo(ExceptionPathType::any, - data_path, data_vertex->pin(), - data_rf, - enable_path->clkEdge(this), - path_ap->pathMinMax(), false, - false); + data_path, data_vertex->pin(), + data_rf, + enable_path->clkEdge(this), + data_path->minMax(this), + false, false, sdc); MultiCyclePath *mcp = dynamic_cast(excpt); PathDelay *path_delay = dynamic_cast(excpt); Arrival src_clk_latency = 0.0; if (path_delay && path_delay->ignoreClkLatency()) src_clk_latency = search_->pathClkPathArrival(data_path); latchRequired(data_path, enable_path, disable_path, mcp, - path_delay, src_clk_latency, setup, - required, borrow, adjusted_data_arrival, - time_given_to_startpoint); + path_delay, src_clk_latency, setup, + required, borrow, adjusted_data_arrival, + time_given_to_startpoint); } // Find the latch enable open/close path from the close/open path. Path * -Latches::latchEnableOtherPath(const Path *path, - const PathAnalysisPt *tgt_clk_path_ap) const +Latches::latchEnableOtherPath(const Path *path) const { Vertex *vertex = path->vertex(this); const ClockEdge *clk_edge = path->clkEdge(this); const ClockEdge *other_clk_edge = path->clkInfo(this)->isPulseClk() ? clk_edge:clk_edge->opposite(); const RiseFall *other_rf = path->transition(this)->opposite(); - VertexPathIterator path_iter(vertex, other_rf, tgt_clk_path_ap, this); + VertexPathIterator path_iter(vertex, path->scene(this), + path->minMax(this), + other_rf, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); if (path->isClock(this) - && path->clkEdge(this) == other_clk_edge) { + && path->clkEdge(this) == other_clk_edge) { return path; } } @@ -281,25 +293,26 @@ Latches::latchEnableOtherPath(const Path *path, Path * Latches::latchEnablePath(const Path *q_path, - const Edge *d_q_edge) const + const Edge *d_q_edge) const { const ClockEdge *en_clk_edge = q_path->clkEdge(this); - PathAnalysisPt *path_ap = q_path->pathAnalysisPt(this); - const PathAnalysisPt *tgt_clk_path_ap = path_ap->tgtClkAnalysisPt(); + const Scene *scene = q_path->scene(this); + const Mode *mode = scene->mode(); + const MinMax *tgt_min_max = q_path->tgtClkMinMax(this); const Instance *latch = network_->instance(q_path->pin(this)); Vertex *en_vertex; const RiseFall *en_rf; LatchEnableState state; - latchDtoQEnable(d_q_edge, latch, en_vertex, en_rf, state); + latchDtoQEnable(d_q_edge, latch, mode, en_vertex, en_rf, state); if (state == LatchEnableState::enabled) { - VertexPathIterator path_iter(en_vertex, en_rf, tgt_clk_path_ap, this); + VertexPathIterator path_iter(en_vertex, scene, tgt_min_max, en_rf, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); const ClockEdge *clk_edge = path->clkEdge(this); if (path->isClock(this) - && clk_edge == en_clk_edge) { - return path; + && clk_edge == en_clk_edge) { + return path; } } } @@ -312,123 +325,163 @@ Latches::latchEnablePath(const Path *q_path, // the enable open edge. void Latches::latchOutArrival(const Path *data_path, - const TimingArc *d_q_arc, - const Edge *d_q_edge, - const PathAnalysisPt *path_ap, - // Return values. - Tag *&q_tag, - ArcDelay &arc_delay, - Arrival &q_arrival) + const TimingArc *d_q_arc, + const Edge *d_q_edge, + // Return values. + Tag *&q_tag, + ArcDelay &arc_delay, + Arrival &q_arrival) { + q_tag = nullptr; + arc_delay = 0.0; + q_arrival = 0.0; + + Scene *scene = data_path->scene(this); + Sdc *sdc = scene->sdc(); + const Mode *mode = scene->mode(); Vertex *data_vertex = d_q_edge->from(graph_); const Instance *inst = network_->instance(data_vertex->pin()); + const MinMax *min_max = data_path->minMax(this); + DcalcAPIndex dcalc_ap = data_path->dcalcAnalysisPtIndex(this); Vertex *enable_vertex; const RiseFall *enable_rf; LatchEnableState state; - latchDtoQEnable(d_q_edge, inst, enable_vertex, enable_rf, state); + latchDtoQEnable(d_q_edge, inst, mode, enable_vertex, enable_rf, state); // Latch enable may be missing if library is malformed. switch (state) { case LatchEnableState::closed: - // Latch is disabled by constant enable. + // Latch is always closed because enable is constant. break; case LatchEnableState::open: { + // Latch is always open because enable is constant. ExceptionPath *excpt = exceptionTo(data_path, nullptr); if (!(excpt && excpt->isFalse())) { arc_delay = search_->deratedDelay(data_vertex, d_q_arc, d_q_edge, - false, path_ap); - q_arrival = data_path->arrival() + arc_delay; - q_tag = data_path->tag(this); + false, min_max, dcalc_ap, sdc); + q_arrival = delaySum(data_path->arrival(), arc_delay, this); + // Copy the data tag but remove the drprClkPath. + // Levelization does not traverse latch D->Q edges, so in some cases + // level(Q) < level(D) + // Note that + // level(crprClkPath(data)) < level(D) + // The danger is that + // level(crprClkPath(data)) == level(Q) + // or some other downstream vertex. + // This can lead to data races when finding arrivals at the same level + // use multiple threads. + // Kill the crprClklPath to be safe. + const ClkInfo *data_clk_info = data_path->clkInfo(this); + const ClkInfo *q_clk_info = + search_->findClkInfo(scene, + data_clk_info->clkEdge(), + data_clk_info->clkSrc(), + data_clk_info->isPropagated(), + data_clk_info->genClkSrc(), + data_clk_info->isGenClkSrcPath(), + data_clk_info->pulseClkSense(), + data_clk_info->insertion(), + data_clk_info->latency(), + data_clk_info->uncertainties(), + min_max, nullptr); + q_tag = search_->findTag(scene, d_q_arc->toEdge()->asRiseFall(), + min_max, q_clk_info, false, + nullptr, false, data_path->tag(this)->states(), + false, nullptr); } - } break; + } case LatchEnableState::enabled: { - const PathAnalysisPt *tgt_clk_path_ap = path_ap->tgtClkAnalysisPt(); - VertexPathIterator enable_iter(enable_vertex, enable_rf, - tgt_clk_path_ap, this); + const MinMax *tgt_min_max = data_path->tgtClkMinMax(this); + VertexPathIterator enable_iter(enable_vertex, scene, tgt_min_max, + enable_rf, this); while (enable_iter.hasNext()) { Path *enable_path = enable_iter.next(); - const ClkInfo *en_clk_info = enable_path->clkInfo(this); - const ClockEdge *en_clk_edge = en_clk_info->clkEdge(); - if (enable_path->isClock(this)) { - ExceptionPath *excpt = exceptionTo(data_path, en_clk_edge); - // D->Q is disabled when if there is a path delay -to D or EN clk. - if (!(excpt && (excpt->isFalse() - || excpt->isPathDelay()))) { - Path *disable_path = latchEnableOtherPath(enable_path, tgt_clk_path_ap); - Delay borrow, time_given_to_startpoint; - Arrival adjusted_data_arrival; - Required required; - latchRequired(data_path, enable_path, disable_path, path_ap, - required, borrow, adjusted_data_arrival, - time_given_to_startpoint); - if (delayGreater(borrow, 0.0, this)) { - // Latch is transparent when data arrives. - arc_delay = search_->deratedDelay(data_vertex, d_q_arc, d_q_edge, - false, path_ap); - q_arrival = adjusted_data_arrival + arc_delay; - // Tag switcheroo - data passing thru gets latch enable tag. - // States and path ap come from Q, everything else from enable. - Path *crpr_clk_path = crprActive() ? enable_path : nullptr; - const ClkInfo *q_clk_info = - search_->findClkInfo(en_clk_edge, - en_clk_info->clkSrc(), - en_clk_info->isPropagated(), - en_clk_info->genClkSrc(), - en_clk_info->isGenClkSrcPath(), - en_clk_info->pulseClkSense(), - en_clk_info->insertion(), - en_clk_info->latency(), - en_clk_info->uncertainties(), - path_ap, - crpr_clk_path); - const RiseFall *q_rf = d_q_arc->toEdge()->asRiseFall(); - ExceptionStateSet *states = nullptr; - // Latch data pin is a valid exception -from pin. - if (sdc_->exceptionFromStates(data_path->pin(this), - data_path->transition(this), - nullptr, nullptr, // clk below - MinMax::max(), states) - // -from enable non-filter exceptions apply. - && sdc_->exceptionFromStates(enable_vertex->pin(), - enable_rf, - en_clk_edge->clock(), - en_clk_edge->transition(), - MinMax::max(), false, states)) - q_tag = search_->findTag(q_rf, path_ap, q_clk_info, false, - nullptr, false, states, true, nullptr); - } - return; - } - } + const ClkInfo *en_clk_info = enable_path->clkInfo(this); + const ClockEdge *en_clk_edge = en_clk_info->clkEdge(); + if (enable_path->isClock(this)) { + ExceptionPath *excpt = exceptionTo(data_path, en_clk_edge); + // D->Q is disabled when if there is a path delay -to D or EN clk. + if (!(excpt && (excpt->isFalse() + || excpt->isPathDelay()))) { + Path *disable_path = latchEnableOtherPath(enable_path); + Delay borrow, time_given_to_startpoint; + Arrival adjusted_data_arrival; + Required required; + latchRequired(data_path, enable_path, disable_path, + required, borrow, adjusted_data_arrival, + time_given_to_startpoint); + if (delayGreater(borrow, 0.0, this)) { + // Latch is transparent when data arrives. + arc_delay = search_->deratedDelay(data_vertex, d_q_arc, d_q_edge, + false, min_max, dcalc_ap, sdc); + q_arrival = delaySum(adjusted_data_arrival, arc_delay, this); + // Tag switcheroo - data passing thru gets latch enable tag. + // States and path ap come from Q, everything else from enable. + Path *crpr_clk_path = crprActive(mode) ? enable_path : nullptr; + const ClkInfo *q_clk_info = + search_->findClkInfo(scene, + en_clk_edge, + en_clk_info->clkSrc(), + en_clk_info->isPropagated(), + en_clk_info->genClkSrc(), + en_clk_info->isGenClkSrcPath(), + en_clk_info->pulseClkSense(), + en_clk_info->insertion(), + en_clk_info->latency(), + en_clk_info->uncertainties(), + min_max, crpr_clk_path); + ExceptionStateSet *states = nullptr; + // Latch data pin is a valid exception -from pin. + if (sdc->exceptionFromStates(data_path->pin(this), + data_path->transition(this), + nullptr, nullptr, // clk below + MinMax::max(), states) + // -from enable non-filter exceptions apply. + && sdc->exceptionFromStates(enable_vertex->pin(), + enable_rf, + en_clk_edge->clock(), + en_clk_edge->transition(), + MinMax::max(), false, states)) + q_tag = search_->findTag(scene, d_q_arc->toEdge()->asRiseFall(), + MinMax::max(), q_clk_info, false, + nullptr, false, states, true, nullptr); + } + return; + } + } } // No enable path found. - } break; } + } } ExceptionPath * Latches::exceptionTo(const Path *data_path, - const ClockEdge *en_clk_edge) + const ClockEdge *en_clk_edge) { + Sdc *sdc = data_path->sdc(this); // Look for exceptions -to data or -to enable clk. return search_->exceptionTo(ExceptionPathType::any, - data_path, - data_path->pin(this), - data_path->transition(this), - en_clk_edge, - data_path->minMax(this), - false, false); + data_path, + data_path->pin(this), + data_path->transition(this), + en_clk_edge, + data_path->minMax(this), + false, false, sdc); } ArcDelay Latches::latchSetupMargin(Vertex *data_vertex, - const RiseFall *data_rf, - const Path *disable_path, - const PathAnalysisPt *path_ap) const + const RiseFall *data_rf, + const Path *disable_path) const { if (disable_path) { + const Mode *mode = disable_path->mode(this); + const Sdc *sdc = mode->sdc(); Vertex *enable_vertex = disable_path->vertex(this); + const MinMax *min_max = disable_path->minMax(this); + DcalcAPIndex dcalc_ap = disable_path->dcalcAnalysisPtIndex(this); const RiseFall *disable_rf = disable_path->transition(this); VertexInEdgeIterator edge_iter(data_vertex, graph_); while (edge_iter.hasNext()) { @@ -436,16 +489,16 @@ Latches::latchSetupMargin(Vertex *data_vertex, const TimingRole *role = edge->role(); Vertex *from_vertex = edge->from(graph_); if (role == TimingRole::setup() - && from_vertex == enable_vertex - && !edge->isDisabledCond() - && !sdc_->isDisabledCondDefault(edge)) { - TimingArcSet *arc_set = edge->timingArcSet(); + && from_vertex == enable_vertex + && !mode->sim()->isDisabledCond(edge) + && !sdc->isDisabledCondDefault(edge)) { + TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *check_arc : arc_set->arcs()) { - if (check_arc->toEdge()->asRiseFall() == data_rf - && check_arc->fromEdge()->asRiseFall() == disable_rf) - return search_->deratedDelay(from_vertex, check_arc, edge, - false, path_ap); - } + if (check_arc->toEdge()->asRiseFall() == data_rf + && check_arc->fromEdge()->asRiseFall() == disable_rf) + return search_->deratedDelay(from_vertex, check_arc, edge, + false, min_max, dcalc_ap, sdc); + } } } } @@ -454,23 +507,21 @@ Latches::latchSetupMargin(Vertex *data_vertex, void Latches::latchTimeGivenToStartpoint(const Path *d_path, - const Path *q_path, - const Edge *d_q_edge, - // Return values. - Arrival &time_given, - Path *&enable_path) const + const Path *q_path, + const Edge *d_q_edge, + // Return values. + Arrival &time_given, + Path *&enable_path) const { enable_path = latchEnablePath(q_path, d_q_edge); if (enable_path && enable_path->isClock(this)) { - const PathAnalysisPt *path_ap = q_path->pathAnalysisPt(this); - const PathAnalysisPt *tgt_clk_path_ap = path_ap->tgtClkAnalysisPt(); - Path *disable_path = latchEnableOtherPath(enable_path, tgt_clk_path_ap); + Path *disable_path = latchEnableOtherPath(enable_path); Delay borrow; Required required; Arrival adjusted_data_arrival; - latchRequired(d_path, enable_path, disable_path, path_ap, - required, borrow, adjusted_data_arrival, time_given); + latchRequired(d_path, enable_path, disable_path, + required, borrow, adjusted_data_arrival, time_given); } else { time_given = 0.0; @@ -480,11 +531,12 @@ Latches::latchTimeGivenToStartpoint(const Path *d_path, void Latches::latchDtoQEnable(const Edge *d_q_edge, - const Instance *inst, - // Return values. - Vertex *&enable_vertex, - const RiseFall *&enable_rf, - LatchEnableState &state) const + const Instance *inst, + const Mode *mode, + // Return values. + Vertex *&enable_vertex, + const RiseFall *&enable_rf, + LatchEnableState &state) const { enable_vertex = nullptr; state = LatchEnableState::open; @@ -495,38 +547,41 @@ Latches::latchDtoQEnable(const Edge *d_q_edge, const FuncExpr *enable_func; cell->latchEnable(d_q_set, enable_port, enable_func, enable_rf); if (enable_port) { + const Sdc *sdc = mode->sdc(); + Sim *sim = mode->sim(); Pin *enable_pin = network_->findPin(inst, enable_port); if (enable_pin) { - enable_vertex = graph_->pinLoadVertex(enable_pin); - if (enable_vertex->isDisabledConstraint()) - state = LatchEnableState::open; - else { - // See if constant values in the latch enable expression force - // it to be continuously open or closed. - LogicValue enable_value = enable_func - ? sim_->evalExpr(enable_func, inst) - : sim_->logicValue(enable_pin); - switch (enable_value) { - case LogicValue::zero: - case LogicValue::fall: - state = LatchEnableState::closed; - break; - case LogicValue::one: - case LogicValue::rise: - state = LatchEnableState::open; - break; - case LogicValue::unknown: - state = LatchEnableState::enabled; - break; - } - } + enable_vertex = graph_->pinLoadVertex(enable_pin); + if (sdc->isDisabledConstraint(enable_pin)) + state = LatchEnableState::open; + else { + // See if constant values in the latch enable expression force + // it to be continuously open or closed. + LogicValue enable_value = enable_func + ? sim->evalExpr(enable_func, inst) + : sim->simValue(enable_pin); + switch (enable_value) { + case LogicValue::zero: + case LogicValue::fall: + state = LatchEnableState::closed; + break; + case LogicValue::one: + case LogicValue::rise: + state = LatchEnableState::open; + break; + case LogicValue::unknown: + state = LatchEnableState::enabled; + break; + } + } } } } } LatchEnableState -Latches::latchDtoQState(const Edge *edge) const +Latches::latchDtoQState(const Edge *edge, + const Mode *mode) const { const Vertex *from_vertex = edge->from(graph_); const Pin *from_pin = from_vertex->pin(); @@ -534,17 +589,18 @@ Latches::latchDtoQState(const Edge *edge) const Vertex *enable_vertex; const RiseFall *enable_rf; LatchEnableState state; - latchDtoQEnable(edge, inst, enable_vertex, enable_rf, state); + latchDtoQEnable(edge, inst, mode, enable_vertex, enable_rf, state); return state; } // Latch D->Q arc looks combinational when the enable pin is disabled // or constant. bool -Latches::isLatchDtoQ(const Edge *edge) const +Latches::isLatchDtoQ(const Edge *edge, + const Mode *mode) const { return edge->role() == TimingRole::latchDtoQ() - && latchDtoQState(edge) == LatchEnableState::enabled; + && latchDtoQState(edge, mode) == LatchEnableState::enabled; } -} // namespace +} // namespace sta diff --git a/search/Latches.hh b/search/Latches.hh index 689e16850..4d31e8d5a 100644 --- a/search/Latches.hh +++ b/search/Latches.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,10 +24,15 @@ #pragma once +#include "Delay.hh" #include "GraphClass.hh" -#include "SearchClass.hh" +#include "LibertyClass.hh" +#include "Mode.hh" +#include "NetworkClass.hh" #include "SdcClass.hh" +#include "SearchClass.hh" #include "StaState.hh" +#include "Transition.hh" namespace sta { @@ -39,74 +44,73 @@ class Latches : public StaState public: Latches(StaState *sta); void latchTimeGivenToStartpoint(const Path *d_path, - const Path *q_path, - const Edge *d_q_edge, - // Return values. - Arrival &time_given, - Path *&enable_path) const; + const Path *q_path, + const Edge *d_q_edge, + // Return values. + Arrival &time_given, + Path *&enable_path) const; void latchRequired(const Path *data_path, - const Path *enable_path, - const Path *disable_path, - const MultiCyclePath *mcp, - const PathDelay *path_delay, - Arrival src_clk_latency, - const ArcDelay &margin, - // Return values. - Required &required, - Delay &borrow, - Arrival &adjusted_data_arrival, - Delay &time_given_to_startpoint) const; - void latchRequired(const Path *data_path, - const Path *enable_path, - const Path *disable_path, - const PathAnalysisPt *path_ap, - // Return values. - Required &required, - Delay &borrow, - Arrival &adjusted_data_arrival, - Delay &time_given_to_startpoint) const; + const Path *enable_path, + const Path *disable_path, + const MultiCyclePath *mcp, + const PathDelay *path_delay, + Arrival src_clk_latency, + const ArcDelay &margin, + // Return values. + Required &required, + Delay &borrow, + Arrival &adjusted_data_arrival, + Delay &time_given_to_startpoint) const; void latchBorrowInfo(const Path *data_path, - const Path *enable_path, - const Path *disable_path, - const ArcDelay &margin, - bool ignore_clk_latency, - // Return values. - float &nom_pulse_width, - Delay &open_latency, - Delay &latency_diff, - float &open_uncertainty, - Crpr &open_crpr, - Crpr &crpr_diff, - Delay &max_borrow, - bool &borrow_limit_exists) const; - bool isLatchDtoQ(const Edge *edge) const; + const Path *enable_path, + const Path *disable_path, + const ArcDelay &margin, + bool ignore_clk_latency, + // Return values. + float &nom_pulse_width, + Delay &open_latency, + Delay &latency_diff, + float &open_uncertainty, + Crpr &open_crpr, + Crpr &crpr_diff, + Delay &max_borrow, + bool &borrow_limit_exists) const; + bool isLatchDtoQ(const Edge *edge, + const Mode *mode) const; // Find the latch EN->Q edge for a D->Q edge. void latchDtoQEnable(const Edge *d_q_edge, - const Instance *inst, - // Return values. - Vertex *&enable_vertex, - const RiseFall *&enable_rf, - LatchEnableState &state) const; - LatchEnableState latchDtoQState(const Edge *d_q_edge) const; - Path *latchEnableOtherPath(const Path *path, - const PathAnalysisPt *tgt_clk_path_ap) const; + const Instance *inst, + const Mode *mode, + // Return values. + Vertex *&enable_vertex, + const RiseFall *&enable_rf, + LatchEnableState &state) const; + LatchEnableState latchDtoQState(const Edge *d_q_edge, + const Mode *mode) const; + Path *latchEnableOtherPath(const Path *path) const; Path *latchEnablePath(const Path *q_path, const Edge *d_q_edge) const; void latchOutArrival(const Path *data_path, - const TimingArc *d_q_arc, - const Edge *d_q_edge, - const PathAnalysisPt *path_ap, - Tag *&q_tag, - ArcDelay &arc_delay, - Arrival &q_arrival); + const TimingArc *d_q_arc, + const Edge *d_q_edge, + Tag *&q_tag, + ArcDelay &arc_delay, + Arrival &q_arrival); protected: + void latchRequired(const Path *data_path, + const Path *enable_path, + const Path *disable_path, + // Return values. + Required &required, + Delay &borrow, + Arrival &adjusted_data_arrival, + Delay &time_given_to_startpoint) const; ArcDelay latchSetupMargin(Vertex *data_vertex, - const RiseFall *data_rf, - const Path *disable_path, - const PathAnalysisPt *path_ap) const; + const RiseFall *data_rf, + const Path *disable_path) const; ExceptionPath *exceptionTo(const Path *data_path, - const ClockEdge *en_clk_edge); + const ClockEdge *en_clk_edge); }; -} // namespace +} // namespace sta diff --git a/search/Levelize.cc b/search/Levelize.cc index 69273479b..2219a91d4 100644 --- a/search/Levelize.cc +++ b/search/Levelize.cc @@ -1,66 +1,61 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Levelize.hh" #include +#include #include -#include "Report.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" -#include "Stats.hh" -#include "TimingRole.hh" -#include "PortDirection.hh" -#include "Network.hh" -#include "Sdc.hh" #include "Graph.hh" #include "GraphCmp.hh" -#include "SearchPred.hh" -#include "Variables.hh" #include "GraphDelayCalc.hh" +#include "Mode.hh" +#include "Network.hh" +#include "PortDirection.hh" +#include "Report.hh" +#include "Sdc.hh" +#include "Stats.hh" +#include "TimingRole.hh" +#include "Variables.hh" namespace sta { -using std::max; - Levelize::Levelize(StaState *sta) : StaState(sta), - search_pred_(sta), - levelized_(false), - levels_valid_(false), - max_level_(0), - level_space_(10), - roots_(graph_), - relevelize_from_(graph_), - observer_(nullptr) + roots_(makeVertexSet(sta)), + relevelize_from_(makeVertexSet(sta)) { } Levelize::~Levelize() { delete observer_; - loops_.deleteContents(); + for (auto loop : loops_) + delete loop; } void @@ -84,7 +79,9 @@ Levelize::clear() roots_.clear(); relevelize_from_.clear(); clearLoopEdges(); - loops_.deleteContentsClear(); + for (auto loop : loops_) + delete loop; + loops_.clear(); loop_edges_.clear(); max_level_ = 0; } @@ -92,11 +89,8 @@ Levelize::clear() void Levelize::clearLoopEdges() { - EdgeSet::Iterator edge_iter(disabled_loop_edges_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); + for (Edge *edge : disabled_loop_edges_) edge->setIsDisabledLoop(false); - } disabled_loop_edges_.clear(); } @@ -107,7 +101,7 @@ Levelize::ensureLevelized() if (levelized_) relevelize(); else - levelize(); + findLevels(); } } @@ -115,7 +109,7 @@ Levelize::ensureLevelized() #define setOnPath(on_path) setVisited2(on_path) void -Levelize::levelize() +Levelize::findLevels() { Stats stats(debug_, report_); debugPrint(debug_, "levelize", 1, "levelize"); @@ -163,8 +157,7 @@ Levelize::findRoots() while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); if (isRoot(vertex)) { - debugPrint(debug_, "levelize", 2, "root %s%s", - vertex->to_string(this).c_str(), + debugPrint(debug_, "levelize", 2, "root {}{}", vertex->to_string(this), hasFanout(vertex) ? " fanout" : ""); roots_.insert(vertex); } @@ -173,11 +166,10 @@ Levelize::findRoots() size_t fanout_roots = 0; for (Vertex *root : roots_) { if (hasFanout(root)) - fanout_roots++; + fanout_roots++; } - debugPrint(debug_, "levelize", 1, "Found %zu roots %zu with fanout", - roots_.size(), - fanout_roots); + debugPrint(debug_, "levelize", 1, "Found {} roots {} with fanout", + roots_.size(), fanout_roots); } } @@ -186,43 +178,44 @@ Levelize::findRoots() bool Levelize::isRoot(Vertex *vertex) { - if (search_pred_.searchTo(vertex)) { - VertexInEdgeIterator edge_iter1(vertex, graph_); - while (edge_iter1.hasNext()) { - Edge *edge = edge_iter1.next(); - Vertex *from_vertex = edge->from(graph_); - if (search_pred_.searchFrom(from_vertex) - && search_pred_.searchThru(edge)) - return false; - } - // Levelize bidirect driver as if it was a fanout of the bidirect load. - return !(graph_delay_calc_->bidirectDrvrSlewFromLoad(vertex->pin()) - && vertex->isBidirectDriver()); + VertexInEdgeIterator edge_iter1(vertex, graph_); + while (edge_iter1.hasNext()) { + Edge *edge = edge_iter1.next(); + if (searchThru(edge)) + return false; } - else - return false; + // Levelize bidirect driver as if it was a fanout of the bidirect load. + return !(graph_delay_calc_->bidirectDrvrSlewFromLoad(vertex->pin()) + && vertex->isBidirectDriver()); +} + +bool +Levelize::searchThru(Edge *edge) +{ + const TimingRole *role = edge->role(); + return !role->isTimingCheck() && role != TimingRole::latchDtoQ() + && !edge->isDisabledLoop() + // Register/latch preset/clr edges are disabled by default. + && !(role == TimingRole::regSetClr() && !variables_->presetClrArcsEnabled()) + && !(edge->isBidirectInstPath() && !variables_->bidirectInstPathsEnabled()); } bool Levelize::hasFanout(Vertex *vertex) { bool has_fanout = false; - if (search_pred_.searchFrom(vertex)) { - VertexOutEdgeIterator edge_iter2(vertex, graph_); - while (edge_iter2.hasNext()) { - Edge *edge = edge_iter2.next(); - Vertex *to_vertex = edge->from(graph_); - if (search_pred_.searchTo(to_vertex) - && search_pred_.searchThru(edge)) { - has_fanout = true; - break; - } - } - // Levelize bidirect driver as if it was a fanout of the bidirect load. - if (graph_delay_calc_->bidirectDrvrSlewFromLoad(vertex->pin()) - && !vertex->isBidirectDriver()) + VertexOutEdgeIterator edge_iter2(vertex, graph_); + while (edge_iter2.hasNext()) { + Edge *edge = edge_iter2.next(); + if (searchThru(edge)) { has_fanout = true; + break; + } } + // Levelize bidirect driver as if it was a fanout of the bidirect load. + if (graph_delay_calc_->bidirectDrvrSlewFromLoad(vertex->pin()) + && !vertex->isBidirectDriver()) + has_fanout = true; return has_fanout; } @@ -268,11 +261,10 @@ Levelize::findBackEdges(EdgeSeq &path, EdgeSet back_edges; while (!stack.empty()) { VertexEdgeIterPair vertex_iter = stack.top(); - Vertex *vertex = vertex_iter.first; - VertexOutEdgeIterator *edge_iter = vertex_iter.second; + const auto &[vertex, edge_iter] = vertex_iter; if (edge_iter->hasNext()) { Edge *edge = edge_iter->next(); - if (search_pred_.searchThru(edge)) { + if (searchThru(edge)) { Vertex *to_vertex = edge->to(graph_); if (!to_vertex->visited()) { to_vertex->setVisited(true); @@ -280,7 +272,7 @@ Levelize::findBackEdges(EdgeSeq &path, path.push_back(edge); stack.emplace(to_vertex, new VertexOutEdgeIterator(to_vertex, graph_)); } - else if (to_vertex->visited2()) { // on path + else if (to_vertex->visited2()) { // on path // Found a back edge (loop). recordLoop(edge, path); back_edges.insert(edge); @@ -310,10 +302,10 @@ Levelize::findCycleBackEdges() if (unvisited.size() < 100) sort(unvisited, VertexNameLess(network_)); size_t back_edge_count = 0; - VertexSet visited(graph_); + VertexSet visited = makeVertexSet(this); for (Vertex *vertex : unvisited) { - if (visited.find(vertex) == visited.end()) { - VertexSet path_vertices(graph_); + if (!visited.contains(vertex)) { + VertexSet path_vertices = makeVertexSet(this); EdgeSeq path; FindBackEdgesStack stack; visited.insert(vertex); @@ -321,11 +313,11 @@ Levelize::findCycleBackEdges() stack.emplace(vertex, new VertexOutEdgeIterator(vertex, graph_)); EdgeSet back_edges = findBackEdges(path, stack); for (Edge *back_edge : back_edges) - roots_.insert(back_edge->from(graph_)); + roots_.insert(back_edge->to(graph_)); back_edge_count += back_edges.size(); } } - debugPrint(debug_, "levelize", 1, "Found %zu cycle back edges", back_edge_count); + debugPrint(debug_, "levelize", 1, "Found {} cycle back edges", back_edge_count); } // Find vertices in cycles that are were not accessible from roots. @@ -336,8 +328,7 @@ Levelize::findUnvisitedVertices() VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); - if (!vertex->visited() - && search_pred_.searchFrom(vertex)) + if (!vertex->visited()) unvisited.push_back(vertex); } return unvisited; @@ -349,34 +340,31 @@ VertexSeq Levelize::findTopologicalOrder() { Stats stats(debug_, report_); - std::map in_degree; + std::map in_degree; VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); - 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)) - in_degree[to_vertex] += 1; - if (edge->role() == TimingRole::latchDtoQ()) - latch_d_to_q_edges_.insert(edge); - } - // Levelize bidirect driver as if it was a fanout of the bidirect load. - const Pin *pin = vertex->pin(); - if (graph_delay_calc_->bidirectDrvrSlewFromLoad(pin) - && !vertex->isBidirectDriver()) { - Vertex *to_vertex = graph_->pinDrvrVertex(pin);; - if (search_pred_.searchTo(to_vertex)) - in_degree[to_vertex] += 1; - } + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (searchThru(edge)) + in_degree[to_vertex] += 1; + if (edge->role() == TimingRole::latchDtoQ()) + latch_d_to_q_edges_.insert(edge); + } + // Levelize bidirect driver as if it was a fanout of the bidirect load. + const Pin *pin = vertex->pin(); + if (graph_delay_calc_->bidirectDrvrSlewFromLoad(pin) + && !vertex->isBidirectDriver()) { + Vertex *to_vertex = graph_->pinDrvrVertex(pin); + ; + in_degree[to_vertex] += 1; } } - std::deque queue; + std::deque queue; for (Vertex *root : roots_) queue.push_back(root); @@ -385,19 +373,16 @@ Levelize::findTopologicalOrder() Vertex *vertex = queue.front(); queue.pop_front(); topo_order.push_back(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)) { - const auto &to_degree_itr = in_degree.find(to_vertex); - int &to_in_degree = to_degree_itr->second; - to_in_degree -= 1; - if (to_in_degree == 0) - queue.push_back(to_vertex); - } + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (searchThru(edge)) { + const auto &to_degree_itr = in_degree.find(to_vertex); + int &to_in_degree = to_degree_itr->second; + to_in_degree -= 1; + if (to_in_degree == 0) + queue.push_back(to_vertex); } } // Levelize bidirect driver as if it was a fanout of the bidirect load. @@ -405,13 +390,11 @@ Levelize::findTopologicalOrder() if (graph_delay_calc_->bidirectDrvrSlewFromLoad(pin) && !vertex->isBidirectDriver()) { Vertex *to_vertex = graph_->pinDrvrVertex(pin); - if (search_pred_.searchTo(to_vertex)) { - const auto °ree_itr = in_degree.find(to_vertex); - int &in_degree = degree_itr->second; - in_degree -= 1; - if (in_degree == 0) - queue.push_back(to_vertex); - } + const auto °ree_itr = in_degree.find(to_vertex); + int &in_degree = degree_itr->second; + in_degree -= 1; + if (in_degree == 0) + queue.push_back(to_vertex); } } @@ -420,14 +403,14 @@ Levelize::findTopologicalOrder() while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); if (in_degree[vertex] != 0) - debugPrint(debug_, "levelize", 2, "topological sort missing %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "levelize", 2, "topological sort missing {}", + vertex->to_string(this)); } } if (debug_->check("levelize", 3)) { - report_->reportLine("Topological sort"); + report_->report("Topological sort"); for (Vertex *vertex : topo_order) - report_->reportLine("%s", vertex->to_string(this).c_str()); + report_->report("{}", vertex->to_string(this)); } stats.report("Levelize topological sort"); return topo_order; @@ -435,16 +418,17 @@ Levelize::findTopologicalOrder() void Levelize::recordLoop(Edge *edge, - EdgeSeq &path) + EdgeSeq &path) { - debugPrint(debug_, "levelize", 2, "Loop edge %s (%s)", - edge->to_string(this).c_str(), - edge->role()->to_string().c_str()); + debugPrint(debug_, "levelize", 2, "Loop edge {} ({})", + edge->to_string(this), edge->role()->to_string()); EdgeSeq *loop_edges = loopEdges(path, edge); GraphLoop *loop = new GraphLoop(loop_edges); loops_.push_back(loop); - if (variables_->dynamicLoopBreaking()) - sdc_->makeLoopExceptions(loop); + if (variables_->dynamicLoopBreaking()) { + for (Mode *mode : modes_) + mode->sdc()->makeLoopExceptions(loop); + } // Record disabled loop edges so they can be cleared without // traversing the entire graph to find them. @@ -454,28 +438,24 @@ Levelize::recordLoop(Edge *edge, EdgeSeq * Levelize::loopEdges(EdgeSeq &path, - Edge *closing_edge) + Edge *closing_edge) { debugPrint(debug_, "loop", 2, "Loop"); EdgeSeq *loop_edges = new EdgeSeq; // Skip the "head" of the path up to where closing_edge closes the loop. Pin *loop_pin = closing_edge->to(graph_)->pin(); bool copy = false; - EdgeSeq::Iterator edge_iter(path); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); + for (Edge *edge : path) { Pin *from_pin = edge->from(graph_)->pin(); if (from_pin == loop_pin) copy = true; if (copy) { - debugPrint(debug_, "loop", 2, " %s", - edge->to_string(this).c_str()); + debugPrint(debug_, "loop", 2, " {}", edge->to_string(this)); loop_edges->push_back(edge); loop_edges_.insert(edge); } } - debugPrint(debug_, "loop", 2, " %s", - closing_edge->to_string(this).c_str()); + debugPrint(debug_, "loop", 2, " {}", closing_edge->to_string(this)); loop_edges->push_back(closing_edge); loop_edges_.insert(closing_edge); return loop_edges; @@ -485,12 +465,10 @@ void Levelize::reportPath(EdgeSeq &path) const { bool first_edge = true; - EdgeSeq::Iterator edge_iter(path); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); + for (Edge *edge : path) { if (first_edge) - report_->reportLine(" %s", edge->from(graph_)->to_string(this).c_str()); - report_->reportLine(" %s", edge->to(graph_)->to_string(this).c_str()); + report_->report(" {}", edge->from(graph_)->to_string(this)); + report_->report(" {}", edge->to(graph_)->to_string(this)); first_edge = false; } } @@ -503,25 +481,22 @@ Levelize::assignLevels(VertexSeq &topo_sorted) for (Vertex *root : roots_) setLevel(root, 0); for (Vertex *vertex : topo_sorted) { - if (vertex->level() != -1 - && search_pred_.searchFrom(vertex)) { + if (vertex->level() != -1) { 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)) - setLevel(to_vertex, max(to_vertex->level(), - vertex->level() + level_space_)); + if (searchThru(edge)) + setLevel(to_vertex, + std::max(to_vertex->level(), vertex->level() + level_space_)); } // Levelize bidirect driver as if it was a fanout of the bidirect load. const Pin *pin = vertex->pin(); if (graph_delay_calc_->bidirectDrvrSlewFromLoad(pin) && !vertex->isBidirectDriver()) { Vertex *to_vertex = graph_->pinDrvrVertex(pin); - if (search_pred_.searchTo(to_vertex)) - setLevel(to_vertex, max(to_vertex->level(), - vertex->level() + level_space_)); + setLevel(to_vertex, + std::max(to_vertex->level(), vertex->level() + level_space_)); } } } @@ -536,26 +511,28 @@ Levelize::assignLevels(VertexSeq &topo_sorted) void Levelize::ensureLatchLevels() { - EdgeSet::Iterator latch_edge_iter(latch_d_to_q_edges_); - while (latch_edge_iter.hasNext()) { - Edge *edge = latch_edge_iter.next(); + for (Edge *edge : latch_d_to_q_edges_) { Vertex *from = edge->from(graph_); Vertex *to = edge->to(graph_); - if (from->level() == to->level()) - setLevel(from, from->level() + level_space_); + if (from->level() == to->level()) { + Level adjusted_level = from->level() + level_space_; + debugPrint(debug_, "levelize", 2, "latch {} {} (adjusted {}) -> {} {}", + from->to_string(this), from->level(), adjusted_level, + to->to_string(this), to->level()); + setLevel(from, adjusted_level); + } } latch_d_to_q_edges_.clear(); } void -Levelize::setLevel(Vertex *vertex, - Level level) +Levelize::setLevel(Vertex *vertex, + Level level) { - debugPrint(debug_, "levelize", 2, "set level %s %d", - vertex->to_string(this).c_str(), - level); + debugPrint(debug_, "levelize", 3, "set level {} {}", + vertex->to_string(this), level); vertex->setLevel(level); - max_level_ = max(level, max_level_); + max_level_ = std::max(level, max_level_); if (level >= Graph::vertex_level_max) report_->critical(616, "maximum logic level exceeded"); } @@ -570,23 +547,6 @@ Levelize::invalid() } } -void -Levelize::invalidFrom(Vertex *vertex) -{ - if (levelized_) { - debugPrint(debug_, "levelize", 1, "level invalid from %s", - vertex->to_string(this).c_str()); - VertexInEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - Vertex *from_vertex = edge->from(graph_); - relevelize_from_.insert(from_vertex); - } - relevelize_from_.insert(vertex); - levels_valid_ = false; - } -} - void Levelize::deleteVertexBefore(Vertex *vertex) { @@ -600,8 +560,8 @@ void Levelize::relevelizeFrom(Vertex *vertex) { if (levelized_) { - debugPrint(debug_, "levelize", 1, "level invalid from %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "levelize", 1, "level invalid from {}", + vertex->to_string(this)); relevelize_from_.insert(vertex); levels_valid_ = false; } @@ -610,10 +570,9 @@ Levelize::relevelizeFrom(Vertex *vertex) void Levelize::deleteEdgeBefore(Edge *edge) { - if (levelized_ - && loop_edges_.hasKey(edge)) { - debugPrint(debug_, "levelize", 2, "delete loop edge %s", - edge->to_string(this).c_str()); + if (levelized_ && loop_edges_.contains(edge)) { + debugPrint(debug_, "levelize", 2, "delete loop edge {}", + edge->to_string(this)); disabled_loop_edges_.erase(edge); // Relevelize if a loop edge is removed. Incremental levelization // fails because the DFS path will be missing. @@ -634,15 +593,13 @@ void Levelize::relevelize() { for (Vertex *vertex : relevelize_from_) { - debugPrint(debug_, "levelize", 1, "relevelize from %s", - vertex->to_string(this).c_str()); - if (search_pred_.searchFrom(vertex)) { - if (isRoot(vertex)) - roots_.insert(vertex); - VertexSet path_vertices(graph_); - EdgeSeq path; - visit(vertex, nullptr, vertex->level(), 1, path_vertices, path); - } + debugPrint(debug_, "levelize", 2, "relevelize from {}", + vertex->to_string(this)); + if (isRoot(vertex)) + roots_.insert(vertex); + VertexSet path_vertices = makeVertexSet(this); + EdgeSeq path; + visit(vertex, nullptr, vertex->level(), 1, path_vertices, path); } ensureLatchLevels(); levels_valid_ = true; @@ -651,11 +608,11 @@ Levelize::relevelize() void Levelize::visit(Vertex *vertex, - Edge *from, + Edge *from, Level level, - Level level_space, + Level level_space, VertexSet &path_vertices, - EdgeSeq &path) + EdgeSeq &path) { Pin *from_pin = vertex->pin(); setLevelIncr(vertex, level); @@ -663,33 +620,40 @@ Levelize::visit(Vertex *vertex, if (from) path.push_back(from); - 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 (path_vertices.find(to_vertex) != path_vertices.end()) - // Back edges form feedback loops. - recordLoop(edge, path); - else if (to_vertex->level() <= level) - visit(to_vertex, edge, level+level_space, level_space, - path_vertices, path); - } - if (edge->role() == TimingRole::latchDtoQ()) - latch_d_to_q_edges_.insert(edge); + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (searchThru(edge)) { + if (path_vertices.contains(to_vertex)) + // Back edges form feedback loops. + recordLoop(edge, path); + else if (to_vertex->level() <= level) + visit(to_vertex, edge, level + level_space, level_space, path_vertices, + path); } - // Levelize bidirect driver as if it was a fanout of the bidirect load. - if (graph_delay_calc_->bidirectDrvrSlewFromLoad(from_pin) - && !vertex->isBidirectDriver()) { - Vertex *to_vertex = graph_->pinDrvrVertex(from_pin); - if (search_pred_.searchTo(to_vertex) - && (to_vertex->level() <= level)) - visit(to_vertex, nullptr, level+level_space, level_space, - path_vertices, path); + + const TimingRole *role = edge->role(); + if (role->isLatchDtoQ()) + latch_d_to_q_edges_.insert(edge); + if (role->isLatchEnToQ()) { + VertexInEdgeIterator edge_iter2(to_vertex, graph_); + while (edge_iter2.hasNext()) { + Edge *edge2 = edge_iter2.next(); + if (edge2->role()->isLatchDtoQ()) + latch_d_to_q_edges_.insert(edge2); + } } } + + // Levelize bidirect driver as if it was a fanout of the bidirect load. + if (graph_delay_calc_->bidirectDrvrSlewFromLoad(from_pin) + && !vertex->isBidirectDriver()) { + Vertex *to_vertex = graph_->pinDrvrVertex(from_pin); + if (to_vertex->level() <= level) + visit(to_vertex, nullptr, level + level_space, level_space, path_vertices, + path); + } path_vertices.erase(vertex); if (from) path.pop_back(); @@ -698,24 +662,23 @@ Levelize::visit(Vertex *vertex, bool Levelize::isDisabledLoop(Edge *edge) const { - return disabled_loop_edges_.hasKey(edge); + return disabled_loop_edges_.contains(edge); } void -Levelize::setLevelIncr(Vertex *vertex, +Levelize::setLevelIncr(Vertex *vertex, Level level) { - debugPrint(debug_, "levelize", 2, "set level %s %d", - vertex->to_string(this).c_str(), - level); + debugPrint(debug_, "levelize", 2, "set level {} {}", + vertex->to_string(this), level); if (vertex->level() != level) { if (observer_) observer_->levelChangedBefore(vertex); vertex->setLevel(level); } - max_level_ = max(level, max_level_); + max_level_ = std::max(level, max_level_); if (level >= Graph::vertex_level_max) - criticalError(617, "maximum logic level exceeded"); + criticalError(618, "maximum logic level exceeded"); } void @@ -724,24 +687,19 @@ Levelize::checkLevels() VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); - if (search_pred_.searchTo(vertex)) { - Level level = vertex->level(); - VertexInEdgeIterator edge_iter1(vertex, graph_); - while (edge_iter1.hasNext()) { - Edge *edge = edge_iter1.next(); - Vertex *from_vertex = edge->from(graph_); - Level from_level = from_vertex->level(); - if (search_pred_.searchFrom(from_vertex) - && search_pred_.searchThru(edge) - && from_level >= level - // Loops with no entry edges are all level zero. - && !(from_level == 0 && level == 0)) - report_->warn(617, "level check failed %s %d -> %s %d", - from_vertex->name(network_), - from_vertex->level(), - vertex->name(network_), - level); - } + Level level = vertex->level(); + VertexInEdgeIterator edge_iter1(vertex, graph_); + while (edge_iter1.hasNext()) { + Edge *edge = edge_iter1.next(); + Vertex *from_vertex = edge->from(graph_); + Level from_level = from_vertex->level(); + if (searchThru(edge) + && from_level >= level + // Loops with no entry edges are all level zero. + && !(from_level == 0 && level == 0)) + report_->warn(617, "level check failed {} {} -> {} {}", + from_vertex->name(network_), from_vertex->level(), + vertex->name(network_), level); } } } @@ -753,22 +711,16 @@ GraphLoop::GraphLoop(EdgeSeq *edges) : { } -GraphLoop::~GraphLoop() -{ - delete edges_; -} +GraphLoop::~GraphLoop() { delete edges_; } bool GraphLoop::isCombinational() const { - EdgeSeq::Iterator edge_iter(edges_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); + for (Edge *edge : *edges_) { const TimingRole *role = edge->role(); - if (!(role == TimingRole::wire() - || role == TimingRole::combinational() - || role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable())) + if (!(role == TimingRole::wire() || role == TimingRole::combinational() + || role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable())) return false; } return true; @@ -780,14 +732,12 @@ GraphLoop::report(const StaState *sta) const Graph *graph = sta->graph(); Report *report = sta->report(); bool first_edge = true; - EdgeSeq::Iterator loop_edge_iter(edges_); - while (loop_edge_iter.hasNext()) { - Edge *edge = loop_edge_iter.next(); + for (Edge *edge : *edges_) { if (first_edge) - report->reportLine(" %s", edge->from(graph)->to_string(sta).c_str()); - report->reportLine(" %s", edge->to(graph)->to_string(graph).c_str()); + report->report(" {}", edge->from(graph)->to_string(sta)); + report->report(" {}", edge->to(graph)->to_string(sta)); first_edge = false; } } -} // namespace +} // namespace sta diff --git a/search/Levelize.hh b/search/Levelize.hh index fb9c1af91..a1b680e8f 100644 --- a/search/Levelize.hh +++ b/search/Levelize.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,26 +25,27 @@ #pragma once #include +#include +#include -#include "NetworkClass.hh" -#include "SdcClass.hh" #include "Graph.hh" -#include "SearchPred.hh" #include "StaState.hh" namespace sta { class SearchPred; class LevelizeObserver; +class GraphLoop; -typedef std::pair VertexEdgeIterPair; -typedef std::stack FindBackEdgesStack; +using VertexEdgeIterPair = std::pair; +using FindBackEdgesStack = std::stack; +using GraphLoopSeq = std::vector; class Levelize : public StaState { public: Levelize(StaState *sta); - virtual ~Levelize(); + ~Levelize() override; // Space between initially assigned levels that is filled in by // incremental levelization. Set level space before levelization. void setLevelSpace(Level space); @@ -52,7 +53,6 @@ public: void ensureLevelized(); void invalid(); // Levels downstream from vertex are invalid. - void invalidFrom(Vertex *vertex); void relevelizeFrom(Vertex *vertex); void deleteVertexBefore(Vertex *vertex); void deleteEdgeBefore(Edge *edge); @@ -61,6 +61,7 @@ public: VertexSet &roots() { return roots_; } bool isRoot(Vertex *vertex); bool hasFanout(Vertex *vertex); + bool searchThru(Edge *edge); // Reset to virgin state. void clear(); // Edge is disabled to break combinational loops. @@ -71,7 +72,7 @@ public: void setObserver(LevelizeObserver *observer); void checkLevels(); // Public for regression testing. - void levelize(); + void findLevels(); protected: void findRoots(); @@ -96,25 +97,24 @@ protected: VertexSet &path_vertices, EdgeSeq &path); void setLevel(Vertex *vertex, - Level level); + Level level); void setLevelIncr(Vertex *vertex, Level level); void clearLoopEdges(); void deleteLoops(); void reportPath(EdgeSeq &path) const; - SearchPredNonLatch2 search_pred_; - bool levelized_; - bool levels_valid_; - Level max_level_; - Level level_space_; + bool levelized_{false}; + bool levels_valid_{false}; + Level max_level_{0}; + Level level_space_{10}; VertexSet roots_; VertexSet relevelize_from_; GraphLoopSeq loops_; EdgeSet loop_edges_; EdgeSet disabled_loop_edges_; EdgeSet latch_d_to_q_edges_; - LevelizeObserver *observer_; + LevelizeObserver *observer_{nullptr}; }; // Loops broken by levelization may not necessarily be combinational. @@ -123,7 +123,7 @@ protected: class GraphLoop { public: - explicit GraphLoop(EdgeSeq *edges); + GraphLoop(EdgeSeq *edges); ~GraphLoop(); EdgeSeq *edges() { return edges_; } bool isCombinational() const; @@ -136,10 +136,9 @@ private: class LevelizeObserver { public: - LevelizeObserver() {} - virtual ~LevelizeObserver() {} + virtual ~LevelizeObserver() = default; virtual void levelsChangedBefore() = 0; virtual void levelChangedBefore(Vertex *vertex) = 0; }; -} // namespace +} // namespace sta diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index 7529cfb37..4d141385e 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -1,99 +1,106 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "MakeTimingModel.hh" #include "MakeTimingModelPvt.hh" #include +#include #include +#include +#include +#include +#include +#include "ArcDelayCalc.hh" +#include "ClkDelays.hh" +#include "Clock.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" -#include "Units.hh" -#include "Transition.hh" +#include "Delay.hh" +#include "Graph.hh" +#include "GraphClass.hh" +#include "GraphDelayCalc.hh" #include "Liberty.hh" -#include "TimingArc.hh" -#include "TableModel.hh" -#include "liberty/LibertyBuilder.hh" +#include "LibertyClass.hh" #include "Network.hh" +#include "NetworkClass.hh" +#include "Path.hh" +#include "PathEnd.hh" #include "PortDirection.hh" -#include "Corner.hh" -#include "DcalcAnalysisPt.hh" -#include "GraphDelayCalc.hh" +#include "RiseFallMinMax.hh" +#include "Scene.hh" #include "Sdc.hh" -#include "StaState.hh" -#include "Graph.hh" -#include "PathEnd.hh" +#include "SdcClass.hh" #include "Search.hh" #include "Sta.hh" +#include "StaState.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Transition.hh" +#include "Units.hh" #include "VisitPathEnds.hh" -#include "ArcDelayCalc.hh" -#include "ClkLatency.hh" +#include "liberty/LibertyBuilder.hh" namespace sta { -using std::string; -using std::min; -using std::max; -using std::make_shared; - LibertyLibrary * -makeTimingModel(const char *lib_name, - const char *cell_name, - const char *filename, - const Corner *corner, +makeTimingModel(std::string_view lib_name, + std::string_view cell_name, + std::string_view filename, + const Scene *scene, const bool scalar, Sta *sta) { - MakeTimingModel maker(lib_name, cell_name, filename, corner, scalar, sta); + MakeTimingModel maker(lib_name, cell_name, filename, scene, scalar, sta); return maker.makeTimingModel(); } -MakeTimingModel::MakeTimingModel(const char *lib_name, - const char *cell_name, - const char *filename, - const Corner *corner, +MakeTimingModel::MakeTimingModel(std::string_view lib_name, + std::string_view cell_name, + std::string_view filename, + const Scene *scene, const bool scalar, Sta *sta) : StaState(sta), lib_name_(lib_name), cell_name_(cell_name), filename_(filename), - corner_(corner), + scene_(scene), scalar_(scalar), cell_(nullptr), min_max_(MinMax::max()), - lib_builder_(new LibertyBuilder), - tbl_template_index_(1), - sdc_backup_(nullptr), + lib_builder_(new LibertyBuilder(debug_, + report_)), + sdc_(scene->sdc()), sta_(sta) { + scenes_.insert(scene_); } -MakeTimingModel::~MakeTimingModel() -{ - delete lib_builder_; -} +MakeTimingModel::~MakeTimingModel() { delete lib_builder_; } LibertyLibrary * MakeTimingModel::makeTimingModel() @@ -116,7 +123,7 @@ MakeTimingModel::makeTimingModel() if (!scalar_) restoreSdc(); - + return library_; } @@ -124,7 +131,7 @@ MakeTimingModel::makeTimingModel() void MakeTimingModel::saveSdc() { - sdc_backup_ = new Sdc(this); + sdc_backup_ = new Sdc(sdc_->mode(), this); swapSdcWithBackup(); sta_->delaysInvalid(); } @@ -192,28 +199,25 @@ MakeTimingModel::findArea() void MakeTimingModel::makePorts() { - const DcalcAnalysisPt *dcalc_ap = corner_->findDcalcAnalysisPt(min_max_); Instance *top_inst = network_->topInstance(); Cell *top_cell = network_->cell(top_inst); CellPortIterator *port_iter = network_->portIterator(top_cell); while (port_iter->hasNext()) { Port *port = port_iter->next(); - const char *port_name = network_->name(port); + std::string port_name(network_->name(port)); if (network_->isBus(port)) { int from_index = network_->fromIndex(port); int to_index = network_->toIndex(port); - BusDcl *bus_dcl = new BusDcl(port_name, from_index, to_index); - library_->addBusDcl(bus_dcl); - LibertyPort *lib_port = lib_builder_->makeBusPort(cell_, port_name, - from_index, to_index, - bus_dcl); + BusDcl *bus_dcl = library_->makeBusDcl(port_name, from_index, to_index); + LibertyPort *lib_port = + lib_builder_->makeBusPort(cell_, port_name, from_index, to_index, bus_dcl); lib_port->setDirection(network_->direction(port)); PortMemberIterator *member_iter = network_->memberIterator(port); while (member_iter->hasNext()) { Port *bit_port = member_iter->next(); Pin *pin = network_->findPin(top_inst, bit_port); LibertyPort *lib_bit_port = modelPort(pin); - float load_cap = graph_delay_calc_->loadCap(pin, dcalc_ap); + float load_cap = graph_delay_calc_->loadCap(pin, scene_, min_max_); lib_bit_port->setCapacitance(load_cap); } delete member_iter; @@ -222,7 +226,7 @@ MakeTimingModel::makePorts() LibertyPort *lib_port = lib_builder_->makePort(cell_, port_name); lib_port->setDirection(network_->direction(port)); Pin *pin = network_->findPin(top_inst, port); - float load_cap = graph_delay_calc_->loadCap(pin, dcalc_ap); + float load_cap = graph_delay_calc_->loadCap(pin, scene_, min_max_); lib_port->setCapacitance(load_cap); } } @@ -234,8 +238,7 @@ MakeTimingModel::checkClock(Clock *clk) { for (const Pin *pin : clk->leafPins()) { if (!network_->isTopLevelPort(pin)) - report_->warn(1355, "clock %s pin %s is inside model block.", - clk->name(), + report_->warn(1380, "clock {} pin {} is inside model block.", clk->name(), network_->pathName(pin)); } } @@ -246,21 +249,19 @@ class MakeEndTimingArcs : public PathEndVisitor { public: MakeEndTimingArcs(Sta *sta); - MakeEndTimingArcs(const MakeEndTimingArcs&) = default; - virtual ~MakeEndTimingArcs() {} - virtual PathEndVisitor *copy() const; - virtual void visit(PathEnd *path_end); + MakeEndTimingArcs(const MakeEndTimingArcs &) = default; + PathEndVisitor *copy() const override; + void visit(PathEnd *path_end) override; void setInputRf(const RiseFall *input_rf); const ClockEdgeDelays &margins() const { return margins_; } private: - const RiseFall *input_rf_; + const RiseFall *input_rf_{nullptr}; ClockEdgeDelays margins_; Sta *sta_; }; MakeEndTimingArcs::MakeEndTimingArcs(Sta *sta) : - input_rf_(nullptr), sta_(sta) { } @@ -281,27 +282,24 @@ void MakeEndTimingArcs::visit(PathEnd *path_end) { Path *src_path = path_end->path(); + const Sdc *sdc = src_path->sdc(sta_); const Clock *src_clk = src_path->clock(sta_); const ClockEdge *tgt_clk_edge = path_end->targetClkEdge(sta_); - if (src_clk == sta_->sdc()->defaultArrivalClock() - && tgt_clk_edge) { + if (src_clk == sdc->defaultArrivalClock() && tgt_clk_edge) { Network *network = sta_->network(); Debug *debug = sta_->debug(); const MinMax *min_max = path_end->minMax(sta_); Arrival data_delay = src_path->arrival(); Delay clk_latency = path_end->targetClkDelay(sta_); ArcDelay check_margin = path_end->margin(sta_); - Delay margin = min_max == MinMax::max() - ? data_delay - clk_latency + check_margin - : clk_latency - data_delay + check_margin; + Delay margin = (min_max == MinMax::max()) + ? delaySum(delayDiff(data_delay, clk_latency, sta_), check_margin, sta_) + : delaySum(delayDiff(clk_latency, data_delay, sta_), check_margin, sta_); float delay1 = delayAsFloat(margin, MinMax::max(), sta_); - debugPrint(debug, "make_timing_model", 2, "%s -> %s clock %s %s %s %s", - input_rf_->shortName(), - network->pathName(src_path->pin(sta_)), - tgt_clk_edge->name(), - path_end->typeName(), - min_max->to_string().c_str(), - delayAsString(margin, sta_)); + debugPrint(debug, "make_timing_model", 2, "{} -> {} clock {} {} {} {}", + input_rf_->shortName(), network->pathName(src_path->pin(sta_)), + tgt_clk_edge->name(), path_end->typeName(), + min_max->to_string(), delayAsString(margin, sta_)); if (debug->check("make_timing_model", 3)) sta_->reportPathEnd(path_end); @@ -311,7 +309,7 @@ MakeEndTimingArcs::visit(PathEnd *path_end) margins.value(input_rf_, min_max, max_margin, max_exists); // Always max margin, even for min/hold checks. margins.setValue(input_rf_, min_max, - max_exists ? max(max_margin, delay1) : delay1); + max_exists ? std::max(max_margin, delay1) : delay1); } } @@ -340,34 +338,32 @@ MakeTimingModel::findTimingFromInput(Port *input_port) { Instance *top_inst = network_->topInstance(); Pin *input_pin = network_->findPin(top_inst, input_port); - if (!sta_->isClockSrc(input_pin)) { + if (!sdc_->isClock(input_pin)) { MakeEndTimingArcs end_visitor(sta_); OutputPinDelays output_delays; for (const RiseFall *input_rf : RiseFall::range()) { const RiseFallBoth *input_rf1 = input_rf->asRiseFallBoth(); - sta_->setInputDelay(input_pin, input_rf1, - sdc_->defaultArrivalClock(), - sdc_->defaultArrivalClockEdge()->transition(), - nullptr, false, false, MinMaxAll::all(), true, 0.0); + sta_->setInputDelay(input_pin, input_rf1, sdc_->defaultArrivalClock(), + sdc_->defaultArrivalClockEdge()->transition(), nullptr, + false, false, MinMaxAll::all(), true, 0.0, sdc_); PinSet *from_pins = new PinSet(network_); from_pins->insert(input_pin); - ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr, - input_rf1); + ExceptionFrom *from = + sta_->makeExceptionFrom(from_pins, nullptr, nullptr, input_rf1, sdc_); search_->findFilteredArrivals(from, nullptr, nullptr, false, false); end_visitor.setInputRf(input_rf); VertexSeq endpoints = search_->filteredEndpoints(); VisitPathEnds visit_ends(sta_); for (Vertex *end : endpoints) - visit_ends.visitPathEnds(end, corner_, MinMaxAll::all(), true, &end_visitor); + visit_ends.visitPathEnds(end, scenes_, MinMaxAll::all(), true, &end_visitor); findOutputDelays(input_rf, output_delays); search_->deleteFilteredArrivals(); - sta_->removeInputDelay(input_pin, input_rf1, - sdc_->defaultArrivalClock(), + sta_->removeInputDelay(input_pin, input_rf1, sdc_->defaultArrivalClock(), sdc_->defaultArrivalClockEdge()->transition(), - MinMaxAll::all()); + MinMaxAll::all(), sdc_); } makeSetupHoldTimingArcs(input_pin, end_visitor.margins()); makeInputOutputTimingArcs(input_pin, output_delays); @@ -405,7 +401,7 @@ void MakeTimingModel::makeSetupHoldTimingArcs(const Pin *input_pin, const ClockEdgeDelays &clk_margins) { - for (const auto& [clk_edge, margins] : clk_margins) { + for (const auto &[clk_edge, margins] : clk_margins) { for (const MinMax *min_max : MinMax::range()) { bool setup = (min_max == MinMax::max()); TimingArcAttrsPtr attrs = nullptr; @@ -414,16 +410,14 @@ MakeTimingModel::makeSetupHoldTimingArcs(const Pin *input_pin, bool exists; margins.value(input_rf, min_max, margin, exists); if (exists) { - debugPrint(debug_, "make_timing_model", 2, "%s %s %s -> clock %s %s", - sta_->network()->pathName(input_pin), - input_rf->shortName(), - min_max == MinMax::max() ? "setup" : "hold", - clk_edge->name(), + debugPrint(debug_, "make_timing_model", 2, "{} {} {} -> clock {} {}", + sta_->network()->pathName(input_pin), input_rf->shortName(), + min_max == MinMax::max() ? "setup" : "hold", clk_edge->name(), delayAsString(margin, sta_)); - ScaleFactorType scale_type = setup - ? ScaleFactorType::setup - : ScaleFactorType::hold; - TimingModel *check_model = makeScalarCheckModel(margin, scale_type, input_rf); + ScaleFactorType scale_type = + setup ? ScaleFactorType::setup : ScaleFactorType::hold; + TimingModel *check_model = + makeScalarCheckModel(margin, scale_type, input_rf); if (attrs == nullptr) attrs = std::make_shared(); attrs->setModel(input_rf, check_model); @@ -435,12 +429,10 @@ MakeTimingModel::makeSetupHoldTimingArcs(const Pin *input_pin, LibertyPort *clk_port = modelPort(clk_pin); if (clk_port) { const RiseFall *clk_rf = clk_edge->transition(); - const TimingRole *role = setup - ? TimingRole::setup() - : TimingRole::hold(); - lib_builder_->makeFromTransitionArcs(cell_, clk_port, - input_port, nullptr, - clk_rf, role, attrs); + const TimingRole *role = + setup ? TimingRole::setup() : TimingRole::hold(); + lib_builder_->makeFromTransitionArcs(cell_, clk_port, input_port, + nullptr, clk_rf, role, attrs); } } } @@ -452,7 +444,7 @@ void MakeTimingModel::makeInputOutputTimingArcs(const Pin *input_pin, OutputPinDelays &output_pin_delays) { - for (const auto& [output_pin, output_delays] : output_pin_delays) { + for (const auto &[output_pin, output_delays] : output_pin_delays) { TimingArcAttrsPtr attrs = nullptr; for (const RiseFall *output_rf : RiseFall::range()) { const MinMax *min_max = MinMax::max(); @@ -460,11 +452,9 @@ MakeTimingModel::makeInputOutputTimingArcs(const Pin *input_pin, bool exists; output_delays.delays.value(output_rf, min_max, delay, exists); if (exists) { - debugPrint(debug_, "make_timing_model", 2, "%s -> %s %s delay %s", - network_->pathName(input_pin), - network_->pathName(output_pin), - output_rf->shortName(), - delayAsString(delay, sta_)); + debugPrint(debug_, "make_timing_model", 2, "{} -> {} {} delay {}", + network_->pathName(input_pin), network_->pathName(output_pin), + output_rf->shortName(), delayAsString(delay, sta_)); TimingModel *gate_model; if (scalar_) gate_model = makeGateModelScalar(delay, output_rf); @@ -495,7 +485,7 @@ MakeTimingModel::findClkedOutputPaths() { InstancePinIterator *output_iter = network_->pinIterator(network_->topInstance()); while (output_iter->hasNext()) { - Pin *output_pin = output_iter->next(); + Pin *output_pin = output_iter->next(); if (network_->direction(output_pin)->isOutput()) { ClockEdgeDelays clk_delays; LibertyPort *output_port = modelPort(output_pin); @@ -509,11 +499,10 @@ MakeTimingModel::findClkedOutputPaths() const MinMax *min_max = path->minMax(sta_); Arrival delay = path->arrival(); RiseFallMinMax &delays = clk_delays[clk_edge]; - delays.mergeValue(output_rf, min_max, - delayAsFloat(delay, min_max, sta_)); + delays.mergeValue(output_rf, min_max, delayAsFloat(delay, min_max, sta_)); } } - for (const auto& [clk_edge, delays] : clk_delays) { + for (const auto &[clk_edge, delays] : clk_delays) { for (const Pin *clk_pin : clk_edge->clock()->pins()) { LibertyPort *clk_port = modelPort(clk_pin); if (clk_port) { @@ -523,8 +512,8 @@ MakeTimingModel::findClkedOutputPaths() float delay = delays.value(output_rf, min_max_) - clk_edge->time(); TimingModel *gate_model; if (scalar_) { - const DcalcAnalysisPt *dcalc_ap = corner_->findDcalcAnalysisPt(min_max_); - Slew slew = graph_->slew(output_vertex, output_rf, dcalc_ap->index()); + DcalcAPIndex dcalc_ap_index = scene_->dcalcAnalysisPtIndex(min_max_); + Slew slew = graph_->slew(output_vertex, output_rf, dcalc_ap_index); gate_model = makeGateModelScalar(delay, slew, output_rf); } else @@ -534,10 +523,9 @@ MakeTimingModel::findClkedOutputPaths() attrs->setModel(output_rf, gate_model); } if (attrs) { - lib_builder_->makeFromTransitionArcs(cell_, clk_port, - output_port, nullptr, - clk_rf, TimingRole::regClkToQ(), - attrs); + lib_builder_->makeFromTransitionArcs(cell_, clk_port, output_port, + nullptr, clk_rf, + TimingRole::regClkToQ(), attrs); } } } @@ -558,7 +546,7 @@ MakeTimingModel::findClkTreeDelays() while (port_iter->hasNext()) { Port *port = port_iter->next(); if (network_->direction(port)->isInput()) { - const char *port_name = network_->name(port); + std::string port_name = network_->name(port); LibertyPort *lib_port = cell_->findLibertyPort(port_name); Pin *pin = network_->findPin(top_inst, port); if (pin && sdc_->isClock(pin)) { @@ -566,10 +554,12 @@ MakeTimingModel::findClkTreeDelays() ClockSet *clks = sdc_->findClocks(pin); if (clks->size() == 1) { for (const Clock *clk : *clks) { - ClkDelays delays = sta_->findClkDelays(clk, true); + ClkDelays delays = sta_->findClkDelays(clk, scene_, true); for (const MinMax *min_max : MinMax::range()) { - makeClkTreePaths(lib_port, min_max, TimingSense::positive_unate, delays); - makeClkTreePaths(lib_port, min_max, TimingSense::negative_unate, delays); + makeClkTreePaths(lib_port, min_max, TimingSense::positive_unate, + delays); + makeClkTreePaths(lib_port, min_max, TimingSense::negative_unate, + delays); } } } @@ -587,15 +577,14 @@ MakeTimingModel::makeClkTreePaths(LibertyPort *lib_port, { TimingArcAttrsPtr attrs = nullptr; for (const RiseFall *clk_rf : RiseFall::range()) { - const RiseFall *end_rf = (sense == TimingSense::positive_unate) - ? clk_rf - : clk_rf->opposite(); + const RiseFall *end_rf = + (sense == TimingSense::positive_unate) ? clk_rf : clk_rf->opposite(); Path clk_path; Delay insertion, delay, latency; float lib_clk_delay; bool exists; - delays.delay(clk_rf, end_rf, min_max, insertion, delay, - lib_clk_delay, latency, clk_path, exists); + delays.delay(clk_rf, end_rf, min_max, insertion, delay, lib_clk_delay, latency, + clk_path, exists); if (exists) { TimingModel *model = makeGateModelScalar(delay, end_rf); if (attrs == nullptr) @@ -606,9 +595,9 @@ MakeTimingModel::makeClkTreePaths(LibertyPort *lib_port, if (attrs) { attrs->setTimingSense(sense); const TimingRole *role = (min_max == MinMax::min()) - ? TimingRole::clockTreePathMin() - : TimingRole::clockTreePathMax(); - lib_builder_->makeClockTreePathArcs(cell_, lib_port, role, min_max, attrs); + ? TimingRole::clockTreePathMin() + : TimingRole::clockTreePathMax(); + lib_builder_->makeClockTreePathArcs(cell_, lib_port, role, attrs); } } @@ -625,13 +614,13 @@ MakeTimingModel::makeScalarCheckModel(float value, ScaleFactorType scale_factor_type, const RiseFall *rf) { - TablePtr table = make_shared(value); + TablePtr table = std::make_shared
(value); TableTemplate *tbl_template = library_->findTableTemplate("scalar", TableTemplateType::delay); - TableModel *table_model = new TableModel(table, tbl_template, - scale_factor_type, rf); - CheckTableModel *check_model = new CheckTableModel(cell_, table_model, nullptr); - return check_model; + TableModel *check_table = new TableModel(table, tbl_template, scale_factor_type, rf); + TableModels *check_tables = new TableModels(check_table); + CheckTableModel *check = new CheckTableModel(cell_, check_tables); + return check; } TimingModel * @@ -639,16 +628,17 @@ MakeTimingModel::makeGateModelScalar(Delay delay, Slew slew, const RiseFall *rf) { - TablePtr delay_table = make_shared(delayAsFloat(delay)); - TablePtr slew_table = make_shared(delayAsFloat(slew)); + TablePtr delay_table = std::make_shared
(delayAsFloat(delay)); + TablePtr slew_table = std::make_shared
(delayAsFloat(slew)); TableTemplate *tbl_template = library_->findTableTemplate("scalar", TableTemplateType::delay); TableModel *delay_model = new TableModel(delay_table, tbl_template, ScaleFactorType::cell, rf); + TableModels *delay_models = new TableModels(delay_model); TableModel *slew_model = new TableModel(slew_table, tbl_template, ScaleFactorType::cell, rf); - GateTableModel *gate_model = new GateTableModel(cell_, delay_model, nullptr, - slew_model, nullptr, + TableModels *slew_models = new TableModels(slew_model); + GateTableModel *gate_model = new GateTableModel(cell_, delay_models, slew_models, nullptr, nullptr); return gate_model; } @@ -657,13 +647,13 @@ TimingModel * MakeTimingModel::makeGateModelScalar(Delay delay, const RiseFall *rf) { - TablePtr delay_table = make_shared(delayAsFloat(delay)); + TablePtr delay_table = std::make_shared
(delayAsFloat(delay)); TableTemplate *tbl_template = library_->findTableTemplate("scalar", TableTemplateType::delay); TableModel *delay_model = new TableModel(delay_table, tbl_template, ScaleFactorType::cell, rf); - GateTableModel *gate_model = new GateTableModel(cell_, delay_model, nullptr, - nullptr, nullptr, + TableModels *models = new TableModels(delay_model); + GateTableModel *gate_model = new GateTableModel(cell_, models, nullptr, nullptr, nullptr); return gate_model; } @@ -675,14 +665,14 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, Delay delay, const RiseFall *rf) { - const DcalcAnalysisPt *dcalc_ap = corner_->findDcalcAnalysisPt(min_max_); - const Pvt *pvt = dcalc_ap->operatingConditions(); + const Pvt *pvt = sdc_->operatingConditions(min_max_); PinSet *drvrs = network_->drivers(network_->net(network_->term(output_pin))); + DcalcAPIndex ap_index = scene_->dcalcAnalysisPtIndex(min_max_); const Pin *drvr_pin = *drvrs->begin(); const LibertyPort *drvr_port = network_->libertyPort(drvr_pin); if (drvr_port) { const LibertyCell *drvr_cell = drvr_port->libertyCell(); - for (TimingArcSet *arc_set : drvr_cell->timingArcSets(nullptr, drvr_port)) { + for (TimingArcSet *arc_set : drvr_cell->timingArcSetsTo(drvr_port)) { for (TimingArc *drvr_arc : arc_set->arcs()) { // Use the first timing arc to simplify life. if (drvr_arc->toEdge()->asRiseFall() == rf) { @@ -691,55 +681,59 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, const Pin *gate_in_pin = network_->findPin(drvr_inst, gate_in_port); if (gate_in_pin) { Vertex *gate_in_vertex = graph_->pinLoadVertex(gate_in_pin); - Slew in_slew = graph_->slew(gate_in_vertex, - drvr_arc->fromEdge()->asRiseFall(), - dcalc_ap->index()); + Slew in_slew = graph_->slew( + gate_in_vertex, drvr_arc->fromEdge()->asRiseFall(), ap_index); float in_slew1 = delayAsFloat(in_slew); - GateTableModel *drvr_gate_model = drvr_arc->gateTableModel(dcalc_ap); + GateTableModel *drvr_gate_model = + drvr_arc->gateTableModel(scene_, min_max_); if (drvr_gate_model) { - float output_load_cap = graph_delay_calc_->loadCap(output_pin, dcalc_ap); - ArcDelay drvr_self_delay; - Slew drvr_self_slew; - drvr_gate_model->gateDelay(pvt, in_slew1, output_load_cap, false, + float output_load_cap = graph_delay_calc_->loadCap(output_pin, + scene_, + min_max_); + float drvr_self_delay, drvr_self_slew; + drvr_gate_model->gateDelay(pvt, in_slew1, output_load_cap, drvr_self_delay, drvr_self_slew); - const TableModel *drvr_table = drvr_gate_model->delayModel(); + const TableModel *drvr_table = drvr_gate_model->delayModels()->model(); const TableTemplate *drvr_template = drvr_table->tblTemplate(); const TableAxis *drvr_load_axis = loadCapacitanceAxis(drvr_table); if (drvr_load_axis) { - const FloatSeq *drvr_axis_values = drvr_load_axis->values(); + const FloatSeq &drvr_axis_values = drvr_load_axis->values(); FloatSeq *load_values = new FloatSeq; FloatSeq *slew_values = new FloatSeq; - for (size_t i = 0; i < drvr_axis_values->size(); i++) { - float load_cap = (*drvr_axis_values)[i]; + for (float load_cap : drvr_axis_values) { // get slew from driver input pin - ArcDelay gate_delay; - Slew gate_slew; - drvr_gate_model->gateDelay(pvt, in_slew1, load_cap, false, + float gate_delay, gate_slew; + drvr_gate_model->gateDelay(pvt, in_slew1, load_cap, gate_delay, gate_slew); // Remove the self delay driving the output pin net load cap. - load_values->push_back(delayAsFloat(delay + gate_delay - - drvr_self_delay)); + load_values->push_back(delayAsFloat(delay) + + gate_delay + - drvr_self_delay); slew_values->push_back(delayAsFloat(gate_slew)); } - FloatSeq *axis_values = new FloatSeq(*drvr_axis_values); - TableAxisPtr load_axis = - std::make_shared(TableAxisVariable::total_output_net_capacitance, - axis_values); + FloatSeq axis_values = drvr_axis_values; + TableAxisPtr load_axis = std::make_shared( + TableAxisVariable::total_output_net_capacitance, + std::move(axis_values)); - TablePtr delay_table = make_shared(load_values, load_axis); - TablePtr slew_table = make_shared(slew_values, load_axis); + TablePtr delay_table = + std::make_shared
(load_values, load_axis); + TablePtr slew_table = + std::make_shared
(slew_values, load_axis); - TableTemplate *model_template = ensureTableTemplate(drvr_template, - load_axis); + TableTemplate *model_template = + ensureTableTemplate(drvr_template, load_axis); TableModel *delay_model = new TableModel(delay_table, model_template, ScaleFactorType::cell, rf); + TableModels *delay_models = new TableModels(delay_model); TableModel *slew_model = new TableModel(slew_table, model_template, ScaleFactorType::cell, rf); + TableModels *slew_models = new TableModels(slew_model); GateTableModel *gate_model = new GateTableModel(cell_, - delay_model, nullptr, - slew_model, nullptr, + delay_models, + slew_models, nullptr, nullptr); return gate_model; } @@ -750,22 +744,22 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, } } Vertex *output_vertex = graph_->pinLoadVertex(output_pin); - Slew slew = graph_->slew(output_vertex, rf, dcalc_ap->index()); + Slew slew = graph_->slew(output_vertex, rf, ap_index); return makeGateModelScalar(delay, slew, rf); } TableTemplate * MakeTimingModel::ensureTableTemplate(const TableTemplate *drvr_template, - TableAxisPtr load_axis) + const TableAxisPtr &load_axis) { - TableTemplate *model_template = template_map_.findKey(drvr_template); + TableTemplate *model_template = findKey(template_map_, drvr_template); if (model_template == nullptr) { - string template_name = "template_"; + std::string template_name = "template_"; template_name += std::to_string(tbl_template_index_++); - model_template = new TableTemplate(template_name.c_str()); + model_template = + library_->makeTableTemplate(template_name, TableTemplateType::delay); model_template->setAxis1(load_axis); - library_->addTableTemplate(model_template, TableTemplateType::delay); template_map_[drvr_template] = model_template; } return model_template; @@ -775,13 +769,16 @@ const TableAxis * MakeTimingModel::loadCapacitanceAxis(const TableModel *table) { if (table->axis1() - && table->axis1()->variable() == TableAxisVariable::total_output_net_capacitance) + && table->axis1()->variable() + == TableAxisVariable::total_output_net_capacitance) return table->axis1(); else if (table->axis2() - && table->axis2()->variable() == TableAxisVariable::total_output_net_capacitance) + && table->axis2()->variable() + == TableAxisVariable::total_output_net_capacitance) return table->axis2(); else if (table->axis3() - && table->axis3()->variable() == TableAxisVariable::total_output_net_capacitance) + && table->axis3()->variable() + == TableAxisVariable::total_output_net_capacitance) return table->axis3(); else return nullptr; @@ -799,9 +796,9 @@ TimingSense OutputDelays::timingSense() const { if (rf_path_exists[RiseFall::riseIndex()][RiseFall::riseIndex()] - && rf_path_exists[RiseFall::fallIndex()][RiseFall::fallIndex()] - && !rf_path_exists[RiseFall::riseIndex()][RiseFall::fallIndex()] - && !rf_path_exists[RiseFall::fallIndex()][RiseFall::riseIndex()]) + && rf_path_exists[RiseFall::fallIndex()][RiseFall::fallIndex()] + && !rf_path_exists[RiseFall::riseIndex()][RiseFall::fallIndex()] + && !rf_path_exists[RiseFall::fallIndex()][RiseFall::riseIndex()]) return TimingSense::positive_unate; else if (rf_path_exists[RiseFall::riseIndex()][RiseFall::fallIndex()] && rf_path_exists[RiseFall::fallIndex()][RiseFall::riseIndex()] @@ -817,4 +814,4 @@ OutputDelays::timingSense() const return TimingSense::none; } -} // namespace +} // namespace sta diff --git a/search/MakeTimingModel.hh b/search/MakeTimingModel.hh index d98cf7b68..2da1c09c9 100644 --- a/search/MakeTimingModel.hh +++ b/search/MakeTimingModel.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,18 +24,20 @@ #pragma once +#include + namespace sta { class LibertyLibrary; -class Corner; +class Scene; class Sta; LibertyLibrary * -makeTimingModel(const char *lib_name, - const char *cell_name, - const char *filename, - const Corner *corner, +makeTimingModel(std::string_view lib_name, + std::string_view cell_name, + std::string_view filename, + const Scene *scene, const bool scalar, Sta *sta); -} // namespace +} // namespace sta diff --git a/search/MakeTimingModelPvt.hh b/search/MakeTimingModelPvt.hh index 2b976a0fd..919afc66f 100644 --- a/search/MakeTimingModelPvt.hh +++ b/search/MakeTimingModelPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,12 +25,17 @@ #pragma once #include +#include +#include +#include "Delay.hh" #include "LibertyClass.hh" +#include "NetworkClass.hh" +#include "RiseFallMinMax.hh" +#include "Scene.hh" #include "SdcClass.hh" #include "SearchClass.hh" #include "StaState.hh" -#include "RiseFallMinMax.hh" namespace sta { @@ -48,19 +53,19 @@ public: bool rf_path_exists[RiseFall::index_count][RiseFall::index_count]; }; -typedef std::map ClockEdgeDelays; -typedef std::map OutputPinDelays; +using ClockEdgeDelays = std::map; +using OutputPinDelays = std::map; class MakeTimingModel : public StaState { public: - MakeTimingModel(const char *lib_name, - const char *cell_name, - const char *filename, - const Corner *corner, + MakeTimingModel(std::string_view lib_name, + std::string_view cell_name, + std::string_view filename, + const Scene *scene, const bool scalar, Sta *sta); - ~MakeTimingModel(); + ~MakeTimingModel() override; LibertyLibrary *makeTimingModel(); private: @@ -95,7 +100,7 @@ private: Delay delay, const RiseFall *rf); TableTemplate *ensureTableTemplate(const TableTemplate *drvr_template, - TableAxisPtr load_axis); + const TableAxisPtr &load_axis); const TableAxis *loadCapacitanceAxis(const TableModel *table); LibertyPort *modelPort(const Pin *pin); @@ -103,20 +108,22 @@ private: void restoreSdc(); void swapSdcWithBackup(); - const char *lib_name_; - const char *cell_name_; - const char *filename_; - const Corner *corner_; + std::string lib_name_; + std::string cell_name_; + std::string filename_; + const Scene *scene_; + SceneSet scenes_; const bool scalar_; LibertyLibrary *library_; - LibertyCell *cell_; + LibertyCell *cell_{nullptr}; const MinMax *min_max_; LibertyBuilder *lib_builder_; // Output driver table model template to model template. - Map template_map_; - int tbl_template_index_; - Sdc *sdc_backup_; + std::map template_map_; + int tbl_template_index_{1}; + Sdc *sdc_; + Sdc *sdc_backup_{nullptr}; Sta *sta_; }; -} // namespace +} // namespace sta diff --git a/search/Mode.cc b/search/Mode.cc new file mode 100644 index 000000000..f822ff924 --- /dev/null +++ b/search/Mode.cc @@ -0,0 +1,148 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#include + +#include "Mode.hh" + +#include "ClkNetwork.hh" +#include "Genclks.hh" +#include "PathGroup.hh" +#include "Sdc.hh" +#include "Sim.hh" + +namespace sta { + +Mode::Mode(std::string_view name, + size_t mode_index, + StaState *sta) : + name_(name), + mode_index_(mode_index), + sdc_(new Sdc(this, sta)), + sim_(new Sim(sta)), + clk_network_(new ClkNetwork(this, sta)), + genclks_(new Genclks(this, sta)), + sta_(sta) +{ +} + +Mode::~Mode() +{ + delete sdc_; + delete sim_; + delete clk_network_; + delete genclks_; + delete path_groups_; +} + +void +Mode::copyState(const StaState *sta) +{ + sta_->copyState(sta); + sdc_->copyState(sta); + sim_->copyState(sta); + clk_network_->copyState(sta); + genclks_->copyState(sta); +} + +void +Mode::clear() +{ + scenes_.clear(); + sim_->clear(); + clk_network_->clear(); + genclks_->clear(); +} + +void +Mode::addScene(Scene *scene) +{ + scenes_.push_back(scene); +} + +void +Mode::removeScene(Scene *scene) +{ + // std iterators just plain suck + auto tail = std::ranges::remove(scenes_, scene); + scenes_.erase(tail.begin(), tail.end()); +} + +SceneSet +Mode::sceneSet() const +{ + SceneSet scenes; + for (Scene *scene : scenes_) + scenes.insert(scene); + return scenes; +} + +//////////////////////////////////////////////////////////////// + +PathGroups * +Mode::makePathGroups(int group_path_count, + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + StringSeq &group_names, + bool setup, + bool hold, + bool recovery, + bool removal, + bool clk_gating_setup, + bool clk_gating_hold, + bool unconstrained_paths) +{ + path_groups_ = new PathGroups(group_path_count, + endpoint_path_count, + unique_pins, unique_edges, + slack_min, slack_max, + group_names, + setup, hold, + recovery, removal, + clk_gating_setup, clk_gating_hold, + unconstrained_paths, + this); + return path_groups_; +} + +void +Mode::deletePathGroups() +{ + delete path_groups_; + path_groups_ = nullptr; +} + +PathGroupSeq +Mode::pathGroups(const PathEnd *path_end) const +{ + if (path_groups_) + return path_groups_->pathGroups(path_end); + else + return PathGroupSeq(); +} + +} // namespace sta diff --git a/search/Path.cc b/search/Path.cc index bf8fc2de2..2dcc621e6 100644 --- a/search/Path.cc +++ b/search/Path.cc @@ -1,40 +1,50 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Path.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" -#include "Network.hh" -#include "Graph.hh" +#include +#include + #include "Clock.hh" -#include "DcalcAnalysisPt.hh" -#include "Corner.hh" -#include "PathAnalysisPt.hh" +#include "Delay.hh" +#include "Format.hh" +#include "Graph.hh" +#include "GraphClass.hh" +#include "Mode.hh" +#include "Network.hh" +#include "NetworkClass.hh" +#include "Sdc.hh" +#include "SdcClass.hh" +#include "Search.hh" +#include "SearchClass.hh" +#include "StaState.hh" #include "Tag.hh" #include "TagGroup.hh" -#include "Search.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "VertexId.hh" namespace sta { @@ -51,8 +61,8 @@ Path::Path() : Path::Path(const Path *path) : prev_path_(path ? path->prev_path_ : nullptr), - arrival_(path ? path->arrival_ : 0.0), - required_(path ? path->required_ : 0.0), + arrival_(path ? path->arrival_ : delay_zero), + required_(path ? path->required_ : delay_zero), vertex_id_(path ? path->vertex_id_ : vertex_id_null), tag_index_(path ? path->tag_index_ : tag_index_null), is_enum_(path ? path->is_enum_ : false), @@ -76,7 +86,7 @@ Path::Path(Vertex *vertex, Path::Path(Vertex *vertex, Tag *tag, - Arrival arrival, + const Arrival &arrival, Path *prev_path, Edge *prev_edge, TimingArc *prev_arc, @@ -100,7 +110,7 @@ Path::Path(Vertex *vertex, Path::Path(Vertex *vertex, Tag *tag, - Arrival arrival, + const Arrival &arrival, Path *prev_path, Edge *prev_edge, TimingArc *prev_arc, @@ -123,21 +133,14 @@ Path::Path(Vertex *vertex, } } -Path:: ~Path() -{ - if (is_enum_ && prev_path_ && prev_path_->is_enum_) - delete prev_path_; -} - void Path::init(Vertex *vertex, - Arrival arrival, + const Arrival &arrival, const StaState *sta) { const Graph *graph = sta->graph(); vertex_id_ = graph->id(vertex); - tag_index_ = tag_index_null, - prev_path_ = nullptr; + tag_index_ = tag_index_null, prev_path_ = nullptr; prev_arc_idx_ = 0; arrival_ = arrival; required_ = 0.0; @@ -151,8 +154,7 @@ Path::init(Vertex *vertex, { const Graph *graph = sta->graph(); vertex_id_ = graph->id(vertex); - tag_index_ = tag->index(), - prev_path_ = nullptr; + tag_index_ = tag->index(), prev_path_ = nullptr; prev_arc_idx_ = 0; arrival_ = 0.0; required_ = 0.0; @@ -162,13 +164,12 @@ Path::init(Vertex *vertex, void Path::init(Vertex *vertex, Tag *tag, - Arrival arrival, + const Arrival &arrival, const StaState *sta) { const Graph *graph = sta->graph(); vertex_id_ = graph->id(vertex); - tag_index_ = tag->index(), - prev_path_ = nullptr; + tag_index_ = tag->index(), prev_path_ = nullptr; prev_arc_idx_ = 0; arrival_ = arrival; required_ = 0.0; @@ -178,15 +179,14 @@ Path::init(Vertex *vertex, void Path::init(Vertex *vertex, Tag *tag, - Arrival arrival, + const Arrival &arrival, Path *prev_path, Edge *prev_edge, TimingArc *prev_arc, const StaState *sta) { const Graph *graph = sta->graph(); - tag_index_ = tag->index(), - prev_path_ = prev_path; + tag_index_ = tag->index(), prev_path_ = prev_path; if (prev_path) { prev_edge_id_ = graph->id(prev_edge); prev_arc_idx_ = prev_arc->index(); @@ -205,15 +205,13 @@ Path::to_string(const StaState *sta) const { if (isNull()) return "null path"; - else { - const PathAnalysisPt *path_ap = pathAnalysisPt(sta); - return stringPrintTmp("%s %s %s/%d %d", - vertex(sta)->to_string(sta).c_str(), - transition(sta)->to_string().c_str(), - path_ap->pathMinMax()->to_string().c_str(), - path_ap->index(), - tagIndex(sta)); - } + else + return sta::format("{} {} {}/{} {}", + vertex(sta)->to_string(sta), + transition(sta)->shortName(), + scene(sta)->name(), + minMax(sta)->to_string(), + tagIndex(sta)); } bool @@ -230,7 +228,7 @@ Path::vertex(const StaState *sta) const const Edge *edge = graph->edge(prev_edge_id_); return edge->to(graph); } - else + else return graph->vertex(vertex_id_); } @@ -259,6 +257,24 @@ Path::tag(const StaState *sta) const return search->tag(tag_index_); } +Scene * +Path::scene(const StaState *sta) const +{ + return tag(sta)->scene(); +} + +Mode * +Path::mode(const StaState *sta) const +{ + return tag(sta)->scene()->mode(); +} + +Sdc * +Path::sdc(const StaState *sta) const +{ + return tag(sta)->scene()->sdc(); +} + void Path::setTag(Tag *tag) { @@ -306,26 +322,26 @@ Path::isClock(const StaState *sta) const const MinMax * Path::minMax(const StaState *sta) const { - return tag(sta)->minMax(sta); + return tag(sta)->minMax(); } -PathAPIndex -Path::pathAnalysisPtIndex(const StaState *sta) const +DcalcAPIndex +Path::dcalcAnalysisPtIndex(const StaState *sta) const { - return pathAnalysisPt(sta)->index(); + return scene(sta)->dcalcAnalysisPtIndex(minMax(sta)); } -DcalcAnalysisPt * -Path::dcalcAnalysisPt(const StaState *sta) const +PathAPIndex +Path::pathAnalysisPtIndex(const StaState *sta) const { - return pathAnalysisPt(sta)->dcalcAnalysisPt(); + return scene(sta)->pathIndex(minMax(sta)); } Slew Path::slew(const StaState *sta) const { - return sta->graph()->slew(vertex(sta), transition(sta), - dcalcAnalysisPt(sta)->index()); + DcalcAPIndex slew_index = scene(sta)->dcalcAnalysisPtIndex(minMax(sta)); + return sta->graph()->slew(vertex(sta), transition(sta), slew_index); } const RiseFall * @@ -340,12 +356,6 @@ Path::rfIndex(const StaState *sta) const return transition(sta)->index(); } -PathAnalysisPt * -Path::pathAnalysisPt(const StaState *sta) const -{ - return tag(sta)->pathAnalysisPt(sta); -} - void Path::setArrival(Arrival arrival) { @@ -362,9 +372,9 @@ Slack Path::slack(const StaState *sta) const { if (minMax(sta) == MinMax::max()) - return required_ - arrival_; + return delayDiff(required_, arrival_, sta); else - return arrival_ - required_; + return delayDiff(arrival_, required_, sta); } Path * @@ -401,7 +411,7 @@ Path::prevArc(const StaState *sta) const TimingArcSet *arc_set = edge->timingArcSet(); return arc_set->findTimingArc(prev_arc_idx_); } - else + else return nullptr; } @@ -412,7 +422,7 @@ Path::prevEdge(const StaState *sta) const const Graph *graph = sta->graph(); return graph->edge(prev_edge_id_); } - else + else return nullptr; } @@ -445,8 +455,7 @@ void Path::checkPrevPath(const StaState *sta) const { if (prev_path_ && prev_path_->isNull()) - sta->report()->reportLine("path %s prev path is null.", - to_string(sta).c_str()); + sta->report()->report("path {} prev path is null.", to_string(sta)); if (prev_path_ && !prev_path_->isNull()) { Graph *graph = sta->graph(); Edge *edge = prevEdge(sta); @@ -454,10 +463,9 @@ Path::checkPrevPath(const StaState *sta) const Vertex *prev_edge_vertex = edge->from(graph); if (prev_vertex != prev_edge_vertex) { Network *network = sta->network(); - sta->report()->reportLine("path %s prev path corrupted %s vs %s.", - to_string(sta).c_str(), - prev_vertex->name(network), - prev_edge_vertex->name(network)); + sta->report()->report("path {} prev path corrupted {} vs {}.", to_string(sta), + prev_vertex->name(network), + prev_edge_vertex->name(network)); } } } @@ -470,6 +478,23 @@ Path::setIsEnum(bool is_enum) //////////////////////////////////////////////////////////////// +const MinMax * +Path::tgtClkMinMax(const StaState *sta) const +{ + const MinMax *min_max = minMax(sta); + switch (mode(sta)->sdc()->analysisType()) { + case AnalysisType::single: + case AnalysisType::bc_wc: + return min_max; + case AnalysisType::ocv: + return min_max->opposite(); + default: + // suppress gcc warning + return min_max; + } +} +//////////////////////////////////////////////////////////////// + Path * Path::vertexPath(const Path *path, const StaState *sta) @@ -512,8 +537,8 @@ Path::vertexPath(const Vertex *vertex, int Path::cmpPinTrClk(const Path *path1, - const Path *path2, - const StaState *sta) + const Path *path2, + const StaState *sta) { if (path1 && path2) { const Pin *pin1 = path1->pin(sta); @@ -523,11 +548,11 @@ Path::cmpPinTrClk(const Path *path1, int tr_index1 = path1->rfIndex(sta); int tr_index2 = path2->rfIndex(sta); if (tr_index1 == tr_index2) - return cmpClk(path1, path2, sta); + return cmpClk(path1, path2, sta); else if (tr_index1 < tr_index2) - return -1; + return -1; else - return 1; + return 1; } else if (network->pathNameLess(pin1, pin2)) return -1; @@ -544,8 +569,8 @@ Path::cmpPinTrClk(const Path *path1, int Path::cmpClk(const Path *path1, - const Path *path2, - const StaState *sta) + const Path *path2, + const StaState *sta) { const ClockEdge *clk_edge1 = path1->clkEdge(sta); const ClockEdge *clk_edge2 = path2->clkEdge(sta); @@ -559,8 +584,7 @@ Path::cmpClk(const Path *path1, else return 1; } - else if (clk_edge1 == nullptr - && clk_edge2 == nullptr) + else if (clk_edge1 == nullptr && clk_edge2 == nullptr) return 0; else if (clk_edge2) return -1; @@ -570,15 +594,14 @@ Path::cmpClk(const Path *path1, bool Path::equal(const Path *path1, - const Path *path2, - const StaState *sta) + const Path *path2, + const StaState *sta) { return (path1 == nullptr && path2 == nullptr) - || (path1 - && path2 - && path1->vertexId(sta) == path2->vertexId(sta) - // Tag equal implies transition and path ap equal. - && path1->tagIndex(sta) == path2->tagIndex(sta)); + || (path1 && path2 + && path1->vertexId(sta) == path2->vertexId(sta) + // Tag equal implies transition and path ap equal. + && path1->tagIndex(sta) == path2->tagIndex(sta)); } //////////////////////////////////////////////////////////////// @@ -590,23 +613,23 @@ PathLess::PathLess(const StaState *sta) : bool PathLess::operator()(const Path *path1, - const Path *path2) const + const Path *path2) const { return Path::less(path1, path2, sta_); } bool Path::less(const Path *path1, - const Path *path2, - const StaState *sta) + const Path *path2, + const StaState *sta) { return cmp(path1, path2, sta) < 0; } int Path::cmp(const Path *path1, - const Path *path2, - const StaState *sta) + const Path *path2, + const StaState *sta) { if (path1 == path2) return 0; @@ -625,19 +648,19 @@ Path::cmp(const Path *path1, TagIndex tag_index1 = path1->tagIndex(sta); TagIndex tag_index2 = path2->tagIndex(sta); if (tag_index1 == tag_index2) - return 0; + return 0; else if (tag_index1 < tag_index2) - return -1; + return -1; else - return 1; + return 1; } } } int Path::cmpNoCrpr(const Path *path1, - const Path *path2, - const StaState *sta) + const Path *path2, + const StaState *sta) { VertexId vertex_id1 = path1->vertexId(sta); VertexId vertex_id2 = path2->vertexId(sta); @@ -651,8 +674,8 @@ Path::cmpNoCrpr(const Path *path1, int Path::cmpAll(const Path *path1, - const Path *path2, - const StaState *sta) + const Path *path2, + const StaState *sta) { const Path *p1 = path1; const Path *p2 = path2; @@ -682,8 +705,8 @@ Path::cmpAll(const Path *path1, bool Path::lessAll(const Path *path1, - const Path *path2, - const StaState *sta) + const Path *path2, + const StaState *sta) { return cmpAll(path1, path2, sta) < 0; } @@ -691,12 +714,12 @@ Path::lessAll(const Path *path1, //////////////////////////////////////////////////////////////// VertexPathIterator::VertexPathIterator(Vertex *vertex, - const StaState *sta) : + const StaState *sta) : search_(sta->search()), - filtered_(false), - rf_(nullptr), - path_ap_(nullptr), + scene_(nullptr), min_max_(nullptr), + rf_(nullptr), + filtered_(false), paths_(vertex->paths()), path_count_(0), path_index_(0), @@ -709,38 +732,16 @@ VertexPathIterator::VertexPathIterator(Vertex *vertex, } } -// Iterate over vertex paths with the same transition and -// analysis pt but different but different tags. VertexPathIterator::VertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap, - const StaState *sta) : + const Scene *scene, + const MinMax *min_max, + const RiseFall *rf, + const StaState *sta) : search_(sta->search()), - filtered_(true), + scene_(scene), + min_max_(min_max), rf_(rf), - path_ap_(path_ap), - min_max_(nullptr), - paths_(vertex->paths()), - path_count_(0), - path_index_(0), - next_(nullptr) -{ - TagGroup *tag_group = search_->tagGroup(vertex); - if (tag_group) { - path_count_ = tag_group->pathCount(); - findNext(); - } -} - -VertexPathIterator::VertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max, - const StaState *sta) : - search_(sta->search()), filtered_(true), - rf_(rf), - path_ap_(nullptr), - min_max_(min_max), paths_(vertex->paths()), path_count_(0), path_index_(0), @@ -754,15 +755,14 @@ VertexPathIterator::VertexPathIterator(Vertex *vertex, } VertexPathIterator::VertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap, - const MinMax *min_max, - const StaState *sta) : + const RiseFall *rf, + const MinMax *min_max, + const StaState *sta) : search_(sta->search()), - filtered_(true), - rf_(rf), - path_ap_(path_ap), + scene_(nullptr), min_max_(min_max), + rf_(rf), + filtered_(true), paths_(vertex->paths()), path_count_(0), path_index_(0), @@ -782,12 +782,9 @@ VertexPathIterator::findNext() Path *path = &paths_[path_index_++]; if (filtered_) { const Tag *tag = path->tag(search_); - if ((rf_ == nullptr - || tag->rfIndex() == rf_->index()) - && (path_ap_ == nullptr - || tag->pathAPIndex() == path_ap_->index()) - && (min_max_ == nullptr - || tag->pathAnalysisPt(search_)->pathMinMax() == min_max_)) { + if ((scene_ == nullptr || path->scene(search_) == scene_) + && (rf_ == nullptr || tag->rfIndex() == rf_->index()) + && (min_max_ == nullptr || path->minMax(search_) == min_max_)) { next_ = path; return; } @@ -800,10 +797,6 @@ VertexPathIterator::findNext() next_ = nullptr; } -VertexPathIterator::~VertexPathIterator() -{ -} - bool VertexPathIterator::hasNext() { @@ -818,4 +811,4 @@ VertexPathIterator::next() return path; } -} // namespace +} // namespace sta diff --git a/search/PathAnalysisPt.cc b/search/PathAnalysisPt.cc index 476a8649d..e69de29bb 100644 --- a/search/PathAnalysisPt.cc +++ b/search/PathAnalysisPt.cc @@ -1,73 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#include "PathAnalysisPt.hh" - -#include "StringUtil.hh" -#include "Corner.hh" -#include "Search.hh" - -namespace sta { - -PathAnalysisPt::PathAnalysisPt(Corner *corner, - PathAPIndex index, - const MinMax *path_min_max, - DcalcAnalysisPt *dcalc_ap) : - corner_(corner), - index_(index), - path_min_max_(path_min_max), - tgt_clk_ap_(nullptr), - dcalc_ap_(dcalc_ap) -{ -} - -std::string -PathAnalysisPt::to_string() const -{ - std::string name = corner_->name(); - name += '/'; - name += path_min_max_->to_string(); - return name; -} - -void -PathAnalysisPt::setTgtClkAnalysisPt(PathAnalysisPt *path_ap) -{ - tgt_clk_ap_ = path_ap; -} - -PathAnalysisPt * -PathAnalysisPt::insertionAnalysisPt(const EarlyLate *early_late) const -{ - return insertion_aps_[early_late->index()]; -} - -void -PathAnalysisPt::setInsertionAnalysisPt(const EarlyLate *early_late, - PathAnalysisPt *ap) -{ - insertion_aps_[early_late->index()] = ap; -} - -} // namespace diff --git a/search/PathEnd.cc b/search/PathEnd.cc index 12b539d4a..e917d89f6 100644 --- a/search/PathEnd.cc +++ b/search/PathEnd.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,33 +24,32 @@ #include "PathEnd.hh" +#include "ClkInfo.hh" +#include "Clock.hh" +#include "DataCheck.hh" #include "Debug.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" +#include "ExceptionPath.hh" +#include "Graph.hh" +#include "Latches.hh" #include "Liberty.hh" +#include "Mode.hh" #include "Network.hh" -#include "Graph.hh" -#include "Clock.hh" +#include "PathExpanded.hh" #include "PortDelay.hh" -#include "DataCheck.hh" +#include "ReportPath.hh" #include "Sdc.hh" -#include "ExceptionPath.hh" -#include "ClkInfo.hh" -#include "Tag.hh" -#include "PathAnalysisPt.hh" #include "Search.hh" -#include "ReportPath.hh" #include "Sim.hh" -#include "Latches.hh" #include "StaState.hh" -#include "PathExpanded.hh" +#include "Tag.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" #include "search/Crpr.hh" namespace sta { PathEnd::PathEnd(Path *path) : - path_(path), - path_group_(nullptr) + path_(path) { } @@ -75,13 +74,13 @@ PathEnd::vertex(const StaState *sta) const const MinMax * PathEnd::minMax(const StaState *sta) const { - return path_->pathAnalysisPt(sta)->pathMinMax(); + return path_->minMax(sta); } const EarlyLate * PathEnd::pathEarlyLate(const StaState *sta) const { - return path_->pathAnalysisPt(sta)->pathMinMax(); + return path_->minMax(sta); } const EarlyLate * @@ -96,25 +95,13 @@ PathEnd::transition(const StaState *sta) const return path_->transition(sta); } -PathAPIndex -PathEnd::pathIndex(const StaState *sta) const -{ - return path_->pathAnalysisPtIndex(sta); -} - -PathAnalysisPt * -PathEnd::pathAnalysisPt(const StaState *sta) const -{ - return path_->pathAnalysisPt(sta); -} - const ClockEdge * PathEnd::sourceClkEdge(const StaState *sta) const { return path_->clkEdge(sta); } -Arrival +const Arrival & PathEnd::dataArrivalTime(const StaState *) const { return path_->arrival(); @@ -123,13 +110,17 @@ PathEnd::dataArrivalTime(const StaState *) const Arrival PathEnd::dataArrivalTimeOffset(const StaState *sta) const { - return dataArrivalTime(sta) + sourceClkOffset(sta); + return delaySum(dataArrivalTime(sta), + sourceClkOffset(sta), + sta); } Required PathEnd::requiredTimeOffset(const StaState *sta) const { - return requiredTime(sta) + sourceClkOffset(sta); + return delaySum(requiredTime(sta), + sourceClkOffset(sta), + sta); } const RiseFall * @@ -271,9 +262,9 @@ Crpr PathEnd::checkCrpr(const StaState *sta) const { if (checkRole(sta)->genericRole() == TimingRole::hold()) - return -crpr(sta); + return delayDiff(delay_zero, crpr(sta), sta); else - return crpr(sta);; + return crpr(sta); } Crpr @@ -290,7 +281,7 @@ PathEnd::multiCyclePath() const int PathEnd::exceptPathCmp(const PathEnd *path_end, - const StaState *) const + const StaState *) const { Type type1 = type(); Type type2 = path_end->type(); @@ -306,47 +297,48 @@ PathEnd::exceptPathCmp(const PathEnd *path_end, Delay PathEnd::checkTgtClkDelay(const Path *tgt_clk_path, - const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta) + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta) { Delay insertion, latency; checkTgtClkDelay(tgt_clk_path, tgt_clk_edge, check_role, sta, - insertion, latency); - return Delay(insertion + latency); + insertion, latency); + return delaySum(insertion, latency, sta); } void PathEnd::checkTgtClkDelay(const Path *tgt_clk_path, - const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta, - // Return values. - Delay &insertion, - Delay &latency) + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta, + // Return values. + Delay &insertion, + Delay &latency) { if (tgt_clk_path) { Search *search = sta->search(); // If propagated clk, adjust required time for target clk network delay. const MinMax *min_max = tgt_clk_path->minMax(sta); const EarlyLate *early_late = check_role->tgtClkEarlyLate(); - const PathAnalysisPt *tgt_path_ap = tgt_clk_path->pathAnalysisPt(sta); const ClkInfo *clk_info = tgt_clk_path->clkInfo(sta); const Pin *tgt_src_pin = clk_info->clkSrc(); const Clock *tgt_clk = tgt_clk_edge->clock(); const RiseFall *tgt_clk_rf = tgt_clk_edge->transition(); + const Mode *mode = tgt_clk_path->mode(sta); insertion = search->clockInsertion(tgt_clk, tgt_src_pin, tgt_clk_rf, - min_max, early_late, tgt_path_ap); + min_max, early_late, mode); if (clk_info->isPropagated() - // Data check target clock is always propagated. - || check_role->isDataCheck()) { + // Data check target clock is always propagated. + || check_role->isDataCheck()) { // Propagated clock. Propagated arrival is seeded with // early_late==path_min_max insertion delay. Arrival clk_arrival = tgt_clk_path->arrival(); Delay path_insertion = search->clockInsertion(tgt_clk, tgt_src_pin, - tgt_clk_rf, min_max, - min_max, tgt_path_ap); - latency = delayRemove(clk_arrival - tgt_clk_edge->time(), path_insertion); + tgt_clk_rf, min_max, + min_max, mode); + latency = delayRemove(delayDiff(clk_arrival, tgt_clk_edge->time(), sta), + path_insertion); } else // Ideal clock. @@ -360,20 +352,20 @@ PathEnd::checkTgtClkDelay(const Path *tgt_clk_path, float PathEnd::checkClkUncertainty(const ClockEdge *src_clk_edge, - const ClockEdge *tgt_clk_edge, - const Path *tgt_clk_path, - const TimingRole *check_role, - const StaState *sta) + const ClockEdge *tgt_clk_edge, + const Path *tgt_clk_path, + const TimingRole *check_role, + const Sdc *sdc) { float inter_clk; bool inter_exists; - checkInterClkUncertainty(src_clk_edge, tgt_clk_edge, check_role, sta, - inter_clk, inter_exists); + checkInterClkUncertainty(src_clk_edge, tgt_clk_edge, check_role, sdc, + inter_clk, inter_exists); if (inter_exists) return inter_clk; else - return checkTgtClkUncertainty(tgt_clk_path, tgt_clk_edge, check_role, sta); + return checkTgtClkUncertainty(tgt_clk_path, tgt_clk_edge, check_role, sdc); } float @@ -383,11 +375,11 @@ PathEnd::checkTgtClkUncertainty(const Path *tgt_clk_path, const StaState *sta) { const MinMax *min_max = check_role->pathMinMax(); - ClockUncertainties *uncertainties = nullptr; + const ClockUncertainties *uncertainties = nullptr; if (tgt_clk_path && tgt_clk_path->isClock(sta)) uncertainties = tgt_clk_path->clkInfo(sta)->uncertainties(); else if (tgt_clk_edge) - uncertainties = tgt_clk_edge->clock()->uncertainties(); + uncertainties = &tgt_clk_edge->clock()->uncertainties(); float uncertainty = 0.0; if (uncertainties) { bool exists; @@ -403,13 +395,12 @@ PathEnd::checkTgtClkUncertainty(const Path *tgt_clk_path, void PathEnd::checkInterClkUncertainty(const ClockEdge *src_clk_edge, - const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta, - float &uncertainty, - bool &exists) + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const Sdc *sdc, + float &uncertainty, + bool &exists) { - Sdc *sdc = sta->sdc(); if (src_clk_edge && src_clk_edge != sdc->defaultArrivalClockEdge() && tgt_clk_edge) { @@ -420,13 +411,21 @@ PathEnd::checkInterClkUncertainty(const ClockEdge *src_clk_edge, check_role->pathMinMax(), uncertainty, exists); if (exists - && check_role->genericRole() == TimingRole::setup()) + && check_role->genericRole() == TimingRole::setup()) uncertainty = -uncertainty; } else exists = false; } +bool +PathEnd::ignoreClkLatency(const Path *path, + PathDelay *path_delay, + const StaState *sta) +{ + return path_delay->ignoreClkLatency() && !path->isClock(sta); +} + //////////////////////////////////////////////////////////////// void @@ -457,7 +456,7 @@ PathEndUnconstrained::PathEndUnconstrained(Path *path) : PathEnd * PathEndUnconstrained::copy() const { - return new PathEndUnconstrained(path_); + return new PathEndUnconstrained(*this); } bool @@ -512,22 +511,10 @@ PathEndUnconstrained::typeName() const //////////////////////////////////////////////////////////////// PathEndClkConstrained::PathEndClkConstrained(Path *path, - Path *clk_path) : + Path *clk_path) : PathEnd(path), clk_path_(clk_path), - crpr_(0.0), - crpr_valid_(false) -{ -} - -PathEndClkConstrained::PathEndClkConstrained(Path *path, - Path *clk_path, - Crpr crpr, - bool crpr_valid) : - PathEnd(path), - clk_path_(clk_path), - crpr_(crpr), - crpr_valid_(crpr_valid) + crpr_(0.0) { } @@ -542,18 +529,18 @@ float PathEndClkConstrained::sourceClkOffset(const StaState *sta) const { return sourceClkOffset(sourceClkEdge(sta), - targetClkEdge(sta), - checkRole(sta), - sta); + targetClkEdge(sta), + checkRole(sta), + sta); } float PathEndClkConstrained::sourceClkOffset(const ClockEdge *src_clk_edge, - const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta) const + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta) const { - Sdc *sdc = sta->sdc(); + Sdc *sdc = path_->sdc(sta); CycleAccting *acct = sdc->cycleAccting(src_clk_edge, tgt_clk_edge); return acct->sourceTimeOffset(check_role); } @@ -590,7 +577,7 @@ PathEndClkConstrained::targetClkOffset(const StaState *sta) const const ClockEdge *src_clk_edge = sourceClkEdge(sta); const ClockEdge *tgt_clk_edge = targetClkEdge(sta); const TimingRole *check_role = checkRole(sta); - Sdc *sdc = sta->sdc(); + Sdc *sdc = path_->sdc(sta); CycleAccting *acct = sdc->cycleAccting(src_clk_edge, tgt_clk_edge); return acct->targetTimeOffset(check_role); } @@ -620,7 +607,7 @@ PathEndClkConstrained::targetClkTime(const StaState *sta) const const ClockEdge *src_clk_edge = sourceClkEdge(sta); const ClockEdge *tgt_clk_edge = targetClkEdge(sta); const TimingRole *check_role = checkRole(sta); - Sdc *sdc = sta->sdc(); + Sdc *sdc = path_->sdc(sta); CycleAccting *acct = sdc->cycleAccting(src_clk_edge, tgt_clk_edge); return acct->requiredTime(check_role); } @@ -628,20 +615,23 @@ PathEndClkConstrained::targetClkTime(const StaState *sta) const Arrival PathEndClkConstrained::targetClkArrival(const StaState *sta) const { - return targetClkArrivalNoCrpr(sta) - + checkCrpr(sta); + return delaySum(targetClkArrivalNoCrpr(sta), checkCrpr(sta), sta); } Arrival PathEndClkConstrained::targetClkArrivalNoCrpr(const StaState *sta) const { - return targetClkTime(sta) - + targetClkDelay(sta) - + checkClkUncertainty(sourceClkEdge(sta), - targetClkEdge(sta), - targetClkPath(), - checkRole(sta), sta) - + targetClkMcpAdjustment(sta); + Sdc *sdc = path_->sdc(sta); + Arrival clk_arrival = delaySum(targetClkDelay(sta), + targetClkTime(sta), + sta); + float uncertainty = checkClkUncertainty(sourceClkEdge(sta), + targetClkEdge(sta), + targetClkPath(), + checkRole(sta), + sdc); + return delaySum(delaySum(clk_arrival, uncertainty, sta), + targetClkMcpAdjustment(sta), sta); } Delay @@ -655,8 +645,8 @@ PathEndClkConstrained::targetClkInsertionDelay(const StaState *sta) const { Arrival insertion, latency; checkTgtClkDelay(targetClkPath(), targetClkEdge(sta), - checkRole(sta), sta, - insertion, latency); + checkRole(sta), sta, + insertion, latency); return insertion; } @@ -667,10 +657,11 @@ PathEndClkConstrained::targetNonInterClkUncertainty(const StaState *sta) const const ClockEdge *tgt_clk_edge = targetClkEdge(sta); const TimingRole *check_role = checkRole(sta); + Sdc *sdc = path_->sdc(sta); float inter_clk; bool inter_exists; checkInterClkUncertainty(src_clk_edge, tgt_clk_edge, check_role, - sta, inter_clk, inter_exists); + sdc, inter_clk, inter_exists); if (inter_exists) // This returns non inter-clock uncertainty. return 0.0; @@ -681,11 +672,12 @@ PathEndClkConstrained::targetNonInterClkUncertainty(const StaState *sta) const float PathEndClkConstrained::interClkUncertainty(const StaState *sta) const { + Sdc *sdc = path_->sdc(sta); float uncertainty; bool exists; checkInterClkUncertainty(sourceClkEdge(sta), targetClkEdge(sta), - checkRole(sta), sta, - uncertainty, exists); + checkRole(sta), sdc, + uncertainty, exists); if (exists) return uncertainty; else @@ -695,8 +687,9 @@ PathEndClkConstrained::interClkUncertainty(const StaState *sta) const float PathEndClkConstrained::targetClkUncertainty(const StaState *sta) const { + Sdc *sdc = path_->sdc(sta); return checkClkUncertainty(sourceClkEdge(sta), targetClkEdge(sta), - targetClkPath(), checkRole(sta), sta); + targetClkPath(), checkRole(sta), sdc); } Crpr @@ -713,8 +706,9 @@ PathEndClkConstrained::crpr(const StaState *sta) const Required PathEndClkConstrained::requiredTime(const StaState *sta) const { - return requiredTimeNoCrpr(sta) - + checkCrpr(sta); + return delaySum(requiredTimeNoCrpr(sta), + checkCrpr(sta), + sta); } Required @@ -723,9 +717,9 @@ PathEndClkConstrained::requiredTimeNoCrpr(const StaState *sta) const Arrival tgt_clk_arrival = targetClkArrivalNoCrpr(sta); ArcDelay check_margin = margin(sta); if (checkGenericRole(sta) == TimingRole::setup()) - return tgt_clk_arrival - check_margin; + return delayDiff(tgt_clk_arrival, check_margin, sta); else - return tgt_clk_arrival + check_margin; + return delaySum(tgt_clk_arrival, check_margin, sta); } Slack @@ -734,14 +728,14 @@ PathEndClkConstrained::slack(const StaState *sta) const Arrival arrival = dataArrivalTime(sta); Required required = requiredTime(sta); if (checkGenericRole(sta) == TimingRole::setup()) - return required - arrival; + return delayDiff(required, arrival, sta); else - return arrival - required; + return delayDiff(arrival, required, sta); } int PathEndClkConstrained::exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const + const StaState *sta) const { int cmp = PathEnd::exceptPathCmp(path_end, sta); if (cmp == 0) { @@ -757,23 +751,13 @@ PathEndClkConstrained::exceptPathCmp(const PathEnd *path_end, //////////////////////////////////////////////////////////////// PathEndClkConstrainedMcp::PathEndClkConstrainedMcp(Path *path, - Path *clk_path, - MultiCyclePath *mcp) : + Path *clk_path, + MultiCyclePath *mcp) : PathEndClkConstrained(path, clk_path), mcp_(mcp) { } -PathEndClkConstrainedMcp::PathEndClkConstrainedMcp(Path *path, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid) : - PathEndClkConstrained(path, clk_path, crpr, crpr_valid), - mcp_(mcp) -{ -} - float PathEndClkConstrainedMcp::targetClkMcpAdjustment(const StaState *sta) const { @@ -782,56 +766,56 @@ PathEndClkConstrainedMcp::targetClkMcpAdjustment(const StaState *sta) const float PathEndClkConstrainedMcp::checkMcpAdjustment(const Path *path, - const ClockEdge *tgt_clk_edge, - const StaState *sta) const + const ClockEdge *tgt_clk_edge, + const StaState *sta) const { if (mcp_) { const TimingRole *check_role = checkRole(sta); const MinMax *min_max = check_role->pathMinMax(); const ClockEdge *src_clk_edge = path->clkEdge(sta); - Sdc *sdc = sta->sdc(); + Sdc *sdc = path_->sdc(sta); if (min_max == MinMax::max()) return PathEnd::checkSetupMcpAdjustment(src_clk_edge, tgt_clk_edge, - mcp_, setupDefaultCycles(), sdc); + mcp_, setupDefaultCycles(), sdc); else { // Hold check. // Default arrival clock is a proxy for the target clock. if (src_clk_edge == nullptr) - src_clk_edge = tgt_clk_edge; + src_clk_edge = tgt_clk_edge; else if (src_clk_edge->clock() == sdc->defaultArrivalClock()) - src_clk_edge = tgt_clk_edge->clock()->edge(src_clk_edge->transition()); + src_clk_edge = tgt_clk_edge->clock()->edge(src_clk_edge->transition()); const MultiCyclePath *setup_mcp; const MultiCyclePath *hold_mcp; // Hold checks also need the setup mcp for cycle accounting. findHoldMcps(tgt_clk_edge, setup_mcp, hold_mcp, sta); if (setup_mcp && hold_mcp) { - int setup_mult = setup_mcp->pathMultiplier(MinMax::max()); - int hold_mult = hold_mcp->pathMultiplier(MinMax::min()); - const ClockEdge *setup_clk_edge = - setup_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; - float setup_period = setup_clk_edge->clock()->period(); - const ClockEdge *hold_clk_edge = - hold_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; - float hold_period = hold_clk_edge->clock()->period(); - return (setup_mult - 1) * setup_period - hold_mult * hold_period; + int setup_mult = setup_mcp->pathMultiplier(MinMax::max()); + int hold_mult = hold_mcp->pathMultiplier(MinMax::min()); + const ClockEdge *setup_clk_edge = + setup_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; + float setup_period = setup_clk_edge->clock()->period(); + const ClockEdge *hold_clk_edge = + hold_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; + float hold_period = hold_clk_edge->clock()->period(); + return (setup_mult - 1) * setup_period - hold_mult * hold_period; } else if (hold_mcp) { - int mult = hold_mcp->pathMultiplier(min_max); - const ClockEdge *clk_edge = - hold_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; - float period = clk_edge->clock()->period(); - return -mult * period; + int mult = hold_mcp->pathMultiplier(min_max); + const ClockEdge *clk_edge = + hold_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; + float period = clk_edge->clock()->period(); + return -mult * period; } else if (setup_mcp) { - int mult = setup_mcp->pathMultiplier(min_max); - const ClockEdge *clk_edge = - setup_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; - float period = clk_edge->clock()->period(); - return (mult - 1) * period; + int mult = setup_mcp->pathMultiplier(min_max); + const ClockEdge *clk_edge = + setup_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; + float period = clk_edge->clock()->period(); + return (mult - 1) * period; } else - return 0.0; + return 0.0; } } else @@ -840,10 +824,10 @@ PathEndClkConstrainedMcp::checkMcpAdjustment(const Path *path, float PathEnd::checkSetupMcpAdjustment(const ClockEdge *src_clk_edge, - const ClockEdge *tgt_clk_edge, - const MultiCyclePath *mcp, - int default_cycles, - Sdc *sdc) + const ClockEdge *tgt_clk_edge, + const MultiCyclePath *mcp, + int default_cycles, + Sdc *sdc) { if (mcp) { // Default arrival clock is a proxy for the target clock. @@ -854,7 +838,7 @@ PathEnd::checkSetupMcpAdjustment(const ClockEdge *src_clk_edge, if (mcp->minMax()->matches(MinMax::max())) { int mult = mcp->pathMultiplier(MinMax::max()); const ClockEdge *clk_edge = - mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; + mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; float period = clk_edge->clock()->period(); return (mult - default_cycles) * period; } @@ -871,16 +855,16 @@ PathEndClkConstrained::slackNoCrpr(const StaState *sta) const Arrival arrival = dataArrivalTime(sta); Required required = requiredTimeNoCrpr(sta); if (checkGenericRole(sta) == TimingRole::setup()) - return required - arrival; + return delayDiff(required, arrival, sta); else - return arrival - required; + return delayDiff(arrival, required, sta); } void PathEndClkConstrainedMcp::findHoldMcps(const ClockEdge *tgt_clk_edge, - const MultiCyclePath *&setup_mcp, - const MultiCyclePath *&hold_mcp, - const StaState *sta) const + const MultiCyclePath *&setup_mcp, + const MultiCyclePath *&hold_mcp, + const StaState *sta) const { Pin *pin = path_->pin(sta); @@ -892,25 +876,27 @@ PathEndClkConstrainedMcp::findHoldMcps(const ClockEdge *tgt_clk_edge, hold_mcp = mcp_; setup_mcp = dynamic_cast(search->exceptionTo(ExceptionPathType::multi_cycle, - path_, pin, rf, - tgt_clk_edge, - MinMax::max(), true, - false)); + path_, pin, rf, + tgt_clk_edge, + MinMax::max(), true, + false, + path_->sdc(sta))); } else { setup_mcp = mcp_; hold_mcp = dynamic_cast(search->exceptionTo(ExceptionPathType::multi_cycle, - path_, pin, rf, - tgt_clk_edge, - MinMax::min(), true, - false)); + path_, pin, rf, + tgt_clk_edge, + MinMax::min(), true, + false, + path_->sdc(sta))); } } int PathEndClkConstrainedMcp::exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const + const StaState *sta) const { int cmp = PathEndClkConstrained::exceptPathCmp(path_end, sta); if (cmp == 0) { @@ -931,35 +917,21 @@ PathEndClkConstrainedMcp::exceptPathCmp(const PathEnd *path_end, //////////////////////////////////////////////////////////////// PathEndCheck::PathEndCheck(Path *path, - TimingArc *check_arc, - Edge *check_edge, - Path *clk_path, - MultiCyclePath *mcp, - const StaState *) : + TimingArc *check_arc, + Edge *check_edge, + Path *clk_path, + MultiCyclePath *mcp, + const StaState *) : PathEndClkConstrainedMcp(path, clk_path, mcp), check_arc_(check_arc), check_edge_(check_edge) { } -PathEndCheck::PathEndCheck(Path *path, - TimingArc *check_arc, - Edge *check_edge, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid) : - PathEndClkConstrainedMcp(path, clk_path, mcp, crpr, crpr_valid), - check_arc_(check_arc), - check_edge_(check_edge) -{ -} - PathEnd * PathEndCheck::copy() const { - return new PathEndCheck(path_, check_arc_, check_edge_, - clk_path_, mcp_, crpr_, crpr_valid_); + return new PathEndCheck(*this); } PathEnd::Type @@ -996,13 +968,15 @@ ArcDelay PathEndCheck::margin(const StaState *sta) const { return sta->search()->deratedDelay(clk_path_->vertex(sta), - check_arc_, check_edge_, false, - pathAnalysisPt(sta)); + check_arc_, check_edge_, false, + path_->minMax(sta), + path_->dcalcAnalysisPtIndex(sta), + path_->sdc(sta)); } int PathEndCheck::exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const + const StaState *sta) const { int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta); if (cmp == 0) { @@ -1022,10 +996,18 @@ PathEndCheck::exceptPathCmp(const PathEnd *path_end, Delay PathEndCheck::clkSkew(const StaState *sta) { - return sourceClkDelay(sta) - targetClkDelay(sta) - crpr(sta) - // Uncertainty decreases slack, but increases skew. - - checkTgtClkUncertainty(clk_path_, clk_path_->clkEdge(sta), - checkRole(sta), sta); + Delay skew = delayDiff(sourceClkDelay(sta), + targetClkDelay(sta), + sta); + skew = delayDiff(skew, crpr(sta), sta); + // Uncertainty decreases slack, but increases skew. + skew = delayDiff(skew, + checkTgtClkUncertainty(clk_path_, + clk_path_->clkEdge(sta), + checkRole(sta), + sta), + sta); + return skew; } Delay @@ -1040,7 +1022,8 @@ PathEndCheck::sourceClkDelay(const StaState *sta) const Arrival clk_arrival = src_clk_path->arrival(); const ClockEdge *src_clk_edge = src_clk_info->clkEdge(); Delay insertion = sourceClkInsertionDelay(sta); - return delayRemove(clk_arrival - src_clk_edge->time(), insertion); + return delayRemove(delayDiff(clk_arrival, src_clk_edge->time(), sta), + insertion); } else // Ideal clock. @@ -1057,9 +1040,17 @@ PathEndCheck::requiredTimeNoCrpr(const StaState *sta) const ArcDelay check_margin = margin(sta); float macro_clk_tree_delay = macroClkTreeDelay(sta); if (checkGenericRole(sta) == TimingRole::setup()) - return tgt_clk_arrival - (check_margin + macro_clk_tree_delay); + return delayDiff(tgt_clk_arrival, + delaySum(check_margin, + macro_clk_tree_delay, + sta), + sta); else - return tgt_clk_arrival + (check_margin - macro_clk_tree_delay); + return delaySum(tgt_clk_arrival, + delayDiff(check_margin, + macro_clk_tree_delay, + sta), + sta); } float @@ -1077,7 +1068,7 @@ PathEndCheck::macroClkTreeDelay(const StaState *sta) const if (clk_port) { const MinMax *min_max = clk_path_->minMax(sta); const RiseFall *rf = clk_path_->transition(sta); - float slew = delayAsFloat(clk_path_->slew(sta)); + float slew = delayAsFloat(clk_path_->slew(sta), min_max, sta); return clk_port->clkTreeDelay(slew, rf, min_max); } } @@ -1087,51 +1078,30 @@ PathEndCheck::macroClkTreeDelay(const StaState *sta) const //////////////////////////////////////////////////////////////// PathEndLatchCheck::PathEndLatchCheck(Path *path, - TimingArc *check_arc, - Edge *check_edge, - Path *disable_path, - MultiCyclePath *mcp, - PathDelay *path_delay, - const StaState *sta) : + TimingArc *check_arc, + Edge *check_edge, + Path *disable_path, + MultiCyclePath *mcp, + PathDelay *path_delay, + const StaState *sta) : PathEndCheck(path, check_arc, check_edge, nullptr, mcp, sta), disable_path_(disable_path), path_delay_(path_delay), src_clk_arrival_(0.0) { Latches *latches = sta->latches(); - Path *enable_path = - latches->latchEnableOtherPath(disable_path, - disable_path->pathAnalysisPt(sta)); + Path *enable_path = latches->latchEnableOtherPath(disable_path); clk_path_ = enable_path; Search *search = sta->search(); // Same as PathEndPathDelay::findRequired. - if (path_delay_ && ignoreClkLatency(sta)) + if (path_delay_ && PathEnd::ignoreClkLatency(path_, path_delay_, sta)) src_clk_arrival_ = search->pathClkPathArrival(path_); } -PathEndLatchCheck::PathEndLatchCheck(Path *path, - TimingArc *check_arc, - Edge *check_edge, - Path *clk_path, - Path *disable_path, - MultiCyclePath *mcp, - PathDelay *path_delay, - Delay src_clk_arrival, - Crpr crpr, - bool crpr_valid) : - PathEndCheck(path, check_arc, check_edge, clk_path, mcp, crpr, crpr_valid), - disable_path_(disable_path), - path_delay_(path_delay), - src_clk_arrival_(src_clk_arrival) -{ -} - PathEnd * PathEndLatchCheck::copy() const { - return new PathEndLatchCheck(path_, check_arc_, check_edge_, - clk_path_, disable_path_, mcp_, path_delay_, - src_clk_arrival_, crpr_, crpr_valid_); + return new PathEndLatchCheck(*this); } PathEnd::Type @@ -1177,15 +1147,15 @@ PathEndLatchCheck::sourceClkOffset(const StaState *sta) const return pathDelaySrcClkOffset(path_, path_delay_, src_clk_arrival_, sta); else return PathEndClkConstrained::sourceClkOffset(sourceClkEdge(sta), - disable_path_->clkEdge(sta), - TimingRole::setup(), - sta); + disable_path_->clkEdge(sta), + TimingRole::setup(), + sta); } const TimingRole * PathEndLatchCheck::checkRole(const StaState *sta) const { - if (clk_path_->clkInfo(sta)->isPulseClk()) + if (clk_path_ && clk_path_->clkInfo(sta)->isPulseClk()) // Pulse latches use register cycle accounting. return TimingRole::setup(); else @@ -1219,9 +1189,9 @@ PathEndLatchCheck::requiredTime(const StaState *sta) const Arrival borrow, adjusted_data_arrival, time_given_to_startpoint; Latches *latches = sta->latches(); latches->latchRequired(path_, targetClkPath(), latchDisable(), - mcp_, path_delay_, src_clk_arrival_, margin(sta), - required, borrow, adjusted_data_arrival, - time_given_to_startpoint); + mcp_, path_delay_, src_clk_arrival_, margin(sta), + required, borrow, adjusted_data_arrival, + time_given_to_startpoint); return required; } @@ -1232,47 +1202,47 @@ PathEndLatchCheck::borrow(const StaState *sta) const Required required; Arrival borrow, adjusted_data_arrival, time_given_to_startpoint; latches->latchRequired(path_, targetClkPath(), latchDisable(), - mcp_, path_delay_, src_clk_arrival_, margin(sta), - required, borrow, adjusted_data_arrival, - time_given_to_startpoint); + mcp_, path_delay_, src_clk_arrival_, margin(sta), + required, borrow, adjusted_data_arrival, + time_given_to_startpoint); return borrow; } void PathEndLatchCheck::latchRequired(const StaState *sta, - // Return values. - Required &required, - Delay &borrow, - Arrival &adjusted_data_arrival, - Delay &time_given_to_startpoint) const + // Return values. + Required &required, + Delay &borrow, + Arrival &adjusted_data_arrival, + Delay &time_given_to_startpoint) const { Latches *latches = sta->latches(); latches->latchRequired(path_, targetClkPath(), latchDisable(), - mcp_, path_delay_, src_clk_arrival_, margin(sta), - required, borrow, adjusted_data_arrival, - time_given_to_startpoint); + mcp_, path_delay_, src_clk_arrival_, margin(sta), + required, borrow, adjusted_data_arrival, + time_given_to_startpoint); } void PathEndLatchCheck::latchBorrowInfo(const StaState *sta, - // Return values. - float &nom_pulse_width, - Delay &open_latency, - Delay &latency_diff, - float &open_uncertainty, - Crpr &open_crpr, - Crpr &crpr_diff, - Delay &max_borrow, - bool &borrow_limit_exists) const + // Return values. + float &nom_pulse_width, + Delay &open_latency, + Delay &latency_diff, + float &open_uncertainty, + Crpr &open_crpr, + Crpr &crpr_diff, + Delay &max_borrow, + bool &borrow_limit_exists) const { Latches *latches = sta->latches(); latches->latchBorrowInfo(path_, targetClkPath(), latchDisable(), - margin(sta), - path_delay_ && ignoreClkLatency(sta), - nom_pulse_width, open_latency, - latency_diff, open_uncertainty, - open_crpr, crpr_diff, max_borrow, - borrow_limit_exists); + margin(sta), + path_delay_ && ignoreClkLatency(sta), + nom_pulse_width, open_latency, + latency_diff, open_uncertainty, + open_crpr, crpr_diff, max_borrow, + borrow_limit_exists); } Arrival @@ -1283,22 +1253,24 @@ PathEndLatchCheck::targetClkWidth(const StaState *sta) const Arrival enable_arrival = search->clkPathArrival(clk_path_); const ClkInfo *enable_clk_info = clk_path_->clkInfo(sta); if (enable_clk_info->isPulseClk()) - return disable_arrival - enable_arrival; + return delayDiff(disable_arrival, enable_arrival, sta); else { if (delayGreater(enable_arrival, disable_arrival, sta)) { const Clock *disable_clk = enable_clk_info->clock(); if (disable_clk) - disable_arrival += disable_clk->period(); + disable_arrival = delaySum(disable_arrival, + disable_clk->period(), + sta); } - return disable_arrival - enable_arrival; + return delayDiff(disable_arrival, enable_arrival, sta); } } int PathEndLatchCheck::exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const + const StaState *sta) const { - int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta); + int cmp = PathEndCheck::exceptPathCmp(path_end, sta); if (cmp == 0) { const PathEndLatchCheck *path_end2 = dynamic_cast(path_end); @@ -1325,32 +1297,20 @@ PathEndLatchCheck::ignoreClkLatency(const StaState *sta) const /////////////////////////////////////////////////////////////// PathEndOutputDelay::PathEndOutputDelay(OutputDelay *output_delay, - Path *path, - Path *clk_path, - MultiCyclePath *mcp, - const StaState *) : + Path *path, + Path *clk_path, + MultiCyclePath *mcp, + const StaState *) : // No target clk_path_ for output delays. PathEndClkConstrainedMcp(path, clk_path, mcp), output_delay_(output_delay) { } -PathEndOutputDelay::PathEndOutputDelay(OutputDelay *output_delay, - Path *path, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid) : - PathEndClkConstrainedMcp(path, clk_path, mcp, crpr, crpr_valid), - output_delay_(output_delay) -{ -} - PathEnd * PathEndOutputDelay::copy() const { - return new PathEndOutputDelay(output_delay_, path_, clk_path_, - mcp_, crpr_, crpr_valid_); + return new PathEndOutputDelay(*this); } PathEnd::Type @@ -1385,8 +1345,8 @@ PathEndOutputDelay::margin(const StaState *sta) const float PathEnd::outputDelayMargin(OutputDelay *output_delay, - const Path *path, - const StaState *sta) + const Path *path, + const StaState *sta) { const RiseFall *rf = path->transition(sta); const MinMax *min_max = path->minMax(sta); @@ -1423,10 +1383,14 @@ PathEndOutputDelay::targetClkArrivalNoCrpr(const StaState *sta) const else { const ClockEdge *tgt_clk_edge = targetClkEdge(sta); const TimingRole *check_role = checkRole(sta); - return targetClkTime(sta) - + tgtClkDelay(tgt_clk_edge, check_role, sta) - + targetClkUncertainty(sta) - + checkMcpAdjustment(path_, tgt_clk_edge, sta); + Arrival base = delaySum(targetClkTime(sta), + tgtClkDelay(tgt_clk_edge, check_role, sta), + sta); + return delaySum(delaySum(base, + targetClkUncertainty(sta), + sta), + checkMcpAdjustment(path_, tgt_clk_edge, sta), + sta); } } @@ -1452,42 +1416,41 @@ PathEndOutputDelay::targetClkDelay(const StaState *sta) const Arrival PathEndOutputDelay::tgtClkDelay(const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta) const + const TimingRole *check_role, + const StaState *sta) const { Arrival insertion, latency; tgtClkDelay(tgt_clk_edge, check_role, sta, - insertion, latency); - return insertion + latency; + insertion, latency); + return delaySum(insertion, latency, sta); } void PathEndOutputDelay::tgtClkDelay(const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta, - // Return values. - Arrival &insertion, - Arrival &latency) const + const TimingRole *check_role, + const StaState *sta, + // Return values. + Arrival &insertion, + Arrival &latency) const { // Early late: setup early, hold late. const EarlyLate *early_late = check_role->tgtClkEarlyLate(); // Latency min_max depends on bc_wc or ocv. - const PathAnalysisPt *path_ap = path_->pathAnalysisPt(sta); - const MinMax *latency_min_max = path_ap->tgtClkAnalysisPt()->pathMinMax(); + const MinMax *latency_min_max = path_->tgtClkMinMax(sta); Clock *tgt_clk = tgt_clk_edge->clock(); const RiseFall *tgt_clk_rf = tgt_clk_edge->transition(); + const Mode *mode = path_->mode(sta); if (!output_delay_->sourceLatencyIncluded()) insertion = sta->search()->clockInsertion(tgt_clk, - tgt_clk->defaultPin(), - tgt_clk_rf, - latency_min_max, - early_late, path_ap); + tgt_clk->defaultPin(), + tgt_clk_rf, + latency_min_max, + early_late, mode); else insertion = 0.0; - const Sdc *sdc = sta->sdc(); if (!tgt_clk->isPropagated() && !output_delay_->networkLatencyIncluded()) - latency = sdc->clockLatency(tgt_clk, tgt_clk_rf, latency_min_max); + latency = mode->sdc()->clockLatency(tgt_clk, tgt_clk_rf, latency_min_max); else latency = 0.0; } @@ -1500,14 +1463,14 @@ PathEndOutputDelay::targetClkInsertionDelay(const StaState *sta) const else { Arrival insertion, latency; tgtClkDelay(targetClkEdge(sta), checkRole(sta), sta, - insertion, latency); + insertion, latency); return insertion; } } int PathEndOutputDelay::exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const + const StaState *sta) const { int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta); if (cmp == 0) { @@ -1528,35 +1491,21 @@ PathEndOutputDelay::exceptPathCmp(const PathEnd *path_end, //////////////////////////////////////////////////////////////// PathEndGatedClock::PathEndGatedClock(Path *gating_ref, - Path *clk_path, - const TimingRole *check_role, - MultiCyclePath *mcp, - ArcDelay margin, - const StaState *) : + Path *clk_path, + const TimingRole *check_role, + MultiCyclePath *mcp, + ArcDelay margin, + const StaState *) : PathEndClkConstrainedMcp(gating_ref, clk_path, mcp), check_role_(check_role), margin_(margin) { } -PathEndGatedClock::PathEndGatedClock(Path *gating_ref, - Path *clk_path, - const TimingRole *check_role, - MultiCyclePath *mcp, - ArcDelay margin, - Crpr crpr, - bool crpr_valid) : - PathEndClkConstrainedMcp(gating_ref, clk_path, mcp, crpr, crpr_valid), - check_role_(check_role), - margin_(margin) -{ -} - PathEnd * PathEndGatedClock::copy() const { - return new PathEndGatedClock(path_, clk_path_, check_role_, - mcp_, margin_, crpr_, crpr_valid_); + return new PathEndGatedClock(*this); } PathEnd::Type @@ -1591,7 +1540,7 @@ PathEndGatedClock::reportShort(const ReportPath *report) const int PathEndGatedClock::exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const + const StaState *sta) const { int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta); if (cmp == 0) { @@ -1612,10 +1561,10 @@ PathEndGatedClock::exceptPathCmp(const PathEnd *path_end, //////////////////////////////////////////////////////////////// PathEndDataCheck::PathEndDataCheck(DataCheck *check, - Path *data_path, - Path *data_clk_path, - MultiCyclePath *mcp, - const StaState *sta) : + Path *data_path, + Path *data_clk_path, + MultiCyclePath *mcp, + const StaState *sta) : PathEndClkConstrainedMcp(data_path, nullptr, mcp), data_clk_path_(data_clk_path), check_(check) @@ -1639,15 +1588,15 @@ PathEndDataCheck::clkPath(Path *path, if (prev_arc) { const TimingRole *prev_role = prev_arc->role(); if (prev_role == TimingRole::regClkToQ() - || prev_role == TimingRole::latchEnToQ()) { + || prev_role == TimingRole::latchEnToQ()) { prev_path = p->prevPath(); - return prev_path; + return prev_path; } else if (prev_role == TimingRole::latchDtoQ()) { - const Latches *latches = sta->latches(); - Edge *prev_edge = p->prevEdge(sta); - Path *enable_path = latches->latchEnablePath(p, prev_edge); - return enable_path; + const Latches *latches = sta->latches(); + Edge *prev_edge = p->prevEdge(sta); + Path *enable_path = latches->latchEnablePath(p, prev_edge); + return enable_path; } } p = prev_path; @@ -1655,24 +1604,10 @@ PathEndDataCheck::clkPath(Path *path, return nullptr; } -PathEndDataCheck::PathEndDataCheck(DataCheck *check, - Path *data_path, - Path *data_clk_path, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid) : - PathEndClkConstrainedMcp(data_path, clk_path, mcp, crpr, crpr_valid), - data_clk_path_(data_clk_path), - check_(check) -{ -} - PathEnd * PathEndDataCheck::copy() const { - return new PathEndDataCheck(check_, path_, data_clk_path_, - clk_path_, mcp_, crpr_, crpr_valid_); + return new PathEndDataCheck(*this); } PathEnd::Type @@ -1699,17 +1634,23 @@ PathEndDataCheck::requiredTimeNoCrpr(const StaState *sta) const { Arrival data_clk_arrival = data_clk_path_->arrival(); float data_clk_time = data_clk_path_->clkEdge(sta)->time(); - Arrival data_clk_delay = data_clk_arrival - data_clk_time; - Arrival tgt_clk_arrival = targetClkTime(sta) - + data_clk_delay - + targetClkUncertainty(sta) - + targetClkMcpAdjustment(sta); + Arrival data_clk_delay = delayDiff(data_clk_arrival, + data_clk_time, + sta); + Arrival tgt_clk_arrival = + delaySum(delaySum(targetClkTime(sta), + data_clk_delay, + sta), + delaySum(targetClkUncertainty(sta), + targetClkMcpAdjustment(sta), + sta), + sta); ArcDelay check_margin = margin(sta); if (checkGenericRole(sta) == TimingRole::setup()) - return tgt_clk_arrival - check_margin; + return delayDiff(tgt_clk_arrival, check_margin, sta); else - return tgt_clk_arrival + check_margin; + return delaySum(tgt_clk_arrival, check_margin, sta); } ArcDelay @@ -1718,9 +1659,9 @@ PathEndDataCheck::margin(const StaState *sta) const float margin; bool margin_exists; check_->margin(data_clk_path_->transition(sta), - path_->transition(sta), - path_->minMax(sta), - margin, margin_exists); + path_->transition(sta), + path_->minMax(sta), + margin, margin_exists); return margin; } @@ -1747,7 +1688,7 @@ PathEndDataCheck::reportShort(const ReportPath *report) const int PathEndDataCheck::exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const + const StaState *sta) const { int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta); if (cmp == 0) { @@ -1768,8 +1709,8 @@ PathEndDataCheck::exceptPathCmp(const PathEnd *path_end, //////////////////////////////////////////////////////////////// PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, - Path *path, - const StaState *sta): + Path *path, + const StaState *sta): PathEndClkConstrained(path, nullptr), path_delay_(path_delay), check_arc_(nullptr), @@ -1780,9 +1721,9 @@ PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, } PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, - Path *path, - OutputDelay *output_delay, - const StaState *sta): + Path *path, + OutputDelay *output_delay, + const StaState *sta): PathEndClkConstrained(path, nullptr), path_delay_(path_delay), check_arc_(nullptr), @@ -1793,11 +1734,11 @@ PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, } PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, - Path *path, - Path *clk_path, - TimingArc *check_arc, - Edge *check_edge, - const StaState *sta) : + Path *path, + Path *clk_path, + TimingArc *check_arc, + Edge *check_edge, + const StaState *sta) : PathEndClkConstrained(path, clk_path), path_delay_(path_delay), check_arc_(check_arc), @@ -1807,30 +1748,10 @@ PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, findSrcClkArrival(sta); } -PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, - Path *path, - Path *clk_path, - TimingArc *check_arc, - Edge *check_edge, - OutputDelay *output_delay, - Arrival src_clk_arrival, - Crpr crpr, - bool crpr_valid) : - PathEndClkConstrained(path, clk_path, crpr, crpr_valid), - path_delay_(path_delay), - check_arc_(check_arc), - check_edge_(check_edge), - output_delay_(output_delay), - src_clk_arrival_(src_clk_arrival) -{ -} - PathEnd * PathEndPathDelay::copy() const { - return new PathEndPathDelay(path_delay_, path_, clk_path_, - check_arc_, check_edge_, output_delay_, - src_clk_arrival_, crpr_, crpr_valid_); + return new PathEndPathDelay(*this); } PathEnd::Type @@ -1848,7 +1769,7 @@ PathEndPathDelay::typeName() const void PathEndPathDelay::findSrcClkArrival(const StaState *sta) { - if (ignoreClkLatency(sta)) { + if (PathEnd::ignoreClkLatency(path_, path_delay_, sta)) { Search *search = sta->search(); src_clk_arrival_ = search->pathClkPathArrival(path_); } @@ -1891,8 +1812,10 @@ PathEndPathDelay::margin(const StaState *sta) const { if (check_arc_) { return sta->search()->deratedDelay(check_edge_->from(sta->graph()), - check_arc_, check_edge_, false, - pathAnalysisPt(sta)); + check_arc_, check_edge_, false, + path_->minMax(sta), + path_->dcalcAnalysisPtIndex(sta), + path_->sdc(sta)); } else if (output_delay_) return outputDelayMargin(output_delay_, path_, sta); @@ -1915,15 +1838,15 @@ PathEnd::clkSkew(const StaState *) // Helper shared by PathEndLatchCheck. float PathEnd::pathDelaySrcClkOffset(const Path *path, - PathDelay *path_delay, - Arrival src_clk_arrival, - const StaState *sta) + PathDelay *path_delay, + Arrival src_clk_arrival, + const StaState *sta) { float offset = 0.0; const ClockEdge *clk_edge = path->clkEdge(sta); if (clk_edge) { if (ignoreClkLatency(path, path_delay, sta)) - offset = -delayAsFloat(src_clk_arrival); + offset = -delayAsFloat(src_clk_arrival, path->minMax(sta), sta); else // Arrival includes src clock edge time that is not counted in the // path delay. @@ -1932,14 +1855,6 @@ PathEnd::pathDelaySrcClkOffset(const Path *path, return offset; } -bool -PathEnd::ignoreClkLatency(const Path *path, - PathDelay *path_delay, - const StaState *sta) -{ - return path_delay->ignoreClkLatency() && !path->isClock(sta); -} - const ClockEdge * PathEndPathDelay::targetClkEdge(const StaState *sta) const { @@ -1966,8 +1881,9 @@ PathEndPathDelay::targetClkArrivalNoCrpr(const StaState *sta) const { const ClockEdge *tgt_clk_edge = targetClkEdge(sta); if (tgt_clk_edge) - return targetClkDelay(sta) - + targetClkUncertainty(sta); + return delaySum(targetClkDelay(sta), + targetClkUncertainty(sta), + sta); else if (clk_path_) return clk_path_->arrival(); else @@ -1985,19 +1901,29 @@ PathEndPathDelay::requiredTime(const StaState *sta) const { float delay = path_delay_->delay(); if (path_delay_->ignoreClkLatency()) { - Required src_offset = path_->isClock(sta) - ? path_->clkEdge(sta)->time() - : src_clk_arrival_; - return src_offset + delay - + ((minMax(sta) == MinMax::max()) ? -margin(sta) : margin(sta)); + Delay with_delay = path_->isClock(sta) + ? Delay(path_->clkEdge(sta)->time() + delay) + : delaySum(src_clk_arrival_, delay, sta); + ArcDelay m = margin(sta); + return (minMax(sta) == MinMax::max()) + ? delayDiff(with_delay, m, sta) + : delaySum(with_delay, m, sta); } else { Arrival tgt_clk_arrival = targetClkArrival(sta); float src_clk_offset = sourceClkOffset(sta); // Path delay includes target clk latency and timing check setup/hold // margin or external departure at target. - return delay - src_clk_offset + tgt_clk_arrival - + ((minMax(sta) == MinMax::max()) ? -margin(sta) : margin(sta)); + Delay base = delaySum(tgt_clk_arrival, + delay, + sta); + Delay with_src = delayDiff(base, + src_clk_offset, + sta); + ArcDelay m = margin(sta); + return (minMax(sta) == MinMax::max()) + ? delayDiff(with_src, m, sta) + : delaySum(with_src, m, sta); } } @@ -2009,7 +1935,7 @@ PathEndPathDelay::ignoreClkLatency(const StaState *sta) const int PathEndPathDelay::exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const + const StaState *sta) const { int cmp = PathEndClkConstrained::exceptPathCmp(path_end, sta); if (cmp == 0) { @@ -2019,11 +1945,11 @@ PathEndPathDelay::exceptPathCmp(const PathEnd *path_end, if (path_delay_ == path_delay2) { const TimingArc *check_arc2 = path_end2->check_arc_; if (check_arc_ == check_arc2) - return 0; + return 0; else if (check_arc_ < check_arc2) - return -1; + return -1; else - return 1; + return 1; } else if (path_delay_ < path_delay2) return -1; @@ -2036,32 +1962,22 @@ PathEndPathDelay::exceptPathCmp(const PathEnd *path_end, //////////////////////////////////////////////////////////////// -PathEndLess::PathEndLess(const StaState *sta) : - sta_(sta) -{ -} - -bool -PathEndLess::operator()(const PathEnd *path_end1, - const PathEnd *path_end2) const -{ - return PathEnd::less(path_end1, path_end2, sta_); -} - bool PathEnd::less(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta) + const PathEnd *path_end2, + bool cmp_slack, + const StaState *sta) { - return cmp(path_end1, path_end2, sta) < 0; + return cmp(path_end1, path_end2, cmp_slack, sta) < 0; } int PathEnd::cmp(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta) + const PathEnd *path_end2, + bool cmp_slack, + const StaState *sta) { - int cmp = path_end1->isUnconstrained() + int cmp = !cmp_slack || path_end1->isUnconstrained() ? -cmpArrival(path_end1, path_end2, sta) : cmpSlack(path_end1, path_end2, sta); if (cmp == 0) { @@ -2073,9 +1989,9 @@ PathEnd::cmp(const PathEnd *path_end1, const Path *clk_path2 = path_end2->targetClkPath(); cmp = Path::cmpPinTrClk(clk_path1, clk_path2, sta); if (cmp == 0) { - cmp = Path::cmpAll(path1, path2, sta); - if (cmp == 0) - cmp = Path::cmpAll(clk_path1, clk_path2, sta); + cmp = Path::cmpAll(path1, path2, sta); + if (cmp == 0) + cmp = Path::cmpAll(clk_path1, clk_path2, sta); } } } @@ -2084,27 +2000,27 @@ PathEnd::cmp(const PathEnd *path_end1, int PathEnd::cmpSlack(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta) + const PathEnd *path_end2, + const StaState *sta) { Slack slack1 = path_end1->slack(sta); Slack slack2 = path_end2->slack(sta); - if (delayZero(slack1) - && delayZero(slack2) + if (delayZero(slack1, sta) + && delayZero(slack2, sta) && path_end1->isLatchCheck() && path_end2->isLatchCheck()) { Arrival borrow1 = path_end1->borrow(sta); Arrival borrow2 = path_end2->borrow(sta); // Latch slack is zero if there is borrowing so break ties // based on borrow time. - if (delayEqual(borrow1, borrow2)) + if (delayEqual(borrow1, borrow2, sta)) return 0; else if (delayGreater(borrow1, borrow2, sta)) return -1; else return 1; } - else if (delayEqual(slack1, slack2)) + else if (delayEqual(slack1, slack2, sta)) return 0; else if (delayLess(slack1, slack2, sta)) return -1; @@ -2114,13 +2030,13 @@ PathEnd::cmpSlack(const PathEnd *path_end1, int PathEnd::cmpArrival(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta) + const PathEnd *path_end2, + const StaState *sta) { Arrival arrival1 = path_end1->dataArrivalTime(sta); Arrival arrival2 = path_end2->dataArrivalTime(sta); const MinMax *min_max = path_end1->minMax(sta); - if (delayEqual(arrival1, arrival2)) + if (delayEqual(arrival1, arrival2, sta)) return 0; else if (delayLess(arrival1, arrival2, min_max, sta)) return -1; @@ -2130,8 +2046,8 @@ PathEnd::cmpArrival(const PathEnd *path_end1, int PathEnd::cmpNoCrpr(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta) + const PathEnd *path_end2, + const StaState *sta) { int cmp = path_end1->exceptPathCmp(path_end2, sta); if (cmp == 0) { @@ -2145,14 +2061,32 @@ PathEnd::cmpNoCrpr(const PathEnd *path_end1, //////////////////////////////////////////////////////////////// -PathEndSlackLess::PathEndSlackLess(const StaState *sta) : +PathEndLess::PathEndLess(bool cmp_slack, + const StaState *sta) : + cmp_slack_(cmp_slack), + sta_(sta) +{ +} + +bool +PathEndLess::operator()(const PathEnd *path_end1, + const PathEnd *path_end2) const +{ + return PathEnd::less(path_end1, path_end2, cmp_slack_, sta_); +} + +//////////////////////////////////////////////////////////////// + +PathEndSlackLess::PathEndSlackLess(bool cmp_slack, + const StaState *sta) : + cmp_slack_(cmp_slack), sta_(sta) { } bool PathEndSlackLess::operator()(const PathEnd *path_end1, - const PathEnd *path_end2) const + const PathEnd *path_end2) const { int cmp = path_end1->isUnconstrained() ? -PathEnd::cmpArrival(path_end1, path_end2, sta_) @@ -2169,7 +2103,7 @@ PathEndNoCrprLess::PathEndNoCrprLess(const StaState *sta) : bool PathEndNoCrprLess::operator()(const PathEnd *path_end1, - const PathEnd *path_end2) const + const PathEnd *path_end2) const { int cmp = path_end1->exceptPathCmp(path_end2, sta_); if (cmp == 0) { @@ -2181,4 +2115,4 @@ PathEndNoCrprLess::operator()(const PathEnd *path_end1, return cmp < 0; } -} // namespace +} // namespace sta diff --git a/search/PathEnum.cc b/search/PathEnum.cc index c8763ed40..c285b8f99 100644 --- a/search/PathEnum.cc +++ b/search/PathEnum.cc @@ -1,51 +1,59 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "PathEnum.hh" +#include +#include +#include +#include + #include "Debug.hh" +#include "Delay.hh" #include "Error.hh" -#include "Fuzzy.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" -#include "Network.hh" -#include "Sdc.hh" #include "Graph.hh" -#include "PathAnalysisPt.hh" -#include "Tag.hh" -#include "Search.hh" +#include "GraphClass.hh" #include "Latches.hh" -#include "PathEnd.hh" +#include "Mode.hh" +#include "Network.hh" #include "Path.hh" +#include "PathEnd.hh" +#include "Sdc.hh" +#include "Search.hh" +#include "SearchClass.hh" +#include "Tag.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "VertexVisitor.hh" namespace sta { -// A diversion is an alternate path formed by changing the previous +// A diversion is an alternate path formed by changing the previous // path/arc of before_div to after_div/div_arc in path. // // div_arc -// after_div<--------+ +// after_div<--------+ // | // <--...--before_div<--...--path<---path_end // @@ -53,7 +61,7 @@ class Diversion { public: Diversion(PathEnd *path_end, - Path *after_div); + Path *after_div); PathEnd *pathEnd() const { return path_end_; } Path *divPath() const { return after_div_; } @@ -63,7 +71,7 @@ class Diversion }; Diversion::Diversion(PathEnd *path_end, - Path *after_div) : + Path *after_div) : path_end_(path_end), after_div_(after_div) { @@ -87,20 +95,19 @@ DiversionGreater::DiversionGreater(const StaState *sta) : // the same delay is kept in the queue. bool DiversionGreater::operator()(Diversion *div1, - Diversion *div2) const + Diversion *div2) const { PathEnd *path_end1 = div1->pathEnd(); PathEnd *path_end2 = div2->pathEnd(); - return PathEnd::cmp(path_end1, path_end2, sta_) > 0; + return PathEnd::cmp(path_end1, path_end2, true, sta_) > 0; } static void deleteDiversionPathEnd(Diversion *div) { PathEnd *div_end = div->pathEnd(); - Path *div_path = div_end->path(); - if (div_path->isEnum()) - delete div_path; + // This does NOT delete the div_end->path() even if it is enum. + // Search::deletePathss takes care of that. delete div_end; delete div; } @@ -108,34 +115,32 @@ deleteDiversionPathEnd(Diversion *div) //////////////////////////////////////////////////////////////// PathEnum::PathEnum(size_t group_path_count, - size_t endpoint_path_count, - bool unique_pins, - bool unique_edges, - bool cmp_slack, - const StaState *sta) : + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + bool cmp_slack, + const StaState *sta) : StaState(sta), cmp_slack_(cmp_slack), group_path_count_(group_path_count), endpoint_path_count_(endpoint_path_count), unique_pins_(unique_pins), unique_edges_(unique_edges), - div_queue_(DiversionGreater(sta)), - div_count_(0), - inserts_pruned_(false), - next_(nullptr) + div_queue_(DiversionGreater(sta)) { } void PathEnum::insert(PathEnd *path_end) { - debugPrint(debug_, "path_enum", 1, "insert %s", - path_end->path()->to_string(this).c_str()); - debugPrint(debug_, "path_enum", 2, "diversion %s %s %s", - path_end->path()->to_string(this).c_str(), + debugPrint(debug_, "path_enum", 1, "insert {}", + path_end->path()->to_string(this)); + debugPrint(debug_, "path_enum", 2, "diversion {} {} {}", + path_end->path()->to_string(this), cmp_slack_ ? "slack" : "delay", - delayAsString(cmp_slack_ ? path_end->slack(this) : - path_end->dataArrivalTime(this), this)); + delayAsString(cmp_slack_ ? path_end->slack(this) + : path_end->dataArrivalTime(this), + this)); Diversion *div = new Diversion(path_end, path_end->path()); div_queue_.push(div); div_count_++; @@ -155,13 +160,11 @@ PathEnum::~PathEnum() bool PathEnum::hasNext() { - if (unique_pins_ - && !inserts_pruned_) { + if (unique_pins_ && !inserts_pruned_) { pruneDiversionQueue(); inserts_pruned_ = true; } - if (next_ == nullptr - && !div_queue_.empty()) + if (next_ == nullptr && !div_queue_.empty()) findNext(); return next_ != nullptr; } @@ -183,15 +186,14 @@ PathEnum::findNext() Diversion *div = div_queue_.top(); div_queue_.pop(); PathEnd *path_end = div->pathEnd(); + Path *path = path_end->path(); Vertex *vertex = path_end->vertex(this); path_counts_[vertex]++; if (debug_->check("path_enum", 2)) { - Path *path = path_end->path(); - report_->reportLine("path_enum: next path %zu %s delay %s slack %s", - path_counts_[vertex], - path->to_string(this).c_str(), - delayAsString(path_end->dataArrivalTime(this), this), - delayAsString(path_end->slack(this), this)); + report_->report("path_enum: next path {} {} delay {} slack {}", + path_counts_[vertex], path->to_string(this), + delayAsString(path_end->dataArrivalTime(this), this), + delayAsString(path_end->slack(this), this)); reportDiversionPath(div); } @@ -201,16 +203,14 @@ PathEnum::findNext() makeDiversions(path_end, div->divPath()); // Caller owns the path end now, so don't delete it. next_ = path_end; - //search_->saveEnumPath(path_end->path()); delete div; break; } else { // We have endpoint_path_count paths for this endpoint, // so we are done with it. - debugPrint(debug_, "path_enum", 1, - "endpoint_path_count reached for %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "path_enum", 1, "endpoint_path_count reached for {}", + vertex->to_string(this)); deleteDiversionPathEnd(div); } } @@ -224,12 +224,10 @@ PathEnum::reportDiversionPath(Diversion *div) Path *p = path_end->path(); Path *after_div = div->divPath(); while (p) { - report_->reportLine("path_enum: %s %s%s", - p->to_string(this).c_str(), - delayAsString(p->arrival(), this), - Path::equal(p, after_div, this) ? " <-after diversion" : ""); - if (p != path - && network_->isLatchData(p->pin(this))) + report_->report("path_enum: {} {}{}", p->to_string(this), + delayAsString(p->arrival(), this), + Path::equal(p, after_div, this) ? " <-after diversion" : ""); + if (p != path && network_->isLatchData(p->pin(this))) break; p = p->prevPath(); } @@ -237,54 +235,56 @@ PathEnum::reportDiversionPath(Diversion *div) //////////////////////////////////////////////////////////////// -typedef std::set> VisitedFanins; -typedef std::pair VertexEdge; +using VisitedFanins = std::set>; +using VertexEdge = std::pair; class PathEnumFaninVisitor : public PathVisitor { public: PathEnumFaninVisitor(PathEnd *path_end, - Path *before_div, - bool unique_pins, - bool unique_edges, - PathEnum *path_enum); - virtual VertexVisitor *copy() const override; + Path *before_div, + bool unique_pins, + bool unique_edges, + PathEnum *path_enum); + VertexVisitor *copy() const override; void visitFaninPathsThru(Path *before_div, - Vertex *prev_vertex, - TimingArc *prev_arc); - virtual bool visitFromToPath(const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - Tag *from_tag, - Path *from_path, - const Arrival &from_arrival, - Edge *edge, - TimingArc *arc, - ArcDelay arc_delay, - Vertex *to_vertex, - const RiseFall *to_rf, - Tag *to_tag, - Arrival &to_arrival, - const MinMax *min_max, - const PathAnalysisPt *path_ap) override; - -private: - void makeDivertedPathEnd(Path *after_div, - Edge *div_edge, - TimingArc *div_arc, - // Return values. - PathEnd *&div_end, - Path *&after_div_copy); + Vertex *prev_vertex, + TimingArc *prev_arc); + bool visitFromToPath(const Pin *from_pin, + Vertex *from_vertex, + const RiseFall *from_rf, + Tag *from_tag, + Path *from_path, + const Arrival &from_arrival, + Edge *edge, + TimingArc *arc, + ArcDelay arc_delay, + Vertex *to_vertex, + const RiseFall *to_rf, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max) override; + + void visit(Vertex *) override {} // Not used. + +protected: bool visitEdge(const Pin *from_pin, Vertex *from_vertex, Edge *edge, const Pin *to_pin, Vertex *to_vertex) override; - virtual void visit(Vertex *) override {} // Not used. + +private: + void makeDivertedPathEnd(Path *after_div, + Edge *div_edge, + TimingArc *div_arc, + // Return values. + PathEnd *&div_end, + Path *&after_div_copy); void insertUniqueEdgeDiv(Diversion *div); void reportDiversion(const Edge *edge, const TimingArc *div_arc, - Path *after_div); + Path *after_div); PathEnd *path_end_; Path *before_div_; @@ -294,21 +294,22 @@ class PathEnumFaninVisitor : public PathVisitor Slack path_end_slack_; Tag *before_div_tag_; - int before_div_rf_index_; - PathAPIndex before_div_ap_index_; + size_t before_div_rf_index_; + Scene *scene_; + const MinMax *min_max_; Arrival before_div_arrival_; TimingArc *prev_arc_; Vertex *prev_vertex_; bool crpr_active_; VisitedFanins visited_fanins_; - std::map unique_edge_divs_; + std::map unique_edge_divs_; }; PathEnumFaninVisitor::PathEnumFaninVisitor(PathEnd *path_end, - Path *before_div, - bool unique_pins, - bool unique_edges, - PathEnum *path_enum) : + Path *before_div, + bool unique_pins, + bool unique_edges, + PathEnum *path_enum) : PathVisitor(path_enum), path_end_(path_end), before_div_(before_div), @@ -319,9 +320,10 @@ PathEnumFaninVisitor::PathEnumFaninVisitor(PathEnd *path_end, path_end_slack_(path_end->slack(this)), before_div_tag_(before_div_->tag(this)), before_div_rf_index_(before_div_tag_->rfIndex()), - before_div_ap_index_(before_div_tag_->pathAPIndex()), + scene_(before_div_->scene(this)), + min_max_(before_div_->minMax(this)), before_div_arrival_(before_div_->arrival()), - crpr_active_(crprActive()) + crpr_active_(crprActive(path_end->path()->mode(this))) { } @@ -329,19 +331,20 @@ VertexVisitor * PathEnumFaninVisitor::copy() const { return new PathEnumFaninVisitor(path_end_, before_div_, unique_pins_, - unique_edges_, path_enum_); + unique_edges_, path_enum_); } void PathEnumFaninVisitor::visitFaninPathsThru(Path *before_div, - Vertex *prev_vertex, - TimingArc *prev_arc) + Vertex *prev_vertex, + TimingArc *prev_arc) { before_div_ = before_div; before_div_tag_ = before_div_->tag(this); before_div_arrival_ = before_div_->arrival(); before_div_rf_index_ = before_div_tag_->rfIndex(); - before_div_ap_index_ = before_div_tag_->pathAPIndex(); + scene_ = before_div->scene(this); + min_max_ = before_div->minMax(this); prev_arc_ = prev_arc; prev_vertex_ = prev_vertex; @@ -367,27 +370,27 @@ PathEnumFaninVisitor::visitEdge(const Pin *from_pin, TagGroup *from_tag_group = search_->tagGroup(from_vertex); if (from_tag_group) { TimingArcSet *arc_set = edge->timingArcSet(); - VertexPathIterator from_iter(from_vertex, search_); + VertexPathIterator from_iter(from_vertex, scene_, min_max_, nullptr, search_); while (from_iter.hasNext()) { Path *from_path = from_iter.next(); - // Filter paths by path ap. - PathAnalysisPt *path_ap = from_path->pathAnalysisPt(this); - if (path_ap->index() == before_div_ap_index_) { - const MinMax *min_max = path_ap->pathMinMax(); + const Mode *mode = from_path->mode(this); + const Sdc *sdc = mode->sdc(); + if (pred_->searchFrom(from_vertex, mode) && pred_->searchThru(edge, mode) + && pred_->searchTo(to_vertex, mode) + // Fanin paths are broken by path delay internal pin startpoints. + && !sdc->isPathDelayInternalFromBreak(to_pin)) { const RiseFall *from_rf = from_path->transition(this); TimingArc *arc1, *arc2; arc_set->arcsFrom(from_rf, arc1, arc2); // Filter arcs by to edge. - if (arc1 && arc1->toEdge()->asRiseFall()->index() == before_div_rf_index_) { - if (!visitArc(from_pin, from_vertex, from_rf, from_path, - edge, arc1, to_pin, to_vertex, - min_max, path_ap)) + if (arc1 && arc1->toEdge()->asRiseFall()->index() == before_div_rf_index_) { + if (!visitArc(from_pin, from_vertex, from_rf, from_path, edge, arc1, + to_pin, to_vertex, min_max_, mode)) return false; } if (arc2 && arc2->toEdge()->asRiseFall()->index() == before_div_rf_index_) { - if (!visitArc(from_pin, from_vertex, from_rf, from_path, - edge, arc2, to_pin, to_vertex, - min_max, path_ap)) + if (!visitArc(from_pin, from_vertex, from_rf, from_path, edge, arc2, + to_pin, to_vertex, min_max_, mode)) return false; } } @@ -398,37 +401,36 @@ PathEnumFaninVisitor::visitEdge(const Pin *from_pin, bool PathEnumFaninVisitor::visitFromToPath(const Pin *, - Vertex *from_vertex, - const RiseFall *from_rf, - Tag *, - Path *from_path, + Vertex *from_vertex, + const RiseFall *from_rf, + Tag *, + Path *from_path, const Arrival &, - Edge *edge, - TimingArc *arc, - ArcDelay, - Vertex *to_vertex, - const RiseFall *to_rf, - Tag *to_tag, - Arrival & /* to_arrival */, - const MinMax * /* min_max */, - const PathAnalysisPt *path_ap) + Edge *edge, + TimingArc *arc, + ArcDelay, + Vertex *to_vertex, + const RiseFall *to_rf, + Tag *to_tag, + Arrival & /* to_arrival */, + const MinMax *) { // These paths fanin to before_div_ so we know to_vertex matches. if ((!unique_pins_ || from_vertex != prev_vertex_) - && (!unique_edges_ - || from_vertex != prev_vertex_ + && (!unique_edges_ || from_vertex != prev_vertex_ || from_rf != prev_arc_->fromEdge()->asRiseFall()) && arc != prev_arc_ && Tag::matchNoCrpr(to_tag, before_div_tag_) // Ignore paths that only differ by crpr from same vertex/edge. - && (!crpr_active_ - || visited_fanins_.find({from_vertex, arc}) == visited_fanins_.end())) { - debugPrint(debug_, "path_enum", 3, "visit fanin %s -> %s %s %s", - from_path->to_string(this).c_str(), - to_vertex->to_string(this).c_str(), - to_rf->to_string().c_str(), - delayAsString(search_->deratedDelay(from_vertex, arc, edge, - false,path_ap), this)); + && (!crpr_active_ || !visited_fanins_.contains({from_vertex, arc}))) { + debugPrint(debug_, "path_enum", 3, "visit fanin {} -> {} {} {}", + from_path->to_string(this), + to_vertex->to_string(this), to_rf->shortName(), + delayAsString( + search_->deratedDelay( + from_vertex, arc, edge, false, from_path->minMax(this), + from_path->dcalcAnalysisPtIndex(this), from_path->sdc(this)), + this)); PathEnd *div_end; Path *after_div_copy; // Make the diverted path end to check slack with from_path crpr. @@ -445,21 +447,18 @@ PathEnumFaninVisitor::visitFromToPath(const Pin *, else { if (debug_->check("path_enum", 3)) { bool unique_pins = !(!unique_pins_ || from_vertex != prev_vertex_); - bool unique_edges = !(!unique_edges_ - || from_rf != prev_arc_->fromEdge()->asRiseFall()); + bool unique_edges = + !(!unique_edges_ || from_rf != prev_arc_->fromEdge()->asRiseFall()); bool same_arc = !(arc != prev_arc_); bool tag_march = !Tag::matchNoCrpr(to_tag, before_div_tag_); - bool crpr = !(!crpr_active_ - || visited_fanins_.find({from_vertex, arc}) - == visited_fanins_.end()); - debugPrint(debug_, "path_enum", 3, " pruned %s%s%s%s%s %s %s", + bool crpr = + !(!crpr_active_ + || !visited_fanins_.contains({from_vertex, arc})); + debugPrint(debug_, "path_enum", 3, " pruned {}{}{}{}{} {} {}", unique_pins ? "unique_pins " : "", - unique_edges ? "unique_edges " : "", - same_arc ? "same_arc " : "", - tag_march ? "tag_march " : "", - crpr ? "crpr " : "", - edge->to_string(this).c_str(), - arc->to_string().c_str()); + unique_edges ? "unique_edges " : "", same_arc ? "same_arc " : "", + tag_march ? "tag_march " : "", crpr ? "crpr " : "", + edge->to_string(this), arc->to_string()); } } return true; @@ -486,14 +485,14 @@ PathEnumFaninVisitor::insertUniqueEdgeDiv(Diversion *div) void PathEnumFaninVisitor::makeDivertedPathEnd(Path *after_div, Edge *div_edge, - TimingArc *div_arc, - // Return values. - PathEnd *&div_end, - Path *&after_div_copy) + TimingArc *div_arc, + // Return values. + PathEnd *&div_end, + Path *&after_div_copy) { Path *div_path; - path_enum_->makeDivertedPath(path_end_->path(), before_div_, after_div, - div_edge, div_arc, div_path, after_div_copy); + path_enum_->makeDivertedPath(path_end_->path(), before_div_, after_div, div_edge, + div_arc, div_path, after_div_copy); div_end = path_end_->copy(); div_end->setPath(div_path); } @@ -501,28 +500,25 @@ PathEnumFaninVisitor::makeDivertedPathEnd(Path *after_div, void PathEnumFaninVisitor::reportDiversion(const Edge *div_edge, const TimingArc *div_arc, - Path *after_div) -{ + Path *after_div) +{ if (debug_->check("path_enum", 3)) { Path *path = path_end_->path(); - const PathAnalysisPt *path_ap = path->pathAnalysisPt(this); Arrival path_delay = path_enum_->cmp_slack_ ? path_end_->slack(this) : path_end_->dataArrivalTime(this); - Arrival div_delay = path_delay - path_enum_->divSlack(before_div_, - after_div, div_edge, - div_arc, path_ap); + Arrival div_delay = delayDiff(path_delay, + path_enum_->divSlack(before_div_, + after_div, div_edge, + div_arc), + this); Path *div_prev = before_div_->prevPath(); - report_->reportLine("path_enum: diversion %s %s %s -> %s", - path->to_string(this).c_str(), - path_enum_->cmp_slack_ ? "slack" : "delay", - delayAsString(path_delay, this), - delayAsString(div_delay, this)); - report_->reportLine("path_enum: from %s -> %s", - div_prev->to_string(this).c_str(), - before_div_->to_string(this).c_str()); - report_->reportLine("path_enum: to %s", - after_div->to_string(this).c_str()); + report_->report("path_enum: diversion {} {} {} -> {}", path->to_string(this), + path_enum_->cmp_slack_ ? "slack" : "delay", + delayAsString(path_delay, this), delayAsString(div_delay, this)); + report_->report("path_enum: from {} -> {}", div_prev->to_string(this), + before_div_->to_string(this)); + report_->report("path_enum: to {}", after_div->to_string(this)); } } @@ -548,8 +544,8 @@ PathEnum::pruneDiversionQueue() Diversion *div = div_queue_.top(); Vertex *vertex = div->pathEnd()->vertex(this); if (end_count < group_path_count_ - && ((unique_pins_ && path_counts[vertex] == 0) - || (!unique_pins_ && path_counts[vertex] < endpoint_path_count_))) { + && ((unique_pins_ && path_counts[vertex] == 0) + || (!unique_pins_ && path_counts[vertex] < endpoint_path_count_))) { divs.push_back(div); path_counts[vertex]++; end_count++; @@ -566,10 +562,9 @@ PathEnum::pruneDiversionQueue() Arrival PathEnum::divSlack(Path *before_div, - Path *after_div, + Path *after_div, const Edge *div_edge, - const TimingArc *div_arc, - const PathAnalysisPt *path_ap) + const TimingArc *div_arc) { Arrival before_div_arrival = before_div->arrival(); if (div_edge) { @@ -577,16 +572,19 @@ PathEnum::divSlack(Path *before_div, Arrival div_arrival; ArcDelay div_delay; Tag *q_tag; - latches_->latchOutArrival(after_div, div_arc, div_edge, path_ap, - q_tag, div_delay, div_arrival); - return div_arrival - before_div_arrival; + latches_->latchOutArrival(after_div, div_arc, div_edge, + q_tag, div_delay, div_arrival); + return delayDiff(div_arrival, before_div_arrival, this); } else { + DcalcAPIndex dcalc_ap = before_div->dcalcAnalysisPtIndex(this); ArcDelay div_delay = search_->deratedDelay(div_edge->from(graph_), - div_arc, div_edge, - false, path_ap); - Arrival div_arrival = search_->clkPathArrival(after_div) + div_delay; - return div_arrival - before_div_arrival; + div_arc, div_edge, + false, before_div->minMax(this), + dcalc_ap, + before_div->sdc(this)); + Arrival div_arrival = delaySum(search_->clkPathArrival(after_div), div_delay, this); + return delayDiff(div_arrival, before_div_arrival, this); } } else { @@ -599,13 +597,13 @@ PathEnum::divSlack(Path *before_div, // starting at "before" to the beginning of the path. void PathEnum::makeDiversions(PathEnd *path_end, - Path *before) + Path *before) { Path *path = before; Path *prev_path = path->prevPath(); TimingArc *prev_arc = path->prevArc(this); - PathEnumFaninVisitor fanin_visitor(path_end, path, unique_pins_, - unique_edges_, this); + PathEnumFaninVisitor fanin_visitor(path_end, path, unique_pins_, unique_edges_, + this); while (prev_path) { // Fanin visitor does all the work. // While visiting the fanins the fanin_visitor finds the @@ -614,8 +612,7 @@ PathEnum::makeDiversions(PathEnd *path_end, // Do not enumerate beyond latch D to Q edges. // This breaks latch loop paths. const TimingRole *prev_role = prev_arc->role(); - if (prev_role == TimingRole::latchDtoQ() - || prev_role == TimingRole::regClkToQ()) + if (prev_role == TimingRole::latchDtoQ() || prev_role == TimingRole::regClkToQ()) break; path = prev_path; prev_path = path->prevPath(); @@ -625,13 +622,13 @@ PathEnum::makeDiversions(PathEnd *path_end, void PathEnum::makeDivertedPath(Path *path, - Path *before_div, - Path *after_div, + Path *before_div, + Path *after_div, Edge *div_edge, - TimingArc *div_arc, - // Returned values. - Path *&div_path, - Path *&after_div_copy) + TimingArc *div_arc, + // Returned values. + Path *&div_path, + Path *&after_div_copy) { div_path = nullptr; after_div_copy = nullptr; @@ -643,14 +640,11 @@ PathEnum::makeDivertedPath(Path *path, Path *prev_copy = nullptr; while (p) { // prev_path made in next pass. - Path *copy = new Path(p->vertex(this), - p->tag(this), - p->arrival(), - // Replaced on next pass. - p->prevPath(), - p->prevEdge(this), - p->prevArc(this), - true, this); + Path *copy = + new Path(p->vertex(this), p->tag(this), p->arrival(), + // Replaced on next pass. + p->prevPath(), p->prevEdge(this), p->prevArc(this), true, this); + search_->saveEnumPath(copy); if (prev_copy) prev_copy->setPrevPath(copy); copies.push_back(copy); @@ -659,8 +653,7 @@ PathEnum::makeDivertedPath(Path *path, after_div_copy = copy; if (first) div_path = copy; - else if (found_div - && network_->isLatchData(p->pin(this))) + else if (found_div && network_->isLatchData(p->pin(this))) break; if (p == before_div) { // Replaced on next pass. @@ -683,7 +676,7 @@ PathEnum::makeDivertedPath(Path *path, void PathEnum::updatePathHeadDelays(PathSeq &paths, - Path *after_div) + Path *after_div) { Tag *prev_tag = after_div->tag(this); const ClkInfo *prev_clk_info = prev_tag->clkInfo(); @@ -696,50 +689,47 @@ PathEnum::updatePathHeadDelays(PathSeq &paths, Edge *edge = path->prevEdge(this); if (edge) { Arrival arrival; - PathAnalysisPt *path_ap = path->pathAnalysisPt(this); const MinMax *min_max = path->minMax(this); - if (i == path_idx_max - && edge->role()->isLatchDtoQ() - && min_max == MinMax::max()) { - ArcDelay arc_delay; - Tag *q_tag; - latches_->latchOutArrival(after_div, arc, edge, path_ap, - q_tag, arc_delay, arrival); - path->setArrival(arrival); - path->setTag(q_tag); - prev_clk_info = q_tag->clkInfo(); + if (i == path_idx_max && edge->role()->isLatchDtoQ() + && min_max == MinMax::max()) { + ArcDelay arc_delay; + Tag *q_tag; + latches_->latchOutArrival(after_div, arc, edge, q_tag, arc_delay, arrival); + path->setArrival(arrival); + path->setTag(q_tag); + prev_clk_info = q_tag->clkInfo(); } else { - ArcDelay arc_delay = search_->deratedDelay(edge->from(graph_), - arc, edge, false, path_ap); - arrival = prev_arrival + arc_delay; - path->setArrival(arrival); - const Tag *tag = path->tag(this); - const ClkInfo *clk_info = tag->clkInfo(); - if (crprActive() - && clk_info != prev_clk_info - // D->Q paths use the EN->Q clk info so no need to update. - && arc->role() != TimingRole::latchDtoQ()) { - // When crpr is enabled the diverion may be from another crpr clk pin, - // so update the tags to use the corresponding ClkInfo. - Tag *updated_tag = search_->findTag(path->transition(this), - path_ap, - prev_clk_info, - tag->isClock(), - tag->inputDelay(), - tag->isSegmentStart(), - tag->states(), false, nullptr); - path->setTag(updated_tag); - } - debugPrint(debug_, "path_enum", 5, "update arrival %s %s %s -> %s", - path->vertex(this)->to_string(this).c_str(), - path->tag(this)->to_string(this).c_str(), - delayAsString(path->arrival(), this), - delayAsString(arrival, this)); + ArcDelay arc_delay = search_->deratedDelay(edge->from(graph_), + arc, edge, false, + path->minMax(this), + path->dcalcAnalysisPtIndex(this), + path->sdc(this)); + arrival = delaySum(prev_arrival, arc_delay, this); + path->setArrival(arrival); + const Tag *tag = path->tag(this); + const ClkInfo *clk_info = tag->clkInfo(); + if (crprActive(path->mode(this)) + && clk_info != prev_clk_info + // D->Q paths use the EN->Q clk info so no need to update. + && arc->role() != TimingRole::latchDtoQ()) { + // When crpr is enabled the diverion may be from another crpr clk pin, + // so update the tags to use the corresponding ClkInfo. + Tag *updated_tag = search_->findTag( + path->scene(this), path->transition(this), path->minMax(this), + prev_clk_info, tag->isClock(), tag->inputDelay(), + tag->isSegmentStart(), tag->states(), false, nullptr); + path->setTag(updated_tag); + } + debugPrint(debug_, "path_enum", 5, "update arrival {} {} {} -> {}", + path->vertex(this)->to_string(this), + path->tag(this)->to_string(this), + delayAsString(path->arrival(), this), + delayAsString(arrival, this)); } prev_arrival = arrival; } } } -} // namespace +} // namespace sta diff --git a/search/PathEnum.hh b/search/PathEnum.hh index 1f13f1e9f..bcf856fd6 100644 --- a/search/PathEnum.hh +++ b/search/PathEnum.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,13 +24,16 @@ #pragma once +#include #include +#include +#include "Delay.hh" #include "Iterator.hh" -#include "Vector.hh" -#include "StaState.hh" -#include "SearchClass.hh" +#include "LibertyClass.hh" #include "Path.hh" +#include "SearchClass.hh" +#include "StaState.hh" namespace sta { @@ -38,9 +41,9 @@ class Diversion; class PathEnumFaninVisitor; class DiversionGreater; -typedef Vector DiversionSeq; -typedef std::priority_queue DiversionQueue; +using DiversionSeq = std::vector; +using DiversionQueue = std::priority_queue; class DiversionGreater { @@ -48,7 +51,7 @@ public: DiversionGreater(); DiversionGreater(const StaState *sta); bool operator()(Diversion *div1, - Diversion *div2) const; + Diversion *div2) const; private: const StaState *sta_; @@ -59,36 +62,35 @@ class PathEnum : public Iterator, StaState { public: PathEnum(size_t group_path_count, - size_t endpoint_path_count, - bool unique_pins, - bool unique_edges, - bool cmp_slack, - const StaState *sta); + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + bool cmp_slack, + const StaState *sta); // Insert path ends that are enumerated in slack/arrival order. void insert(PathEnd *path_end); - virtual ~PathEnum(); - virtual bool hasNext(); - virtual PathEnd *next(); + ~PathEnum() override; + bool hasNext() override; + PathEnd *next() override; private: void makeDiversions(PathEnd *path_end, - Path *before); + Path *before); void insert(Diversion *div); void makeDivertedPath(Path *path, - Path *before_div, - Path *after_div, + Path *before_div, + Path *after_div, Edge *div_edge, - TimingArc *div_arc, - // Returned values. - Path *&div_path, - Path *&after_div_copy); + TimingArc *div_arc, + // Returned values. + Path *&div_path, + Path *&after_div_copy); void updatePathHeadDelays(PathSeq &path, - Path *after_div); - Arrival divSlack(Path *path, - Path *after_div, + Path *after_div); + Arrival divSlack(Path *before_div, + Path *after_div, const Edge *div_edge, - const TimingArc *div_arc, - const PathAnalysisPt *path_ap); + const TimingArc *div_arc); void reportDiversionPath(Diversion *div); void pruneDiversionQueue(); void findNext(); @@ -99,13 +101,13 @@ private: bool unique_pins_; bool unique_edges_; DiversionQueue div_queue_; - int div_count_; + int div_count_{0}; // Number of paths returned for each endpoint (limit to endpoint_path_count). VertexPathCountMap path_counts_; - bool inserts_pruned_; - PathEnd *next_; + bool inserts_pruned_{false}; + PathEnd *next_{nullptr}; friend class PathEnumFaninVisitor; }; -} // namespace +} // namespace sta diff --git a/search/PathExpanded.cc b/search/PathExpanded.cc index 170c280a7..bfc082bcc 100644 --- a/search/PathExpanded.cc +++ b/search/PathExpanded.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,14 +24,15 @@ #include "PathExpanded.hh" -#include "TimingRole.hh" -#include "PortDirection.hh" -#include "Network.hh" #include "Clock.hh" -#include "Search.hh" -#include "Path.hh" -#include "Latches.hh" #include "Genclks.hh" +#include "Latches.hh" +#include "Mode.hh" +#include "Network.hh" +#include "Path.hh" +#include "PortDirection.hh" +#include "Search.hh" +#include "TimingRole.hh" namespace sta { @@ -41,16 +42,16 @@ PathExpanded::PathExpanded(const StaState *sta) : } PathExpanded::PathExpanded(const Path *path, - // Expand generated clk source paths. - bool expand_genclks, - const StaState *sta) : + // Expand generated clk source paths. + bool expand_genclks, + const StaState *sta) : sta_(sta) { expand(path, expand_genclks); } PathExpanded::PathExpanded(const Path *path, - const StaState *sta) : + const StaState *sta) : sta_(sta) { expand(path, false); @@ -58,9 +59,10 @@ PathExpanded::PathExpanded(const Path *path, void PathExpanded::expand(const Path *path, - bool expand_genclks) + bool expand_genclks) { const Latches *latches = sta_->latches(); + const Mode *mode = path->mode(sta_); // Push the paths from the end into an array of Paths. const Path *p = path; const Path *last_path = nullptr; @@ -71,25 +73,25 @@ PathExpanded::expand(const Path *path, if (!found_start) { if (prev_path) { const TimingArc *prev_arc = p->prevArc(sta_); - const TimingRole *prev_role = prev_arc->role(); - if (prev_role == TimingRole::regClkToQ() - || prev_role == TimingRole::latchEnToQ()) { - start_index_ = i; - found_start = true; - } - else if (prev_role == TimingRole::latchDtoQ()) { - const Edge *prev_edge = p->prevEdge(sta_); - if (prev_edge && latches->isLatchDtoQ(prev_edge)) { - start_index_ = i; - found_start = true; - - paths_.push_back(p); - // Push latch D path. - paths_.push_back(prev_path); - // This breaks latch loop paths. - break; - } - } + const TimingRole *prev_role = prev_arc->role(); + if (prev_role == TimingRole::regClkToQ() + || prev_role == TimingRole::latchEnToQ()) { + start_index_ = i; + found_start = true; + } + else if (prev_role->isLatchDtoQ()) { + const Edge *prev_edge = p->prevEdge(sta_); + if (prev_edge && latches->isLatchDtoQ(prev_edge, mode)) { + start_index_ = i; + found_start = true; + + paths_.push_back(p); + // Push latch D path. + paths_.push_back(prev_path); + // This breaks latch loop paths. + break; + } + } } } paths_.push_back(p); @@ -110,20 +112,21 @@ PathExpanded::expandGenclk(const Path *clk_path) if (clk_path) { const Clock *src_clk = clk_path->clock(sta_); if (src_clk && src_clk->isGenerated()) { - const Path *src_path = sta_->search()->genclks()->srcPath(clk_path); + const Mode *mode = clk_path->mode(sta_); + const Path *src_path = mode->genclks()->srcPath(clk_path); if (src_path) { - // The head of the genclk src path is already in paths_, - // so skip past it. - Path *prev_path = src_path->prevPath(); - Path *p = prev_path; - Path *last_path = nullptr; - while (p) { - prev_path = p->prevPath(); - paths_.push_back(p); - last_path = p; - p = prev_path; - } - expandGenclk(last_path); + // The head of the genclk src path is already in paths_, + // so skip past it. + Path *prev_path = src_path->prevPath(); + Path *p = prev_path; + Path *last_path = nullptr; + while (p) { + prev_path = p->prevPath(); + paths_.push_back(p); + last_path = p; + p = prev_path; + } + expandGenclk(last_path); } } } @@ -188,14 +191,15 @@ PathExpanded::clkPath() const const TimingArc *prev_arc = startPrevArc(); if (start && prev_arc) { const TimingRole *role = prev_arc->role(); - if (role == TimingRole::latchDtoQ()) { + if (role->isLatchDtoQ()) { Edge *prev_edge = start->prevEdge(sta_); - if (prev_edge && latches->isLatchDtoQ(prev_edge)) { - return latches->latchEnablePath(start, prev_edge); + const Mode *mode = start->mode(sta_); + if (prev_edge && latches->isLatchDtoQ(prev_edge, mode)) { + return latches->latchEnablePath(start, prev_edge); } } else if (role == TimingRole::regClkToQ() - || role == TimingRole::latchEnToQ()) { + || role == TimingRole::latchEnToQ()) { const Path *start_prev = startPrevPath(); if (start_prev) return start_prev; @@ -208,9 +212,9 @@ PathExpanded::clkPath() const void PathExpanded::latchPaths(// Return values. - const Path *&d_path, - const Path *&q_path, - Edge *&d_q_edge) const + const Path *&d_path, + const Path *&q_path, + Edge *&d_q_edge) const { d_path = nullptr; q_path = nullptr; @@ -221,9 +225,10 @@ PathExpanded::latchPaths(// Return values. && prev_arc && prev_arc->role() == TimingRole::latchDtoQ()) { Edge *prev_edge = start->prevEdge(sta_); + const Mode *mode = start->mode(sta_); // This breaks latch loop paths. if (prev_edge - && sta_->latches()->isLatchDtoQ(prev_edge)) { + && sta_->latches()->isLatchDtoQ(prev_edge, mode)) { d_path = startPrevPath(); q_path = start; d_q_edge = prev_edge; @@ -231,4 +236,4 @@ PathExpanded::latchPaths(// Return values. } } -} // namespace +} // namespace sta diff --git a/search/PathGroup.cc b/search/PathGroup.cc index 0f8041d39..86687a830 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,67 +26,72 @@ #include #include +#include +#include +#include -#include "Stats.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" -#include "Mutex.hh" -#include "Fuzzy.hh" -#include "MinMax.hh" #include "DispatchQueue.hh" #include "ExceptionPath.hh" -#include "Sdc.hh" +#include "Fuzzy.hh" #include "Graph.hh" +#include "MinMax.hh" +#include "Mode.hh" +#include "Mutex.hh" #include "PathEnd.hh" -#include "PathAnalysisPt.hh" -#include "Tag.hh" -#include "Corner.hh" +#include "PathEnum.hh" +#include "Scene.hh" +#include "Sdc.hh" #include "Search.hh" +#include "Stats.hh" +#include "StringUtil.hh" +#include "Tag.hh" #include "VisitPathEnds.hh" -#include "PathEnum.hh" namespace sta { -size_t PathGroup::group_path_count_max = std::numeric_limits::max(); +size_t PathGroup::group_path_count_max = std::numeric_limits::max(); PathGroup * -PathGroup::makePathGroupSlack(const char *name, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - const StaState *sta) +PathGroup::makePathGroupSlack(std::string_view name, + size_t group_path_count, + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + const StaState *sta) { return new PathGroup(name, group_path_count, endpoint_path_count, - unique_pins, unique_edges, slack_min, slack_max, - true, MinMax::min(), sta); + unique_pins, unique_edges, slack_min, slack_max, + true, MinMax::min(), sta); } PathGroup * -PathGroup::makePathGroupArrival(const char *name, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - const MinMax *min_max, - const StaState *sta) +PathGroup::makePathGroupArrival(std::string_view name, + size_t group_path_count, + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + const MinMax *min_max, + const StaState *sta) { return new PathGroup(name, group_path_count, endpoint_path_count, - unique_pins, unique_edges, 0.0, 0.0, - false, min_max, sta); + unique_pins, unique_edges, 0.0, 0.0, + false, min_max, sta); } -PathGroup::PathGroup(const char *name, - size_t group_path_count, - size_t endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - bool cmp_slack, - const MinMax *min_max, - const StaState *sta) : +PathGroup::PathGroup(std::string_view name, + size_t group_path_count, + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + bool cmp_slack, + const MinMax *min_max, + const StaState *sta) : name_(name), group_path_count_(group_path_count), endpoint_path_count_(endpoint_path_count), @@ -95,7 +100,7 @@ PathGroup::PathGroup(const char *name, slack_min_(slack_min), slack_max_(slack_max), min_max_(min_max), - compare_slack_(cmp_slack), + cmp_slack_(cmp_slack), threshold_(min_max->initValue()), sta_(sta) { @@ -103,7 +108,7 @@ PathGroup::PathGroup(const char *name, PathGroup::~PathGroup() { - path_ends_.deleteContents(); + deleteContents(path_ends_); } bool @@ -114,18 +119,18 @@ PathGroup::saveable(PathEnd *path_end) LockGuard lock(lock_); threshold = threshold_; } - if (compare_slack_) { + if (cmp_slack_) { // Crpr increases the slack, so check the slack // without crpr first because it is expensive to find. - Slack slack = path_end->slackNoCrpr(sta_); - if (!delayIsInitValue(slack, min_max_) - && delayLessEqual(slack, threshold, sta_) - && delayLessEqual(slack, slack_max_, sta_)) { + Slack slack_no_crpr = path_end->slackNoCrpr(sta_); + if (!delayIsInitValue(slack_no_crpr, min_max_) + && delayLessEqual(slack_no_crpr, threshold, sta_) + && delayLessEqual(slack_no_crpr, slack_max_, sta_)) { // Now check with crpr. - slack = path_end->slack(sta_); + Slack slack = path_end->slack(sta_); return delayLessEqual(slack, threshold, sta_) - && delayLessEqual(slack, slack_max_, sta_) - && delayGreaterEqual(slack, slack_min_, sta_); + && delayLessEqual(slack, slack_max_, sta_) + && delayGreaterEqual(slack, slack_min_, sta_); } } else { @@ -144,15 +149,15 @@ PathGroup::saveable(PathEnd *path_end) bool PathGroup::enumMinSlackUnderMin(PathEnd *path_end) { - if (compare_slack_ + if (cmp_slack_ && endpoint_path_count_ > 1 && slack_min_ > -INF) { const Path *path = path_end->path(); - PathAnalysisPt *other_ap = path->pathAnalysisPt(sta_)->tgtClkAnalysisPt(); const Tag *tag = path->tag(sta_); VertexPathIterator other_iter(path->vertex(sta_), - path->transition(sta_), - other_ap, sta_); + path->scene(sta_), + path->tgtClkMinMax(sta_), + path->transition(sta_), sta_); while (other_iter.hasNext()) { Path *other = other_iter.next(); if (Tag::matchCrpr(other->tag(sta_), tag)) { @@ -174,7 +179,7 @@ PathGroup::insert(PathEnd *path_end) { LockGuard lock(lock_); path_ends_.push_back(path_end); - path_end->setPathGroup(this); + path_end->setPathGroup(this); if (group_path_count_ != group_path_count_max && path_ends_.size() > group_path_count_ * 2) prune(); @@ -186,13 +191,12 @@ PathGroup::prune() sort(); VertexPathCountMap path_counts; size_t end_count = 0; - for (unsigned i = 0; i < path_ends_.size(); i++) { - PathEnd *path_end = path_ends_[i]; + for (PathEnd *path_end : path_ends_) { Vertex *vertex = path_end->vertex(sta_); // Squish up to endpoint_path_count path ends per vertex // up to the front of path_ends_. if (end_count < group_path_count_ - && path_counts[vertex] < endpoint_path_count_) { + && path_counts[vertex] < endpoint_path_count_) { path_ends_[end_count++] = path_end; path_counts[vertex]++; } @@ -204,7 +208,7 @@ PathGroup::prune() // Set a threshold to the bottom of the sorted list that future // inserts need to beat. PathEnd *last_end = path_ends_[end_count - 1]; - if (compare_slack_) + if (cmp_slack_) threshold_ = delayAsFloat(last_end->slack(sta_)); else threshold_ = delayAsFloat(last_end->dataArrivalTime(sta_)); @@ -218,13 +222,6 @@ PathGroup::pushEnds(PathEndSeq &path_ends) path_ends.push_back(path_end); } -PathGroupIterator * -PathGroup::iterator() -{ - ensureSortedMaxPaths(); - return new PathGroupIterator(path_ends_); -} - void PathGroup::ensureSortedMaxPaths() { @@ -237,40 +234,35 @@ PathGroup::ensureSortedMaxPaths() void PathGroup::sort() { - sta::sort(path_ends_, PathEndLess(sta_)); + sta::sort(path_ends_, PathEndLess(cmp_slack_, sta_)); } void PathGroup::clear() { - LockGuard lock(lock_); threshold_ = min_max_->initValue(); + LockGuard lock(lock_); path_ends_.clear(); } //////////////////////////////////////////////////////////////// - -const char *PathGroups::path_delay_group_name_ = "path_delay"; -const char *PathGroups::gated_clk_group_name_ = "gated_clock"; -const char *PathGroups::async_group_name_ = "asynchronous"; -const char *PathGroups::unconstrained_group_name_ = "unconstrained"; - -PathGroups::PathGroups(int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - PathGroupNameSet *group_names, - bool setup, - bool hold, - bool recovery, - bool removal, - bool clk_gating_setup, - bool clk_gating_hold, - bool unconstrained, - const StaState *sta) : - StaState(sta), +PathGroups::PathGroups(size_t group_path_count, + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + StringSeq &group_names, + bool setup, + bool hold, + bool recovery, + bool removal, + bool clk_gating_setup, + bool clk_gating_hold, + bool unconstrained, + const Mode *mode) : + StaState(mode->sta()), + mode_(mode), group_path_count_(group_path_count), endpoint_path_count_(endpoint_path_count), unique_pins_(unique_pins), @@ -278,102 +270,107 @@ PathGroups::PathGroups(int group_path_count, slack_min_(slack_min), slack_max_(slack_max) { + StringSet groups; + for (std::string &group_name : group_names) + groups.insert(group_name); + makeGroups(group_path_count, endpoint_path_count, unique_pins, unique_edges, - slack_min, slack_max, group_names, - setup, recovery, clk_gating_setup, unconstrained, - MinMax::max()); + slack_min, slack_max, groups, + setup, recovery, clk_gating_setup, unconstrained, + MinMax::max()); makeGroups(group_path_count, endpoint_path_count, unique_pins, unique_edges, - slack_min, slack_max, group_names, - hold, removal, clk_gating_hold, unconstrained, - MinMax::min()); + slack_min, slack_max, groups, + hold, removal, clk_gating_hold, unconstrained, + MinMax::min()); } void -PathGroups::makeGroups(int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - PathGroupNameSet *group_names, - bool setup_hold, - bool async, - bool gated_clk, - bool unconstrained, - const MinMax *min_max) +PathGroups::makeGroups(size_t group_path_count, + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + StringSet &group_names, + bool setup_hold, + bool async, + bool gated_clk, + bool unconstrained, + const MinMax *min_max) { - int mm_index = min_max->index(); + size_t mm_index = min_max->index(); if (setup_hold) { - for (const auto [name, group] : sdc_->groupPaths()) { + const Sdc *sdc = mode_->sdc(); + for (const auto& [name, group] : sdc->groupPaths()) { if (reportGroup(name, group_names)) { - PathGroup *group = PathGroup::makePathGroupSlack(name, - group_path_count, - endpoint_path_count, - unique_pins, - unique_edges, - slack_min, slack_max, - this); - named_map_[mm_index][name] = group; + PathGroup *group = PathGroup::makePathGroupSlack(name, + group_path_count, + endpoint_path_count, + unique_pins, + unique_edges, + slack_min, slack_max, + this); + named_map_[mm_index][name] = group; } } - for (auto clk : sdc_->clks()) { - const char *clk_name = clk->name(); + for (Clock *clk : sdc->clocks()) { + const std::string &clk_name = clk->name(); if (reportGroup(clk_name, group_names)) { - PathGroup *group = PathGroup::makePathGroupSlack(clk_name, - group_path_count, - endpoint_path_count, - unique_pins, - unique_edges, - slack_min, slack_max, - this); - clk_map_[mm_index][clk] = group; + PathGroup *group = PathGroup::makePathGroupSlack(clk_name, + group_path_count, + endpoint_path_count, + unique_pins, + unique_edges, + slack_min, slack_max, + this); + clk_map_[mm_index][clk] = group; } } } if (setup_hold - && reportGroup(path_delay_group_name_, group_names)) + && reportGroup(std::string(path_delay_group_name_), group_names)) path_delay_[mm_index] = PathGroup::makePathGroupSlack(path_delay_group_name_, - group_path_count, - endpoint_path_count, - unique_pins, - unique_edges, - slack_min, slack_max, - this); + group_path_count, + endpoint_path_count, + unique_pins, + unique_edges, + slack_min, slack_max, + this); else path_delay_[mm_index] = nullptr; if (gated_clk - && reportGroup(gated_clk_group_name_, group_names)) + && reportGroup(std::string(gated_clk_group_name_), group_names)) gated_clk_[mm_index] = PathGroup::makePathGroupSlack(gated_clk_group_name_, - group_path_count, - endpoint_path_count, - unique_pins, - unique_edges, - slack_min, slack_max, - this); + group_path_count, + endpoint_path_count, + unique_pins, + unique_edges, + slack_min, slack_max, + this); else gated_clk_[mm_index] = nullptr; if (async - && reportGroup(async_group_name_, group_names)) + && reportGroup(std::string(async_group_name_), group_names)) async_[mm_index] = PathGroup::makePathGroupSlack(async_group_name_, - group_path_count, - endpoint_path_count, - unique_pins, - unique_edges, - slack_min, slack_max, - this); + group_path_count, + endpoint_path_count, + unique_pins, + unique_edges, + slack_min, slack_max, + this); else async_[mm_index] = nullptr; if (unconstrained - && reportGroup(unconstrained_group_name_, group_names)) + && reportGroup(std::string(unconstrained_group_name_), group_names)) unconstrained_[mm_index] = PathGroup::makePathGroupArrival(unconstrained_group_name_, - group_path_count, endpoint_path_count, - unique_pins, unique_edges, min_max, this); + group_path_count, endpoint_path_count, + unique_pins, unique_edges, min_max, this); else unconstrained_[mm_index] = nullptr; } @@ -381,8 +378,8 @@ PathGroups::makeGroups(int group_path_count, PathGroups::~PathGroups() { for (auto mm_index : MinMax::rangeIndex()) { - named_map_[mm_index].deleteContents(); - clk_map_[mm_index].deleteContents(); + deleteContents(named_map_[mm_index]); + deleteContents(clk_map_[mm_index]); delete path_delay_[mm_index]; delete gated_clk_[mm_index]; delete async_[mm_index]; @@ -391,26 +388,33 @@ PathGroups::~PathGroups() } PathGroup * -PathGroups::findPathGroup(const char *name, - const MinMax *min_max) const +PathGroups::findPathGroup(const std::string &name, + const MinMax *min_max) const { - return named_map_[min_max->index()].findKey(name); + auto itr = named_map_[min_max->index()].find(name); + if (itr != named_map_[min_max->index()].end()) + return itr->second; + else + return nullptr; } PathGroup * PathGroups::findPathGroup(const Clock *clock, - const MinMax *min_max) const + const MinMax *min_max) const { - return clk_map_[min_max->index()].findKey(clock); + auto itr = clk_map_[min_max->index()].find(clock); + if (itr != clk_map_[min_max->index()].end()) + return itr->second; + else + return nullptr; } bool -PathGroups::reportGroup(const char *group_name, - PathGroupNameSet *group_names) const +PathGroups::reportGroup(const std::string &group_name, + StringSet &group_names) const { - return group_names == nullptr - || group_names->empty() - || group_names->hasKey(group_name); + return group_names.empty() + || group_names.contains(group_name); } PathGroupSeq @@ -420,19 +424,21 @@ PathGroups::pathGroups(const PathEnd *path_end) const PathGroup *path_group = nullptr; ExceptionPathSeq group_paths = search_->groupPathsTo(path_end); const MinMax *min_max = path_end->minMax(this); - int mm_index = min_max->index(); + size_t mm_index = min_max->index(); if (path_end->isUnconstrained()) path_group = unconstrained_[mm_index]; // GroupPaths have precedence. else if (!group_paths.empty()) { for (ExceptionPath *group_path : group_paths) { - if (group_path->isDefault()) - path_groups.push_back(path_delay_[mm_index]); + if (group_path->isDefault()) { + if (path_delay_[mm_index]) + path_groups.push_back(path_delay_[mm_index]); + } else { - const char *group_name = group_path->name(); - PathGroup *group = findPathGroup(group_name, min_max); - if (group) - path_groups.push_back(group); + std::string group_name(group_path->name()); + PathGroup *group = findPathGroup(group_name, min_max); + if (group) + path_groups.push_back(group); } } } @@ -440,7 +446,7 @@ PathGroups::pathGroups(const PathEnd *path_end) const const TimingRole *check_role = path_end->checkRole(this); const Clock *tgt_clk = path_end->targetClk(this); if (check_role == TimingRole::removal() - || check_role == TimingRole::recovery()) + || check_role == TimingRole::recovery()) path_group = async_[mm_index]; else path_group = findPathGroup(tgt_clk, min_max); @@ -456,7 +462,7 @@ PathGroups::pathGroups(const PathEnd *path_end) const PathDelay *path_delay = path_end->pathDelay(); const Clock *tgt_clk = path_end->targetClk(this); if (tgt_clk - && !path_delay->ignoreClkLatency()) + && !path_delay->ignoreClkLatency()) path_group = findPathGroup(tgt_clk, min_max); else path_group = path_delay_[mm_index]; @@ -467,12 +473,12 @@ PathGroups::pathGroups(const PathEnd *path_end) const } // Mirrors PathGroups::pathGroup. -StdStringSeq +StringSeq PathGroups::pathGroupNames(const PathEnd *path_end, - const StaState *sta) + const StaState *sta) { - StdStringSeq group_names; - const char *group_name = nullptr; + StringSeq group_names; + std::string group_name; const Search *search = sta->search(); ExceptionPathSeq group_paths = search->groupPathsTo(path_end); if (path_end->isUnconstrained()) @@ -481,22 +487,22 @@ PathGroups::pathGroupNames(const PathEnd *path_end, // GroupPaths have precedence. for (ExceptionPath *group_path : group_paths) { if (group_path->isDefault()) - group_names.push_back(path_delay_group_name_); + group_names.emplace_back(path_delay_group_name_); else - group_names.push_back(group_path->name()); + group_names.emplace_back(group_path->name()); } } else if (path_end->isCheck() || path_end->isLatchCheck()) { const TimingRole *check_role = path_end->checkRole(sta); const Clock *tgt_clk = path_end->targetClk(sta); if (check_role == TimingRole::removal() - || check_role == TimingRole::recovery()) + || check_role == TimingRole::recovery()) group_name = async_group_name_; else group_name = tgt_clk->name(); } else if (path_end->isOutputDelay() - || path_end->isDataCheck()) { + || path_end->isDataCheck()) { const Clock *tgt_clk = path_end->targetClk(sta); if (tgt_clk) group_name = tgt_clk->name(); @@ -509,26 +515,40 @@ PathGroups::pathGroupNames(const PathEnd *path_end, PathDelay *path_delay = path_end->pathDelay(); const Clock *tgt_clk = path_end->targetClk(sta); if (tgt_clk - && !path_delay->ignoreClkLatency()) + && !path_delay->ignoreClkLatency()) group_name = tgt_clk->name(); else group_name = path_delay_group_name_; } - if (group_name) + if (!group_name.empty()) group_names.push_back(group_name); return group_names; } +GroupPath * +PathGroups::groupPathTo(const PathEnd *path_end, + const StaState *sta) +{ + const Path *path = path_end->path(); + const Pin *pin = path->pin(sta); + ExceptionPath *exception = + sta->search()->exceptionTo(ExceptionPathType::group_path, path, + pin, path->transition(sta), + path_end->targetClkEdge(sta), + path->minMax(sta), false, false, + path->sdc(sta)); + return dynamic_cast(exception); +} + void -PathGroups::pushGroupPathEnds(PathEndSeq &path_ends) +PathGroups::pushEnds(PathEndSeq &path_ends) { - for (auto min_max : MinMax::range()) { - int mm_index = min_max->index(); - for (auto name_group : sdc_->groupPaths()) { - const char *name = name_group.first; - PathGroup *path_group = findPathGroup(name, min_max); + for (const MinMax *min_max : MinMax::range()) { + size_t mm_index = min_max->index(); + for (std::string &group_name : pathGroupNames()) { + PathGroup *path_group = findPathGroup(group_name, min_max); if (path_group) - path_group->pushEnds(path_ends); + path_group->pushEnds(path_ends); } if (async_[mm_index]) @@ -540,64 +560,74 @@ PathGroups::pushGroupPathEnds(PathEndSeq &path_ends) if (path_delay_[mm_index]) path_delay_[mm_index]->pushEnds(path_ends); - ClockSeq clks; - sdc_->sortedClocks(clks); - ClockSeq::Iterator clk_iter(clks); - while (clk_iter.hasNext()) { - Clock *clk = clk_iter.next(); + const Sdc *sdc = mode_->sdc(); + ClockSeq clks = sdc->sortedClocks(); + for (Clock *clk : clks) { PathGroup *path_group = findPathGroup(clk, min_max); if (path_group) - path_group->pushEnds(path_ends); + path_group->pushEnds(path_ends); } } } +StringSeq +PathGroups::pathGroupNames() +{ + std::set group_names1; + const Sdc *sdc = mode_->sdc(); + for (const auto& [name, group] : sdc->groupPaths()) + group_names1.insert(name); + StringSeq group_names2; + for (const std::string &name : group_names1) + group_names2.push_back(name); + sort(group_names2); + return group_names2; +} + + void PathGroups::pushUnconstrainedPathEnds(PathEndSeq &path_ends, - const MinMaxAll *min_max) + const MinMaxAll *min_max) { - Set groups; - for (auto path_ap : corners_->pathAnalysisPts()) { - const MinMax *path_min_max = path_ap->pathMinMax(); - if (min_max->matches(path_min_max)) { - int mm_index = path_min_max->index(); - PathGroup *group = unconstrained_[mm_index]; - if (group - // For multiple corner path APs use the same group. - // Only report it once. - && !groups.findKey(group)) { - group->pushEnds(path_ends); - groups.insert(group); - } + std::set groups; + for (const MinMax *mm : min_max->range()) { + size_t mm_index = mm->index(); + PathGroup *group = unconstrained_[mm_index]; + if (group + // For multiple scene path APs use the same group. + // Only report it once. + && !groups.contains(group)) { + group->pushEnds(path_ends); + groups.insert(group); } } } //////////////////////////////////////////////////////////////// -typedef Map PathGroupEndMap; -typedef Map PathGroupEndsMap; -typedef Set PathEndNoCrprSet; +using PathGroupEndMap = std::map; +using PathGroupEndsMap = std::map; +using PathEndNoCrprSet = std::set; static bool exceptionToEmpty(ExceptionTo *to); -PathEndSeq +void PathGroups::makePathEnds(ExceptionTo *to, - bool unconstrained_paths, - const Corner *corner, - const MinMaxAll *min_max, - bool sort_by_slack) + const SceneSeq &scenes, + const MinMaxAll *min_max, + bool sort_by_slack, + bool unconstrained_paths, + // Return value. + PathEndSeq &path_ends) { Stats stats(debug_, report_); makeGroupPathEnds(to, group_path_count_, endpoint_path_count_, - unique_pins_, unique_edges_, corner, min_max); + unique_pins_, unique_edges_, scenes, min_max); - PathEndSeq path_ends; - pushGroupPathEnds(path_ends); - if (sort_by_slack) { - sort(path_ends, PathEndLess(this)); - } + pushEnds(path_ends); + if (sort_by_slack) + sort(path_ends, PathEndLess(true, this)); if (unconstrained_paths && path_ends.empty()) @@ -605,7 +635,6 @@ PathGroups::makePathEnds(ExceptionTo *to, pushUnconstrainedPathEnds(path_ends, min_max); stats.report("Make path ends"); - return path_ends; } //////////////////////////////////////////////////////////////// @@ -617,22 +646,22 @@ class MakePathEnds1 : public PathEndVisitor public: MakePathEnds1(PathGroups *path_groups); MakePathEnds1(const MakePathEnds1&) = default; - virtual PathEndVisitor *copy() const; - virtual void visit(PathEnd *path_end); - virtual void vertexEnd(Vertex *vertex); + PathEndVisitor *copy() const override; + void visit(PathEnd *path_end) override; + void vertexEnd(Vertex *vertex) override; private: void visitPathEnd(PathEnd *path_end, - PathGroup *group); + PathGroup *group); PathGroups *path_groups_; PathGroupEndMap ends_; - PathEndLess cmp_; + PathEndLess less_; }; MakePathEnds1::MakePathEnds1(PathGroups *path_groups) : path_groups_(path_groups), - cmp_(path_groups) + less_(true, path_groups) { } @@ -651,15 +680,15 @@ MakePathEnds1::visit(PathEnd *path_end) void MakePathEnds1::visitPathEnd(PathEnd *path_end, - PathGroup *group) + PathGroup *group) { if (group->saveable(path_end)) { // Only keep the path end with the smallest slack/latest arrival. - PathEnd *worst_end = ends_.findKey(group); + PathEnd *worst_end = findKey(ends_, group); if (worst_end) { - if (cmp_(path_end, worst_end)) { - ends_[group] = path_end->copy(); - delete worst_end; + if (less_(path_end, worst_end)) { + ends_[group] = path_end->copy(); + delete worst_end; } } else @@ -671,11 +700,7 @@ MakePathEnds1::visitPathEnd(PathEnd *path_end, void MakePathEnds1::vertexEnd(Vertex *) { - PathGroupEndMap::Iterator group_iter(ends_); - while (group_iter.hasNext()) { - PathGroup *group; - PathEnd *end; - group_iter.next(group, end); + for (auto [group, end] : ends_) { // visitPathEnd already confirmed slack is saveable. if (end) { group->insert(end); @@ -693,33 +718,33 @@ MakePathEnds1::vertexEnd(Vertex *) class MakePathEndsAll : public PathEndVisitor { public: - MakePathEndsAll(int endpoint_path_count, + MakePathEndsAll(size_t endpoint_path_count, PathGroups *path_groups); MakePathEndsAll(const MakePathEndsAll&) = default; - virtual ~MakePathEndsAll(); - virtual PathEndVisitor *copy() const; - virtual void visit(PathEnd *path_end); - virtual void vertexEnd(Vertex *vertex); + ~MakePathEndsAll() override; + PathEndVisitor *copy() const override; + void visit(PathEnd *path_end) override; + void vertexEnd(Vertex *vertex) override; private: void visitPathEnd(PathEnd *path_end, - PathGroup *group); + PathGroup *group); - int endpoint_path_count_; + size_t endpoint_path_count_; PathGroups *path_groups_; const StaState *sta_; PathGroupEndsMap ends_; - PathEndSlackLess slack_cmp_; - PathEndNoCrprLess path_no_crpr_cmp_; + PathEndSlackLess less_; + PathEndNoCrprLess path_no_crpr_less_; }; -MakePathEndsAll::MakePathEndsAll(int endpoint_path_count, - PathGroups *path_groups) : +MakePathEndsAll::MakePathEndsAll(size_t endpoint_path_count, + PathGroups *path_groups) : endpoint_path_count_(endpoint_path_count), path_groups_(path_groups), sta_(path_groups), - slack_cmp_(path_groups), - path_no_crpr_cmp_(path_groups) + less_(true, path_groups), + path_no_crpr_less_(path_groups) { } @@ -732,13 +757,7 @@ MakePathEndsAll::copy() const MakePathEndsAll::~MakePathEndsAll() { - PathGroupEndsMap::Iterator group_iter(ends_); - while (group_iter.hasNext()) { - PathGroup *group; - PathEndSeq *ends; - group_iter.next(group, ends); - delete ends; - } + deleteContents(ends_); } void @@ -750,9 +769,9 @@ MakePathEndsAll::visit(PathEnd *path_end) void MakePathEndsAll::visitPathEnd(PathEnd *path_end, - PathGroup *group) + PathGroup *group) { - PathEndSeq *ends = ends_.findKey(group); + PathEndSeq *ends = findKey(ends_, group); if (ends == nullptr) { ends = new PathEndSeq; ends_[group] = ends; @@ -764,50 +783,41 @@ void MakePathEndsAll::vertexEnd(Vertex *) { Debug *debug = sta_->debug(); - PathGroupEndsMap::Iterator group_iter(ends_); - while (group_iter.hasNext()) { - PathGroup *group; - PathEndSeq *ends; - group_iter.next(group, ends); + for (auto [group, ends] : ends_) { if (ends) { - sort(ends, slack_cmp_); - PathEndNoCrprSet unique_ends(path_no_crpr_cmp_); - PathEndSeq::Iterator end_iter(ends); - int n = 0; - while (end_iter.hasNext() - && n < endpoint_path_count_) { - PathEnd *path_end = end_iter.next(); - // Only save the worst path end for each crpr tag. - // PathEnum will peel the others. - if (!unique_ends.hasKey(path_end)) { - debugPrint(debug, "path_group", 2, "insert %s %s %s %d", - path_end->vertex(sta_)->to_string(sta_).c_str(), + sort(ends, less_); + PathEndNoCrprSet unique_ends(path_no_crpr_less_); + auto end_iter = ends->begin(); + size_t n = 0; + while (end_iter != ends->end() + && n < endpoint_path_count_) { + PathEnd *path_end = *end_iter++; + // Only save the worst path end for each crpr tag. + // PathEnum will peel the others. + if (!unique_ends.contains(path_end)) { + debugPrint(debug, "path_group", 2, "insert {} {} {} {}", + path_end->vertex(sta_)->to_string(sta_), path_end->typeName(), - path_end->transition(sta_)->to_string().c_str(), + path_end->transition(sta_)->shortName(), path_end->path()->tag(sta_)->index()); - // Give the group a copy of the path end because - // it may delete it during pruning. - if (group->saveable(path_end) + // Give the group a copy of the path end because + // it may delete it during pruning. + if (group->saveable(path_end) || group->enumMinSlackUnderMin(path_end)) { - group->insert(path_end->copy()); - unique_ends.insert(path_end); - n++; - } - } - else - debugPrint(debug, "path_group", 3, "prune %s %s %s %d", - path_end->vertex(sta_)->to_string(sta_).c_str(), + group->insert(path_end->copy()); + unique_ends.insert(path_end); + n++; + } + } + else + debugPrint(debug, "path_group", 3, "prune {} {} {} {}", + path_end->vertex(sta_)->to_string(sta_), path_end->typeName(), - path_end->transition(sta_)->to_string().c_str(), + path_end->transition(sta_)->shortName(), path_end->path()->tag(sta_)->index()); } // Clear ends for next vertex. - PathEndSeq::Iterator end_iter2(ends); - while (end_iter2.hasNext()) { - PathEnd *path_end = end_iter2.next(); - delete path_end; - } - ends->clear(); + deleteContents(*ends); } } } @@ -816,82 +826,80 @@ MakePathEndsAll::vertexEnd(Vertex *) void PathGroups::makeGroupPathEnds(ExceptionTo *to, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - const Corner *corner, - const MinMaxAll *min_max) + size_t group_path_count, + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + const SceneSeq &scenes, + const MinMaxAll *min_max) { if (endpoint_path_count == 1) { MakePathEnds1 make_path_ends(this); - makeGroupPathEnds(to, corner, min_max, &make_path_ends); + makeGroupPathEnds(to, scenes, min_max, &make_path_ends); } else { MakePathEndsAll make_path_ends(endpoint_path_count, this); - makeGroupPathEnds(to, corner, min_max, &make_path_ends); - - for (auto path_min_max : MinMax::range()) { - int mm_index = path_min_max->index(); - for (auto name_group : sdc_->groupPaths()) { - const char *name = name_group.first; - PathGroup *group = findPathGroup(name, path_min_max); + makeGroupPathEnds(to, scenes, min_max, &make_path_ends); + + for (const MinMax *path_min_max : MinMax::range()) { + size_t mm_index = path_min_max->index(); + for (const Mode *mode : Scene::modes(scenes)) { + const Sdc *sdc = mode->sdc(); + for (const auto& [name, groups] : sdc->groupPaths()) { + PathGroup *group = findPathGroup(name, path_min_max); + if (group) + enumPathEnds(group, group_path_count, endpoint_path_count, + unique_pins, unique_edges, true); + } + } + const Sdc *sdc = mode_->sdc(); + for (const Clock *clk : sdc->clocks()) { + PathGroup *group = findPathGroup(clk, path_min_max); if (group) enumPathEnds(group, group_path_count, endpoint_path_count, - unique_pins, unique_edges, true); + unique_pins, unique_edges, true); } - - for (auto clk : sdc_->clks()) { - PathGroup *group = findPathGroup(clk, path_min_max); - if (group) - enumPathEnds(group, group_path_count, endpoint_path_count, - unique_pins, unique_edges, true); - } - PathGroup *group = unconstrained_[mm_index]; if (group) - enumPathEnds(group, group_path_count, endpoint_path_count, - unique_pins, unique_edges, false); + enumPathEnds(group, group_path_count, endpoint_path_count, + unique_pins, unique_edges, false); group = path_delay_[mm_index]; if (group) - enumPathEnds(group, group_path_count, endpoint_path_count, - unique_pins, unique_edges, true); + enumPathEnds(group, group_path_count, endpoint_path_count, + unique_pins, unique_edges, true); group = gated_clk_[mm_index]; if (group) - enumPathEnds(group, group_path_count, endpoint_path_count, - unique_pins, unique_edges, true); + enumPathEnds(group, group_path_count, endpoint_path_count, + unique_pins, unique_edges, true); group = async_[mm_index]; if (group) - enumPathEnds(group, group_path_count, endpoint_path_count, - unique_pins, unique_edges, true); + enumPathEnds(group, group_path_count, endpoint_path_count, + unique_pins, unique_edges, true); } } } void PathGroups::enumPathEnds(PathGroup *group, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - bool cmp_slack) + size_t group_path_count, + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + bool cmp_slack) { // Insert the worst max_path path ends in the group into a path // enumerator. PathEnum path_enum(group_path_count, endpoint_path_count, - unique_pins, unique_edges, cmp_slack, this); - PathGroupIterator *end_iter = group->iterator(); - while (end_iter->hasNext()) { - PathEnd *end = end_iter->next(); + unique_pins, unique_edges, cmp_slack, this); + for (PathEnd *end : group->pathEnds()) { if (group->saveable(end) || group->enumMinSlackUnderMin(end)) path_enum.insert(end); } - delete end_iter; group->clear(); // Parallel path enumeratation to find the endpoint_path_count/max path ends. - for (int n = 0; path_enum.hasNext() && n < group_path_count; n++) { + for (size_t n = 0; path_enum.hasNext() && n < group_path_count; n++) { PathEnd *end = path_enum.next(); if (group->saveable(end)) group->insert(end); @@ -902,32 +910,28 @@ PathGroups::enumPathEnds(PathGroup *group, void PathGroups::makeGroupPathEnds(ExceptionTo *to, - const Corner *corner, - const MinMaxAll *min_max, - PathEndVisitor *visitor) + const SceneSeq &scenes, + const MinMaxAll *min_max, + PathEndVisitor *visitor) { - Network *network = this->network(); - Graph *graph = this->graph(); - Search *search = this->search(); if (exceptionToEmpty(to)) - makeGroupPathEnds(search->endpoints(), corner, min_max, visitor); + makeGroupPathEnds(search_->endpoints(), scenes, min_max, visitor); else { // Only visit -to filter pins. - VertexSet endpoints(graph_); - PinSet pins = to->allPins(network); - PinSet::Iterator pin_iter(pins); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); + ModeSeq modes = Scene::modes(scenes); + VertexSet endpoints = makeVertexSet(this); + PinSet pins = to->allPins(network_); + for (const Pin *pin : pins) { Vertex *vertex, *bidirect_drvr_vertex; - graph->pinVertices(pin, vertex, bidirect_drvr_vertex); + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); if (vertex - && search->isEndpoint(vertex)) - endpoints.insert(vertex); + && search_->isEndpoint(vertex, modes)) + endpoints.insert(vertex); if (bidirect_drvr_vertex - && search->isEndpoint(bidirect_drvr_vertex)) - endpoints.insert(bidirect_drvr_vertex); + && search_->isEndpoint(bidirect_drvr_vertex, modes)) + endpoints.insert(bidirect_drvr_vertex); } - makeGroupPathEnds(&endpoints, corner, min_max, visitor); + makeGroupPathEnds(endpoints, scenes, min_max, visitor); } } @@ -936,7 +940,7 @@ exceptionToEmpty(ExceptionTo *to) { return to == nullptr || (to->pins() == nullptr - && to->instances() == nullptr); + && to->instances() == nullptr); } //////////////////////////////////////////////////////////////// @@ -945,29 +949,29 @@ class MakeEndpointPathEnds : public VertexVisitor { public: MakeEndpointPathEnds(PathEndVisitor *path_end_visitor, - const Corner *corner, - const MinMaxAll *min_max, - const StaState *sta); + const SceneSet &scenes, + const MinMaxAll *min_max, + const StaState *sta); MakeEndpointPathEnds(const MakeEndpointPathEnds &make_path_ends); - ~MakeEndpointPathEnds(); - virtual VertexVisitor *copy() const; - virtual void visit(Vertex *vertex); + ~MakeEndpointPathEnds() override; + VertexVisitor *copy() const override; + void visit(Vertex *vertex) override; private: VisitPathEnds visit_path_ends_; PathEndVisitor *path_end_visitor_; - const Corner *corner_; + const SceneSet scenes_; const MinMaxAll *min_max_; const StaState *sta_; }; MakeEndpointPathEnds::MakeEndpointPathEnds(PathEndVisitor *path_end_visitor, - const Corner *corner, - const MinMaxAll *min_max, - const StaState *sta) : + const SceneSet &scenes, + const MinMaxAll *min_max, + const StaState *sta) : visit_path_ends_(sta), path_end_visitor_(path_end_visitor->copy()), - corner_(corner), + scenes_(scenes), min_max_(min_max), sta_(sta) { @@ -976,7 +980,7 @@ MakeEndpointPathEnds::MakeEndpointPathEnds(PathEndVisitor *path_end_visitor, MakeEndpointPathEnds::MakeEndpointPathEnds(const MakeEndpointPathEnds &make_path_ends) : visit_path_ends_(make_path_ends.sta_), path_end_visitor_(make_path_ends.path_end_visitor_->copy()), - corner_(make_path_ends.corner_), + scenes_(make_path_ends.scenes_), min_max_(make_path_ends.min_max_), sta_(make_path_ends.sta_) { @@ -990,38 +994,41 @@ MakeEndpointPathEnds::~MakeEndpointPathEnds() VertexVisitor * MakeEndpointPathEnds::copy() const { - return new MakeEndpointPathEnds(path_end_visitor_, corner_, min_max_, sta_); + return new MakeEndpointPathEnds(path_end_visitor_, scenes_, min_max_, sta_); } void MakeEndpointPathEnds::visit(Vertex *vertex) { - visit_path_ends_.visitPathEnds(vertex, corner_, min_max_, true, path_end_visitor_); + visit_path_ends_.visitPathEnds(vertex, scenes_, min_max_, + true, path_end_visitor_); } //////////////////////////////////////////////////////////////// void -PathGroups::makeGroupPathEnds(VertexSet *endpoints, - const Corner *corner, - const MinMaxAll *min_max, - PathEndVisitor *visitor) +PathGroups::makeGroupPathEnds(VertexSet &endpoints, + const SceneSeq &scenes, + const MinMaxAll *min_max, + PathEndVisitor *visitor) { if (thread_count_ == 1) { - MakeEndpointPathEnds end_visitor(visitor, corner, min_max, this); - for (auto endpoint : *endpoints) + MakeEndpointPathEnds end_visitor(visitor, Scene::sceneSet(scenes), + min_max, this); + for (Vertex *endpoint : endpoints) end_visitor.visit(endpoint); } else { - Vector visitors(thread_count_, - MakeEndpointPathEnds(visitor, corner, - min_max, this)); - for (const auto endpoint : *endpoints) { - dispatch_queue_->dispatch( [endpoint, &visitors](int i) + std::vector + visitors(thread_count_, + MakeEndpointPathEnds(visitor, Scene::sceneSet(scenes), + min_max, this)); + for (const auto endpoint : endpoints) { + dispatch_queue_->dispatch( [endpoint, &visitors](size_t i) { visitors[i].visit(endpoint); } ); } dispatch_queue_->finishTasks(); } } -} // namespace +} // namespace sta diff --git a/graph/Delay.cc b/search/PocvMode.cc similarity index 68% rename from graph/Delay.cc rename to search/PocvMode.cc index cfe32cffb..46c262e52 100644 --- a/graph/Delay.cc +++ b/search/PocvMode.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,19 +22,27 @@ // // This notice may not be removed or altered from any source distribution. -#include "Machine.hh" -#include "StringUtil.hh" -#include "Units.hh" -#include "StaState.hh" -#include "Delay.hh" +#include "PocvMode.hh" + +#include "EnumNameMap.hh" namespace sta { -const char * -delayAsString(const Delay &delay, - const StaState *sta) +static EnumNameMap pocv_mode_map = + {{PocvMode::scalar, "scalar"}, + {PocvMode::normal, "normal"}, + {PocvMode::skew_normal, "skew_normal"}}; + +const std::string & +pocvModeName(PocvMode mode) +{ + return pocv_mode_map.find(mode); +} + +PocvMode +findPocvMode(std::string_view mode_name) { - return delayAsString(delay, sta, sta->units()->timeUnit()->digits()); + return pocv_mode_map.find(mode_name, PocvMode::scalar); } -} // namespace +} // namespace sta diff --git a/search/Property.cc b/search/Property.cc index 2e6f69192..6ff78217e 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,62 +24,58 @@ #include "Property.hh" -#include "StringUtil.hh" -#include "MinMax.hh" -#include "Transition.hh" -#include "Units.hh" -#include "TimingArc.hh" +#include +#include +#include +#include + +#include "Clock.hh" +#include "Format.hh" +#include "Graph.hh" #include "Liberty.hh" -#include "PortDirection.hh" +#include "MinMax.hh" #include "Network.hh" -#include "Graph.hh" -#include "Clock.hh" -#include "Corner.hh" +#include "Path.hh" +#include "PathGroup.hh" #include "PathEnd.hh" #include "PathExpanded.hh" -#include "Path.hh" -#include "power/Power.hh" +#include "PortDirection.hh" +#include "Scene.hh" #include "Sta.hh" -#include "Variables.hh" -#include "Search.hh" -#include "PathGroup.hh" - -#include +#include "StringUtil.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Transition.hh" +#include "Units.hh" +#include "power/Power.hh" namespace sta { -using std::string; -using std::max; - //////////////////////////////////////////////////////////////// class PropertyTypeWrong : public Exception { public: - PropertyTypeWrong(const char *accessor, - const char *type); - virtual ~PropertyTypeWrong() {} - virtual const char *what() const noexcept; + PropertyTypeWrong(const std::string &accessor, + const std::string &type); + const char *what() const noexcept override; private: - const char *accessor_; - const char *type_; + std::string msg_; }; -PropertyTypeWrong::PropertyTypeWrong(const char *accessor, - const char *type) : - Exception(), - accessor_(accessor), - type_(type) +PropertyTypeWrong::PropertyTypeWrong(const std::string &accessor, + const std::string &type) : + msg_(sta::format("property accessor {} is only valid for {} properties.", + accessor, type)) { } const char * PropertyTypeWrong::what() const noexcept { - return stringPrint("property accessor %s is only valid for %s properties.", - accessor_, type_); + return msg_.c_str(); } class PropertyTypeNotComparable : public Exception @@ -90,19 +86,19 @@ class PropertyTypeNotComparable : public Exception virtual const char *what() const noexcept; private: - const char *type_; + std::string msg_; }; PropertyTypeNotComparable::PropertyTypeNotComparable(const char *type) : Exception(), - type_(type) + msg_(sta::format("property type {} is not comparable.", type)) { } const char * PropertyTypeNotComparable::what() const noexcept { - return stringPrint("property type %s is not comparable.", type_); + return msg_.c_str(); } class PropertiesNotComparable : public Exception @@ -113,63 +109,62 @@ class PropertiesNotComparable : public Exception virtual const char *what() const noexcept; private: - const char *type1_, *type2_; + std::string msg_; }; PropertiesNotComparable::PropertiesNotComparable(const char *type1, const char *type2) : Exception(), - type1_(type1), - type2_(type2) + msg_(sta::format("properties of type {} and {} are not comparable", + type1, + type2)) { } const char * PropertiesNotComparable::what() const noexcept { - return stringPrint("properties of type %s and %s are not comparable", - type1_, - type2_); + return msg_.c_str(); } //////////////////////////////////////////////////////////////// const char *PropertyValue::type_name(Type type) { switch (type) { - case Type::type_none: + case Type::none: return "none"; - case Type::type_string: + case Type::string: return "string"; - case Type::type_float: + case Type::float_: return "float"; - case Type::type_bool: + case Type::bool_: return "bool"; - case Type::type_library: + case Type::library: return "library"; - case Type::type_cell: + case Type::cell: return "cell"; - case Type::type_port: + case Type::port: return "port"; - case Type::type_liberty_library: + case Type::liberty_library: return "liberty_library"; - case Type::type_liberty_cell: + case Type::liberty_cell: return "liberty_cell"; - case Type::type_liberty_port: + case Type::liberty_port: return "liberty_port"; - case Type::type_instance: + case Type::instance: return "instance"; - case Type::type_pin: + case Type::pin: return "pin"; - case Type::type_pins: + case Type::pins: return "pins"; - case Type::type_net: + case Type::net: return "net"; - case Type::type_clk: + case Type::clk: return "clk"; - case Type::type_clks: + case Type::clks: return "clks"; - case Type::type_paths: + case Type::paths: return "paths"; - case Type::type_pwr_activity: + case Type::pwr_activity: return "type_pwr_activity"; default: return "unknown"; @@ -179,169 +174,167 @@ const char *PropertyValue::type_name(Type type) { //////////////////////////////////////////////////////////////// PropertyValue::PropertyValue() : - type_(type_none), + type_(Type::none), unit_(nullptr) { } PropertyValue::PropertyValue(const char *value) : - type_(type_string), - string_(stringCopy(value)), + type_(Type::string), + unit_(nullptr) +{ + string_ = new std::string(value ? value : ""); +} + +PropertyValue::PropertyValue(std::string_view value) : + type_(Type::string), unit_(nullptr) { + string_ = new std::string(value); } -PropertyValue::PropertyValue(std::string &value) : - type_(type_string), - string_(stringCopy(value.c_str())), +PropertyValue::PropertyValue(std::string value) : + type_(Type::string), unit_(nullptr) { + string_ = new std::string(std::move(value)); } PropertyValue::PropertyValue(float value, const Unit *unit) : - type_(type_float), + type_(Type::float_), float_(value), unit_(unit) { } PropertyValue::PropertyValue(bool value) : - type_(type_bool), + type_(Type::bool_), bool_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const LibertyLibrary *value) : - type_(type_liberty_library), + type_(Type::liberty_library), liberty_library_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const LibertyCell *value) : - type_(type_liberty_cell), + type_(Type::liberty_cell), liberty_cell_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const LibertyPort *value) : - type_(type_liberty_port), + type_(Type::liberty_port), liberty_port_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const Library *value) : - type_(type_library), + type_(Type::library), library_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const Cell *value) : - type_(type_cell), + type_(Type::cell), cell_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const Port *value) : - type_(type_port), + type_(Type::port), port_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const Instance *value) : - type_(type_instance), + type_(Type::instance), inst_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const Pin *value) : - type_(type_pin), + type_(Type::pin), pin_(value), unit_(nullptr) { } PropertyValue::PropertyValue(PinSeq *value) : - type_(type_pins), + type_(Type::pins), pins_(value), unit_(nullptr) { } PropertyValue::PropertyValue(PinSet *value) : - type_(type_pins), + type_(Type::pins), pins_(new PinSeq), unit_(nullptr) { - PinSet::Iterator pin_iter(value); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - pins_->push_back( pin); - } + for (const Pin *pin : *value) + pins_->push_back(pin); } PropertyValue::PropertyValue(const PinSet &value) : - type_(type_pins), + type_(Type::pins), pins_(new PinSeq), unit_(nullptr) { - PinSet::ConstIterator pin_iter(value); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - pins_->push_back( pin); - } + for (const Pin *pin : value) + pins_->push_back(pin); } PropertyValue::PropertyValue(const Net *value) : - type_(type_net), + type_(Type::net), net_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const Clock *value) : - type_(type_clk), + type_(Type::clk), clk_(value), unit_(nullptr) { } PropertyValue::PropertyValue(ClockSeq *value) : - type_(type_clks), + type_(Type::clks), clks_(new ClockSeq(*value)), unit_(nullptr) { } PropertyValue::PropertyValue(ClockSet *value) : - type_(type_clks), + type_(Type::clks), clks_(new ClockSeq), unit_(nullptr) { - ClockSet::Iterator clk_iter(value); - while (clk_iter.hasNext()) { - Clock *clk = clk_iter.next(); + for (Clock *clk : *value) clks_->push_back(clk); - } } PropertyValue::PropertyValue(ConstPathSeq *value) : - type_(type_paths), + type_(Type::paths), paths_(new ConstPathSeq(*value)), unit_(nullptr) { } PropertyValue::PropertyValue(PwrActivity *value) : - type_(type_pwr_activity), + type_(Type::pwr_activity), pwr_activity_(*value), unit_(nullptr) { @@ -352,143 +345,144 @@ PropertyValue::PropertyValue(const PropertyValue &value) : unit_(value.unit_) { switch (type_) { - case Type::type_none: + case Type::none: break; - case Type::type_string: - string_ = stringCopy(value.string_); + case Type::string: + string_ = new std::string(*value.string_); break; - case Type::type_float: + case Type::float_: float_ = value.float_; break; - case Type::type_bool: + case Type::bool_: bool_ = value.bool_; break; - case Type::type_liberty_library: + case Type::liberty_library: liberty_library_ = value.liberty_library_; break; - case Type::type_liberty_cell: + case Type::liberty_cell: liberty_cell_ = value.liberty_cell_; break; - case Type::type_liberty_port: + case Type::liberty_port: liberty_port_ = value.liberty_port_; break; - case Type::type_library: + case Type::library: library_ = value.library_; break; - case Type::type_cell: + case Type::cell: cell_ = value.cell_; break; - case Type::type_port: + case Type::port: port_ = value.port_; break; - case Type::type_instance: + case Type::instance: inst_ = value.inst_; break; - case Type::type_pin: + case Type::pin: pin_ = value.pin_; break; - case Type::type_pins: + case Type::pins: pins_ = value.pins_ ? new PinSeq(*value.pins_) : nullptr; break; - case Type::type_net: + case Type::net: net_ = value.net_; break; - case Type::type_clk: + case Type::clk: clk_ = value.clk_; break; - case Type::type_clks: + case Type::clks: clks_ = value.clks_ ? new ClockSeq(*value.clks_) : nullptr; break; - case Type::type_paths: + case Type::paths: paths_ = value.paths_ ? new ConstPathSeq(*value.paths_) : nullptr; break; - case Type::type_pwr_activity: + case Type::pwr_activity: pwr_activity_ = value.pwr_activity_; break; } } -PropertyValue::PropertyValue(PropertyValue &&value) : +PropertyValue::PropertyValue(PropertyValue &&value) noexcept : type_(value.type_), unit_(value.unit_) - { switch (type_) { - case Type::type_none: + case Type::none: break; - case Type::type_string: + case Type::string: string_ = value.string_; value.string_ = nullptr; + value.type_ = Type::none; break; - case Type::type_float: + case Type::float_: float_ = value.float_; break; - case Type::type_bool: + case Type::bool_: bool_ = value.bool_; break; - case Type::type_library: + case Type::library: library_ = value.library_; break; - case Type::type_cell: + case Type::cell: cell_ = value.cell_; break; - case Type::type_port: + case Type::port: port_ = value.port_; break; - case Type::type_liberty_library: + case Type::liberty_library: liberty_library_ = value.liberty_library_; break; - case Type::type_liberty_cell: + case Type::liberty_cell: liberty_cell_ = value.liberty_cell_; break; - case Type::type_liberty_port: + case Type::liberty_port: liberty_port_ = value.liberty_port_; break; - case Type::type_instance: + case Type::instance: inst_ = value.inst_; break; - case Type::type_pin: + case Type::pin: pin_ = value.pin_; break; - case Type::type_pins: + case Type::pins: pins_ = value.pins_; value.pins_ = nullptr; break; - case Type::type_net: + case Type::net: net_ = value.net_; break; - case Type::type_clk: + case Type::clk: clk_ = value.clk_; break; - case Type::type_clks: + case Type::clks: clks_ = value.clks_; // Steal the value. value.clks_ = nullptr; break; - case Type::type_paths: + case Type::paths: paths_ = value.paths_; // Steal the value. - value.clks_ = nullptr; + value.paths_ = nullptr; break; - case Type::type_pwr_activity: + case Type::pwr_activity: pwr_activity_ = value.pwr_activity_; break; } } -PropertyValue::~PropertyValue() -{ +void +PropertyValue::destroyActive() +{ switch (type_) { - case Type::type_string: - stringDelete(string_); + case Type::string: + delete string_; break; - case Type::type_clks: + case Type::clks: delete clks_; break; - case Type::type_pins: + case Type::pins: delete pins_; break; - case Type::type_paths: + case Type::paths: delete paths_; break; default: @@ -496,64 +490,72 @@ PropertyValue::~PropertyValue() } } +PropertyValue::~PropertyValue() +{ + destroyActive(); +} + PropertyValue & PropertyValue::operator=(const PropertyValue &value) { + if (this == &value) + return *this; + destroyActive(); type_ = value.type_; unit_ = value.unit_; switch (type_) { - case Type::type_none: + case Type::none: break; - case Type::type_string: - string_ = stringCopy(value.string_); + case Type::string: + string_ = new std::string(*value.string_); break; - case Type::type_float: + case Type::float_: float_ = value.float_; break; - case Type::type_bool: + case Type::bool_: bool_ = value.bool_; break; - case Type::type_library: + case Type::library: library_ = value.library_; break; - case Type::type_cell: + case Type::cell: cell_ = value.cell_; break; - case Type::type_port: + case Type::port: port_ = value.port_; break; - case Type::type_liberty_library: + case Type::liberty_library: liberty_library_ = value.liberty_library_; break; - case Type::type_liberty_cell: + case Type::liberty_cell: liberty_cell_ = value.liberty_cell_; break; - case Type::type_liberty_port: + case Type::liberty_port: liberty_port_ = value.liberty_port_; break; - case Type::type_instance: + case Type::instance: inst_ = value.inst_; break; - case Type::type_pin: + case Type::pin: pin_ = value.pin_; break; - case Type::type_pins: + case Type::pins: pins_ = value.pins_ ? new PinSeq(*value.pins_) : nullptr; break; - case Type::type_net: + case Type::net: net_ = value.net_; break; - case Type::type_clk: + case Type::clk: clk_ = value.clk_; break; - case Type::type_clks: + case Type::clks: clks_ = value.clks_ ? new ClockSeq(*value.clks_) : nullptr; break; - case Type::type_paths: + case Type::paths: paths_ = value.paths_ ? new ConstPathSeq(*value.paths_) : nullptr; break; - case Type::type_pwr_activity: + case Type::pwr_activity: pwr_activity_ = value.pwr_activity_; break; } @@ -561,67 +563,71 @@ PropertyValue::operator=(const PropertyValue &value) } PropertyValue & -PropertyValue::operator=(PropertyValue &&value) +PropertyValue::operator=(PropertyValue &&value) noexcept { + if (this == &value) + return *this; + destroyActive(); type_ = value.type_; unit_ = value.unit_; switch (type_) { - case Type::type_none: + case Type::none: break; - case Type::type_string: + case Type::string: string_ = value.string_; value.string_ = nullptr; + value.type_ = Type::none; break; - case Type::type_float: + case Type::float_: float_ = value.float_; break; - case Type::type_bool: + case Type::bool_: bool_ = value.bool_; break; - case Type::type_library: + case Type::library: library_ = value.library_; break; - case Type::type_cell: + case Type::cell: cell_ = value.cell_; break; - case Type::type_port: + case Type::port: port_ = value.port_; break; - case Type::type_liberty_library: + case Type::liberty_library: liberty_library_ = value.liberty_library_; break; - case Type::type_liberty_cell: + case Type::liberty_cell: liberty_cell_ = value.liberty_cell_; break; - case Type::type_liberty_port: + case Type::liberty_port: liberty_port_ = value.liberty_port_; break; - case Type::type_instance: + case Type::instance: inst_ = value.inst_; break; - case Type::type_pin: + case Type::pin: pin_ = value.pin_; break; - case Type::type_pins: + case Type::pins: pins_ = value.pins_; value.pins_ = nullptr; break; - case Type::type_net: + case Type::net: net_ = value.net_; break; - case Type::type_clk: + case Type::clk: clk_ = value.clk_; break; - case Type::type_clks: + case Type::clks: clks_ = value.clks_; value.clks_ = nullptr; break; - case Type::type_paths: + case Type::paths: paths_ = value.paths_; - value.clks_ = nullptr; + value.paths_ = nullptr; break; - case Type::type_pwr_activity: + case Type::pwr_activity: pwr_activity_ = value.pwr_activity_; break; } @@ -678,28 +684,28 @@ int PropertyValue::compare(const PropertyValue &rhs, const Network *network, boo std::function my_strcmp = natural ? strcmpnat : strcmp; - string lhs_s, rhs_s; + std::string lhs_s, rhs_s; float diff; switch (type_) { - case Type::type_none: + case Type::none: return 0; - case Type::type_string: - return my_strcmp(string_, rhs.string_); - case Type::type_float: + case Type::string: + return my_strcmp(string_->c_str(), rhs.string_->c_str()); + case Type::float_: diff = float_ - rhs.float_; return (diff > 0) - (diff < 0); - case Type::type_bool: + case Type::bool_: return int(bool_) - int(rhs.bool_); - case Type::type_library: - case Type::type_cell: - case Type::type_port: - case Type::type_liberty_library: - case Type::type_liberty_cell: - case Type::type_liberty_port: - case Type::type_instance: - case Type::type_pin: - case Type::type_net: - case Type::type_clk: + case Type::library: + case Type::cell: + case Type::port: + case Type::liberty_library: + case Type::liberty_cell: + case Type::liberty_port: + case Type::instance: + case Type::pin: + case Type::net: + case Type::clk: // compare by name lhs_s = to_string(network); rhs_s = rhs.to_string(network); @@ -709,58 +715,62 @@ int PropertyValue::compare(const PropertyValue &rhs, const Network *network, boo } } -string +std::string PropertyValue::to_string(const Network *network) const { switch (type_) { - case Type::type_string: - return string_; - case Type::type_float: + case Type::string: + return *string_; + case Type::float_: return unit_->asString(float_, 6); - case Type::type_bool: - return Sta::sta()->booleanPropsAsInt() ? (bool_ ? "1" : "0") : (bool_ ? "true" : "false"); - case Type::type_liberty_library: + case Type::bool_: + // These are TCL true/false values. + if (bool_) + return Sta::sta()->booleanPropsAsInt() ? "1" : "true"; + else + return Sta::sta()->booleanPropsAsInt() ? "0" : "false"; + case Type::liberty_library: return liberty_library_->name(); - case Type::type_liberty_cell: - return liberty_cell_ ? liberty_cell_->name() : ""; - case Type::type_liberty_port: + case Type::liberty_cell: + return liberty_cell_->name(); + case Type::liberty_port: return liberty_port_->name(); - case Type::type_library: - return network->name(library_); - case Type::type_cell: - return network->name(cell_); - case Type::type_port: - return network->name(port_); - case Type::type_instance: + case Type::library: + return std::string(network->name(library_)); + case Type::cell: + return std::string(network->name(cell_)); + case Type::port: + return std::string(network->name(port_)); + case Type::instance: return network->pathName(inst_); - case Type::type_pin: + case Type::pin: return network->pathName(pin_); - case Type::type_net: + case Type::net: return network->pathName(net_); - case Type::type_clk: + case Type::clk: return clk_->name(); - case Type::type_none: - case Type::type_pins: - case Type::type_clks: - case Type::type_paths: - case Type::type_pwr_activity: + case Type::none: + case Type::pins: + case Type::clks: + case Type::paths: + case Type::pwr_activity: return ""; } return ""; } -const char * +const std::string & PropertyValue::stringValue() const { - if (type_ != Type::type_string) + if (type_ != Type::string) throw PropertyTypeWrong("stringValue", "string"); - return string_; + return *string_; } float PropertyValue::floatValue() const { - if (type_ != Type::type_float) + if (type_ != Type::float_) throw PropertyTypeWrong("floatValue", "float"); return float_; } @@ -768,7 +778,7 @@ PropertyValue::floatValue() const bool PropertyValue::boolValue() const { - if (type_ != Type::type_bool) + if (type_ != Type::bool_) throw PropertyTypeWrong("boolValue", "bool"); return bool_; } @@ -782,21 +792,18 @@ Properties::Properties(Sta *sta) : PropertyValue Properties::getProperty(const Library *lib, - const std::string property) + std::string_view property) { Network *network = sta_->cmdNetwork(); if (property == "name" || property == "full_name") return PropertyValue(network->name(lib)); else { - PropertyValue value = registry_library_.getProperty(lib, property, sta_); - if (value.type() != PropertyValue::Type::type_none) + PropertyValue value = registry_library_.getProperty(lib, property, + "library", sta_); + if (value.type() != PropertyValue::Type::none) return value; - else { - sta_->report()->warn(9000, "%s objects do not have a %s property.", - "library", property.c_str()); - return PropertyValue(); - } + throw PropertyUnknown("library", property); } } @@ -804,7 +811,7 @@ Properties::getProperty(const Library *lib, PropertyValue Properties::getProperty(const LibertyLibrary *lib, - const std::string property) + std::string_view property) { if (property == "name" || property == "full_name") @@ -812,14 +819,12 @@ Properties::getProperty(const LibertyLibrary *lib, else if (property == "filename") return PropertyValue(lib->filename()); else { - PropertyValue value = registry_liberty_library_.getProperty(lib, property, sta_); - if (value.type() != PropertyValue::Type::type_none) + PropertyValue value = registry_liberty_library_.getProperty(lib, property, + "liberty_library", + sta_); + if (value.type() != PropertyValue::Type::none) return value; - else { - sta_->report()->warn(9000, "%s objects do not have a %s property.", - "liberty library", property.c_str()); - return PropertyValue(); - } + throw PropertyUnknown("liberty library", property); } } @@ -827,7 +832,7 @@ Properties::getProperty(const LibertyLibrary *lib, PropertyValue Properties::getProperty(const Cell *cell, - const std::string property) + std::string_view property) { Network *network = sta_->cmdNetwork(); if (property == "name" @@ -835,9 +840,9 @@ Properties::getProperty(const Cell *cell, return PropertyValue(network->name(cell)); else if (property == "full_name") { Library *lib = network->library(cell); - string lib_name = network->name(lib); - string cell_name = network->name(cell); - string full_name = lib_name + network->pathDivider() + cell_name; + std::string lib_name(network->name(lib)); + std::string cell_name(network->name(cell)); + std::string full_name = lib_name + network->pathDivider() + cell_name; return PropertyValue(full_name); } else if (property == "library") @@ -845,14 +850,11 @@ Properties::getProperty(const Cell *cell, else if (property == "filename") return PropertyValue(network->filename(cell)); else { - PropertyValue value = registry_cell_.getProperty(cell, property, sta_); - if (value.type() != PropertyValue::Type::type_none) + PropertyValue value = registry_cell_.getProperty(cell, property, + "cell", sta_); + if (value.type() != PropertyValue::Type::none) return value; - else { - sta_->report()->warn(9000, "%s objects do not have a %s property.", - "cell", property.c_str()); - return PropertyValue(); - } + throw PropertyUnknown("cell", property); } } @@ -860,7 +862,7 @@ Properties::getProperty(const Cell *cell, PropertyValue Properties::getProperty(const LibertyCell *cell, - const std::string property) + std::string_view property) { if (property == "name" || property == "base_name") @@ -868,9 +870,9 @@ Properties::getProperty(const LibertyCell *cell, else if (property == "full_name") { Network *network = sta_->cmdNetwork(); LibertyLibrary *lib = cell->libertyLibrary(); - string lib_name = lib->name(); - string cell_name = cell->name(); - string full_name = lib_name + network->pathDivider() + cell_name; + std::string lib_name = lib->name(); + const std::string& cell_name = cell->name(); + std::string full_name = lib_name + network->pathDivider() + cell_name; return PropertyValue(full_name); } else if (property == "filename") @@ -898,14 +900,11 @@ Properties::getProperty(const LibertyCell *cell, else if (property == "area") return PropertyValue(cell->area(), sta_->units()->scalarUnit()); else { - PropertyValue value = registry_liberty_cell_.getProperty(cell, property, sta_); - if (value.type() != PropertyValue::Type::type_none) + PropertyValue value = registry_liberty_cell_.getProperty(cell, property, + "liberty_cell", sta_); + if (value.type() != PropertyValue::Type::none) return value; - else { - sta_->report()->warn(9000, "%s objects do not have a %s property.", - "liberty cell", property.c_str()); - return PropertyValue(); - } + throw PropertyUnknown("liberty cell", property); } } @@ -913,15 +912,15 @@ Properties::getProperty(const LibertyCell *cell, PropertyValue Properties::getProperty(const Port *port, - const std::string property) + std::string_view property) { Network *network = sta_->cmdNetwork(); if (property == "name" - || property == "full_name") + || property == "full_name") return PropertyValue(network->name(port)); else if (property == "direction" - || property == "port_direction") { - const char *name = network->direction(port)->name(); + || property == "port_direction") { + std::string_view name = network->direction(port)->name(); if (sta_->directionPropsShort()) { if (stringEqual(name, "input")) return PropertyValue("in"); @@ -937,80 +936,73 @@ Properties::getProperty(const Port *port, || property == "pin_capacitance") { int fanout = 0; float cap = 0.0, wire_cap = 0.0; + /* for (const Corner *corner : *sta_->corners()) sta_->portExtCaps(port, corner, MinMax::max(), cap, wire_cap, fanout); + */ + sta_->portExtCaps(port, MinMax::max(), sta_->cmdSdc(), cap, wire_cap, fanout); return capacitancePropertyValue(cap); } else if (property == "clocks") { const Instance *top_inst = network->topInstance(); + const Mode *mode = sta_->cmdScene()->mode(); const Pin *pin = network->findPin(top_inst, port); - ClockSet clks = sta_->clocks(pin); + ClockSet clks = sta_->clocks(pin, mode); return PropertyValue(&clks); } else if (property == "clock_domains") { const Instance *top_inst = network->topInstance(); + const Mode *mode = sta_->cmdScene()->mode(); const Pin *pin = network->findPin(top_inst, port); - ClockSet clks = sta_->clockDomains(pin); + ClockSet clks = sta_->clockDomains(pin, mode); return PropertyValue(&clks); } else if (property == "activity") { const Instance *top_inst = network->topInstance(); const Pin *pin = network->findPin(top_inst, port); - PwrActivity activity = sta_->activity(pin); + const Scene *scene = sta_->cmdScene(); + PwrActivity activity = sta_->activity(pin, scene); return PropertyValue(&activity); } else if (property == "slack_max") - return portSlack(port, MinMax::max()); + return portSlack(port, RiseFallBoth::riseFall(), MinMax::max()); else if (property == "slack_max_fall") - return portSlack(port, RiseFall::fall(), MinMax::max()); + return portSlack(port, RiseFallBoth::fall(), MinMax::max()); else if (property == "slack_max_rise") - return portSlack(port, RiseFall::rise(), MinMax::max()); + return portSlack(port, RiseFallBoth::rise(), MinMax::max()); else if (property == "slack_min") - return portSlack(port, MinMax::min()); + return portSlack(port, RiseFallBoth::riseFall(), MinMax::min()); else if (property == "slack_min_fall") - return portSlack(port, RiseFall::fall(), MinMax::min()); + return portSlack(port, RiseFallBoth::fall(), MinMax::min()); else if (property == "slack_min_rise") - return portSlack(port, RiseFall::rise(), MinMax::min()); + return portSlack(port, RiseFallBoth::rise(), MinMax::min()); else if (property == "slew_max") - return portSlew(port, MinMax::max()); + return portSlew(port, RiseFallBoth::fall(), MinMax::max()); else if (property == "slew_max_fall") - return portSlew(port, RiseFall::fall(), MinMax::max()); + return portSlew(port, RiseFallBoth::fall(), MinMax::max()); else if (property == "slew_max_rise") - return portSlew(port, RiseFall::rise(), MinMax::max()); + return portSlew(port, RiseFallBoth::rise(), MinMax::max()); else if (property == "slew_min") - return portSlew(port, MinMax::min()); + return portSlew(port, RiseFallBoth::fall(), MinMax::min()); else if (property == "slew_min_rise") - return portSlew(port, RiseFall::rise(), MinMax::min()); + return portSlew(port, RiseFallBoth::rise(), MinMax::min()); else if (property == "slew_min_fall") - return portSlew(port, RiseFall::fall(), MinMax::min()); + return portSlew(port, RiseFallBoth::fall(), MinMax::min()); else { - PropertyValue value = registry_port_.getProperty(port, property, sta_); - if (value.type() != PropertyValue::Type::type_none) + PropertyValue value = registry_port_.getProperty(port, property, + "port", sta_); + if (value.type() != PropertyValue::Type::none) return value; - else { - sta_->report()->warn(9000, "%s objects do not have a %s property.", - "port", property.c_str()); - return PropertyValue(); - } + throw PropertyUnknown("port", property); } } PropertyValue Properties::portSlew(const Port *port, - const MinMax *min_max) -{ - Network *network = sta_->ensureLibLinked(); - Instance *top_inst = network->topInstance(); - Pin *pin = network->findPin(top_inst, port); - return pinSlew(pin, min_max); -} - -PropertyValue -Properties::portSlew(const Port *port, - const RiseFall *rf, + const RiseFallBoth *rf, const MinMax *min_max) { Network *network = sta_->ensureLibLinked(); @@ -1021,17 +1013,7 @@ Properties::portSlew(const Port *port, PropertyValue Properties::portSlack(const Port *port, - const MinMax *min_max) -{ - Network *network = sta_->ensureLibLinked(); - Instance *top_inst = network->topInstance(); - Pin *pin = network->findPin(top_inst, port); - return pinSlack(pin, min_max); -} - -PropertyValue -Properties::portSlack(const Port *port, - const RiseFall *rf, + const RiseFallBoth *rf, const MinMax *min_max) { Network *network = sta_->ensureLibLinked(); @@ -1044,7 +1026,7 @@ Properties::portSlack(const Port *port, PropertyValue Properties::getProperty(const LibertyPort *port, - const std::string property) + std::string_view property) { if (property == "name") return PropertyValue(port->name()); @@ -1053,8 +1035,8 @@ Properties::getProperty(const LibertyPort *port, else if (property == "lib_cell") return PropertyValue(port->libertyCell()); else if (property == "direction" - || property == "port_direction") { - const char *name = port->direction()->name(); + || property == "port_direction") { + std::string_view name = port->direction()->name(); if (sta_->directionPropsShort()) { if (stringEqual(name, "input")) return PropertyValue("in"); @@ -1121,14 +1103,11 @@ Properties::getProperty(const LibertyPort *port, return delayPropertyValue(delay); } else { - PropertyValue value = registry_liberty_port_.getProperty(port, property, sta_); - if (value.type() != PropertyValue::Type::type_none) + PropertyValue value = registry_liberty_port_.getProperty(port, property, + "liberty_port", sta_); + if (value.type() != PropertyValue::Type::none) return value; - else { - sta_->report()->warn(9000, "%s objects do not have a %s property.", - "liberty port", property.c_str()); - return PropertyValue(); - } + throw PropertyUnknown("liberty port", property); } } @@ -1136,7 +1115,7 @@ Properties::getProperty(const LibertyPort *port, PropertyValue Properties::getProperty(const Instance *inst, - const string property) + std::string_view property) { Network *network = sta_->ensureLinked(); LibertyCell *liberty_cell = network->libertyCell(inst); @@ -1146,8 +1125,12 @@ Properties::getProperty(const Instance *inst, return PropertyValue(network->pathName(inst)); else if (property == "ref_name") return PropertyValue(network->name(network->cell(inst))); - else if (property == "liberty_cell") - return PropertyValue(network->libertyCell(inst)); + else if (property == "liberty_cell") { + LibertyCell *lib_cell = network->libertyCell(inst); + if (lib_cell) + return PropertyValue(lib_cell); + return PropertyValue(std::string("")); + } else if (property == "cell") return PropertyValue(network->cell(inst)); else if (property == "is_hierarchical") @@ -1167,14 +1150,11 @@ Properties::getProperty(const Instance *inst, else if (property == "design_type") return PropertyValue(liberty_cell ? liberty_cell->getDesignType() : "module"); else { - PropertyValue value = registry_instance_.getProperty(inst, property, sta_); - if (value.type() != PropertyValue::Type::type_none) + PropertyValue value = registry_instance_.getProperty(inst, property, + "instance", sta_); + if (value.type() != PropertyValue::Type::none) return value; - else { - sta_->report()->warn(9000, "%s objects do not have a %s property.", - "instance", property.c_str()); - return PropertyValue(); - } + throw PropertyUnknown("instance", property); } } @@ -1182,7 +1162,7 @@ Properties::getProperty(const Instance *inst, PropertyValue Properties::getProperty(const Pin *pin, - const std::string property) + std::string_view property) { Network *network = sta_->ensureLinked(); if (property == "name" @@ -1190,8 +1170,9 @@ Properties::getProperty(const Pin *pin, return PropertyValue(network->portName(pin)); else if (property == "full_name") return PropertyValue(network->pathName(pin)); - else if (property == "direction" || property == "pin_direction") { - const char *name = network->direction(pin)->name(); + else if (property == "direction" + || property == "pin_direction") { + std::string_view name = network->direction(pin)->name(); if (sta_->directionPropsShort()) { if (stringEqual(name, "input")) return PropertyValue("in"); @@ -1213,115 +1194,85 @@ Properties::getProperty(const Pin *pin, else if (property == "is_fall_edge_triggered") return PropertyValue(network->isFallEdgeTriggered(pin)); else if (property == "clocks") { - ClockSet clks = sta_->clocks(pin); + const Mode *mode = sta_->cmdScene()->mode(); + ClockSet clks = sta_->clocks(pin, mode); return PropertyValue(&clks); } else if (property == "clock_domains") { - ClockSet clks = sta_->clockDomains(pin); + const Mode *mode = sta_->cmdScene()->mode(); + ClockSet clks = sta_->clockDomains(pin, mode); return PropertyValue(&clks); } else if (property == "activity") { - PwrActivity activity = sta_->activity(pin); + const Scene *scene = sta_->cmdScene(); + PwrActivity activity = sta_->activity(pin, scene); return PropertyValue(&activity); } else if (property == "arrival_max_rise") - return pinArrival(pin, RiseFall::rise(), MinMax::max()); + return pinArrival(pin, RiseFallBoth::rise(), MinMax::max()); else if (property == "arrival_max_fall") - return pinArrival(pin, RiseFall::fall(), MinMax::max()); + return pinArrival(pin, RiseFallBoth::fall(), MinMax::max()); else if (property == "arrival_min_rise") - return pinArrival(pin, RiseFall::rise(), MinMax::min()); + return pinArrival(pin, RiseFallBoth::rise(), MinMax::min()); else if (property == "arrival_min_fall") - return pinArrival(pin, RiseFall::fall(), MinMax::min()); + return pinArrival(pin, RiseFallBoth::fall(), MinMax::min()); else if (property == "slack_max") - return pinSlack(pin, MinMax::max()); + return pinSlack(pin, RiseFallBoth::riseFall(), MinMax::max()); else if (property == "slack_max_fall") - return pinSlack(pin, RiseFall::fall(), MinMax::max()); + return pinSlack(pin, RiseFallBoth::fall(), MinMax::max()); else if (property == "slack_max_rise") - return pinSlack(pin, RiseFall::rise(), MinMax::max()); + return pinSlack(pin, RiseFallBoth::rise(), MinMax::max()); else if (property == "slack_min") - return pinSlack(pin, MinMax::min()); + return pinSlack(pin, RiseFallBoth::riseFall(), MinMax::min()); else if (property == "slack_min_fall") - return pinSlack(pin, RiseFall::fall(), MinMax::min()); + return pinSlack(pin, RiseFallBoth::fall(), MinMax::min()); else if (property == "slack_min_rise") - return pinSlack(pin, RiseFall::rise(), MinMax::min()); + return pinSlack(pin, RiseFallBoth::rise(), MinMax::min()); else if (property == "slew_max") - return pinSlew(pin, MinMax::max()); + return pinSlew(pin, RiseFallBoth::riseFall(), MinMax::max()); else if (property == "slew_max_fall") - return pinSlew(pin, RiseFall::fall(), MinMax::max()); + return pinSlew(pin, RiseFallBoth::fall(), MinMax::max()); else if (property == "slew_max_rise") - return pinSlew(pin, RiseFall::rise(), MinMax::max()); + return pinSlew(pin, RiseFallBoth::rise(), MinMax::max()); else if (property == "slew_min") - return pinSlew(pin, MinMax::min()); + return pinSlew(pin, RiseFallBoth::riseFall(), MinMax::min()); else if (property == "slew_min_rise") - return pinSlew(pin, RiseFall::rise(), MinMax::min()); + return pinSlew(pin, RiseFallBoth::rise(), MinMax::min()); else if (property == "slew_min_fall") - return pinSlew(pin, RiseFall::fall(), MinMax::min()); + return pinSlew(pin, RiseFallBoth::fall(), MinMax::min()); else { - PropertyValue value = registry_pin_.getProperty(pin, property, sta_); - if (value.type() != PropertyValue::Type::type_none) + PropertyValue value = registry_pin_.getProperty(pin, property, "pin", sta_); + if (value.type() != PropertyValue::Type::none) return value; - else { - sta_->report()->warn(9000, "%s objects do not have a %s property.", - "pin", property.c_str()); - return PropertyValue(); - } + throw PropertyUnknown("pin", property); } } PropertyValue Properties::pinArrival(const Pin *pin, - const RiseFall *rf, + const RiseFallBoth *rf, const MinMax *min_max) { - Arrival arrival = sta_->pinArrival(pin, rf, min_max);; + Arrival arrival = sta_->arrival(pin, rf, min_max);; return PropertyValue(delayPropertyValue(arrival)); } PropertyValue Properties::pinSlack(const Pin *pin, + const RiseFallBoth *rf, const MinMax *min_max) { - Slack slack = sta_->pinSlack(pin, min_max); + Slack slack = sta_->slack(pin, rf, sta_->scenes(), min_max); return PropertyValue(delayPropertyValue(slack)); } -PropertyValue -Properties::pinSlack(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max) -{ - Slack slack = sta_->pinSlack(pin, rf, min_max); - return PropertyValue(delayPropertyValue(slack)); -} - -PropertyValue -Properties::pinSlew(const Pin *pin, - const MinMax *min_max) -{ - Graph *graph = sta_->ensureGraph(); - Vertex *vertex, *bidirect_drvr_vertex; - graph->pinVertices(pin, vertex, bidirect_drvr_vertex); - Slew slew = min_max->initValue(); - if (vertex) { - Slew vertex_slew = sta_->vertexSlew(vertex, min_max); - if (delayGreater(vertex_slew, slew, min_max, sta_)) - slew = vertex_slew; - } - if (bidirect_drvr_vertex) { - Slew vertex_slew = sta_->vertexSlew(bidirect_drvr_vertex, min_max); - if (delayGreater(vertex_slew, slew, min_max, sta_)) - slew = vertex_slew; - } - return delayPropertyValue(slew); -} - PropertyValue Properties::pinSlew(const Pin *pin, - const RiseFall *rf, + const RiseFallBoth *rf, const MinMax *min_max) { Graph *graph = sta_->ensureGraph(); @@ -1329,12 +1280,13 @@ Properties::pinSlew(const Pin *pin, graph->pinVertices(pin, vertex, bidirect_drvr_vertex); Slew slew = min_max->initValue(); if (vertex) { - Slew vertex_slew = sta_->vertexSlew(vertex, rf, min_max); + Slew vertex_slew = sta_->slew(vertex, rf, sta_->scenes(), min_max); if (delayGreater(vertex_slew, slew, min_max, sta_)) slew = vertex_slew; } if (bidirect_drvr_vertex) { - Slew vertex_slew = sta_->vertexSlew(bidirect_drvr_vertex, rf, min_max); + Slew vertex_slew = sta_->slew(bidirect_drvr_vertex, rf, + sta_->scenes(), min_max); if (delayGreater(vertex_slew, slew, min_max, sta_)) slew = vertex_slew; } @@ -1345,7 +1297,7 @@ Properties::pinSlew(const Pin *pin, PropertyValue Properties::getProperty(const Net *net, - const std::string property) + std::string_view property) { Network *network = sta_->ensureLinked(); if (property == "name") @@ -1353,14 +1305,10 @@ Properties::getProperty(const Net *net, else if (property == "full_name") return PropertyValue(network->pathName(net)); else { - PropertyValue value = registry_net_.getProperty(net, property, sta_); - if (value.type() != PropertyValue::Type::type_none) + PropertyValue value = registry_net_.getProperty(net, property, "net", sta_); + if (value.type() != PropertyValue::Type::none) return value; - else { - sta_->report()->warn(9000, "%s objects do not have a %s property.", - "net", property.c_str()); - return PropertyValue(); - } + throw PropertyUnknown("net", property); } } @@ -1368,10 +1316,10 @@ Properties::getProperty(const Net *net, PropertyValue Properties::getProperty(Edge *edge, - const std::string property) + std::string_view property) { if (property == "full_name") { - string full_name = edge->to_string(sta_); + std::string full_name = edge->to_string(sta_); return PropertyValue(full_name); } if (property == "delay_min_fall") @@ -1388,11 +1336,7 @@ Properties::getProperty(Edge *edge, return PropertyValue(edge->from(sta_->graph())->pin()); else if (property == "to_pin") return PropertyValue(edge->to(sta_->graph())->pin()); - else { - sta_->report()->warn(9000, "%s objects do not have a %s property.", - "edge", property.c_str()); - return PropertyValue(); - } + throw PropertyUnknown("edge", property); } PropertyValue @@ -1406,12 +1350,12 @@ Properties::edgeDelay(Edge *edge, for (TimingArc *arc : arc_set->arcs()) { const RiseFall *to_rf = arc->toEdge()->asRiseFall(); if (to_rf == rf) { - for (const Corner *corner : *sta_->corners()) { - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - ArcDelay arc_delay = sta_->arcDelay(edge, arc, dcalc_ap); - if (!delay_exists - || delayGreater(arc_delay, delay, min_max, sta_)) { - delay = arc_delay; + for (const Scene *scene : sta_->scenes()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + const ArcDelay &arc_delay = sta_->arcDelay(edge, arc, ap_index); + if (!delay_exists + || delayGreater(arc_delay, delay, min_max, sta_)) { + delay = arc_delay; delay_exists = true; } } @@ -1424,33 +1368,30 @@ Properties::edgeDelay(Edge *edge, PropertyValue Properties::getProperty(TimingArcSet *arc_set, - const std::string property) + std::string_view property) { if (property == "name" || property == "full_name") { if (arc_set->isWire()) return PropertyValue("wire"); else { - const char *from = arc_set->from()->name(); - const char *to = arc_set->to()->name(); - const char *cell_name = arc_set->libertyCell()->name(); - string name; - stringPrint(name, "%s %s -> %s", cell_name, from, to); + std::string name = sta::format("{} {} -> {} {}", + arc_set->libertyCell()->name(), + arc_set->from()->name(), + arc_set->to()->name(), + arc_set->role()->to_string()); return PropertyValue(name); } } - else { - sta_->report()->warn(9000, "%s objects do not have a %s property.", - "timing arc", property.c_str()); - return PropertyValue(); - } + else + throw PropertyUnknown("timing arc", property); } //////////////////////////////////////////////////////////////// PropertyValue Properties::getProperty(const Clock *clk, - const std::string property) + std::string_view property) { if (property == "name" || property == "full_name") @@ -1466,14 +1407,11 @@ Properties::getProperty(const Clock *clk, else if (property == "is_propagated") return PropertyValue(clk->isPropagated()); else { - PropertyValue value = registry_clock_.getProperty(clk, property, sta_); - if (value.type() != PropertyValue::Type::type_none) + PropertyValue value = registry_clock_.getProperty(clk, property, + "clock", sta_); + if (value.type() != PropertyValue::Type::none) return value; - else { - sta_->report()->warn(9000, "%s objects do not have a %s property.", - "clock", property.c_str()); - return PropertyValue(); - } + throw PropertyUnknown("clock", property); } } @@ -1481,7 +1419,7 @@ Properties::getProperty(const Clock *clk, PropertyValue Properties::getProperty(PathEnd *end, - const std::string property) + std::string_view property) { if (property == "startpoint") { PathExpanded expanded(end->path(), sta_); @@ -1518,16 +1456,13 @@ Properties::getProperty(PathEnd *end, } return PropertyValue(&paths); } - else { - sta_->report()->warn(9000, "%s objects do not have a %s property.", - "path end", property.c_str()); - return PropertyValue(); - } + else + throw PropertyUnknown("path end", property); } PropertyValue Properties::getProperty(Path *path, - const std::string property) + std::string_view property) { if (property == "pin") return PropertyValue(path->pin(sta_)); @@ -1537,11 +1472,7 @@ Properties::getProperty(Path *path, return PropertyValue(delayPropertyValue(path->required())); else if (property == "slack") return PropertyValue(delayPropertyValue(path->slack(sta_))); - else { - sta_->report()->warn(9000, "%s objects do not have a %s property.", - "path", property.c_str()); - return PropertyValue(); - } + throw PropertyUnknown("path", property); } PropertyValue @@ -1565,71 +1496,71 @@ Properties::capacitancePropertyValue(float cap) //////////////////////////////////////////////////////////////// void -Properties::defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler) +Properties::defineProperty(std::string_view property, + const PropertyRegistry::PropertyHandler &handler) { registry_library_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler) +Properties::defineProperty(std::string_view property, + const PropertyRegistry::PropertyHandler &handler) { registry_liberty_library_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler) +Properties::defineProperty(std::string_view property, + const PropertyRegistry::PropertyHandler &handler) { registry_cell_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler) +Properties::defineProperty(std::string_view property, + const PropertyRegistry::PropertyHandler &handler) { registry_liberty_cell_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler) +Properties::defineProperty(std::string_view property, + const PropertyRegistry::PropertyHandler &handler) { registry_port_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler) +Properties::defineProperty(std::string_view property, + const PropertyRegistry::PropertyHandler &handler) { registry_liberty_port_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler) +Properties::defineProperty(std::string_view property, + const PropertyRegistry::PropertyHandler &handler) { registry_instance_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler) +Properties::defineProperty(std::string_view property, + const PropertyRegistry::PropertyHandler &handler) { registry_pin_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler) +Properties::defineProperty(std::string_view property, + const PropertyRegistry::PropertyHandler &handler) { registry_net_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, - PropertyRegistry::PropertyHandler handler) +Properties::defineProperty(std::string_view property, + const PropertyRegistry::PropertyHandler &handler) { registry_clock_.defineProperty(property, handler); } @@ -1639,23 +1570,24 @@ Properties::defineProperty(std::string &property, template PropertyValue PropertyRegistry::getProperty(TYPE object, - const std::string &property, + std::string_view property, + std::string_view type_name, Sta *sta) { - auto itr = registry_.find({property}); + auto itr = registry_.find(property); if (itr != registry_.end()) return itr->second(object, sta); else - return PropertyValue(); + throw PropertyUnknown(type_name, property); } template void -PropertyRegistry::defineProperty(const std::string &property, +PropertyRegistry::defineProperty(std::string_view property, PropertyHandler handler) { - registry_[property] = handler; + registry_[std::string(property)] = std::move(handler); } -} // namespace +} // namespace sta diff --git a/search/Property.i b/search/Property.i index bb3853b28..01c8b4c11 100644 --- a/search/Property.i +++ b/search/Property.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,8 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module properties - %{ #include "Property.hh" @@ -37,7 +35,7 @@ using namespace sta; PropertyValue library_property(const Library *lib, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(lib, property); @@ -45,7 +43,7 @@ library_property(const Library *lib, PropertyValue liberty_library_property(const LibertyLibrary *lib, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(lib, property); @@ -53,7 +51,7 @@ liberty_library_property(const LibertyLibrary *lib, PropertyValue cell_property(const Cell *cell, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(cell, property); @@ -61,7 +59,7 @@ cell_property(const Cell *cell, PropertyValue liberty_cell_property(const LibertyCell *cell, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(cell, property); @@ -69,7 +67,7 @@ liberty_cell_property(const LibertyCell *cell, PropertyValue port_property(const Port *port, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(port, property); @@ -77,7 +75,7 @@ port_property(const Port *port, PropertyValue liberty_port_property(const LibertyPort *port, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(port, property); @@ -85,7 +83,7 @@ liberty_port_property(const LibertyPort *port, PropertyValue instance_property(const Instance *inst, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(inst, property); @@ -93,7 +91,7 @@ instance_property(const Instance *inst, PropertyValue pin_property(const Pin *pin, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(pin, property); @@ -101,7 +99,7 @@ pin_property(const Pin *pin, PropertyValue net_property(const Net *net, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(net, property); @@ -109,7 +107,7 @@ net_property(const Net *net, PropertyValue edge_property(Edge *edge, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(edge, property); @@ -117,7 +115,7 @@ edge_property(Edge *edge, PropertyValue clock_property(Clock *clk, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(clk, property); @@ -125,7 +123,7 @@ clock_property(Clock *clk, PropertyValue path_end_property(PathEnd *end, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(end, property); diff --git a/search/ReportPath.cc b/search/ReportPath.cc index eb775c841..1f9849fb1 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,50 +22,52 @@ // // This notice may not be removed or altered from any source distribution. +#include "ReportPath.hh" + #include // reverse #include // SILIMATE: Regex-based endpoint deduplication -#include "ReportPath.hh" +#include +#include -#include "Report.hh" +#include "ArcDelayCalc.hh" +#include "CheckMaxSkews.hh" +#include "CheckMinPeriods.hh" +#include "CheckMinPulseWidths.hh" +#include "ClkInfo.hh" +#include "ContainerHelpers.hh" #include "Error.hh" -#include "StringUtil.hh" +#include "ExceptionPath.hh" +#include "Format.hh" #include "Fuzzy.hh" -#include "Units.hh" -#include "TimingRole.hh" -#include "Transition.hh" -#include "TimingArc.hh" -#include "Liberty.hh" -#include "PortDirection.hh" -#include "Network.hh" +#include "Genclks.hh" #include "Graph.hh" -#include "PortDelay.hh" -#include "ExceptionPath.hh" +#include "GraphDelayCalc.hh" #include "InputDrive.hh" -#include "Sdc.hh" +#include "Latches.hh" +#include "Liberty.hh" +#include "Mode.hh" +#include "Network.hh" #include "Parasitics.hh" -#include "DcalcAnalysisPt.hh" -#include "ArcDelayCalc.hh" -#include "GraphDelayCalc.hh" -#include "ClkInfo.hh" -#include "Tag.hh" #include "ParseBus.hh" -#include "PathAnalysisPt.hh" -#include "PathGroup.hh" -#include "CheckMinPulseWidths.hh" -#include "CheckMinPeriods.hh" -#include "CheckMaxSkews.hh" #include "Path.hh" -#include "Search.hh" #include "PathExpanded.hh" -#include "Latches.hh" -#include "Corner.hh" -#include "Genclks.hh" +#include "PathGroup.hh" +#include "PortDelay.hh" +#include "PortDirection.hh" +#include "Report.hh" +#include "Scene.hh" +#include "Sdc.hh" +#include "Search.hh" +#include "StringUtil.hh" +#include "Tag.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Transition.hh" +#include "Units.hh" #include "Variables.hh" namespace sta { -using std::string; - static void hierPinsAbove(const Net *net, const Network *network, @@ -80,52 +82,39 @@ hierPinsThruEdge(const Edge *edge, const Network *network, const Graph *graph); -ReportField::ReportField(const char *name, - const char *title, - int width, - bool left_justify, - Unit *unit, - bool enabled) : +ReportField::ReportField(std::string_view name, + std::string_view title, + size_t width, + bool left_justify, + Unit *unit, + bool enabled) : name_(name), - title_(stringCopy(title)), + title_(title), left_justify_(left_justify), unit_(unit), - enabled_(enabled), - blank_(nullptr) + enabled_(enabled) { setWidth(width); } ReportField::~ReportField() -{ - stringDelete(title_); - stringDelete(blank_); -} += default; void -ReportField::setProperties(const char *title, - int width, - bool left_justify) +ReportField::setProperties(std::string_view title, + size_t width, + bool left_justify) { - if (title_) - stringDelete(title_); - title_ = stringCopy(title); + title_ = std::string(title); left_justify_ = left_justify; setWidth(width); } void -ReportField::setWidth(int width) +ReportField::setWidth(size_t width) { width_ = width; - - if (blank_) - stringDelete(blank_); - blank_ = new char[width_ + 1]; - int i; - for (i = 0; i < width_; i++) - blank_[i] = ' '; - blank_[i] = '\0'; + blank_.assign(width_, ' '); } void @@ -136,23 +125,12 @@ ReportField::setEnabled(bool enabled) //////////////////////////////////////////////////////////////// -const float ReportPath::field_blank_ = -1.0; - ReportPath::ReportPath(StaState *sta) : - StaState(sta), - format_(ReportPathFormat::full), - dedup_by_word_(false), - dedup_same_delay_(false), - silimate_dedup_endpoints_rx_(std::nullopt), - no_split_(false), - report_sigmas_(false), - start_end_pt_width_(80), - plus_zero_(nullptr), - minus_zero_(nullptr) + StaState(sta) { - setDigits(2); makeFields(); - setReportFields(false, false, false, false, false, false, false); + setDigits(2); + setReportFields(false, false, false, false, false, false, false, false); } ReportPath::~ReportPath() @@ -163,70 +141,68 @@ ReportPath::~ReportPath() delete field_capacitance_; delete field_slew_; delete field_fanout_; + delete field_variation_; delete field_src_attr_; delete field_edge_; delete field_case_; - - stringDelete(plus_zero_); - stringDelete(minus_zero_); } void ReportPath::makeFields() { + // The order corresponds to the default field order. field_fanout_ = makeField("fanout", "Fanout", 6, false, nullptr, true); field_capacitance_ = makeField("capacitance", "Cap", 6, false, - units_->capacitanceUnit(), true); + units_->capacitanceUnit(), true); field_slew_ = makeField("slew", "Slew", 6, false, units_->timeUnit(), - true); + true); field_incr_ = makeField("incr", "Delay", 6, false, units_->timeUnit(), - true); + true); + field_variation_ = makeField("variation", "Variation", 6, false, + units_->timeUnit(), false); field_total_ = makeField("total", "Time", 6, false, units_->timeUnit(), - true); + true); field_edge_ = makeField("edge", "", 1, false, nullptr, true); field_case_ = makeField("case", "case", 11, false, nullptr, false); field_description_ = makeField("description", "Description", 36, - true, nullptr, true); + true, nullptr, true); field_src_attr_ = makeField("src_attr", "Src Attr", 40, - true, nullptr, true); + true, nullptr, true); } ReportField * -ReportPath::makeField(const char *name, - const char *title, - int width, - bool left_justify, - Unit *unit, - bool enabled) +ReportPath::makeField(std::string_view name, + std::string_view title, + int width, + bool left_justify, + Unit *unit, + bool enabled) { ReportField *field = new ReportField(name, title, width, left_justify, - unit, enabled); + unit, enabled); fields_.push_back(field); return field; } ReportField * -ReportPath::findField(const char *name) const +ReportPath::findField(std::string_view name) const { for (ReportField *field : fields_) { - if (stringEq(name, field->name())) + if (field->name() == name) return field; } return nullptr; } void -ReportPath::setReportFieldOrder(StringSeq *field_names) +ReportPath::setReportFieldOrder(const StringSeq &field_names) { // Disable all fields. - ReportFieldSeq::Iterator field_iter1(fields_); - while (field_iter1.hasNext()) { - ReportField *field = field_iter1.next(); + for (ReportField *field : fields_) field->setEnabled(false); - } ReportFieldSeq next_fields; - for (const char *field_name : *field_names) { + for (const std::string &field_name : field_names) { ReportField *field = findField(field_name); if (field) { next_fields.push_back(field); @@ -247,11 +223,12 @@ ReportPath::setReportFieldOrder(StringSeq *field_names) void ReportPath::setReportFields(bool report_input_pin, bool report_hier_pins, - bool report_net, - bool report_cap, - bool report_slew, - bool report_fanout, - bool report_src_attr) + bool report_net, + bool report_cap, + bool report_slew, + bool report_fanout, + bool report_variation, + bool report_src_attr) { report_input_pin_ = report_input_pin; report_hier_pins_ = report_hier_pins; @@ -260,6 +237,7 @@ ReportPath::setReportFields(bool report_input_pin, field_capacitance_->setEnabled(report_cap); field_slew_->setEnabled(report_slew); field_fanout_->setEnabled(report_fanout); + field_variation_->setEnabled(report_variation); field_src_attr_->setEnabled(report_src_attr); // for debug field_case_->setEnabled(false); @@ -281,17 +259,16 @@ void ReportPath::setDigits(int digits) { digits_ = digits; + minus_zero_ = sta::formatRuntime("-{:.{}f}", 0.0, digits_); + plus_zero_ = sta::formatRuntime("{:.{}f}", 0.0, digits_); - stringDelete(plus_zero_); - stringDelete(minus_zero_); - minus_zero_ = stringPrint("-%.*f", digits_, 0.0); - plus_zero_ = stringPrint("%.*f", digits_, 0.0); -} - -void -ReportPath::setReportSigmas(bool report) -{ - report_sigmas_ = report; + // Numeric field width expands with digits. + int field_width = digits + field_width_extra_; + field_capacitance_->setWidth(field_width); + field_slew_->setWidth(field_width); + field_variation_->setWidth(field_width); + field_incr_->setWidth(field_width); + field_total_->setWidth(field_width); } void @@ -317,12 +294,13 @@ ReportPath::setSilimateDedupEndpointRegex(std::string_view silimate_dedup_endpoi void ReportPath::reportPathEnd(const PathEnd *end) const { - reportPathEnd(end, nullptr); + reportPathEnd(end, nullptr, true); } void ReportPath::reportPathEnd(const PathEnd *end, - const PathEnd *prev_end) const + const PathEnd *prev_end, + bool last) const { switch (format_) { case ReportPathFormat::full: @@ -348,17 +326,17 @@ ReportPath::reportPathEnd(const PathEnd *end, reportSlackOnly(end); break; case ReportPathFormat::json: - reportJson(end, prev_end); + reportJson(end, last); break; } } -inline const char *getEndpointName(const StaState *state, +inline std::string getEndpointName(const StaState *state, const sta::Network *sdc_network, PathEnd *end) { PathExpanded expanded(end->path(), state); const Pin *end_pin = expanded.endPath()->vertex(state)->pin(); - const char *endpoint_name = sdc_network->pathName(end_pin); + std::string endpoint_name = sdc_network->pathName(end_pin); return endpoint_name; } @@ -366,7 +344,7 @@ inline std::string getBusName(const StaState *state, const sta::Network *sdc_network, PathEnd *end) { char escape = sdc_network->pathEscape(); - const char *endpoint_name = getEndpointName(state, sdc_network, end); + std::string endpoint_name = getEndpointName(state, sdc_network, end); bool is_bus; std::string bus_name; int index; @@ -389,16 +367,17 @@ ReportPath::reportPathEnds(const PathEndSeq *ends) const reportPathEndHeader(); if (ends && !ends->empty()) { PathEnd *prev_end = nullptr; - PathEndSeq::ConstIterator end_iter(ends); + PathEndSeq::const_iterator end_iter = ends->begin(); + PathEndSeq::const_iterator end_iter_end = ends->end(); // maybe ends wasn't the best way to name them - Set qualified_endpoints; + std::set qualified_endpoints; if (silimate_dedup_endpoints_rx_.has_value()) { std::regex primary_rx(*silimate_dedup_endpoints_rx_); std::regex slice_rx(R"(\\?\[([0-9]+)(?::([0-9]+))?\\?\])"); - Set endpoints; - while (end_iter.hasNext()) { - PathEnd *end = end_iter.next(); + std::set endpoints; + while (end_iter != end_iter_end) { + PathEnd *end = *end_iter++; auto endpoint_name = getEndpointName(this, sdc_network_, end); auto endpoint_clean = std::regex_replace(endpoint_name, primary_rx, ""); endpoint_clean = std::regex_replace(endpoint_clean, slice_rx, ""); @@ -409,9 +388,9 @@ ReportPath::reportPathEnds(const PathEndSeq *ends) const } } else if (dedup_by_word_) { - Map worst_slack_by_bus; - while (end_iter.hasNext()) { - PathEnd *end = end_iter.next(); + std::map worst_slack_by_bus; + while (end_iter != end_iter_end) { + PathEnd *end = *end_iter++; auto bus_name = getBusName(this, sdc_network_, end); if (bus_name.length()) { if (worst_slack_by_bus.count(bus_name) == 0 || @@ -426,26 +405,39 @@ ReportPath::reportPathEnds(const PathEndSeq *ends) const qualified_endpoints.insert(end); } - end_iter = ends; + end_iter = ends->begin(); - // Reiterate to maintain order; also filter delays - Set found_delays; - while (end_iter.hasNext()) { - auto end = end_iter.next(); + // Second filtering pass: create final list with order preserved, optionally + // also filtered by delay + PathEndSeq filtered; + filtered.reserve(qualified_endpoints.size()); + + std::set found_delays; + while (end_iter != end_iter_end) { + PathEnd *end = *end_iter++; auto end_delay = end->dataArrivalTime(this); if ( (!qualified_endpoints.size() || qualified_endpoints.count(end)) && (!dedup_same_delay_ || !found_delays.count(end_delay)) ) { - reportPathEnd(end, prev_end); - found_delays.insert(end_delay); - prev_end = end; + filtered.push_back(end); + found_delays.insert(end_delay); + prev_end = end; } - } + } + + // Report final list + end_iter = filtered.begin(); + end_iter_end = filtered.end(); + prev_end = nullptr; + while (end_iter != end_iter_end) { + PathEnd *end = *end_iter++; + reportPathEnd(end, prev_end, end_iter == end_iter_end); + } } else { if (format_ != ReportPathFormat::json) - report_->reportLine("No paths found."); + report_->report("No paths found."); } reportPathEndFooter(); } @@ -475,7 +467,6 @@ ReportPath::reportPathEndHeader() const void ReportPath::reportPathEndFooter() const { - string header; switch (format_) { case ReportPathFormat::full: case ReportPathFormat::full_clock: @@ -495,7 +486,7 @@ ReportPath::reportPathEndFooter() const void ReportPath::reportEndpointHeader(const PathEnd *end, - const PathEnd *prev_end) const + const PathEnd *prev_end) const { PathGroup *prev_group = nullptr; if (prev_end) @@ -507,9 +498,7 @@ ReportPath::reportEndpointHeader(const PathEnd *end, const char *setup_hold = (end->minMax(this) == MinMax::min()) ? "min_delay/hold" : "max_delay/setup"; - report_->reportLine("%s group %s", - setup_hold, - group->name()); + report_->report("{} group {}", setup_hold, group->name()); reportBlankLine(); reportEndHeader(); } @@ -526,7 +515,7 @@ ReportPath::reportShort(const PathEndUnconstrained *end) const void ReportPath::reportShort(const PathEndUnconstrained *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportStartpoint(end, expanded); reportUnclockedEndpoint(end, "internal pin"); @@ -542,9 +531,9 @@ ReportPath::reportFull(const PathEndUnconstrained *end) const reportPath(end, expanded); reportLine("data arrival time", end->dataArrivalTimeOffset(this), - end->pathEarlyLate(this)); + end->pathEarlyLate(this)); reportDashLine(); - report_->reportLine("(Path is unconstrained)"); + report_->report("(Path is unconstrained)"); } //////////////////////////////////////////////////////////////// @@ -558,7 +547,7 @@ ReportPath::reportShort(const PathEndCheck *end) const void ReportPath::reportShort(const PathEndCheck *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportStartpoint(end, expanded); reportEndpoint(end); @@ -576,41 +565,41 @@ ReportPath::reportFull(const PathEndCheck *end) const reportSlack(end); } -string +std::string ReportPath::checkRoleString(const PathEnd *end) const { - return stdstrPrint("library %s time", - end->checkRole(this)->to_string().c_str()); + return sta::format("library {} time", + end->checkRole(this)->to_string()); } void ReportPath::reportEndpoint(const PathEndCheck *end) const { Instance *inst = network_->instance(end->vertex(this)->pin()); - const char *inst_name = cmd_network_->pathName(inst); - string clk_name = tgtClkName(end); - const char *rise_fall = asRisingFalling(end->targetClkEndTrans(this)); + std::string inst_name = cmd_network_->pathName(inst); + std::string clk_name = tgtClkName(end); + std::string_view rise_fall = asRisingFalling(end->targetClkEndTrans(this)); const TimingRole *check_role = end->checkRole(this); const TimingRole *check_generic_role = check_role->genericRole(); if (check_role == TimingRole::recovery() || check_role == TimingRole::removal()) { - auto reason = stdstrPrint("%s check against %s-edge clock %s", - check_role->to_string().c_str(), - rise_fall, - clk_name.c_str()); + std::string reason = sta::format("{} check against {}-edge clock {}", + check_role->to_string(), + rise_fall, + clk_name); reportEndpoint(inst_name, reason); } else if (check_generic_role == TimingRole::setup() - || check_generic_role == TimingRole::hold()) { + || check_generic_role == TimingRole::hold()) { LibertyCell *cell = network_->libertyCell(inst); if (cell->isClockGate()) { - auto reason = stdstrPrint("%s clock gating-check end-point clocked by %s", - rise_fall, clk_name.c_str()); + std::string reason = sta::format("{} clock gating-check end-point clocked by {}", + rise_fall, clk_name); reportEndpoint(inst_name, reason); } else { - const char *reg_desc = clkRegLatchDesc(end); - auto reason = stdstrPrint("%s clocked by %s", reg_desc, clk_name.c_str()); + std::string_view reg_desc = clkRegLatchDesc(end); + std::string reason = sta::format("{} clocked by {}", reg_desc, clk_name); reportEndpoint(inst_name, reason); } } @@ -627,7 +616,7 @@ ReportPath::reportShort(const PathEndLatchCheck *end) const void ReportPath::reportShort(const PathEndLatchCheck *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportStartpoint(end, expanded); reportEndpoint(end); @@ -657,19 +646,19 @@ ReportPath::reportFull(const PathEndLatchCheck *end) const Required req_time; Arrival borrow, adjusted_data_arrival, time_given_to_startpoint; end->latchRequired(this, req_time, borrow, adjusted_data_arrival, - time_given_to_startpoint); + time_given_to_startpoint); // Adjust required to requiredTimeOffset. - req_time += end->sourceClkOffset(this); + req_time = delaySum(req_time, end->sourceClkOffset(this), this); if (path_delay) { float delay = path_delay->delay(); reportLine("max_delay", delay, delay, early_late); if (!ignore_clk_latency) { if (reportClkPath() - && isPropagated(end->targetClkPath())) - reportTgtClk(end, delay); + && isPropagated(end->targetClkPath())) + reportTgtClk(end, delay); else { - Delay delay1(delay); - reportCommonClkPessimism(end, delay1); + Delay delay1(delay); + reportCommonClkPessimism(end, delay1); } } } @@ -694,14 +683,14 @@ void ReportPath::reportEndpoint(const PathEndLatchCheck *end) const { Instance *inst = network_->instance(end->vertex(this)->pin()); - const char *inst_name = cmd_network_->pathName(inst); - string clk_name = tgtClkName(end); - const char *reg_desc = latchDesc(end); - auto reason = stdstrPrint("%s clocked by %s", reg_desc, clk_name.c_str()); + std::string clk_name = tgtClkName(end); + std::string_view reg_desc = latchDesc(end); + std::string reason = sta::format("{} clocked by {}", reg_desc, clk_name); + std::string inst_name = cmd_network_->pathName(inst); reportEndpoint(inst_name, reason); } -const char * +std::string_view ReportPath::latchDesc(const PathEndLatchCheck *end) const { TimingArc *check_arc = end->checkArc(); @@ -711,8 +700,8 @@ ReportPath::latchDesc(const PathEndLatchCheck *end) const void ReportPath::reportBorrowing(const PathEndLatchCheck *end, - Arrival &borrow, - Arrival &time_given_to_startpoint) const + Arrival &borrow, + Arrival &time_given_to_startpoint) const { Delay open_latency, latency_diff, max_borrow; float nom_pulse_width, open_uncertainty; @@ -720,41 +709,41 @@ ReportPath::reportBorrowing(const PathEndLatchCheck *end, bool borrow_limit_exists; const EarlyLate *early_late = EarlyLate::late(); end->latchBorrowInfo(this, nom_pulse_width, open_latency, latency_diff, - open_uncertainty, open_crpr, crpr_diff, - max_borrow, borrow_limit_exists); - report_->reportLine("Time Borrowing Information"); + open_uncertainty, open_crpr, crpr_diff, + max_borrow, borrow_limit_exists); + report_->report("Time Borrowing Information"); reportDashLineTotal(); if (borrow_limit_exists) reportLineTotal("user max time borrow", max_borrow, early_late); else { - string tgt_clk_name = tgtClkName(end); + std::string tgt_clk_name = tgtClkName(end); Arrival tgt_clk_width = end->targetClkWidth(this); const Path *tgt_clk_path = end->targetClkPath(); if (tgt_clk_path->clkInfo(search_)->isPropagated()) { - auto width_msg = stdstrPrint("%s nominal pulse width", tgt_clk_name.c_str()); - reportLineTotal(width_msg.c_str(), nom_pulse_width, early_late); - if (!delayZero(latency_diff)) - reportLineTotalMinus("clock latency difference", latency_diff, early_late); + std::string width_msg = sta::format("{} nominal pulse width", tgt_clk_name); + reportLineTotal(width_msg, nom_pulse_width, early_late); + if (!delayZero(latency_diff, this)) + reportLineTotalMinus("clock latency difference", latency_diff, early_late); } else { - auto width_msg = stdstrPrint("%s pulse width", tgt_clk_name.c_str()); - reportLineTotal(width_msg.c_str(), tgt_clk_width, early_late); + std::string width_msg = sta::format("{} pulse width", tgt_clk_name); + reportLineTotal(width_msg, tgt_clk_width, early_late); } ArcDelay margin = end->margin(this); reportLineTotalMinus("library setup time", margin, early_late); reportDashLineTotal(); - if (!delayZero(crpr_diff)) + if (!delayZero(crpr_diff, this)) reportLineTotalMinus("CRPR difference", crpr_diff, early_late); reportLineTotal("max time borrow", max_borrow, early_late); } if (delayGreater(borrow, delay_zero, this) && (!fuzzyZero(open_uncertainty) - || !delayZero(open_crpr))) { + || !delayZero(open_crpr, this))) { reportDashLineTotal(); reportLineTotal("actual time borrow", borrow, early_late); if (!fuzzyZero(open_uncertainty)) reportLineTotal("open edge uncertainty", open_uncertainty, early_late); - if (!delayZero(open_crpr)) + if (!delayZero(open_crpr, this)) reportLineTotal("open edge CRPR", open_crpr, early_late); reportDashLineTotal(); reportLineTotal("time given to startpoint", time_given_to_startpoint, early_late); @@ -775,7 +764,7 @@ ReportPath::reportShort(const PathEndPathDelay *end) const void ReportPath::reportShort(const PathEndPathDelay *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportStartpoint(end, expanded); if (end->targetClk(this)) @@ -792,11 +781,10 @@ ReportPath::reportEndpoint(const PathEndPathDelay *end) const reportEndpointOutputDelay(end); else { Instance *inst = network_->instance(end->vertex(this)->pin()); - const char *inst_name = cmd_network_->pathName(inst); - string clk_name = tgtClkName(end); - const char *reg_desc = clkRegLatchDesc(end); - auto reason = stdstrPrint("%s clocked by %s", reg_desc, clk_name.c_str()); - reportEndpoint(inst_name, reason); + std::string reason = sta::format("{} clocked by {}", + clkRegLatchDesc(end), + tgtClkName(end)); + reportEndpoint(cmd_network_->pathName(inst), reason); } } @@ -823,26 +811,26 @@ ReportPath::reportFull(const PathEndPathDelay *end) const ArcDelay margin = end->margin(this); const MinMax *min_max = path_delay->minMax()->asMinMax(); if (min_max == MinMax::max()) - margin = -margin; + margin = delayDiff(delay_zero, margin, this); - string delay_msg = min_max->to_string() + "_delay"; + std::string delay_msg = min_max->to_string() + "_delay"; float delay = path_delay->delay(); - reportLine(delay_msg.c_str(), delay, delay, early_late); + reportLine(delay_msg, delay, delay, early_late); if (!path_delay->ignoreClkLatency()) { const Clock *tgt_clk = end->targetClk(this); if (tgt_clk) { const Path *tgt_clk_path = end->targetClkPath(); if (reportClkPath() - && isPropagated(tgt_clk_path, tgt_clk)) - reportTgtClk(end, delay, 0.0, true); + && isPropagated(tgt_clk_path, tgt_clk)) + reportTgtClk(end, delay, 0.0, true); else { - Arrival tgt_clk_delay = end->targetClkDelay(this); - Arrival tgt_clk_arrival = delay + tgt_clk_delay; - if (!delayZero(tgt_clk_delay)) - reportLine(clkNetworkDelayIdealProp(isPropagated(tgt_clk_path)), - tgt_clk_delay, tgt_clk_arrival, early_late); - reportClkUncertainty(end, tgt_clk_arrival); - reportCommonClkPessimism(end, tgt_clk_arrival); + Arrival tgt_clk_delay = end->targetClkDelay(this); + Arrival tgt_clk_arrival = delaySum(tgt_clk_delay, delay, this); + if (!delayZero(tgt_clk_delay, this)) + reportLine(clkNetworkDelayIdealProp(isPropagated(tgt_clk_path)), + tgt_clk_delay, tgt_clk_arrival, early_late); + reportClkUncertainty(end, tgt_clk_arrival); + reportCommonClkPessimism(end, tgt_clk_arrival); } } } @@ -861,7 +849,7 @@ ReportPath::isPropagated(const Path *clk_path) const bool ReportPath::isPropagated(const Path *clk_path, - const Clock *clk) const + const Clock *clk) const { if (clk_path) return clk_path->clkInfo(search_)->isPropagated(); @@ -869,7 +857,7 @@ ReportPath::isPropagated(const Path *clk_path, return clk->isPropagated(); } -const char * +std::string_view ReportPath::clkNetworkDelayIdealProp(bool is_prop) const { if (is_prop) @@ -889,7 +877,7 @@ ReportPath::reportShort(const PathEndOutputDelay *end) const void ReportPath::reportShort(const PathEndOutputDelay *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportStartpoint(end, expanded); reportEndpoint(end); @@ -918,13 +906,13 @@ ReportPath::reportEndpointOutputDelay(const PathEndClkConstrained *end) const { Vertex *vertex = end->vertex(this); Pin *pin = vertex->pin(); - const char *pin_name = cmd_network_->pathName(pin); + std::string pin_name = cmd_network_->pathName(pin); const Clock *tgt_clk = end->targetClk(this); if (network_->isTopLevelPort(pin)) { // Pin direction is "output" even for bidirects. if (tgt_clk) { - string clk_name = tgtClkName(end); - auto reason = stdstrPrint("output port clocked by %s", clk_name.c_str()); + std::string reason = sta::format("output port clocked by {}", + tgtClkName(end)); reportEndpoint(pin_name, reason); } else @@ -932,9 +920,8 @@ ReportPath::reportEndpointOutputDelay(const PathEndClkConstrained *end) const } else { if (tgt_clk) { - string clk_name = tgtClkName(end); - auto reason = stdstrPrint("internal path endpoint clocked by %s", - clk_name.c_str()); + std::string reason = sta::format("internal path endpoint clocked by {}", + tgtClkName(end)); reportEndpoint(pin_name, reason); } @@ -954,7 +941,7 @@ ReportPath::reportShort(const PathEndGatedClock *end) const void ReportPath::reportShort(const PathEndGatedClock *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportStartpoint(end, expanded); reportEndpoint(end); @@ -976,16 +963,15 @@ void ReportPath::reportEndpoint(const PathEndGatedClock *end) const { Instance *inst = network_->instance(end->vertex(this)->pin()); - const char *inst_name = cmd_network_->pathName(inst); - string clk_name = tgtClkName(end); + const std::string inst_name = cmd_network_->pathName(inst); const RiseFall *clk_end_rf = end->targetClkEndTrans(this); - const RiseFall *clk_rf = - (end->minMax(this) == MinMax::max()) ? clk_end_rf : clk_end_rf->opposite(); - const char *rise_fall = asRisingFalling(clk_rf); + const RiseFall *clk_rf = (end->minMax(this) == MinMax::max()) + ? clk_end_rf + : clk_end_rf->opposite(); // Note that target clock transition is ignored. - auto reason = stdstrPrint("%s clock gating-check end-point clocked by %s", - rise_fall, - clk_name.c_str()); + std::string reason = sta::format("{} clock gating-check end-point clocked by {}", + asRisingFalling(clk_rf), + tgtClkName(end)); reportEndpoint(inst_name, reason); } @@ -1000,7 +986,7 @@ ReportPath::reportShort(const PathEndDataCheck *end) const void ReportPath::reportShort(const PathEndDataCheck *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportStartpoint(end, expanded); reportEndpoint(end); @@ -1027,13 +1013,14 @@ ReportPath::reportFull(const PathEndDataCheck *end) const PathExpanded clk_expanded(data_clk_path, this); float src_offset = end->sourceClkOffset(this); Delay clk_delay = end->targetClkDelay(this); + const MinMax *min_max = data_clk_path->minMax(this); Arrival clk_arrival = end->targetClkArrival(this); const ClockEdge *tgt_clk_edge = end->targetClkEdge(this); - float prev = delayAsFloat(clk_arrival) + src_offset; - float offset = prev - delayAsFloat(clk_delay) - tgt_clk_edge->time(); + float prev = delayAsFloat(clk_arrival, min_max, this) + src_offset; + float offset = prev - delayAsFloat(clk_delay, min_max, this) - tgt_clk_edge->time(); // Delay to startpoint is already included. reportPath6(data_clk_path, clk_expanded, clk_expanded.startIndex(), - true, false, prev, offset); + true, false, prev, offset); } reportRequired(end, checkRoleReason(end)); reportSlack(end); @@ -1043,12 +1030,10 @@ void ReportPath::reportEndpoint(const PathEndDataCheck *end) const { Instance *inst = network_->instance(end->vertex(this)->pin()); - const char *inst_name = cmd_network_->pathName(inst); - const char *tgt_clk_rf = asRisingFalling(end->dataClkPath()->transition(this)); - const char *tgt_clk_name = end->targetClk(this)->name(); - auto reason = stdstrPrint("%s edge-triggered data to data check clocked by %s", - tgt_clk_rf, - tgt_clk_name); + const std::string inst_name = cmd_network_->pathName(inst); + std::string reason = sta::format("{} edge-triggered data to data check clocked by {}", + asRisingFalling(end->dataClkPath()->transition(this)), + end->targetClk(this)->name()); reportEndpoint(inst_name, reason); } @@ -1057,14 +1042,14 @@ ReportPath::reportEndpoint(const PathEndDataCheck *end) const void ReportPath::reportEndHeader() const { - string line; + std::string line; // Line one. reportDescription("", line); line += ' '; reportField("Required", field_total_, line); line += ' '; reportField("Actual", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); // Line two. line.clear(); @@ -1075,7 +1060,7 @@ ReportPath::reportEndHeader() const reportField("Delay", field_total_, line); line += ' '; reportField("Slack", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); reportDashLine(field_description_->width() + field_total_->width() * 3 + 3); } @@ -1083,14 +1068,14 @@ ReportPath::reportEndHeader() const void ReportPath::reportEndLine(const PathEnd *end) const { - string line; - string endpoint = pathEndpoint(end); - reportDescription(endpoint.c_str(), line); + std::string line; + std::string endpoint = pathEndpoint(end); + reportDescription(endpoint, line); const EarlyLate *early_late = end->pathEarlyLate(this); reportSpaceFieldDelay(end->requiredTimeOffset(this), early_late, line); reportSpaceFieldDelay(end->dataArrivalTimeOffset(this), early_late, line); reportSpaceSlack(end, line); - report_->reportLineString(line); + report_->reportLine(line); } //////////////////////////////////////////////////////////////// @@ -1098,13 +1083,13 @@ ReportPath::reportEndLine(const PathEnd *end) const void ReportPath::reportSummaryHeader() const { - string line; + std::string line; reportDescription("Startpoint", line); line += ' '; reportDescription("Endpoint", line); line += ' '; reportField("Slack", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); reportDashLine(field_description_->width() * 2 + field_total_->width() + 1); } @@ -1112,52 +1097,52 @@ ReportPath::reportSummaryHeader() const void ReportPath::reportSummaryLine(const PathEnd *end) const { - string line; + std::string line; PathExpanded expanded(end->path(), this); const EarlyLate *early_late = end->pathEarlyLate(this); - auto startpoint = pathStartpoint(end, expanded); - reportDescription(startpoint.c_str(), line); + std::string startpoint = pathStartpoint(end, expanded); + reportDescription(startpoint, line); line += ' '; - auto endpoint = pathEndpoint(end); - reportDescription(endpoint.c_str(), line); + std::string endpoint = pathEndpoint(end); + reportDescription(endpoint, line); if (end->isUnconstrained()) reportSpaceFieldDelay(end->dataArrivalTimeOffset(this), early_late, line); else reportSpaceFieldDelay(end->slack(this), EarlyLate::early(), line); - report_->reportLineString(line); + report_->reportLine(line); } -string +std::string ReportPath::pathStartpoint(const PathEnd *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { const Path *start = expanded.startPath(); Pin *pin = start->pin(graph_); - const char *pin_name = cmd_network_->pathName(pin); + std::string pin_name = cmd_network_->pathName(pin); if (network_->isTopLevelPort(pin)) { PortDirection *dir = network_->direction(pin); - return stdstrPrint("%s (%s)", pin_name, dir->name()); + return sta::format("{} ({})", pin_name, dir->name()); } else { Instance *inst = network_->instance(end->vertex(this)->pin()); - const char *cell_name = cmd_network_->name(network_->cell(inst)); - return stdstrPrint("%s (%s)", pin_name, cell_name); + std::string cell_name = cmd_network_->name(network_->cell(inst)); + return sta::format("{} ({})", pin_name, cell_name); } } -string +std::string ReportPath::pathEndpoint(const PathEnd *end) const { Pin *pin = end->vertex(this)->pin(); - const char *pin_name = cmd_network_->pathName(pin); + std::string pin_name = cmd_network_->pathName(pin); if (network_->isTopLevelPort(pin)) { PortDirection *dir = network_->direction(pin); - return stdstrPrint("%s (%s)", pin_name, dir->name()); + return sta::format("{} ({})", pin_name, dir->name()); } else { Instance *inst = network_->instance(end->vertex(this)->pin()); - const char *cell_name = cmd_network_->name(network_->cell(inst)); - return stdstrPrint("%s (%s)", pin_name, cell_name); + std::string cell_name = cmd_network_->name(network_->cell(inst)); + return sta::format("{} ({})", pin_name, cell_name); } } @@ -1166,46 +1151,49 @@ ReportPath::pathEndpoint(const PathEnd *end) const void ReportPath::reportJsonHeader() const { - report_->reportLine("{\"checks\": ["); + report_->report("{{\"checks\": ["); } void ReportPath::reportJsonFooter() const { - report_->reportLine("]"); - report_->reportLine("}"); + report_->report("]"); + report_->report("}}"); } void ReportPath::reportJson(const PathEnd *end, - const PathEnd *prev_end) const + bool last) const { - string result; - if (prev_end) { - result += ", "; - } + std::string result; result += "{\n"; - stringAppend(result, " \"type\": \"%s\",\n", end->typeName()); - stringAppend(result, " \"path_group\": \"%s\",\n", - end->pathGroup()->name()); - stringAppend(result, " \"path_type\": \"%s\",\n", - end->minMax(this)->to_string().c_str()); + result += " \"type\": \""; + result += end->typeName(); + result += "\",\n"; + + result += " \"path_group\": \""; + result += end->pathGroup()->name(); + result += "\",\n"; + + result += " \"path_type\": \""; + result += end->minMax(this)->to_string(); + result += "\",\n"; PathExpanded expanded(end->path(), this); const Pin *startpoint = expanded.startPath()->vertex(this)->pin(); const Pin *endpoint = expanded.endPath()->vertex(this)->pin(); - stringAppend(result, " \"startpoint\": \"%s\",\n", + result += sta::format(" \"startpoint\": \"{}\",\n", sdc_network_->pathName(startpoint)); - stringAppend(result, " \"endpoint\": \"%s\",\n", + result += sta::format(" \"endpoint\": \"{}\",\n", sdc_network_->pathName(endpoint)); const ClockEdge *src_clk_edge = end->sourceClkEdge(this); const Path *src_clk_path = expanded.clkPath(); const Path *tgt_clk_path = end->targetClkPath(); if (src_clk_edge) { - stringAppend(result, " \"source_clock\": \"%s\",\n", + result += sta::format(" \"source_clock\": \"{}\",\n", src_clk_edge->clock()->name()); - stringAppend(result, " \"source_clock_edge\": \"%s\",\n", + result += sta::format(" \"source_clock_edge\": \"{}\",\n", src_clk_edge->transition()->name()); } if (src_clk_path) @@ -1214,57 +1202,59 @@ ReportPath::reportJson(const PathEnd *end, const ClockEdge *tgt_clk_edge = end->targetClkEdge(this); if (tgt_clk_edge) { - stringAppend(result, " \"target_clock\": \"%s\",\n", + result += sta::format(" \"target_clock\": \"{}\",\n", tgt_clk_edge->clock()->name()); - stringAppend(result, " \"target_clock_edge\": \"%s\",\n", + result += sta::format(" \"target_clock_edge\": \"{}\",\n", tgt_clk_edge->transition()->name()); } if (tgt_clk_path) reportJson(end->targetClkPath(), "target_clock_path", 2, true, result); if (end->checkRole(this)) { - stringAppend(result, " \"data_arrival_time\": %.3e,\n", + result += sta::format(" \"data_arrival_time\": {:.3e},\n", delayAsFloat(end->dataArrivalTimeOffset(this))); const MultiCyclePath *mcp = end->multiCyclePath(); if (mcp) - stringAppend(result, " \"multi_cycle_path\": %d,\n", + result += sta::format(" \"multi_cycle_path\": {},\n", mcp->pathMultiplier()); PathDelay *path_delay = end->pathDelay(); if (path_delay) - stringAppend(result, " \"path_delay\": %.3e,\n", + result += sta::format(" \"path_delay\": {:.3e},\n", path_delay->delay()); - stringAppend(result, " \"crpr\": %.3e,\n", + result += sta::format(" \"crpr\": {:.3e},\n", delayAsFloat(end->checkCrpr(this))); - stringAppend(result, " \"margin\": %.3e,\n", + result += sta::format(" \"margin\": {:.3e},\n", delayAsFloat(end->margin(this))); - stringAppend(result, " \"required_time\": %.3e,\n", + result += sta::format(" \"required_time\": {:.3e},\n", delayAsFloat(end->requiredTimeOffset(this))); - stringAppend(result, " \"slack\": %.3e\n", + result += sta::format(" \"slack\": {:.3e}\n", delayAsFloat(end->slack(this))); } result += "}"; - report_->reportLineString(result); + if (!last) + result += ","; + report_->reportLine(result); } void ReportPath::reportJson(const Path *path) const { - string result; + std::string result; result += "{\n"; reportJson(path, "path", 0, false, result); result += "}\n"; - report_->reportLineString(result); + report_->reportLine(result); } void ReportPath::reportJson(const Path *path, - const char *path_name, + std::string_view path_name, int indent, bool trailing_comma, - string &result) const + std::string &result) const { PathExpanded expanded(path, this); reportJson(expanded, path_name, indent, trailing_comma, result); @@ -1272,85 +1262,86 @@ ReportPath::reportJson(const Path *path, void ReportPath::reportJson(const PathExpanded &expanded, - const char *path_name, + std::string_view path_name, int indent, bool trailing_comma, - string &result) const + std::string &result) const { - stringAppend(result, "%*s\"%s\": [\n", indent, "", path_name); + result += sta::format("{:>{}}\"{}\": [\n", "", indent, path_name); for (size_t i = expanded.startIndex(); i < expanded.size(); i++) { const Path *path = expanded.path(i); const Pin *pin = path->vertex(this)->pin(); const Net *net = network_->net(pin); const Instance *inst = network_->instance(pin); const RiseFall *rf = path->transition(this); - DcalcAnalysisPt *dcalc_ap = path->pathAnalysisPt(this)->dcalcAnalysisPt(); + const Scene *scene = path->scene(this); + const MinMax *min_max = path->minMax(this); bool is_driver = network_->isDriver(pin); - stringAppend(result, "%*s {\n", indent, ""); + result += sta::format("{:>{}} {{\n", "", indent); if (inst) { - stringAppend(result, "%*s \"instance\": \"%s\",\n", - indent, "", - sdc_network_->pathName(inst)); + result += sta::format("{:>{}} \"instance\": \"{}\",\n", + "", indent, + sdc_network_->pathName(inst)); Cell *cell = network_->cell(inst); if (cell) - stringAppend(result, "%*s \"cell\": \"%s\",\n", - indent, "", - sdc_network_->name(cell)); - stringAppend(result, "%*s \"verilog_src\": \"%s\",\n", - indent, "", - sdc_network_->getAttribute(inst, "src").c_str()); + result += sta::format("{:>{}} \"cell\": \"{}\",\n", + "", indent, + sdc_network_->name(cell)); + result += sta::format("{:>{}} \"verilog_src\": \"{}\",\n", + "", indent, + sdc_network_->getAttribute(inst, "src")); } - stringAppend(result, "%*s \"pin\": \"%s\",\n", - indent, "", - sdc_network_->pathName(pin)); + result += sta::format("{:>{}} \"pin\": \"{}\",\n", + "", indent, + sdc_network_->pathName(pin)); if (net) { - stringAppend(result, "%*s \"net\": \"%s\",\n", - indent, "", - sdc_network_->pathName(net)); + result += sta::format("{:>{}} \"net\": \"{}\",\n", + "", indent, + sdc_network_->pathName(net)); } PinSeq pins_above; hierPinsAbove(pin, network_, pins_above); if (!pins_above.empty()) { - stringAppend(result, "%*s \"hier_pins\": [\n", indent, ""); + result += sta::format("{:>{}} \"hier_pins\": [\n", "", indent); for (const Pin *hpin : pins_above) { - stringAppend(result, "%*s \"%s\"%s\n", - indent, "", - sdc_network_->pathName(hpin), - (hpin != pins_above.back()) ? "," : ""); + result += sta::format("{:>{}} \"{}\"{}\n", + "", indent, + sdc_network_->pathName(hpin), + (hpin != pins_above.back()) ? "," : ""); } - stringAppend(result, "%*s ],\n", indent, ""); + result += sta::format("{:>{}} ],\n", "", indent); } double x, y; bool exists; network_->location(pin, x, y, exists); if (exists) { - stringAppend(result, "%*s \"x\": %.9f,\n", indent, "", x); - stringAppend(result, "%*s \"y\": %.9f,\n", indent, "", y); + result += sta::format("{:>{}} \"x\": {:.9f},\n", "", indent, x); + result += sta::format("{:>{}} \"y\": {:.9f},\n", "", indent, y); } - stringAppend(result, "%*s \"arrival\": %.3e,\n", - indent, "", - delayAsFloat(path->arrival())); + result += sta::format("{:>{}} \"arrival\": {:.3e},\n", + "", indent, + delayAsFloat(path->arrival())); if (is_driver) - stringAppend(result, "%*s \"capacitance\": %.3e,\n", - indent, "", - graph_delay_calc_->loadCap(pin, rf, dcalc_ap)); - stringAppend(result, "%*s \"slew\": %.3e\n", - indent, "", - delayAsFloat(path->slew(this))); - stringAppend(result, "%*s }%s\n", - indent, "", - (i < expanded.size() - 1) ? "," : ""); + result += sta::format("{:>{}} \"capacitance\": {:.3e},\n", + "", indent, + graph_delay_calc_->loadCap(pin, rf, scene, min_max)); + result += sta::format("{:>{}} \"slew\": {:.3e}\n", + "", indent, + delayAsFloat(path->slew(this))); + result += sta::format("{:>{}} }}{}\n", + "", indent, + (i < expanded.size() - 1) ? "," : ""); } - stringAppend(result, "%*s]%s\n", - indent, "", - trailing_comma ? "," : ""); + result += sta::format("{:>{}}]{}\n", + "", indent, + trailing_comma ? "," : ""); } //////////////////////////////////////////////////////////////// @@ -1358,11 +1349,11 @@ ReportPath::reportJson(const PathExpanded &expanded, void ReportPath::reportSlackOnlyHeader() const { - string line; + std::string line; reportDescription("Group", line); line += ' '; reportField("Slack", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); reportDashLine(field_description_->width() + field_total_->width() + 1); } @@ -1370,21 +1361,21 @@ ReportPath::reportSlackOnlyHeader() const void ReportPath::reportSlackOnly(const PathEnd *end) const { - string line; + std::string line; const EarlyLate *early_late = end->pathEarlyLate(this); reportDescription(end->pathGroup()->name(), line); if (end->isUnconstrained()) reportSpaceFieldDelay(end->dataArrivalTimeOffset(this), early_late, line); else reportSpaceFieldDelay(end->slack(this), early_late, line); - report_->reportLineString(line); + report_->reportLine(line); } //////////////////////////////////////////////////////////////// void -ReportPath::reportMpwCheck(const MinPulseWidthCheck *check, - bool verbose) const +ReportPath::reportMpwCheck(const MinPulseWidthCheck &check, + bool verbose) const { if (verbose) { reportVerbose(check); @@ -1398,20 +1389,20 @@ ReportPath::reportMpwCheck(const MinPulseWidthCheck *check, } void -ReportPath::reportMpwChecks(const MinPulseWidthCheckSeq *checks, - bool verbose) const +ReportPath::reportMpwChecks(const MinPulseWidthCheckSeq &checks, + bool verbose) const { - if (!checks->empty()) { + if (!checks.empty()) { if (verbose) { - for (const MinPulseWidthCheck *check : *checks) { + for (const MinPulseWidthCheck &check : checks) { reportVerbose(check); reportBlankLine(); } } else { reportMpwHeaderShort(); - for (const MinPulseWidthCheck *check : *checks) - reportShort(check); + for (const MinPulseWidthCheck &check : checks) + reportShort(check); } reportBlankLine(); } @@ -1420,13 +1411,13 @@ ReportPath::reportMpwChecks(const MinPulseWidthCheckSeq *checks, void ReportPath::reportMpwHeaderShort() const { - string line; + std::string line; reportDescription("", line); line += ' '; reportField("Required", field_total_, line); line += ' '; reportField("Actual", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); line.clear(); reportDescription("Pin", line); @@ -1436,89 +1427,89 @@ ReportPath::reportMpwHeaderShort() const reportField("Width", field_total_, line); line += ' '; reportField("Slack", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); reportDashLine(field_description_->width() + field_total_->width() * 3 + 3); } void -ReportPath::reportShort(const MinPulseWidthCheck *check) const +ReportPath::reportShort(const MinPulseWidthCheck &check) const { - string line; - const char *pin_name = cmd_network_->pathName(check->pin(this)); - const char *hi_low = mpwCheckHiLow(check); - auto what = stdstrPrint("%s (%s)", pin_name, hi_low); - reportDescription(what.c_str(), line); - reportSpaceFieldTime(check->minWidth(this), line); - reportSpaceFieldDelay(check->width(this), EarlyLate::late(), line); - reportSpaceSlack(check->slack(this), line); - report_->reportLineString(line); + std::string line; + std::string what = sta::format("{} ({})", + cmd_network_->pathName(check.pin(this)), + mpwCheckHiLow(check)); + reportDescription(what, line); + reportSpaceFieldTime(check.minWidth(this), line); + reportSpaceFieldDelay(check.width(this), EarlyLate::late(), line); + reportSpaceSlack(check.slack(this), line); + report_->reportLine(line); } void -ReportPath::reportVerbose(const MinPulseWidthCheck *check) const +ReportPath::reportVerbose(const MinPulseWidthCheck &check) const { - string line; - const char *pin_name = cmd_network_->pathName(check->pin(this)); - line += "Pin: "; - line += pin_name; - report_->reportLineString(line); + std::string pin_name = cmd_network_->pathName(check.pin(this)); + std::string line = "Pin: " + pin_name; + report_->reportLine(line); - report_->reportLine("Check: sequential_clock_pulse_width"); + report_->report("Check: sequential_clock_pulse_width"); reportBlankLine(); reportPathHeader(); const EarlyLate *open_el = EarlyLate::late(); - const ClockEdge *open_clk_edge = check->openClkEdge(this); + const ClockEdge *open_clk_edge = check.openClkEdge(this); const Clock *open_clk = open_clk_edge->clock(); - const char *open_clk_name = open_clk->name(); - const char *open_rise_fall = asRiseFall(open_clk_edge->transition()); float open_clk_time = open_clk_edge->time(); - auto open_clk_msg = stdstrPrint("clock %s (%s edge)", open_clk_name, open_rise_fall); - reportLine(open_clk_msg.c_str(), open_clk_time, open_clk_time, open_el); - - Arrival open_arrival = check->openArrival(this); - bool is_prop = isPropagated(check->openPath()); - const char *clk_ideal_prop = clkNetworkDelayIdealProp(is_prop); - reportLine(clk_ideal_prop, check->openDelay(this), open_arrival, open_el); + std::string open_clk_msg = sta::format("clock {} ({} edge)", + open_clk->name(), + asRiseFall(open_clk_edge->transition())); + reportLine(open_clk_msg, open_clk_time, open_clk_time, open_el); + + Arrival open_arrival = check.openArrival(this); + bool is_prop = isPropagated(check.openPath()); + std::string_view clk_ideal_prop = clkNetworkDelayIdealProp(is_prop); + reportLine(clk_ideal_prop, check.openDelay(this), open_arrival, open_el); reportLine(pin_name, delay_zero, open_arrival, open_el); reportLine("open edge arrival time", open_arrival, open_el); reportBlankLine(); const EarlyLate *close_el = EarlyLate::late(); - const ClockEdge *close_clk_edge = check->closeClkEdge(this); + const ClockEdge *close_clk_edge = check.closeClkEdge(this); const Clock *close_clk = close_clk_edge->clock(); - const char *close_clk_name = close_clk->name(); - const char *close_rise_fall = asRiseFall(close_clk_edge->transition()); - float close_offset = check->closeOffset(this); + float close_offset = check.closeOffset(this); float close_clk_time = close_clk_edge->time() + close_offset; - auto close_clk_msg = stdstrPrint("clock %s (%s edge)", close_clk_name, close_rise_fall); - reportLine(close_clk_msg.c_str(), close_clk_time, close_clk_time, close_el); - Arrival close_arrival = check->closeArrival(this) + close_offset; - reportLine(clk_ideal_prop, check->closeDelay(this), close_arrival, close_el); + std::string close_clk_msg = sta::format("clock {} ({} edge)", + close_clk->name(), + asRiseFall(close_clk_edge->transition())); + reportLine(close_clk_msg, close_clk_time, close_clk_time, close_el); + + Arrival close_arrival = delaySum(check.closeArrival(this), close_offset, this); + reportLine(clk_ideal_prop, check.closeDelay(this), close_arrival, close_el); + reportLine(pin_name, delay_zero, close_arrival, close_el); if (variables_->crprEnabled()) { - Crpr pessimism = check->checkCrpr(this); - close_arrival += pessimism; + Crpr pessimism = check.checkCrpr(this); + close_arrival = delaySum(close_arrival, pessimism, this); reportLine("clock reconvergence pessimism", pessimism, close_arrival, close_el); } reportLine("close edge arrival time", close_arrival, close_el); reportDashLine(); - float min_width = check->minWidth(this); - const char *hi_low = mpwCheckHiLow(check); - auto rpw_msg = stdstrPrint("required pulse width (%s)", hi_low); - reportLine(rpw_msg.c_str(), min_width, EarlyLate::early()); - reportLine("actual pulse width", check->width(this), EarlyLate::early()); + float min_width = check.minWidth(this); + std::string rpw_msg = sta::format("required pulse width ({})", + mpwCheckHiLow(check)); + reportLine(rpw_msg, min_width, EarlyLate::early()); + reportLine("actual pulse width", check.width(this), EarlyLate::early()); reportDashLine(); - reportSlack(check->slack(this)); + reportSlack(check.slack(this)); } -const char * -ReportPath::mpwCheckHiLow(const MinPulseWidthCheck *check) const +std::string_view +ReportPath::mpwCheckHiLow(const MinPulseWidthCheck &check) const { - if (check->openTransition(this) == RiseFall::rise()) + if (check.openTransition(this) == RiseFall::rise()) return "high"; else return "low"; @@ -1527,8 +1518,8 @@ ReportPath::mpwCheckHiLow(const MinPulseWidthCheck *check) const //////////////////////////////////////////////////////////////// void -ReportPath::reportCheck(const MinPeriodCheck *check, - bool verbose) const +ReportPath::reportCheck(const MinPeriodCheck &check, + bool verbose) const { if (verbose) { reportVerbose(check); @@ -1542,20 +1533,20 @@ ReportPath::reportCheck(const MinPeriodCheck *check, } void -ReportPath::reportChecks(const MinPeriodCheckSeq *checks, - bool verbose) const +ReportPath::reportChecks(const MinPeriodCheckSeq &checks, + bool verbose) const { - if (!checks->empty()) { + if (!checks.empty()) { if (verbose) { - for (const MinPeriodCheck *check : *checks) { - reportVerbose(check); + for (const MinPeriodCheck &check : checks) { + reportVerbose(check); reportBlankLine(); } } else { reportPeriodHeaderShort(); - for (const MinPeriodCheck *check : *checks) - reportShort(check); + for (const MinPeriodCheck &check : checks) + reportShort(check); } reportBlankLine(); } @@ -1564,7 +1555,7 @@ ReportPath::reportChecks(const MinPeriodCheckSeq *checks, void ReportPath::reportPeriodHeaderShort() const { - string line; + std::string line; reportDescription("", line); line += ' '; reportField("", field_total_, line); @@ -1572,7 +1563,7 @@ ReportPath::reportPeriodHeaderShort() const reportField("Min", field_total_, line); line += ' '; reportField("", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); line.clear(); reportDescription("Pin", line); @@ -1582,69 +1573,51 @@ ReportPath::reportPeriodHeaderShort() const reportField("Period", field_total_, line); line += ' '; reportField("Slack", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); reportDashLine(field_description_->width() + field_total_->width() * 3 + 3); } void -ReportPath::reportShort(const MinPeriodCheck *check) const +ReportPath::reportShort(const MinPeriodCheck &check) const { - string line; - const char *pin_name = cmd_network_->pathName(check->pin()); + std::string line; + const std::string pin_name = cmd_network_->pathName(check.pin()); reportDescription(pin_name, line); - reportSpaceFieldDelay(check->period(), EarlyLate::early(), line); - reportSpaceFieldDelay(check->minPeriod(this), EarlyLate::early(), line); - reportSpaceSlack(check->slack(this), line); - report_->reportLineString(line); + reportSpaceFieldDelay(check.period(), EarlyLate::early(), line); + reportSpaceFieldDelay(check.minPeriod(this), EarlyLate::early(), line); + reportSpaceSlack(check.slack(this), line); + report_->reportLine(line); } void -ReportPath::reportVerbose(const MinPeriodCheck *check) const +ReportPath::reportVerbose(const MinPeriodCheck &check) const { - string line; - const char *pin_name = cmd_network_->pathName(check->pin()); - line += "Pin: "; - line += pin_name; - report_->reportLineString(line); + std::string line = "Pin: " + cmd_network_->pathName(check.pin()); + report_->reportLine(line); - reportLine("period", check->period(), EarlyLate::early()); - reportLine("min period", -check->minPeriod(this), EarlyLate::early()); + reportLine("period", check.period(), EarlyLate::early()); + reportLine("min period", -check.minPeriod(this), EarlyLate::early()); reportDashLine(); - reportSlack(check->slack(this)); + reportSlack(check.slack(this)); } //////////////////////////////////////////////////////////////// void -ReportPath::reportCheck(const MaxSkewCheck *check, - bool verbose) const -{ - if (verbose) { - reportVerbose(check); - reportBlankLine(); - } - else { - reportMaxSkewHeaderShort(); - reportShort(check); - } - reportBlankLine(); -} - -void -ReportPath::reportChecks(const MaxSkewCheckSeq *checks, - bool verbose) const +ReportPath::reportChecks(const MaxSkewCheckSeq &checks, + bool verbose) const { - if (!checks->empty()) { + if (!checks.empty()) { if (verbose) { - for (const MaxSkewCheck *check : *checks) - reportVerbose(check); + for (const MaxSkewCheck &check : checks) + reportVerbose(check); } else { reportMaxSkewHeaderShort(); - for (const MaxSkewCheck *check : *checks) - reportShort(check); + for (const MaxSkewCheck &check : checks) + reportShort(check); } reportBlankLine(); } @@ -1653,7 +1626,7 @@ ReportPath::reportChecks(const MaxSkewCheckSeq *checks, void ReportPath::reportMaxSkewHeaderShort() const { - string line; + std::string line; reportDescription("", line); line += ' '; reportField("Required", field_total_, line); @@ -1661,7 +1634,7 @@ ReportPath::reportMaxSkewHeaderShort() const reportField("Actual", field_total_, line); line += ' '; reportField("", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); line.clear(); reportDescription("Pin", line); @@ -1671,88 +1644,88 @@ ReportPath::reportMaxSkewHeaderShort() const reportField("Skew", field_total_, line); line += ' '; reportField("Slack", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); reportDashLine(field_description_->width() + field_total_->width() * 3 + 3); } void -ReportPath::reportShort(const MaxSkewCheck *check) const +ReportPath::reportShort(const MaxSkewCheck &check) const { - string line; - Pin *clk_pin = check->clkPin(this); - const char *clk_pin_name = network_->pathName(clk_pin); - TimingArc *check_arc = check->checkArc(); - auto what = stdstrPrint("%s (%s->%s)", - clk_pin_name, - check_arc->fromEdge()->to_string().c_str(), - check_arc->toEdge()->to_string().c_str()); - reportDescription(what.c_str(), line); + std::string line; + Pin *clk_pin = check.clkPin(this); + TimingArc *check_arc = check.checkArc(); + std::string what = sta::format("{} ({}->{})", + network_->pathName(clk_pin), + check_arc->fromEdge()->to_string(), + check_arc->toEdge()->to_string()); + reportDescription(what, line); const EarlyLate *early_late = EarlyLate::early(); - reportSpaceFieldDelay(check->maxSkew(this), early_late, line); - reportSpaceFieldDelay(check->skew(), early_late, line); - reportSpaceSlack(check->slack(this), line); - report_->reportLineString(line); + reportSpaceFieldDelay(check.maxSkew(this), early_late, line); + reportSpaceFieldDelay(check.skew(this), early_late, line); + reportSpaceSlack(check.slack(this), line); + report_->reportLine(line); } void -ReportPath::reportVerbose(const MaxSkewCheck *check) const +ReportPath::reportVerbose(const MaxSkewCheck &check) const { - string line; - const char *clk_pin_name = cmd_network_->pathName(check->clkPin(this)); + std::string line; line += "Constrained Pin: "; - line += clk_pin_name; - report_->reportLineString(line); + line += cmd_network_->pathName(check.clkPin(this)); + report_->reportLine(line); - const char *ref_pin_name = cmd_network_->pathName(check->refPin(this)); line = "Reference Pin: "; - line += ref_pin_name; - report_->reportLineString(line); + line += cmd_network_->pathName(check.refPin(this)); + report_->reportLine(line); line = "Check: max_skew"; - report_->reportLineString(line); + report_->reportLine(line); reportBlankLine(); reportPathHeader(); - reportSkewClkPath("reference pin arrival time", check->refPath()); - reportSkewClkPath("constrained pin arrival time", check->clkPath()); + reportSkewClkPath("reference pin arrival time", check.refPath()); + reportSkewClkPath("constrained pin arrival time", check.clkPath()); reportDashLine(); - reportLine("allowable skew", check->maxSkew(this), EarlyLate::early()); - reportLine("actual skew", check->skew(), EarlyLate::late()); + reportLine("allowable skew", check.maxSkew(this), EarlyLate::early()); + reportLine("actual skew", check.skew(this), EarlyLate::late()); reportDashLine(); - reportSlack(check->slack(this)); + reportSlack(check.slack(this)); } // Based on reportTgtClk. void -ReportPath::reportSkewClkPath(const char *arrival_msg, - const Path *clk_path) const +ReportPath::reportSkewClkPath(std::string_view arrival_msg, + const Path *clk_path) const { const ClockEdge *clk_edge = clk_path->clkEdge(this); const Clock *clk = clk_edge->clock(); const EarlyLate *early_late = clk_path->minMax(this); const RiseFall *clk_rf = clk_edge->transition(); const RiseFall *clk_end_rf = clk_path->transition(this); - string clk_name = clkName(clk, clk_end_rf != clk_rf); + std::string clk_name = clkName(clk, clk_end_rf != clk_rf); float clk_time = clk_edge->time(); const Arrival &clk_arrival = search_->clkPathArrival(clk_path); - Arrival clk_delay = clk_arrival - clk_time; - PathAnalysisPt *path_ap = clk_path->pathAnalysisPt(this); - const MinMax *min_max = path_ap->pathMinMax(); + Arrival clk_delay = delayDiff(clk_arrival, + clk_time, + this); + const MinMax *min_max = clk_path->minMax(this); Vertex *clk_vertex = clk_path->vertex(this); - reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, min_max); + reportClkLine(clk, clk_name, clk_end_rf, clk_time, min_max); bool is_prop = isPropagated(clk_path); if (is_prop && reportClkPath()) { + const Mode *mode = clk_path->mode(this); + const Sdc *sdc = mode->sdc(); const EarlyLate *early_late = TimingRole::skew()->tgtClkEarlyLate(); - if (reportGenClkSrcPath(clk_path, clk, clk_rf, min_max, early_late)) - reportGenClkSrcAndPath(clk_path, clk, clk_rf, early_late, path_ap, - 0.0, 0.0, false); + if (reportGenClkSrcPath(clk_path, clk, clk_rf, min_max, early_late, sdc)) + reportGenClkSrcAndPath(clk_path, clk, clk_rf, early_late, 0.0, 0.0, + false, mode); else { Arrival insertion, latency; PathEnd::checkTgtClkDelay(clk_path, clk_edge, TimingRole::skew(), this, - insertion, latency); + insertion, latency); reportClkSrcLatency(insertion, clk_time, early_late); PathExpanded clk_expanded(clk_path, this); reportPath1(clk_path, clk_expanded, false, 0.0); @@ -1760,8 +1733,8 @@ ReportPath::reportSkewClkPath(const char *arrival_msg, } else { reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, clk_arrival, early_late); - reportLine(descriptionField(clk_vertex).c_str(), clk_arrival, - early_late, clk_end_rf); + reportLine(descriptionField(clk_vertex), clk_arrival, + early_late, clk_end_rf); } reportLine(arrival_msg, search_->clkPathArrival(clk_path), early_late); reportBlankLine(); @@ -1772,7 +1745,7 @@ ReportPath::reportSkewClkPath(const char *arrival_msg, void ReportPath::reportLimitShortHeader(const ReportField *field) const { - string line; + std::string line; reportDescription("Pin", line); line += ' '; reportField("Limit", field, line); @@ -1780,20 +1753,20 @@ ReportPath::reportLimitShortHeader(const ReportField *field) const reportField(field->title(), field, line); line += ' '; reportField("Slack", field, line); - report_->reportLineString(line); + report_->reportLine(line); reportDashLine(field_description_->width() + field->width() * 3 + 3); } void ReportPath::reportLimitShort(const ReportField *field, - Pin *pin, - float value, - float limit, - float slack) const + const Pin *pin, + float value, + float limit, + float slack) const { - string line; - const char *pin_name = cmd_network_->pathName(pin); + std::string line; + const std::string pin_name = cmd_network_->pathName(pin); reportDescription(pin_name, line); line += ' '; reportField(limit, field, line); @@ -1804,20 +1777,20 @@ ReportPath::reportLimitShort(const ReportField *field, line += (slack >= 0.0) ? " (MET)" : " (VIOLATED)"; - report_->reportLineString(line); + report_->reportLine(line); } void ReportPath::reportLimitVerbose(const ReportField *field, - Pin *pin, - const RiseFall *rf, - float value, - float limit, - float slack, - const Corner *corner, + const Pin *pin, + const RiseFall *rf, + float value, + float limit, + float slack, + const Scene *scene, const MinMax *min_max) const { - string line; + std::string line; line += "Pin "; line += cmd_network_->pathName(pin); line += ' '; @@ -1825,64 +1798,65 @@ ReportPath::reportLimitVerbose(const ReportField *field, line += rf->shortName(); else line += ' '; - // Don't report corner if the default corner is the only corner. - if (corner && corners_->count() > 1) { + // Don't report scene if the default scene is the only scene. + if (scene && multiScene()) { line += " (corner "; - line += corner->name(); + line += scene->name(); line += ")"; } - report_->reportLineString(line); + report_->reportLine(line); line = min_max->to_string(); line += ' '; line += field->name(); line += ' '; reportField(limit, field, line); - report_->reportLineString(line); + report_->reportLine(line); line = field->name(); line += " "; reportField(value, field, line); - report_->reportLineString(line); + report_->reportLine(line); - int name_width = strlen(field->name()) + 5; + size_t name_width = field->name().size() + 5; reportDashLine(name_width + field->width()); line = "Slack"; - for (int i = strlen("Slack"); i < name_width; i++) + for (size_t i = strlen("Slack"); i < name_width; i++) line += ' '; reportField(slack, field, line); line += (slack >= 0.0) ? " (MET)" : " (VIOLATED)"; - report_->reportLineString(line); + report_->reportLine(line); } //////////////////////////////////////////////////////////////// void ReportPath::reportStartpoint(const PathEnd *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { const Path *path = end->path(); + const Sdc *sdc = path->sdc(this); const Path *start = expanded.startPath(); const TimingArc *prev_arc = expanded.startPrevArc(); const Edge *prev_edge = start->prevEdge(this); const Pin *pin = start->pin(graph_); const ClockEdge *clk_edge = path->clkEdge(this); const Clock *clk = path->clock(search_); - const char *pin_name = cmd_network_->pathName(pin); + std::string pin_name = cmd_network_->pathName(pin); if (pathFromClkPin(path, pin)) { - const char *clk_name = clk->name(); - auto reason = stdstrPrint("clock source '%s'", clk_name); + const std::string &clk_name = clk->name(); + std::string reason = sta::format("clock source '{}'", clk_name); reportStartpoint(pin_name, reason); } else if (network_->isTopLevelPort(pin)) { if (clk - && clk != sdc_->defaultArrivalClock()) { - const char *clk_name = clk->name(); + && clk != sdc->defaultArrivalClock()) { + const std::string &clk_name = clk->name(); // Pin direction is "input" even for bidirects. - auto reason = stdstrPrint("input port clocked by %s", clk_name); + std::string reason = sta::format("input port clocked by {}", clk_name); reportStartpoint(pin_name, reason); } else @@ -1890,32 +1864,32 @@ ReportPath::reportStartpoint(const PathEnd *end, } else if (network_->isLeaf(pin) && prev_arc) { Instance *inst = network_->instance(pin); - const char *inst_name = cmd_network_->pathName(inst); + std::string inst_name = cmd_network_->pathName(inst); if (clk_edge) { const RiseFall *clk_rf = clk_edge->transition(); const Path *clk_path = expanded.clkPath(); bool clk_inverted = clk_path - && clk_rf != clk_path->transition(this); - string clk_name = clkName(clk, clk_inverted); - const char *reg_desc = edgeRegLatchDesc(prev_edge, prev_arc); - auto reason = stdstrPrint("%s clocked by %s", reg_desc, clk_name.c_str()); + && clk_rf != clk_path->transition(this); + std::string clk_name = clkName(clk, clk_inverted); + std::string_view reg_desc = edgeRegLatchDesc(prev_edge, prev_arc); + std::string reason = sta::format("{} clocked by {}", reg_desc, clk_name); reportStartpoint(inst_name, reason); } else { - const char *reg_desc = edgeRegLatchDesc(prev_edge, prev_arc); - reportStartpoint(inst_name, reg_desc); + std::string_view reg_desc = edgeRegLatchDesc(prev_edge, prev_arc); + reportStartpoint(inst_name, std::string(reg_desc)); } } else if (network_->isLeaf(pin)) { if (clk_edge) { Clock *clk = clk_edge->clock(); - if (clk != sdc_->defaultArrivalClock()) { - const char *clk_name = clk->name(); - auto reason = stdstrPrint("internal path startpoint clocked by %s", clk_name); - reportStartpoint(pin_name, reason); + if (clk != sdc->defaultArrivalClock()) { + std::string reason = sta::format("internal path startpoint clocked by {}", + clk->name()); + reportStartpoint(pin_name, reason); } else - reportStartpoint(pin_name, "internal path startpoint"); + reportStartpoint(pin_name, "internal path startpoint"); } else reportStartpoint(pin_name, "internal pin"); @@ -1935,23 +1909,23 @@ ReportPath::pathFromClkPin(const PathExpanded &expanded) const bool ReportPath::pathFromClkPin(const Path *path, - const Pin *start_pin) const + const Pin *start_pin) const { const Clock *clk = path->clock(search_); return clk - && clk->leafPins().hasKey(const_cast(start_pin)); + && clk->leafPins().contains(const_cast(start_pin)); } void -ReportPath::reportStartpoint(const char *start, - const string reason) const +ReportPath::reportStartpoint(std::string_view start, + const std::string &reason) const { reportStartEndPoint(start, reason, "Startpoint"); } void ReportPath::reportUnclockedEndpoint(const PathEnd *end, - const char *default_reason) const + std::string_view default_reason) const { Vertex *vertex = end->vertex(this); Pin *pin = vertex->pin(); @@ -1964,104 +1938,110 @@ ReportPath::reportUnclockedEndpoint(const PathEnd *end, while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); if (edge->role()->genericRole() == TimingRole::setup()) { - Vertex *clk_vertex = edge->from(graph_); - VertexOutEdgeIterator clk_edge_iter(clk_vertex, graph_); - while (clk_edge_iter.hasNext()) { - Edge *clk_edge = clk_edge_iter.next(); - if (clk_edge->role() == TimingRole::regClkToQ()) { - Instance *inst = network_->instance(pin); - const char *inst_name = cmd_network_->pathName(inst); - const char *reason = regDesc(clk_edge->timingArcSet()->isRisingFallingEdge()); - reportEndpoint(inst_name, reason); - return; - } - if (clk_edge->role() == TimingRole::latchEnToQ()) { - Instance *inst = network_->instance(pin); - const char *inst_name = cmd_network_->pathName(inst); - const char *reason = latchDesc(clk_edge->timingArcSet()->isRisingFallingEdge()); - reportEndpoint(inst_name, reason); - return; - } - } + Vertex *clk_vertex = edge->from(graph_); + VertexOutEdgeIterator clk_edge_iter(clk_vertex, graph_); + while (clk_edge_iter.hasNext()) { + Edge *clk_edge = clk_edge_iter.next(); + Instance *inst = network_->instance(pin); + std::string inst_name = cmd_network_->pathName(inst); + if (clk_edge->role() == TimingRole::regClkToQ()) { + std::string_view reason = + regDesc(clk_edge->timingArcSet()->isRisingFallingEdge()); + reportEndpoint(inst_name, std::string(reason)); + return; + } + if (clk_edge->role() == TimingRole::latchEnToQ()) { + std::string_view reason = + latchDesc(clk_edge->timingArcSet()->isRisingFallingEdge()); + reportEndpoint(inst_name, std::string(reason)); + return; + } + } } } - reportEndpoint(cmd_network_->pathName(pin), default_reason); + reportEndpoint(cmd_network_->pathName(pin), std::string(default_reason)); } else reportEndpoint(cmd_network_->pathName(pin), ""); } void -ReportPath::reportEndpoint(const char *end, - const string reason) const +ReportPath::reportEndpoint(std::string_view end, + const std::string &reason) const { reportStartEndPoint(end, reason, "Endpoint"); } void -ReportPath::reportStartEndPoint(const char *pt, - string reason, - const char *key) const +ReportPath::reportStartEndPoint(std::string_view pt, + const std::string &reason, + std::string_view key) const { - string line; + std::string line; // Account for punctuation in the line. - int line_len = strlen(key) + 2 + strlen(pt) + 2 + reason.size() + 1; + size_t line_len = key.size() + 2 + pt.size() + 2 + reason.size() + 1; if (!no_split_ && line_len > start_end_pt_width_) { - line = key; + line = std::string(key); line += ": "; line += pt; - report_->reportLineString(line); + report_->reportLine(line); line.clear(); - for (unsigned i = 0; i < strlen(key); i++) + for (size_t i = 0; i < key.size(); i++) line += ' '; line += " ("; line += reason; line += ")"; - report_->reportLineString(line); + report_->reportLine(line); } else { - line = key; + line = std::string(key); line += ": "; line += pt; line += " ("; line += reason; line += ")"; - report_->reportLineString(line); + report_->reportLine(line); } } void ReportPath::reportGroup(const PathEnd *end) const { - string line; + std::string line; line = "Path Group: "; PathGroup *group = end->pathGroup(); line += group ? group->name() : "(none)"; - report_->reportLineString(line); + report_->reportLine(line); line = "Path Type: "; line += end->minMax(this)->to_string(); - report_->reportLineString(line); + report_->reportLine(line); - if (corners_->multiCorner()) { + if (modes_.size() > 1) { + line = "Mode: "; + line += end->path()->mode(this)->name(); + report_->reportLine(line); + } + + if (multiScene()) { line = "Corner: "; - line += end->pathAnalysisPt(this)->corner()->name(); - report_->reportLineString(line); + line += end->path()->scene(this)->name(); + report_->reportLine(line); } } //////////////////////////////////////////////////////////////// -string +std::string ReportPath::checkRoleReason(const PathEnd *end) const { - return stdstrPrint("%s time", end->checkRole(this)->to_string().c_str()); + return sta::format("{} time", end->checkRole(this)->to_string()); } -string +std::string ReportPath::tgtClkName(const PathEnd *end) const { const ClockEdge *tgt_clk_edge = end->targetClkEdge(this); @@ -2071,17 +2051,17 @@ ReportPath::tgtClkName(const PathEnd *end) const return clkName(tgt_clk, clk_end_rf != clk_rf); } -string +std::string ReportPath::clkName(const Clock *clk, - bool inverted) const + bool inverted) const { - string name = clk->name(); + std::string name = clk->name(); if (inverted) name += '\''; return name; } -const char * +std::string_view ReportPath::clkRegLatchDesc(const PathEnd *end) const { // Goofy libraries can have registers with both rising and falling @@ -2096,37 +2076,37 @@ ReportPath::clkRegLatchDesc(const PathEnd *end) const TimingArcSet *arc_set = edge->timingArcSet(); const TimingRole *role = arc_set->role(); if (role == TimingRole::regClkToQ() - || role == TimingRole::latchEnToQ()) { + || role == TimingRole::latchEnToQ()) { const RiseFall *arc_rf = arc_set->isRisingFallingEdge(); clk_set = arc_set; if (arc_rf == check_clk_rf) - clk_rf_set = arc_set; + clk_rf_set = arc_set; } } if (clk_rf_set) return checkRegLatchDesc(clk_rf_set->role(), - clk_rf_set->isRisingFallingEdge()); + clk_rf_set->isRisingFallingEdge()); else if (clk_set) return checkRegLatchDesc(clk_set->role(), - clk_set->isRisingFallingEdge()); + clk_set->isRisingFallingEdge()); else return checkRegLatchDesc(TimingRole::regClkToQ(), check_clk_rf); } void ReportPath::reportSrcPathArrival(const PathEnd *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportBlankLine(); reportSrcPath(end, expanded); reportLine("data arrival time", end->dataArrivalTimeOffset(this), - end->pathEarlyLate(this)); + end->pathEarlyLate(this)); reportBlankLine(); } void ReportPath::reportSrcPath(const PathEnd *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportPathHeader(); float src_clk_offset = end->sourceClkOffset(this); @@ -2134,30 +2114,32 @@ ReportPath::reportSrcPath(const PathEnd *end, Arrival src_clk_latency = end->sourceClkLatency(this); const Path *path = end->path(); reportSrcClkAndPath(path, expanded, src_clk_offset, src_clk_insertion, - src_clk_latency, end->isPathDelay()); + src_clk_latency, end->isPathDelay()); } void ReportPath::reportSrcClkAndPath(const Path *path, - const PathExpanded &expanded, - float time_offset, - Arrival clk_insertion, - Arrival clk_latency, - bool is_path_delay) const + const PathExpanded &expanded, + float time_offset, + Arrival clk_insertion, + Arrival clk_latency, + bool is_path_delay) const { const ClockEdge *clk_edge = path->clkEdge(this); + const Mode *mode = path->mode(this); + const Sdc *sdc = mode->sdc(); const MinMax *min_max = path->minMax(this); if (clk_edge) { Clock *clk = clk_edge->clock(); const RiseFall *clk_rf = clk_edge->transition(); float clk_time = clk_edge->time() + time_offset; - if (clk == sdc_->defaultArrivalClock()) { + if (clk == sdc->defaultArrivalClock()) { if (!is_path_delay) { - float clk_end_time = clk_time + time_offset; - const EarlyLate *early_late = min_max; - reportLine("clock (input port clock) (rise edge)", - clk_end_time, clk_end_time, early_late); - reportLine(clkNetworkDelayIdealProp(false), 0.0, clk_end_time, early_late); + float clk_end_time = clk_time + time_offset; + const EarlyLate *early_late = min_max; + reportLine("clock (input port clock) (rise edge)", + clk_end_time, clk_end_time, early_late); + reportLine(clkNetworkDelayIdealProp(false), 0.0, clk_end_time, early_late); } reportPath1(path, expanded, false, time_offset); } @@ -2168,91 +2150,99 @@ ReportPath::reportSrcClkAndPath(const Path *path, const Path *clk_path = expanded.clkPath(); const RiseFall *clk_end_rf; if (clk_path) { - clk_end_time = search_->clkPathArrival(clk_path) + time_offset; - clk_delay = clk_end_time - clk_time; - clk_end_rf = clk_path->transition(this); + clk_end_time = delaySum(search_->clkPathArrival(clk_path), + time_offset, + this); + clk_delay = delayDiff(clk_end_time, + clk_time, + this); + clk_end_rf = clk_path->transition(this); } else { - // Path from input port or clk used as data. - clk_end_rf = clk_rf; - clk_delay = clk_insertion + clk_latency; - clk_end_time = clk_time + clk_delay; - - const Path *first_path = expanded.startPath(); - const InputDelay *input_delay = pathInputDelay(first_path); - if (input_delay) { - path_from_input = true; - const Pin *ref_pin = input_delay->refPin(); - if (ref_pin && clk->isPropagated()) { - Path ref_path; - pathInputDelayRefPath(first_path, input_delay, ref_path); - if (!ref_path.isNull()) { - const Arrival &ref_end_time = ref_path.arrival(); - clk_delay = ref_end_time - clk_time; - clk_end_time = ref_end_time + time_offset; - input_has_ref_path = true; - } - } - } + // Path from input port or clk used as data. + clk_end_rf = clk_rf; + clk_delay = delaySum(clk_insertion, clk_latency, this); + clk_end_time = delaySum(clk_time, clk_delay, this); + + const Path *first_path = expanded.startPath(); + const InputDelay *input_delay = pathInputDelay(first_path); + if (input_delay) { + path_from_input = true; + const Pin *ref_pin = input_delay->refPin(); + if (ref_pin && clk->isPropagated()) { + Path ref_path; + pathInputDelayRefPath(first_path, input_delay, ref_path); + if (!ref_path.isNull()) { + const Arrival &ref_end_time = ref_path.arrival(); + clk_delay = delayDiff(ref_end_time, + clk_time, + this); + clk_end_time = delaySum(ref_end_time, + time_offset, + this); + input_has_ref_path = true; + } + } + } } - string clk_name = clkName(clk, clk_rf != clk_end_rf); + std::string clk_name = clkName(clk, clk_rf != clk_end_rf); bool clk_used_as_data = pathFromClkPin(expanded); bool is_prop = isPropagated(path); const EarlyLate *early_late = min_max; - if (reportGenClkSrcPath(clk_path, clk, clk_rf, min_max, early_late) - && !(path_from_input && !input_has_ref_path)) { - reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, - min_max); - const PathAnalysisPt *path_ap = path->pathAnalysisPt(this); - reportGenClkSrcAndPath(path, clk, clk_rf, early_late, path_ap, - time_offset, time_offset, clk_used_as_data); + if (reportGenClkSrcPath(clk_path, clk, clk_rf, min_max, early_late, sdc) + && !(path_from_input && !input_has_ref_path)) { + reportClkLine(clk, clk_name, clk_end_rf, clk_time, min_max); + reportGenClkSrcAndPath(path, clk, clk_rf, early_late, time_offset, + time_offset, clk_used_as_data, mode); } else if (clk_used_as_data - && pathFromGenPropClk(path, path->minMax(this))) { - reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, min_max); - const ClkInfo *clk_info = path->tag(search_)->clkInfo(); - if (clk_info->isPropagated()) - reportClkSrcLatency(clk_insertion, clk_time, early_late); - reportPath1(path, expanded, true, time_offset); + && pathFromGenPropClk(path, path->minMax(this))) { + reportClkLine(clk, clk_name, clk_end_rf, clk_time, min_max); + const ClkInfo *clk_info = path->tag(search_)->clkInfo(); + if (clk_info->isPropagated()) + reportClkSrcLatency(clk_insertion, clk_time, early_late); + reportPath1(path, expanded, true, time_offset); } else if (is_prop - && reportClkPath() - && !(path_from_input && !input_has_ref_path)) { - reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, early_late); - reportClkSrcLatency(clk_insertion, clk_time, early_late); - reportPath1(path, expanded, false, time_offset); + && reportClkPath() + && !(path_from_input && !input_has_ref_path)) { + reportClkLine(clk, clk_name, clk_end_rf, clk_time, early_late); + reportClkSrcLatency(clk_insertion, clk_time, early_late); + reportPath1(path, expanded, false, time_offset); } else if (clk_used_as_data) { - reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, early_late); - if (delayGreater(clk_insertion, 0.0, this)) - reportClkSrcLatency(clk_insertion, clk_time, early_late); - if (reportClkPath()) - reportPath1(path, expanded, true, time_offset); - else { - Arrival clk_arrival = clk_end_time; - Arrival end_arrival = path->arrival() + time_offset; - Delay clk_delay = end_arrival - clk_arrival; - reportLine("clock network delay", clk_delay, - end_arrival, early_late); - Vertex *end_vertex = path->vertex(this); - reportLine(descriptionField(end_vertex).c_str(), - end_arrival, early_late, clk_end_rf); - } + reportClkLine(clk, clk_name, clk_end_rf, clk_time, early_late); + if (delayGreater(clk_insertion, 0.0, this)) + reportClkSrcLatency(clk_insertion, clk_time, early_late); + if (reportClkPath()) + reportPath1(path, expanded, true, time_offset); + else { + Arrival clk_arrival = clk_end_time; + Arrival end_arrival = delaySum(path->arrival(), + time_offset, + this); + Delay clk_delay = delayDiff(end_arrival, clk_arrival, this); + reportLine("clock network delay", clk_delay, + end_arrival, early_late); + Vertex *end_vertex = path->vertex(this); + reportLine(descriptionField(end_vertex), + end_arrival, early_late, clk_end_rf); + } } else { - if (is_path_delay) { - if (delayGreater(clk_delay, 0.0, this)) - reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, - clk_end_time, early_late); - } - else { - reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, min_max); - Arrival clk_arrival = clk_end_time; - reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, - clk_arrival, early_late); - } - reportPath1(path, expanded, false, time_offset); + if (is_path_delay) { + if (delayGreater(clk_delay, 0.0, this)) + reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, + clk_end_time, early_late); + } + else { + reportClkLine(clk, clk_name, clk_end_rf, clk_time, min_max); + Arrival clk_arrival = clk_end_time; + reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, + clk_arrival, early_late); + } + reportPath1(path, expanded, false, time_offset); } } } @@ -2268,7 +2258,7 @@ ReportPath::reportTgtClk(const PathEnd *end) const void ReportPath::reportTgtClk(const PathEnd *end, - float prev_time) const + float prev_time) const { const Clock *clk = end->targetClk(this); const Path *clk_path = end->targetClkPath(); @@ -2277,8 +2267,8 @@ ReportPath::reportTgtClk(const PathEnd *end, void ReportPath::reportTgtClk(const PathEnd *end, - float prev_time, - bool is_prop) const + float prev_time, + bool is_prop) const { float src_offset = end->sourceClkOffset(this); reportTgtClk(end, prev_time, src_offset, is_prop); @@ -2286,52 +2276,52 @@ ReportPath::reportTgtClk(const PathEnd *end, void ReportPath::reportTgtClk(const PathEnd *end, - float prev_time, - float src_offset, - bool is_prop) const + float prev_time, + float src_offset, + bool is_prop) const { const ClockEdge *clk_edge = end->targetClkEdge(this); - Clock *clk = clk_edge->clock(); + const Clock *clk = clk_edge->clock(); const RiseFall *clk_rf = clk_edge->transition(); const RiseFall *clk_end_rf = end->targetClkEndTrans(this); - string clk_name = clkName(clk, clk_end_rf != clk_rf); + std::string clk_name = clkName(clk, clk_end_rf != clk_rf); float clk_time = prev_time + end->targetClkTime(this) + end->targetClkMcpAdjustment(this) + src_offset; Arrival clk_delay = end->targetClkDelay(this); - Arrival clk_arrival = clk_time + clk_delay; - PathAnalysisPt *path_ap = end->pathAnalysisPt(this)->tgtClkAnalysisPt(); - const MinMax *min_max = path_ap->pathMinMax(); + Arrival clk_arrival = delaySum(clk_delay, clk_time, this); + const MinMax *min_max = end->path()->tgtClkMinMax(this); const Path *clk_path = end->targetClkPath(); - reportClkLine(clk, clk_name.c_str(), clk_end_rf, prev_time, clk_time, min_max); + reportClkLine(clk, clk_name, clk_end_rf, prev_time, clk_time, min_max); const TimingRole *check_role = end->checkRole(this); if (is_prop && reportClkPath()) { float time_offset = prev_time + end->targetClkOffset(this) + end->targetClkMcpAdjustment(this); const EarlyLate *early_late = check_role->tgtClkEarlyLate(); - if (reportGenClkSrcPath(clk_path, clk, clk_rf, min_max, early_late)) { + const Mode *mode = end->path()->mode(this); + const Sdc *sdc = mode->sdc(); + if (reportGenClkSrcPath(clk_path, clk, clk_rf, min_max, early_late, sdc)) { float insertion_offset = - clk_path ? tgtClkInsertionOffet(clk_path, early_late, path_ap) : 0.0; - reportGenClkSrcAndPath(clk_path, clk, clk_rf, early_late, path_ap, - time_offset, time_offset + insertion_offset, false); + clk_path ? tgtClkInsertionOffet(clk_path, early_late) : 0.0; + reportGenClkSrcAndPath(clk_path, clk, clk_rf, early_late, time_offset, + time_offset + insertion_offset, false, mode); } else { Arrival insertion = end->targetClkInsertionDelay(this); if (clk_path) { - reportClkSrcLatency(insertion, clk_time, early_late); - PathExpanded clk_expanded(clk_path, this); - float insertion_offset = tgtClkInsertionOffet(clk_path, early_late, - path_ap); - reportPath6(clk_path, clk_expanded, 0, is_prop, reportClkPath(), - delay_zero, time_offset + insertion_offset); + reportClkSrcLatency(insertion, clk_time, early_late); + PathExpanded clk_expanded(clk_path, this); + float insertion_offset = tgtClkInsertionOffet(clk_path, early_late); + reportPath6(clk_path, clk_expanded, 0, is_prop, reportClkPath(), + delay_zero, time_offset + insertion_offset); } else { - // Output departure. - Arrival clk_arrival = clk_time + clk_delay; - reportLine(clkNetworkDelayIdealProp(clk->isPropagated()), - clk_delay, clk_arrival, min_max); + // Output departure. + Arrival clk_arrival = delaySum(clk_time, clk_delay, this); + reportLine(clkNetworkDelayIdealProp(clk->isPropagated()), + clk_delay, clk_arrival, min_max); } } reportClkUncertainty(end, clk_arrival); @@ -2344,50 +2334,51 @@ ReportPath::reportTgtClk(const PathEnd *end, reportCommonClkPessimism(end, clk_arrival); if (clk_path) { Vertex *clk_vertex = clk_path->vertex(this); - reportLine(descriptionField(clk_vertex).c_str(), - prev_time - + end->targetClkArrival(this) - + end->sourceClkOffset(this), - min_max, clk_end_rf); + reportLine(descriptionField(clk_vertex), + delaySum(delaySum(prev_time, + end->targetClkArrival(this), + this), + end->sourceClkOffset(this), + this), + min_max, clk_end_rf); } } } float ReportPath::tgtClkInsertionOffet(const Path *clk_path, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap) const + const EarlyLate *early_late) const { const ClkInfo *clk_info = clk_path->clkInfo(this); const Pin *src_pin = clk_info->clkSrc(); const ClockEdge *clk_edge = clk_info->clkEdge(); const Clock *clk = clk_edge->clock(); const RiseFall *clk_rf = clk_edge->transition(); - const MinMax *min_max = path_ap->pathMinMax(); + const MinMax *min_max = clk_path->minMax(this); + const Mode *mode = clk_path->mode(this); Arrival path_insertion = search_->clockInsertion(clk, src_pin, clk_rf, - min_max, min_max, - path_ap); + min_max, min_max, mode); Arrival tgt_insertion = search_->clockInsertion(clk, src_pin, clk_rf, - min_max, early_late, - path_ap); - return delayAsFloat(tgt_insertion - path_insertion); + min_max, early_late, mode); + return delayAsFloat(delayDiff(tgt_insertion, path_insertion, this)); } bool ReportPath::pathFromGenPropClk(const Path *clk_path, - const EarlyLate *early_late) const + const EarlyLate *early_late) const { const ClkInfo *clk_info = clk_path->tag(search_)->clkInfo(); const ClockEdge *clk_edge = clk_info->clkEdge(); if (clk_edge) { + const Sdc *sdc = clk_path->sdc(this); const Clock *clk = clk_edge->clock(); float insertion; bool exists; - sdc_->clockInsertion(clk, clk_info->clkSrc(), - clk_edge->transition(), - clk_path->minMax(this), - early_late, - insertion, exists); + sdc->clockInsertion(clk, clk_info->clkSrc(), + clk_edge->transition(), + clk_path->minMax(this), + early_late, + insertion, exists); return !exists && clk->isGeneratedWithPropagatedMaster(); } @@ -2397,104 +2388,112 @@ ReportPath::pathFromGenPropClk(const Path *clk_path, bool ReportPath::isGenPropClk(const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - const EarlyLate *early_late) const + const RiseFall *clk_rf, + const MinMax *min_max, + const EarlyLate *early_late, + const Sdc *sdc) const { float insertion; bool exists; - sdc_->clockInsertion(clk, clk->srcPin(), clk_rf, - min_max, early_late, - insertion, exists); + sdc->clockInsertion(clk, clk->srcPin(), clk_rf, + min_max, early_late, + insertion, exists); return !exists && clk->isGeneratedWithPropagatedMaster(); } void ReportPath::reportClkLine(const Clock *clk, - const char *clk_name, - const RiseFall *clk_rf, - Arrival clk_time, - const MinMax *min_max) const + std::string_view clk_name, + const RiseFall *clk_rf, + Arrival clk_time, + const MinMax *min_max) const { reportClkLine(clk, clk_name, clk_rf, 0.0, clk_time, min_max); } void ReportPath::reportClkLine(const Clock *clk, - const char *clk_name, - const RiseFall *clk_rf, - Arrival prev_time, - Arrival clk_time, - const MinMax *min_max) const -{ - const char *rise_fall = asRiseFall(clk_rf); - auto clk_msg = stdstrPrint("clock %s (%s edge)", clk_name, rise_fall); + std::string_view clk_name, + const RiseFall *clk_rf, + Arrival prev_time, + Arrival clk_time, + const MinMax *min_max) const +{ + std::string_view rise_fall = asRiseFall(clk_rf); + std::string clk_msg = sta::format("clock {} ({} edge)", clk_name, rise_fall); if (clk->isPropagated()) - reportLine(clk_msg.c_str(), clk_time - prev_time, clk_time, min_max); + reportLine(clk_msg, + delayDiff(clk_time, prev_time, this), + clk_time, + min_max); else { // Report ideal clock slew. float clk_slew = clk->slew(clk_rf, min_max); - reportLine(clk_msg.c_str(), clk_slew, clk_time - prev_time, clk_time, min_max); + reportLine(clk_msg, + clk_slew, + delayDiff(clk_time, prev_time, this), + clk_time, + min_max); } } bool ReportPath::reportGenClkSrcPath(const Path *clk_path, - const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - const EarlyLate *early_late) const + const Clock *clk, + const RiseFall *clk_rf, + const MinMax *min_max, + const EarlyLate *early_late, + const Sdc *sdc) const { bool from_gen_prop_clk = clk_path ? pathFromGenPropClk(clk_path, early_late) - : isGenPropClk(clk, clk_rf, min_max, early_late); + : isGenPropClk(clk, clk_rf, min_max, early_late, sdc); return from_gen_prop_clk && format_ == ReportPathFormat::full_clock_expanded; } void ReportPath::reportGenClkSrcAndPath(const Path *path, - const Clock *clk, - const RiseFall *clk_rf, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap, - float time_offset, - float path_time_offset, - bool clk_used_as_data) const + const Clock *clk, + const RiseFall *clk_rf, + const EarlyLate *early_late, + float time_offset, + float path_time_offset, + bool clk_used_as_data, + const Mode *mode) const { const Pin *clk_pin = path ? path->clkInfo(search_)->clkSrc() : clk->defaultPin(); float gclk_time = clk->edge(clk_rf)->time() + time_offset; bool skip_first_path = reportGenClkSrcPath1(clk, clk_pin, clk_rf, - early_late, path_ap, gclk_time, - time_offset, clk_used_as_data); + early_late, gclk_time, + time_offset, clk_used_as_data, + mode); if (path) { PathExpanded expanded(path, this); reportPath2(path, expanded, skip_first_path, clk_used_as_data, - path_time_offset); + path_time_offset); } } bool ReportPath::reportGenClkSrcPath1(const Clock *clk, - const Pin *clk_pin, - const RiseFall *clk_rf, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap, - float gclk_time, - float time_offset, - bool clk_used_as_data) const -{ - PathAnalysisPt *insert_ap = path_ap->insertionAnalysisPt(early_late); - const MinMax *min_max = path_ap->pathMinMax(); - const Path *src_path = search_->genclks()->srcPath(clk, clk_pin, - clk_rf, insert_ap); + const Pin *clk_pin, + const RiseFall *clk_rf, + const EarlyLate *early_late, + float gclk_time, + float time_offset, + bool clk_used_as_data, + const Mode *mode) const +{ + const Path *src_path = mode->genclks()->srcPath(clk, clk_pin, clk_rf, early_late); if (src_path) { const ClkInfo *src_clk_info = src_path->clkInfo(this); const ClockEdge *src_clk_edge = src_clk_info->clkEdge(); const Clock *src_clk = src_clk_info->clock(); + const MinMax *min_max = src_path->minMax(this); if (src_clk) { bool skip_first_path = false; const RiseFall *src_clk_rf = src_clk_edge->transition(); @@ -2502,15 +2501,17 @@ ReportPath::reportGenClkSrcPath1(const Clock *clk, if (src_clk->isGeneratedWithPropagatedMaster() && src_clk_info->isPropagated()) { skip_first_path = reportGenClkSrcPath1(src_clk, src_clk_pin, - src_clk_rf, early_late, path_ap, + src_clk_rf, early_late, gclk_time, time_offset, - clk_used_as_data); + clk_used_as_data, + mode); } else { + const Mode *mode = src_path->mode(this); const Arrival insertion = search_->clockInsertion(src_clk, src_clk_pin, src_clk_rf, - path_ap->pathMinMax(), - early_late, path_ap); + min_max, + early_late, mode); reportClkSrcLatency(insertion, gclk_time, early_late); } PathExpanded src_expanded(src_path, this); @@ -2525,64 +2526,69 @@ ReportPath::reportGenClkSrcPath1(const Clock *clk, if (clk->isPropagated()) reportClkSrcLatency(0.0, gclk_time, early_late); else if (!clk_used_as_data) - reportLine("clock network delay (ideal)", 0.0, gclk_time, min_max); + reportLine("clock network delay (ideal)", 0.0, gclk_time, MinMax::max()); } return src_path != nullptr; } void ReportPath::reportClkSrcLatency(Arrival insertion, - float clk_time, - const EarlyLate *early_late) const + float clk_time, + const EarlyLate *early_late) const { - reportLine("clock source latency", insertion, clk_time + insertion, early_late); + Arrival clk_arrival = delaySum(clk_time, + insertion, + this); + reportLine("clock source latency", insertion, clk_arrival, early_late); } void ReportPath::reportPathLine(const Path *path, - Arrival incr, - Arrival time, - const char *line_case) const + const Delay &incr, + const Arrival &time, + std::string_view line_case) const { Vertex *vertex = path->vertex(this); Pin *pin = vertex->pin(); - const string what = descriptionField(vertex); + const std::string what = descriptionField(vertex); const RiseFall *rf = path->transition(this); bool is_driver = network_->isDriver(pin); - PathAnalysisPt *path_ap = path->pathAnalysisPt(this); - const EarlyLate *early_late = path_ap->pathMinMax(); - DcalcAnalysisPt *dcalc_ap = path_ap->dcalcAnalysisPt(); - DcalcAPIndex ap_index = dcalc_ap->index(); - Slew slew = graph_->slew(vertex, rf, ap_index); + const EarlyLate *early_late = path->minMax(this); + const Scene *scene = path->scene(this); + const MinMax *min_max = path->minMax(this); + DcalcAPIndex slew_index = path->dcalcAnalysisPtIndex(this); + Slew slew = graph_->slew(vertex, rf, slew_index); float cap = field_blank_; Instance *inst = network_->instance(pin); - string src_attr = ""; + std::string src_attr; if (inst) src_attr = network_->getAttribute(inst, "src"); // Don't show capacitance field for input pins. if (is_driver && field_capacitance_->enabled()) - cap = graph_delay_calc_->loadCap(pin, rf, dcalc_ap); - reportLine(what.c_str(), cap, slew, field_blank_, - incr, time, false, early_late, rf, src_attr, - line_case); + cap = graph_delay_calc_->loadCap(pin, rf, scene, min_max); + reportLine(what, cap, slew, field_blank_, incr, field_blank_, + time, false, early_late, rf, src_attr, line_case); } void ReportPath::reportRequired(const PathEnd *end, - string margin_msg) const + const std::string& margin_msg) const { Required req_time = end->requiredTimeOffset(this); const EarlyLate *early_late = end->clkEarlyLate(this); float macro_clk_tree_delay = end->macroClkTreeDelay(this); ArcDelay margin = end->margin(this); if (end->minMax(this) == MinMax::min()) { - margin = -margin; + margin = delayDiff(delay_zero, margin, this); macro_clk_tree_delay = -macro_clk_tree_delay; } if (macro_clk_tree_delay != 0.0) reportLine("macro clock tree delay", -macro_clk_tree_delay, - req_time + margin, early_late); - reportLine(margin_msg.c_str(), -margin, req_time, early_late); + delaySum(req_time, margin, this), early_late); + reportLine(margin_msg, + delayDiff(delay_zero, margin, this), + req_time, + early_late); reportLine("data required time", req_time, early_late); reportDashLine(); } @@ -2592,7 +2598,7 @@ ReportPath::reportSlack(const PathEnd *end) const { const EarlyLate *early_late = end->pathEarlyLate(this); reportLine("data required time", end->requiredTimeOffset(this), - early_late->opposite()); + early_late->opposite()); reportLineNegative("data arrival time", end->dataArrivalTimeOffset(this), early_late); reportDashLine(); reportSlack(end->slack(this)); @@ -2602,7 +2608,7 @@ void ReportPath::reportSlack(Slack slack) const { const EarlyLate *early_late = EarlyLate::early(); - const char *msg = (delayAsFloat(slack, early_late, this) >= 0.0) + std::string_view msg = (delayAsFloat(slack, early_late, this) >= 0.0) ? "slack (MET)" : "slack (VIOLATED)"; reportLine(msg, slack, early_late); @@ -2610,7 +2616,7 @@ ReportPath::reportSlack(Slack slack) const void ReportPath::reportSpaceSlack(const PathEnd *end, - string &result) const + std::string &result) const { Slack slack = end->slack(this); reportSpaceSlack(slack, result); @@ -2618,7 +2624,7 @@ ReportPath::reportSpaceSlack(const PathEnd *end, void ReportPath::reportSpaceSlack(Slack slack, - string &result) const + std::string &result) const { const EarlyLate *early_late = EarlyLate::early(); reportSpaceFieldDelay(slack, early_late, result); @@ -2629,27 +2635,31 @@ ReportPath::reportSpaceSlack(Slack slack, void ReportPath::reportCommonClkPessimism(const PathEnd *end, - Arrival &clk_arrival) const + Arrival &clk_arrival) const { if (variables_->crprEnabled()) { Crpr pessimism = end->checkCrpr(this); - clk_arrival += pessimism; + clk_arrival = delaySum(clk_arrival, pessimism, this); reportLine("clock reconvergence pessimism", pessimism, clk_arrival, - end->clkEarlyLate(this)); + end->clkEarlyLate(this)); } } void ReportPath::reportClkUncertainty(const PathEnd *end, - Arrival &clk_arrival) const + Arrival &clk_arrival) const { const EarlyLate *early_late = end->clkEarlyLate(this); float uncertainty = end->targetNonInterClkUncertainty(this); - clk_arrival += uncertainty; + clk_arrival = delaySum(clk_arrival, + uncertainty, + this); if (uncertainty != 0.0) reportLine("clock uncertainty", uncertainty, clk_arrival, early_late); float inter_uncertainty = end->interClkUncertainty(this); - clk_arrival += inter_uncertainty; + clk_arrival = delaySum(clk_arrival, + inter_uncertainty, + this); if (inter_uncertainty != 0.0) reportLine("inter-clock uncertainty", inter_uncertainty, clk_arrival, early_late); @@ -2659,7 +2669,7 @@ ReportPath::reportClkUncertainty(const PathEnd *end, void ReportPath::reportPath(const PathEnd *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportPathHeader(); // Source clk offset for path delays removes clock phase time. @@ -2683,7 +2693,7 @@ ReportPath::reportPath(const Path *path) const case ReportPathFormat::endpoint: case ReportPathFormat::summary: case ReportPathFormat::slack_only: - report_->reportLine("Format not supported."); + report_->report("Format not supported."); break; } } @@ -2701,9 +2711,9 @@ ReportPath::reportPathFull(const Path *path) const // Main entry point for reporting a path. void ReportPath::reportPath1(const Path *path, - const PathExpanded &expanded, - bool clk_used_as_data, - float time_offset) const + const PathExpanded &expanded, + bool clk_used_as_data, + float time_offset) const { reportPath2(path, expanded, false, clk_used_as_data, time_offset); } @@ -2711,38 +2721,38 @@ ReportPath::reportPath1(const Path *path, // Alternate entry point with skip_first_path arg. void ReportPath::reportPath2(const Path *path, - const PathExpanded &expanded, - bool skip_first_path, - bool clk_used_as_data, - float time_offset) const + const PathExpanded &expanded, + bool skip_first_path, + bool clk_used_as_data, + float time_offset) const { bool clk_is_propagated = path->clkInfo(search_)->isPropagated(); bool report_clk_path = (reportClkPath() && clk_is_propagated) || clk_used_as_data; bool propagated_clk = clk_is_propagated || clk_used_as_data; reportPath4(path, expanded, skip_first_path, propagated_clk, - report_clk_path, time_offset); + report_clk_path, time_offset); } // Alternate entry point with report_clk_path arg. void ReportPath::reportPath3(const Path *path, - const PathExpanded &expanded, - bool report_clk_path, - float time_offset) const + const PathExpanded &expanded, + bool report_clk_path, + float time_offset) const { bool propagated_clk = path->clkInfo(search_)->isPropagated(); reportPath4(path, expanded, false, propagated_clk, - report_clk_path, time_offset); + report_clk_path, time_offset); } void ReportPath::reportPath4(const Path *path, - const PathExpanded &expanded, - bool skip_first_path, - bool propagated_clk, - bool report_clk_path, - float time_offset) const + const PathExpanded &expanded, + bool skip_first_path, + bool propagated_clk, + bool report_clk_path, + float time_offset) const { const Path *d_path, *q_path; Edge *d_q_edge; @@ -2756,61 +2766,69 @@ ReportPath::reportPath4(const Path *path, const EarlyLate *early_late = latch_enable_path->minMax(this); latch_enable_time = search_->clkPathArrival(latch_enable_path); if (report_clk_path) { - PathExpanded enable_expanded(latch_enable_path, this); - // Report the path to the latch enable. - reportPath5(latch_enable_path, enable_expanded, skip_first_path, - propagated_clk, report_clk_path, time_offset); + PathExpanded enable_expanded(latch_enable_path, this); + // Report the path to the latch enable. + reportPath5(latch_enable_path, enable_expanded, skip_first_path, + propagated_clk, report_clk_path, time_offset); } - Arrival time = latch_enable_time + latch_time_given; + Arrival time = delaySum(latch_enable_time, + latch_time_given, + this); Arrival incr = latch_time_given; if (delayGreaterEqual(incr, 0.0, this)) - reportLine("time given to startpoint", incr, time, early_late); + reportLine("time given to startpoint", incr, time, early_late); else - reportLine("time borrowed from startpoint", incr, time, early_late); + reportLine("time borrowed from startpoint", incr, time, early_late); // Override latch D arrival with enable + given. reportPathLine(expanded.path(0), delay_zero, time, "latch_D"); reportPath6(path, expanded, 1, propagated_clk, report_clk_path, - latch_enable_time + latch_time_given, time_offset); + delaySum(latch_enable_time, + latch_time_given, + this), + time_offset); } } else reportPath5(path, expanded, skip_first_path, propagated_clk, - report_clk_path, time_offset); + report_clk_path, time_offset); } void ReportPath::reportPath5(const Path *path, - const PathExpanded &expanded, - bool skip_first_path, - bool propagated_clk, - bool report_clk_path, - float time_offset) const + const PathExpanded &expanded, + bool skip_first_path, + bool propagated_clk, + bool report_clk_path, + float time_offset) const { size_t path_first_index = 0; Arrival prev_time = 0.0; if (skip_first_path) { path_first_index = 1; const Path *start = expanded.path(0); - prev_time = start->arrival() + time_offset; + prev_time = delaySum(start->arrival(), + time_offset, + this); } reportPath6(path, expanded, path_first_index, propagated_clk, - report_clk_path, prev_time, time_offset); + report_clk_path, prev_time, time_offset); } // This does the real workk of reporting an expanded path. void ReportPath::reportPath6(const Path *path, - const PathExpanded &expanded, - size_t path_first_index, - bool propagated_clk, - bool report_clk_path, - Arrival prev_time, - float time_offset) const + const PathExpanded &expanded, + size_t path_first_index, + bool propagated_clk, + bool report_clk_path, + Arrival prev_time, + float time_offset) const { size_t path_last_index = expanded.size() - 1; + const Scene *scene = path->scene(this); const MinMax *min_max = path->minMax(this); - DcalcAnalysisPt *dcalc_ap = path->pathAnalysisPt(this)->dcalcAnalysisPt(); - DcalcAPIndex ap_index = dcalc_ap->index(); + const Sdc *sdc = path->sdc(this); + DcalcAPIndex slew_index = path->dcalcAnalysisPtIndex(this); const Path *clk_path = expanded.clkPath(); Vertex *clk_start = clk_path ? clk_path->vertex(this) : nullptr; for (size_t i = path_first_index; i <= path_last_index; i++) { @@ -2818,107 +2836,113 @@ ReportPath::reportPath6(const Path *path, const TimingArc *prev_arc = path1->prevArc(this); Vertex *vertex = path1->vertex(this); Pin *pin = vertex->pin(); - Arrival time = path1->arrival() + time_offset; + Arrival time = delaySum(path1->arrival(), time_offset, this); Delay incr = 0.0; - const char *line_case = nullptr; + std::string_view line_case; bool is_clk_start = path1->vertex(this) == clk_start; bool is_clk = path1->isClock(search_); Instance *inst = network_->instance(pin); - string src_attr = ""; + std::string src_attr; if (inst) src_attr = network_->getAttribute(inst, "src"); // Always show the search start point (register clk pin). // Skip reporting the clk tree unless it is requested. if (is_clk_start - || report_clk_path - || !is_clk) { + || report_clk_path + || !is_clk) { const RiseFall *rf = path1->transition(this); - Slew slew = graph_->slew(vertex, rf, ap_index); + Slew slew = graph_->slew(vertex, rf, slew_index); if (prev_arc == nullptr) { - // First path. - reportInputExternalDelay(path1, time_offset); - size_t next_index = i + 1; - const Path *next_path = expanded.path(next_index); - if (network_->isTopLevelPort(pin) - && next_path - && !nextArcAnnotated(next_path, next_index, expanded, ap_index) - && hasExtInputDriver(pin, rf, min_max)) { - // Pin is an input port with drive_cell/drive_resistance. - // The delay calculator annotates wire delays on the edges - // from the input to the loads. Report the wire delay on the - // input pin instead. - Arrival next_time = next_path->arrival() + time_offset; - incr = delayIncr(next_time, time, min_max); - time = next_time; - line_case = "input_drive"; - } - else if (is_clk) { - if (!propagated_clk) - // Clock latency at path endpoint in case latency was set - // on a clock pin other than the clock source. - time = search_->clkPathArrival(path1) + time_offset; - incr = 0.0; - line_case = "clk_first"; - } - else { - incr = 0.0; - line_case = "first"; - } + // First path. + reportInputExternalDelay(path1, time_offset); + size_t next_index = i + 1; + const Path *next_path = expanded.path(next_index); + if (network_->isTopLevelPort(pin) + && next_path + && !nextArcAnnotated(next_path, next_index, expanded, slew_index) + && hasExtInputDriver(pin, rf, min_max, sdc)) { + // Pin is an input port with drive_cell/drive_resistance. + // The delay calculator annotates wire delays on the edges + // from the input to the loads. Report the wire delay on the + // input pin instead. + Arrival next_time = delaySum(next_path->arrival(), + time_offset, + this); + incr = delayIncr(next_time, time, min_max); + time = next_time; + line_case = "input_drive"; + } + else if (is_clk) { + if (!propagated_clk) + // Clock latency at path endpoint in case latency was set + // on a clock pin other than the clock source. + time = delaySum(search_->clkPathArrival(path1), time_offset, this); + incr = 0.0; + line_case = "clk_first"; + } + else { + incr = 0.0; + line_case = "first"; + } } else if (is_clk_start - && is_clk - && !report_clk_path) { - // Clock start point and clock path are not reported. - incr = 0.0; - if (!propagated_clk) { - // Ideal clock. - const ClockEdge *src_clk_edge = path->clkEdge(this); - time = search_->clkPathArrival(path1) + time_offset; - if (src_clk_edge) { - Clock *src_clk = src_clk_edge->clock(); - const RiseFall *src_clk_rf = src_clk_edge->transition(); - slew = src_clk->slew(src_clk_rf, min_max); - } - } - line_case = "clk_start"; + && is_clk + && !report_clk_path) { + // Clock start point and clock path are not reported. + incr = 0.0; + if (!propagated_clk) { + // Ideal clock. + const ClockEdge *src_clk_edge = path->clkEdge(this); + time = delaySum(search_->clkPathArrival(path1), + time_offset, + this); + if (src_clk_edge) { + Clock *src_clk = src_clk_edge->clock(); + const RiseFall *src_clk_rf = src_clk_edge->transition(); + slew = src_clk->slew(src_clk_rf, min_max); + } + } + line_case = "clk_start"; } else if (is_clk - && report_clk_path - && !propagated_clk) { - // Zero the clock network delays for ideal clocks. - incr = 0.0; - time = prev_time; - const ClockEdge *src_clk_edge = path->clkEdge(this); - const Clock *src_clk = src_clk_edge->clock(); - const RiseFall *src_clk_rf = src_clk_edge->transition(); - slew = src_clk->slew(src_clk_rf, min_max); - line_case = "clk_ideal"; + && report_clk_path + && !propagated_clk) { + // Zero the clock network delays for ideal clocks. + incr = 0.0; + time = prev_time; + const ClockEdge *src_clk_edge = path->clkEdge(this); + const Clock *src_clk = src_clk_edge->clock(); + const RiseFall *src_clk_rf = src_clk_edge->transition(); + slew = src_clk->slew(src_clk_rf, min_max); + line_case = "clk_ideal"; } else if (is_clk && !is_clk_start) { - incr = delayIncr(time, prev_time, min_max); - line_case = "clk_prop"; + incr = delayIncr(time, prev_time, min_max); + line_case = "clk_prop"; } else { - incr = delayIncr(time, prev_time, min_max); - line_case = "normal"; + incr = delayIncr(time, prev_time, min_max); + line_case = "normal"; } if (vertex->isDriver(network_)) { + // Report delay arc pocv variation between input and driver. + reportVariation(path1); float cap = field_blank_; float fanout = field_blank_; if (field_capacitance_->enabled()) - cap = graph_delay_calc_->loadCap(pin, rf, dcalc_ap); + cap = graph_delay_calc_->loadCap(pin, rf, scene, min_max); if (field_fanout_->enabled()) - fanout = drvrFanout(vertex, dcalc_ap->corner(), min_max); - const string what = descriptionField(vertex); - reportLine(what.c_str(), cap, slew, fanout, - incr, time, false, min_max, rf, src_attr, + fanout = drvrFanout(vertex, scene, min_max); + const std::string what = descriptionField(vertex); + reportLine(what, cap, slew, fanout, + incr, field_blank_, time, false, min_max, rf, src_attr, line_case); if (report_net_) { - const string what2 = descriptionNet(pin); - reportLine(what2.c_str(), field_blank_, field_blank_, field_blank_, - field_blank_, field_blank_, false, min_max, + const std::string what2 = descriptionNet(pin); + reportLine(what2, field_blank_, field_blank_, field_blank_, + field_blank_, field_blank_, field_blank_, false, min_max, nullptr, src_attr, ""); } prev_time = time; @@ -2929,9 +2953,9 @@ ReportPath::reportPath6(const Path *path, || (i == 0) || (i == path_last_index) || is_clk_start) { - const string what = descriptionField(vertex); - reportLine(what.c_str(), field_blank_, slew, field_blank_, - incr, time, false, min_max, rf, src_attr, + const std::string what = descriptionField(vertex); + reportLine(what, field_blank_, slew, field_blank_, + incr, field_blank_, time, false, min_max, rf, src_attr, line_case); prev_time = time; } @@ -2943,54 +2967,95 @@ ReportPath::reportPath6(const Path *path, } void -ReportPath::reportHierPinsThru(const Path *path) const +ReportPath::reportVariation(const Path *path) const { - if (report_hier_pins_) { + if (field_variation_->enabled()) { const Edge *prev_edge = path->prevEdge(this); - if (prev_edge && prev_edge->isWire()) { - for (const Pin *hpin : hierPinsThruEdge(prev_edge, network_, graph_)) { - const string what = descriptionField(hpin); - reportLine(what.c_str(), field_blank_, field_blank_, field_blank_, - field_blank_, field_blank_, false, path->minMax(this), + if (prev_edge) { + const TimingArc *prev_arc = path->prevArc(this); + const MinMax *min_max = path->minMax(this); + DcalcAPIndex slew_index = path->dcalcAnalysisPtIndex(this); + const ArcDelay &arc_delay=graph_->arcDelay(prev_edge, prev_arc,slew_index); + switch (variables_->pocvMode()) { + case PocvMode::normal: { + float std_dev = arc_delay.stdDev(); + reportLine("sigma", field_blank_, field_blank_, field_blank_, + field_blank_, std_dev, field_blank_, true, min_max, nullptr, "", ""); + break; + } + case PocvMode::skew_normal: { + float mean = arc_delay.mean(); + reportLine("mean", field_blank_, field_blank_, field_blank_, + field_blank_, mean, field_blank_, true, min_max, + nullptr, "", ""); + float mean_shift = arc_delay.meanShift(); + reportLine("mean_shift", field_blank_, field_blank_, field_blank_, + field_blank_, mean_shift, field_blank_, true, min_max, + nullptr, "", ""); + float std_dev = arc_delay.stdDev(); + reportLine("std_dev", field_blank_, field_blank_, field_blank_, + field_blank_, std_dev, field_blank_, true, min_max, + nullptr, "", ""); + // skewness is dimensionless, so scale it to the field's time units. + float skewness = arc_delay.skewness() * units_->timeUnit()->scale(); + reportLine("skewness", field_blank_, field_blank_, field_blank_, + field_blank_, skewness, field_blank_, true, min_max, + nullptr, "", ""); + break; + } + default: + break; } } } } Delay -ReportPath::delayIncr(Delay time, - Delay prev, - const MinMax *min_max) const +ReportPath::delayIncr(const Delay &time, + const Delay &prev, + const MinMax *min_max) const { - if (report_sigmas_) - return delayRemove(time, prev); - else - return delayAsFloat(time, min_max, this) - delayAsFloat(prev, min_max, this); + return delayAsFloat(time, min_max, this) - delayAsFloat(prev, min_max, this); +} + +void +ReportPath::reportHierPinsThru(const Path *path) const +{ + if (report_hier_pins_) { + const Edge *prev_edge = path->prevEdge(this); + if (prev_edge && prev_edge->isWire()) { + for (const Pin *hpin : hierPinsThruEdge(prev_edge, network_, graph_)) { + const std::string what = descriptionField(hpin); + reportLine(what, field_blank_, field_blank_, field_blank_, + field_blank_, field_blank_, field_blank_, false, path->minMax(this), + nullptr, "", ""); + } + } + } } bool ReportPath::nextArcAnnotated(const Path *next_path, - size_t next_index, - const PathExpanded &expanded, - DcalcAPIndex ap_index) const + size_t next_index, + const PathExpanded &expanded, + DcalcAPIndex ap_index) const { const TimingArc *arc = expanded.path(next_index)->prevArc(this); Edge *edge = next_path->prevEdge(this); return graph_->arcDelayAnnotated(edge, arc, ap_index); } -string +std::string ReportPath::descriptionField(const Vertex *vertex) const { return descriptionField(vertex->pin()); } -string +std::string ReportPath::descriptionField(const Pin *pin) const { - const char *pin_name = cmd_network_->pathName(pin); - const char *name2; + std::string name2; if (network_->isTopLevelPort(pin)) { PortDirection *dir = network_->direction(pin); // Translate port direction. Note that this is intentionally @@ -3009,22 +3074,21 @@ ReportPath::descriptionField(const Pin *pin) const Instance *inst = network_->instance(pin); name2 = network_->cellName(inst); } - return stdstrPrint("%s (%s)", pin_name, name2); + std::string pin_name = cmd_network_->pathName(pin); + return sta::format("{} ({})", pin_name, name2); } -string +std::string ReportPath::descriptionNet(const Pin *pin) const { - if (network_->isTopLevelPort(pin)) { - const char *pin_name = cmd_network_->pathName(pin); - return stdstrPrint("%s (net)", pin_name); - } + if (network_->isTopLevelPort(pin)) + return sta::format("{} (net)", cmd_network_->pathName(pin)); else { Net *net = network_->net(pin); if (net) { Net *highest_net = network_->highestNetAbove(net); - const char *net_name = cmd_network_->pathName(highest_net); - return stdstrPrint("%s (net)", net_name); + std::string net_name = cmd_network_->pathName(highest_net); + return sta::format("{} (net)", net_name); } else return "(unconnected)"; @@ -3033,9 +3097,10 @@ ReportPath::descriptionNet(const Pin *pin) const float ReportPath::drvrFanout(Vertex *drvr, - const Corner *corner, - const MinMax *min_max) const + const Scene *scene, + const MinMax *min_max) const { + const Sdc *sdc = scene->sdc(); float fanout = 0.0; VertexOutEdgeIterator iter(drvr, graph_); while (iter.hasNext()) { @@ -3045,7 +3110,7 @@ ReportPath::drvrFanout(Vertex *drvr, if (network_->isTopLevelPort(pin)) { // Output port counts as a fanout. Port *port = network_->port(pin); - fanout += sdc_->portExtFanout(port, corner, min_max) + 1; + fanout += sdc->portExtFanout(port, min_max) + 1; } else fanout++; @@ -3056,40 +3121,43 @@ ReportPath::drvrFanout(Vertex *drvr, bool ReportPath::hasExtInputDriver(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max) const + const RiseFall *rf, + const MinMax *min_max, + const Sdc *sdc) const { Port *port = network_->port(pin); - InputDrive *drive = sdc_->findInputDrive(port); + InputDrive *drive = sdc->findInputDrive(port); return (drive - && (drive->hasDriveResistance(rf, min_max) - || drive->hasDriveCell(rf, min_max))); + && (drive->hasDriveResistance(rf, min_max) + || drive->hasDriveCell(rf, min_max))); } void ReportPath::reportInputExternalDelay(const Path *first_path, - float time_offset) const + float time_offset) const { const Pin *first_pin = first_path->pin(graph_); if (!pathFromClkPin(first_path, first_pin)) { const RiseFall *rf = first_path->transition(this); - Arrival time = first_path->arrival() + time_offset; + Arrival time = delaySum(first_path->arrival(), + time_offset, + this); const EarlyLate *early_late = first_path->minMax(this); InputDelay *input_delay = pathInputDelay(first_path); if (input_delay) { const Pin *ref_pin = input_delay->refPin(); if (ref_pin) { - Path ref_path; - pathInputDelayRefPath(first_path, input_delay, ref_path); - if (!ref_path.isNull() && reportClkPath()) { - PathExpanded ref_expanded(&ref_path, this); - reportPath3(&ref_path, ref_expanded, true, 0.0); - } + Path ref_path; + pathInputDelayRefPath(first_path, input_delay, ref_path); + if (!ref_path.isNull() && reportClkPath()) { + PathExpanded ref_expanded(&ref_path, this); + reportPath3(&ref_path, ref_expanded, true, 0.0); + } } float input_arrival = - input_delay->delays()->value(rf, first_path->minMax(this)); + input_delay->delays()->value(rf, first_path->minMax(this)); reportLine("input external delay", input_arrival, time, - early_late, rf); + early_late, rf); } else if (network_->isTopLevelPort(first_pin)) reportLine("input external delay", 0.0, time, early_late, rf); @@ -3105,17 +3173,17 @@ ReportPath::pathInputDelay(const Path *first_path) const void ReportPath::pathInputDelayRefPath(const Path *path, - const InputDelay *input_delay, - // Return value. - Path &ref_path) const + const InputDelay *input_delay, + // Return value. + Path &ref_path) const { const Pin *ref_pin = input_delay->refPin(); const RiseFall *ref_rf = input_delay->refTransition(); Vertex *ref_vertex = graph_->pinDrvrVertex(ref_pin); if (ref_vertex) { - const PathAnalysisPt *path_ap = path->pathAnalysisPt(this); const ClockEdge *clk_edge = path->clkEdge(this); - VertexPathIterator path_iter(ref_vertex, ref_rf, path_ap, this); + VertexPathIterator path_iter(ref_vertex, path->scene(this), + path->minMax(this), ref_rf,this); while (path_iter.hasNext()) { Path *path = path_iter.next(); if (path->isClock(this) @@ -3132,107 +3200,103 @@ ReportPath::pathInputDelayRefPath(const Path *path, void ReportPath::reportPathHeader() const { - string line; + std::string line; bool first_field = true; for (const ReportField *field : fields_) { if (field->enabled()) { if (!first_field) - line += ' '; + line += ' '; reportField(field->title(), field, line); first_field = false; } } trimRight(line); - report_->reportLineString(line); + report_->reportLine(line); reportDashLine(); } // Report total. void -ReportPath::reportLine(const char *what, - Delay total, - const EarlyLate *early_late) const +ReportPath::reportLine(std::string_view what, + Delay total, + const EarlyLate *early_late) const { - reportLine(what, field_blank_, field_blank_, field_blank_, - field_blank_, total, false, early_late, nullptr, - "", nullptr); + reportLine(what, field_blank_, field_blank_, field_blank_, field_blank_, + field_blank_, total, false, early_late, nullptr, "", ""); } // Report negative total. void -ReportPath::reportLineNegative(const char *what, - Delay total, - const EarlyLate *early_late) const +ReportPath::reportLineNegative(std::string_view what, + Delay total, + const EarlyLate *early_late) const { reportLine(what, field_blank_, field_blank_, field_blank_, - field_blank_, total, true, early_late, nullptr, - "", nullptr); + field_blank_, field_blank_, total, true /* tota_with_minus */, + early_late, nullptr, "", ""); } // Report total, and transition suffix. void -ReportPath::reportLine(const char *what, - Delay total, - const EarlyLate *early_late, - const RiseFall *rf) const +ReportPath::reportLine(std::string_view what, + Delay total, + const EarlyLate *early_late, + const RiseFall *rf) const { reportLine(what, field_blank_, field_blank_, field_blank_, - field_blank_, total, false, early_late, rf, "", - nullptr); + field_blank_, field_blank_, total, false, early_late, rf, "", ""); } // Report increment, and total. void -ReportPath::reportLine(const char *what, - Delay incr, - Delay total, - const EarlyLate *early_late) const +ReportPath::reportLine(std::string_view what, + const Delay &incr, + const Delay &total, + const EarlyLate *early_late) const { reportLine(what, field_blank_, field_blank_, field_blank_, - incr, total, false, early_late, nullptr, "", - nullptr); + incr, field_blank_, total, false, early_late, nullptr, "", ""); } // Report increment, total, and transition suffix. void -ReportPath::reportLine(const char *what, - Delay incr, - Delay total, - const EarlyLate *early_late, - const RiseFall *rf) const +ReportPath::reportLine(std::string_view what, + const Delay &incr, + const Delay &total, + const EarlyLate *early_late, + const RiseFall *rf) const { reportLine(what, field_blank_, field_blank_, field_blank_, - incr, total, false, early_late, rf, "", - nullptr); + incr, field_blank_, total, false, early_late, rf, "", ""); } // Report slew, increment, and total. void -ReportPath::reportLine(const char *what, - Slew slew, - Delay incr, - Delay total, - const EarlyLate *early_late) const +ReportPath::reportLine(std::string_view what, + const Slew &slew, + const Delay &incr, + const Delay &total, + const EarlyLate *early_late) const { reportLine(what, field_blank_, slew, field_blank_, - incr, total, false, early_late, nullptr, - "", nullptr); + incr, field_blank_, total, false, early_late, nullptr, "", ""); } void -ReportPath::reportLine(const char *what, - float cap, - Slew slew, - float fanout, - Delay incr, - Delay total, - bool total_with_minus, - const EarlyLate *early_late, - const RiseFall *rf, - string src_attr, - const char *line_case) const +ReportPath::reportLine(std::string_view what, + float cap, + const Slew &slew, + float fanout, + const Delay &incr, + float variation, + const Delay &total, + bool total_with_minus, + const EarlyLate *early_late, + const RiseFall *rf, + std::string_view src_attr, + std::string_view line_case) const { - string line; + std::string line; size_t field_index = 0; bool first_field = true; for (const ReportField *field : fields_) { @@ -3240,89 +3304,93 @@ ReportPath::reportLine(const char *what, if (field->enabled()) { if (!first_field) - line += ' '; + line += ' '; if (field == field_description_) - reportDescription(what, first_field, last_field, line); + reportDescription(what, first_field, last_field, line); else if (field == field_fanout_) { - if (fanout == field_blank_) - reportFieldBlank(field, line); - else - line += stdstrPrint("%*d", - field_fanout_->width(), - static_cast(fanout)); + if (fanout == field_blank_) + reportFieldBlank(field, line); + else + line += sta::format("{:{}}", + fanout, + field_fanout_->width()); } else if (field == field_capacitance_) - reportField(cap, field, line); + reportField(cap, field, line); else if (field == field_slew_) - reportFieldDelay(slew, early_late, field, line); + reportFieldDelay(slew, early_late, field, line); else if (field == field_incr_) - reportFieldDelay(incr, early_late, field, line); + reportFieldDelay(incr, early_late, field, line); + else if (field == field_variation_) + reportFieldDelay(variation, early_late, field, line); else if (field == field_total_) { - if (total_with_minus) - reportFieldDelayMinus(total, early_late, field, line); - else - reportFieldDelay(total, early_late, field, line); + if (total_with_minus) + reportFieldDelayMinus(total, early_late, field, line); + else + reportFieldDelay(total, early_late, field, line); } else if (field == field_edge_) { - if (rf) - reportField(rf->shortName(), field, line); - else - reportFieldBlank(field, line); + if (rf) + reportField(rf->shortName(), field, line); + else + reportFieldBlank(field, line); } + else if (field == field_variation_) + reportFieldBlank(field, line); else if (field == field_src_attr_) { - if (src_attr != "") - reportField(src_attr.c_str(), field, line); - else - reportFieldBlank(field, line); + if (!src_attr.empty()) + reportField(src_attr, field, line); + else + reportFieldBlank(field, line); } - else if (field == field_case_ && line_case) - line += line_case; + else if (field == field_case_) + line += line_case; first_field = false; } field_index++; } // Trim trailing spaces and report the line. - string line_stdstr = line; + std::string line_stdstr = line; trimRight(line_stdstr); - report_->reportLineString(line_stdstr.c_str()); + report_->reportLine(line_stdstr); } //////////////////////////////////////////////////////////////// // Only the total field. void -ReportPath::reportLineTotal(const char *what, - Delay incr, - const EarlyLate *early_late) const +ReportPath::reportLineTotal(std::string_view what, + const Delay &incr, + const EarlyLate *early_late) const { reportLineTotal1(what, incr, false, early_late); } // Only the total field and always with leading minus sign. void -ReportPath::reportLineTotalMinus(const char *what, - Delay decr, - const EarlyLate *early_late) const +ReportPath::reportLineTotalMinus(std::string_view what, + const Delay &decr, + const EarlyLate *early_late) const { reportLineTotal1(what, decr, true, early_late); } void -ReportPath::reportLineTotal1(const char *what, - Delay incr, - bool incr_with_minus, - const EarlyLate *early_late) const +ReportPath::reportLineTotal1(std::string_view what, + const Delay &incr, + bool incr_with_minus, + const EarlyLate *early_late) const { - string line; + std::string line; reportDescription(what, line); line += ' '; if (incr_with_minus) reportFieldDelayMinus(incr, early_late, field_total_, line); else reportFieldDelay(incr, early_late, field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); } void @@ -3334,43 +3402,43 @@ ReportPath::reportDashLineTotal() const //////////////////////////////////////////////////////////////// void -ReportPath::reportDescription(const char *what, - string &line) const +ReportPath::reportDescription(std::string_view what, + std::string &line) const { reportDescription(what, false, false, line); } void -ReportPath::reportDescription(const char *what, - bool first_field, - bool last_field, - string &line) const +ReportPath::reportDescription(std::string_view what, + bool first_field, + bool last_field, + std::string &line) const { line += what; - int length = strlen(what); + size_t length = what.size(); if (!no_split_ && first_field && length > field_description_->width()) { reportBlankLine(); - for (int i = 0; i < field_description_->width(); i++) + for (size_t i = 0; i < field_description_->width(); i++) line += ' '; } else if (!last_field) { - for (int i = length; i < field_description_->width(); i++) + for (size_t i = length; i < field_description_->width(); i++) line += ' '; } } void ReportPath::reportFieldTime(float value, - ReportField *field, - string &line) const + ReportField *field, + std::string &line) const { if (delayAsFloat(value) == field_blank_) reportFieldBlank(field, line); else { - const char *str = units_->timeUnit()->asString(value, digits_); - if (stringEq(str, minus_zero_)) + std::string str = units_->timeUnit()->asString(value, digits_); + if (str == minus_zero_) // Filter "-0.00" fields. str = plus_zero_; reportField(str, field, line); @@ -3379,28 +3447,28 @@ ReportPath::reportFieldTime(float value, void ReportPath::reportSpaceFieldTime(float value, - string &line) const + std::string &line) const { line += ' '; reportFieldTime(value, field_total_, line); } void -ReportPath::reportSpaceFieldDelay(Delay value, - const EarlyLate *early_late, - string &line) const +ReportPath::reportSpaceFieldDelay(const Delay &value, + const EarlyLate *early_late, + std::string &line) const { line += ' '; reportTotalDelay(value, early_late, line); } void -ReportPath::reportTotalDelay(Delay value, - const EarlyLate *early_late, - string &line) const +ReportPath::reportTotalDelay(const Delay &value, + const EarlyLate *early_late, + std::string &line) const { - const char *str = delayAsString(value, early_late, this, digits_); - if (stringEq(str, minus_zero_)) + std::string str = delayAsString(value, early_late, digits_, this); + if (str == minus_zero_) // Filter "-0.00" fields. str = plus_zero_; reportField(str, field_total_, line); @@ -3408,19 +3476,18 @@ ReportPath::reportTotalDelay(Delay value, // Total time always with leading minus sign. void -ReportPath::reportFieldDelayMinus(Delay value, - const EarlyLate *early_late, - const ReportField *field, - string &line) const +ReportPath::reportFieldDelayMinus(const Delay &value, + const EarlyLate *early_late, + const ReportField *field, + std::string &line) const { if (delayAsFloat(value) == field_blank_) reportFieldBlank(field, line); else { - const char *str = report_sigmas_ - ? delayAsString(-value, this, digits_) - // Opposite min/max for negative value. - : delayAsString(-value, early_late->opposite(), this, digits_); - if (stringEq(str, plus_zero_)) + // Opposite min/max for negative value. + std::string str = delayAsString(delayDiff(delay_zero, value, this), + early_late->opposite(), digits_, this); + if (str == plus_zero_) // Force leading minus sign. str = minus_zero_; reportField(str, field, line); @@ -3428,18 +3495,16 @@ ReportPath::reportFieldDelayMinus(Delay value, } void -ReportPath::reportFieldDelay(Delay value, - const EarlyLate *early_late, - const ReportField *field, - string &line) const +ReportPath::reportFieldDelay(const Delay &value, + const EarlyLate *early_late, + const ReportField *field, + std::string &line) const { - if (delayAsFloat(value) == field_blank_) + if (value.mean() == field_blank_) reportFieldBlank(field, line); else { - const char *str = report_sigmas_ - ? delayAsString(value, this, digits_) - : delayAsString(value, early_late, this, digits_); - if (stringEq(str, minus_zero_)) + std::string str = delayAsString(value, early_late, digits_, this); + if (str == minus_zero_) // Filter "-0.00" fields. str = plus_zero_; reportField(str, field, line); @@ -3448,34 +3513,33 @@ ReportPath::reportFieldDelay(Delay value, void ReportPath::reportField(float value, - const ReportField *field, - string &line) const + const ReportField *field, + std::string &line) const { if (value == field_blank_) reportFieldBlank(field, line); else { Unit *unit = field->unit(); if (unit) { - const char *value_str = unit->asString(value, digits_); + std::string value_str = unit->asString(value, digits_); reportField(value_str, field, line); } else { // fanout - string value_str; - stringPrint(value_str, "%.0f", value); - reportField(value_str.c_str(), field, line); + std::string value_str = sta::format("{:.0f}", value); + reportField(value_str, field, line); } } } void -ReportPath::reportField(const char *value, - const ReportField *field, - string &line) const +ReportPath::reportField(std::string_view value, + const ReportField *field, + std::string &line) const { if (field->leftJustify()) line += value; - for (int i = strlen(value); i < field->width(); i++) + for (size_t i = static_cast(value.size()); i < field->width(); i++) line += ' '; if (!field->leftJustify()) line += value; @@ -3483,7 +3547,7 @@ ReportPath::reportField(const char *value, void ReportPath::reportFieldBlank(const ReportField *field, - string &line) const + std::string &line) const { line += field->blank(); } @@ -3491,24 +3555,24 @@ ReportPath::reportFieldBlank(const ReportField *field, void ReportPath::reportDashLine() const { - string line; + std::string line; for (const ReportField *field : fields_) { if (field->enabled()) { - for (int i = 0; i < field->width(); i++) - line += '-'; + for (size_t i = 0; i < field->width(); i++) + line += '-'; } } line += "------"; - report_->reportLineString(line); + report_->reportLine(line); } void ReportPath::reportDashLine(int line_width) const { - string line; + std::string line; for (int i = 0; i < line_width; i++) line += '-'; - report_->reportLineString(line); + report_->reportLine(line); } void @@ -3526,7 +3590,7 @@ ReportPath::reportClkPath() const //////////////////////////////////////////////////////////////// -const char * +std::string_view ReportPath::asRisingFalling(const RiseFall *rf) const { if (rf == RiseFall::rise()) @@ -3535,7 +3599,7 @@ ReportPath::asRisingFalling(const RiseFall *rf) const return "falling"; } -const char * +std::string_view ReportPath::asRiseFall(const RiseFall *rf) const { if (rf == RiseFall::rise()) @@ -3545,9 +3609,9 @@ ReportPath::asRiseFall(const RiseFall *rf) const } // Find the startpoint type from the first path edge. -const char * +std::string_view ReportPath::edgeRegLatchDesc(const Edge *first_edge, - const TimingArc *first_arc) const + const TimingArc *first_arc) const { const TimingRole *role = first_arc->role(); if (role == TimingRole::latchDtoQ()) { @@ -3558,7 +3622,7 @@ ReportPath::edgeRegLatchDesc(const Edge *first_edge, const FuncExpr *enable_func; const RiseFall *enable_rf; cell->latchEnable(first_edge->timingArcSet(), - enable_port, enable_func, enable_rf); + enable_port, enable_func, enable_rf); return latchDesc(enable_rf); } } @@ -3570,21 +3634,21 @@ ReportPath::edgeRegLatchDesc(const Edge *first_edge, return regDesc(first_arc->fromEdge()->asRiseFall()); } -const char * +std::string_view ReportPath::checkRegLatchDesc(const TimingRole *role, - const RiseFall *clk_rf) const + const RiseFall *clk_rf) const { if (role == TimingRole::regClkToQ()) return regDesc(clk_rf); else if (role == TimingRole::latchEnToQ() - || role == TimingRole::latchDtoQ()) + || role == TimingRole::latchDtoQ()) return latchDesc(clk_rf); else // Default when we don't know better. return "edge-triggered flip-flop"; } -const char * +std::string_view ReportPath::regDesc(const RiseFall *clk_rf) const { if (clk_rf == RiseFall::rise()) @@ -3595,7 +3659,7 @@ ReportPath::regDesc(const RiseFall *clk_rf) const return "edge-triggered flip-flop"; } -const char * +std::string_view ReportPath::latchDesc(const RiseFall *clk_rf) const { return (clk_rf == RiseFall::rise()) @@ -3617,7 +3681,7 @@ hierPinsThruEdge(const Edge *edge, hierPinsAbove(drvr_pin, network, drvr_hpins); hierPinsAbove(load_pin, network, load_hpins); if (drvr_hpins.empty()) { - std::reverse(load_hpins.begin(), load_hpins.end()); + std::ranges::reverse(load_hpins); return load_hpins; } if (load_hpins.empty()) @@ -3676,4 +3740,4 @@ hierPinsAbove(const Net *net, } } -} // namespace +} // namespace sta diff --git a/search/ReportPath.hh b/search/ReportPath.hh index 9014d7d9c..6d7c6d6f5 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,46 +24,59 @@ #pragma once +#include #include #include // SILIMATE: Custom regex-based deduplication by removal of matching parts from endpoints +#include +#include -#include "StringSeq.hh" -#include "SearchClass.hh" +#include "CheckMaxSkews.hh" +#include "CheckMinPeriods.hh" +#include "CheckMinPulseWidths.hh" +#include "Clock.hh" +#include "Delay.hh" +#include "GraphClass.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "Mode.hh" +#include "NetworkClass.hh" +#include "Path.hh" #include "PathEnd.hh" +#include "Sdc.hh" +#include "SearchClass.hh" +#include "StringUtil.hh" namespace sta { -class Corner; -class DcalcAnalysisPt; +class Scene; class PathExpanded; class ReportField; -typedef Vector ReportFieldSeq; +using ReportFieldSeq = std::vector; class ReportPath : public StaState { public: ReportPath(StaState *sta); - virtual ~ReportPath(); + ~ReportPath() override; ReportPathFormat pathFormat() const { return format_; } void setPathFormat(ReportPathFormat format); - void setReportFieldOrder(StringSeq *field_names); + void setReportFieldOrder(const StringSeq &field_names); void setReportFields(bool report_input_pin, bool report_hier_pins, - bool report_net, - bool report_cap, - bool report_slew, - bool report_fanout, - bool report_src_attr); + bool report_net, + bool report_cap, + bool report_slew, + bool report_fanout, + bool report_variation, + bool report_src_attr); int digits() const { return digits_; } void setDigits(int digits); void setNoSplit(bool no_split); - bool reportSigmas() const { return report_sigmas_; } - void setReportSigmas(bool report); void setReportDedupByWord(bool dedup_by_word); void setReportDedupSameDelay(bool dedup_same_delay); void setSilimateDedupEndpointRegex(std::string_view silimate_dedup_endpoints_rx); // SILIMATE: Custom regex-based deduplication by removal of matching parts from endpoints - ReportField *findField(const char *name) const; + ReportField *findField(std::string_view name) const; // Header above reportPathEnd results. void reportPathEndHeader() const; @@ -73,9 +86,11 @@ public: // Format report_path_endpoint only: // Previous path end is used to: // - detect path group changes so headers are reported by group. - // - JSON format: if not first, add a comma before appending new path + // Last is used to: + // - If set to true, a comma is not emitted after in JSON mode. void reportPathEnd(const PathEnd *end, - const PathEnd *prev_end) const; + const PathEnd *prev_end, + bool last) const; void reportPathEnds(const PathEndSeq *ends) const; void reportPath(const Path *path) const; @@ -98,15 +113,15 @@ public: void reportJsonHeader() const; void reportJsonFooter() const; void reportJson(const PathEnd *end, - const PathEnd *prev_end) const; + bool last) const; void reportJson(const Path *path) const; void reportJson(const Path *path, - const char *path_name, + std::string_view path_name, int indent, bool trailing_comma, std::string &result) const; void reportJson(const PathExpanded &expanded, - const char *path_name, + std::string_view path_name, int indent, bool trailing_comma, std::string &result) const; @@ -120,44 +135,42 @@ public: void reportSlackOnlyHeader() const; void reportSlackOnly(const PathEnd *end) const; - void reportMpwCheck(const MinPulseWidthCheck *check, - bool verbose) const; - void reportMpwChecks(const MinPulseWidthCheckSeq *checks, - bool verbose) const; + void reportMpwCheck(const MinPulseWidthCheck &check, + bool verbose) const; + void reportMpwChecks(const MinPulseWidthCheckSeq &checks, + bool verbose) const; void reportMpwHeaderShort() const; - void reportShort(const MinPulseWidthCheck *check) const; - void reportVerbose(const MinPulseWidthCheck *check) const; + void reportShort(const MinPulseWidthCheck &check) const; + void reportVerbose(const MinPulseWidthCheck &check) const; - void reportCheck(const MinPeriodCheck *check, - bool verbose) const; - void reportChecks(const MinPeriodCheckSeq *checks, - bool verbose) const; + void reportCheck(const MinPeriodCheck &check, + bool verbose) const; + void reportChecks(const MinPeriodCheckSeq &checks, + bool verbose) const; void reportPeriodHeaderShort() const; - void reportShort(const MinPeriodCheck *check) const; - void reportVerbose(const MinPeriodCheck *check) const; + void reportShort(const MinPeriodCheck &check) const; + void reportVerbose(const MinPeriodCheck &check) const; - void reportCheck(const MaxSkewCheck *check, - bool verbose) const; - void reportChecks(const MaxSkewCheckSeq *checks, - bool verbose) const; + void reportChecks(const MaxSkewCheckSeq &checks, + bool verbose) const; void reportMaxSkewHeaderShort() const; - void reportShort(const MaxSkewCheck *check) const; - void reportVerbose(const MaxSkewCheck *check) const; + void reportShort(const MaxSkewCheck &check) const; + void reportVerbose(const MaxSkewCheck &check) const; void reportLimitShortHeader(const ReportField *field) const; void reportLimitShort(const ReportField *field, - Pin *pin, - float value, - float limit, - float slack) const; + const Pin *pin, + float value, + float limit, + float slack) const; void reportLimitVerbose(const ReportField *field, - Pin *pin, - const RiseFall *rf, - float value, - float limit, - float slack, - const Corner *corner, - const MinMax *min_max) const; + const Pin *pin, + const RiseFall *rf, + float value, + float limit, + float slack, + const Scene *scene, + const MinMax *min_max) const; ReportField *fieldSlew() const { return field_slew_; } ReportField *fieldFanout() const { return field_fanout_; } ReportField *fieldCapacitance() const { return field_capacitance_; } @@ -165,28 +178,28 @@ public: protected: void makeFields(); - ReportField *makeField(const char *name, - const char *title, - int width, - bool left_justify, - Unit *unit, - bool enabled); + ReportField *makeField(std::string_view name, + std::string_view title, + int width, + bool left_justify, + Unit *unit, + bool enabled); void reportEndpointHeader(const PathEnd *end, - const PathEnd *prev_end) const; + const PathEnd *prev_end) const; void reportShort(const PathEndUnconstrained *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportShort(const PathEndCheck *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportShort(const PathEndLatchCheck *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportShort(const PathEndPathDelay *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportShort(const PathEndOutputDelay *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportShort(const PathEndGatedClock *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportShort(const PathEndDataCheck *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportEndpoint(const PathEndOutputDelay *end) const; void reportEndpointOutputDelay(const PathEndClkConstrained *end) const; void reportEndpoint(const PathEndPathDelay *end) const; @@ -195,225 +208,229 @@ protected: std::string pathStartpoint(const PathEnd *end, const PathExpanded &expanded) const; void reportBorrowing(const PathEndLatchCheck *end, - Arrival &borrow, - Arrival &time_given_to_startpoint) const; + Arrival &borrow, + Arrival &time_given_to_startpoint) const; void reportEndpoint(const PathEndDataCheck *end) const; - const char *clkNetworkDelayIdealProp(bool is_ideal) const; + std::string_view clkNetworkDelayIdealProp(bool is_prop) const; std::string checkRoleReason(const PathEnd *end) const; std::string checkRoleString(const PathEnd *end) const; virtual void reportGroup(const PathEnd *end) const; void reportStartpoint(const PathEnd *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportUnclockedEndpoint(const PathEnd *end, - const char *default_reason) const; + std::string_view default_reason) const; void reportEndpoint(const PathEndCheck *end) const; void reportEndpoint(const PathEndLatchCheck *end) const; - const char *latchDesc(const PathEndLatchCheck *end) const; - void reportStartpoint(const char *start, - const std::string reason) const; - void reportEndpoint(const char *end, - const std::string reason) const; - void reportStartEndPoint(const char *pt, - const std::string reason, - const char *key) const; + std::string_view latchDesc(const PathEndLatchCheck *end) const; + void reportStartpoint(std::string_view start, + const std::string &reason) const; + void reportEndpoint(std::string_view end, + const std::string &reason) const; + void reportStartEndPoint(std::string_view pt, + const std::string &reason, + std::string_view key) const; std::string tgtClkName(const PathEnd *end) const; - const char *clkRegLatchDesc(const PathEnd *end) const; + std::string_view clkRegLatchDesc(const PathEnd *end) const; void reportSrcPath(const PathEnd *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportTgtClk(const PathEnd *end) const; void reportTgtClk(const PathEnd *end, - float prev_time) const; + float prev_time) const; void reportTgtClk(const PathEnd *end, - float prev_time, - bool is_prop) const; + float prev_time, + bool is_prop) const; void reportTgtClk(const PathEnd *end, float prev_time, float src_offset, bool is_prop) const; bool pathFromGenPropClk(const Path *clk_path, - const EarlyLate *early_late) const; + const EarlyLate *early_late) const; bool isGenPropClk(const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - const EarlyLate *early_late) const; + const RiseFall *clk_rf, + const MinMax *min_max, + const EarlyLate *early_late, + const Sdc *sdc) const; void reportSrcClkAndPath(const Path *path, - const PathExpanded &expanded, - float time_offset, - Arrival clk_insertion, - Arrival clk_latency, - bool is_path_delay) const; + const PathExpanded &expanded, + float time_offset, + Arrival clk_insertion, + Arrival clk_latency, + bool is_path_delay) const; bool reportGenClkSrcPath(const Path *clk_path, const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - const EarlyLate *early_late) const; + const RiseFall *clk_rf, + const MinMax *min_max, + const EarlyLate *early_late, + const Sdc *sdc) const; void reportGenClkSrcAndPath(const Path *path, - const Clock *clk, - const RiseFall *clk_rf, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap, - float time_offset, - float path_time_offset, - bool clk_used_as_data) const; + const Clock *clk, + const RiseFall *clk_rf, + const EarlyLate *early_late, + float time_offset, + float path_time_offset, + bool clk_used_as_data, + const Mode *mode) const; bool reportGenClkSrcPath1(const Clock *clk, - const Pin *clk_pin, - const RiseFall *clk_rf, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap, - float gclk_time, - float time_offset, - bool clk_used_as_data) const; + const Pin *clk_pin, + const RiseFall *clk_rf, + const EarlyLate *early_late, + float gclk_time, + float time_offset, + bool clk_used_as_data, + const Mode *mode) const; void reportClkSrcLatency(Arrival insertion, - float clk_time, - const EarlyLate *early_late) const; + float clk_time, + const EarlyLate *early_late) const; void reportPathLine(const Path *path, - Delay incr, - Arrival time, - const char *line_case) const; + const Delay &incr, + const Arrival &time, + std::string_view line_case) const; void reportCommonClkPessimism(const PathEnd *end, - Arrival &clk_arrival) const ; + Arrival &clk_arrival) const ; void reportClkUncertainty(const PathEnd *end, - Arrival &clk_arrival) const ; + Arrival &clk_arrival) const ; void reportClkLine(const Clock *clk, - const char *clk_name, - const RiseFall *clk_rf, - Arrival clk_time, - const MinMax *min_max) const ; + std::string_view clk_name, + const RiseFall *clk_rf, + Arrival clk_time, + const MinMax *min_max) const ; void reportClkLine(const Clock *clk, - const char *clk_name, - const RiseFall *clk_rf, - Arrival prev_time, - Arrival clk_time, - const MinMax *min_max) const ; + std::string_view clk_name, + const RiseFall *clk_rf, + Arrival prev_time, + Arrival clk_time, + const MinMax *min_max) const ; void reportRequired(const PathEnd *end, - std::string margin_msg) const ; + const std::string& margin_msg) const ; void reportSlack(const PathEnd *end) const ; void reportSlack(Slack slack) const ; void reportSpaceSlack(const PathEnd *end, - std::string &line) const ; + std::string &result) const ; void reportSpaceSlack(Slack slack, - std::string &line) const ; + std::string &result) const ; void reportSrcPathArrival(const PathEnd *end, - const PathExpanded &expanded) const ; + const PathExpanded &expanded) const ; void reportPath(const PathEnd *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportPathFull(const Path *path) const; void reportPathHeader() const; void reportPath1(const Path *path, - const PathExpanded &expanded, - bool clk_used_as_data, - float time_offset) const; + const PathExpanded &expanded, + bool clk_used_as_data, + float time_offset) const; void reportPath2(const Path *path, - const PathExpanded &expanded, - bool skip_first_path, - bool clk_used_as_data, - float time_offset) const; + const PathExpanded &expanded, + bool skip_first_path, + bool clk_used_as_data, + float time_offset) const; void reportPath3(const Path *path, - const PathExpanded &expanded, - bool report_clk_path, - float time_offset) const; + const PathExpanded &expanded, + bool report_clk_path, + float time_offset) const; void reportPath4(const Path *path, - const PathExpanded &expanded, - bool skip_first_path, - bool propagated_clk, - bool report_clk_path, - float time_offset) const; + const PathExpanded &expanded, + bool skip_first_path, + bool propagated_clk, + bool report_clk_path, + float time_offset) const; void reportPath5(const Path *path, - const PathExpanded &expanded, - bool skip_first_path, - bool propagated_clk, - bool report_clk_path, - float time_offset) const; + const PathExpanded &expanded, + bool skip_first_path, + bool propagated_clk, + bool report_clk_path, + float time_offset) const; void reportPath6(const Path *path, - const PathExpanded &expanded, - size_t path_first_index, - bool propagated_clk, - bool report_clk_path, - Arrival prev_time, - float time_offset) const; + const PathExpanded &expanded, + size_t path_first_index, + bool propagated_clk, + bool report_clk_path, + Arrival prev_time, + float time_offset) const; + void reportVariation(const Path *path) const; void reportHierPinsThru(const Path *path) const; void reportInputExternalDelay(const Path *path, - float time_offset) const; - void reportLine(const char *what, - Delay total, - const EarlyLate *early_late) const; - void reportLineNegative(const char *what, - Delay total, - const EarlyLate *early_late) const; - void reportLine(const char *what, - Delay total, - const EarlyLate *early_late, - const RiseFall *rf) const; - void reportLine(const char *what, - Delay incr, - Delay total, - const EarlyLate *early_late) const; - void reportLine(const char *what, - Delay incr, - Delay total, - const EarlyLate *early_late, - const RiseFall *rf) const; - void reportLine(const char *what, - Slew slew, - Delay incr, - Delay total, - const EarlyLate *early_late) const; - void reportLine(const char *what, - float cap, - Slew slew, - float fanout, - Delay incr, - Delay total, - bool total_with_minus, - const EarlyLate *early_late, - const RiseFall *rf, - std::string src_attr, - const char *line_case) const; - void reportLineTotal(const char *what, - Delay incr, - const EarlyLate *early_late) const; - void reportLineTotalMinus(const char *what, - Delay decr, - const EarlyLate *early_late) const; - void reportLineTotal1(const char *what, - Delay incr, - bool incr_with_minus, - const EarlyLate *early_late) const; + float time_offset) const; + void reportLine(std::string_view what, + Delay total, + const EarlyLate *early_late) const; + void reportLineNegative(std::string_view what, + Delay total, + const EarlyLate *early_late) const; + void reportLine(std::string_view what, + Delay total, + const EarlyLate *early_late, + const RiseFall *rf) const; + void reportLine(std::string_view what, + const Delay &incr, + const Delay &total, + const EarlyLate *early_late) const; + void reportLine(std::string_view what, + const Delay &incr, + const Delay &total, + const EarlyLate *early_late, + const RiseFall *rf) const; + void reportLine(std::string_view what, + const Slew &slew, + const Delay &incr, + const Delay &total, + const EarlyLate *early_late) const; + void reportLine(std::string_view what, + float cap, + const Slew &slew, + float fanout, + const Delay &incr, + float variation, + const Delay &total, + bool total_with_minus, + const EarlyLate *early_late, + const RiseFall *rf, + std::string_view src_attr, + std::string_view line_case) const; + void reportLineTotal(std::string_view what, + const Delay &incr, + const EarlyLate *early_late) const; + void reportLineTotalMinus(std::string_view what, + const Delay &decr, + const EarlyLate *early_late) const; + void reportLineTotal1(std::string_view what, + const Delay &incr, + bool incr_with_minus, + const EarlyLate *early_late) const; void reportDashLineTotal() const; - void reportDescription(const char *what, - std::string &result) const; - void reportDescription(const char *what, - bool first_field, - bool last_field, - std::string &result) const; + void reportDescription(std::string_view what, + std::string &line) const; + void reportDescription(std::string_view what, + bool first_field, + bool last_field, + std::string &line) const; void reportFieldTime(float value, - ReportField *field, - std::string &result) const; + ReportField *field, + std::string &line) const; void reportSpaceFieldTime(float value, - std::string &result) const; - void reportSpaceFieldDelay(Delay value, - const EarlyLate *early_late, - std::string &result) const; - void reportFieldDelayMinus(Delay value, - const EarlyLate *early_late, - const ReportField *field, - std::string &result) const; - void reportTotalDelay(Delay value, - const EarlyLate *early_late, - std::string &result) const; - void reportFieldDelay(Delay value, - const EarlyLate *early_late, - const ReportField *field, - std::string &result) const; + std::string &line) const; + void reportSpaceFieldDelay(const Delay &value, + const EarlyLate *early_late, + std::string &line) const; + void reportFieldDelayMinus(const Delay &value, + const EarlyLate *early_late, + const ReportField *field, + std::string &line) const; + void reportTotalDelay(const Delay &value, + const EarlyLate *early_late, + std::string &line) const; + void reportFieldDelay(const Delay &value, + const EarlyLate *early_late, + const ReportField *field, + std::string &line) const; void reportField(float value, - const ReportField *field, - std::string &result) const; - void reportField(const char *value, - const ReportField *field, - std::string &result) const; + const ReportField *field, + std::string &line) const; + void reportField(std::string_view value, + const ReportField *field, + std::string &line) const; void reportFieldBlank(const ReportField *field, - std::string &result) const; + std::string &line) const; void reportDashLine() const; void reportDashLine(int line_width) const; void reportBlankLine() const; @@ -424,66 +441,66 @@ protected: std::string clkName(const Clock *clk, bool inverted) const; bool hasExtInputDriver(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max) const; + const RiseFall *rf, + const MinMax *min_max, + const Sdc *sdc) const; float drvrFanout(Vertex *drvr, - const Corner *corner, - const MinMax *min_max) const; - const char *mpwCheckHiLow(const MinPulseWidthCheck *check) const; - void reportSkewClkPath(const char *arrival_msg, - const Path *clk_path) const; - const char *edgeRegLatchDesc(const Edge *edge, - const TimingArc *arc) const; - const char *checkRegLatchDesc(const TimingRole *role, - const RiseFall *clk_rf) const; - const char *regDesc(const RiseFall *clk_rf) const; - const char *latchDesc(const RiseFall *clk_rf) const; + const Scene *scene, + const MinMax *min_max) const; + std::string_view mpwCheckHiLow(const MinPulseWidthCheck &check) const; + void reportSkewClkPath(std::string_view arrival_msg, + const Path *clk_path) const; + std::string_view edgeRegLatchDesc(const Edge *edge, + const TimingArc *arc) const; + std::string_view checkRegLatchDesc(const TimingRole *role, + const RiseFall *clk_rf) const; + std::string_view regDesc(const RiseFall *clk_rf) const; + std::string_view latchDesc(const RiseFall *clk_rf) const; void pathClkPath(const Path *path, - const Path &clk_path) const; + const Path &clk_path) const; bool isPropagated(const Path *clk_path) const; bool isPropagated(const Path *clk_path, - const Clock *clk) const; + const Clock *clk) const; bool pathFromClkPin(const PathExpanded &expanded) const; bool pathFromClkPin(const Path *path, - const Pin *start_pin) const; + const Pin *start_pin) const; void latchPaths(const Path *path, // Return values. Path &d_path, - Path &q_path, - Edge *&d_q_edge) const; + Path &q_path, + Edge *&d_q_edge) const; bool nextArcAnnotated(const Path *next_path, - size_t next_index, - const PathExpanded &expanded, - DcalcAPIndex ap_index) const; + size_t next_index, + const PathExpanded &expanded, + DcalcAPIndex ap_index) const; float tgtClkInsertionOffet(const Path *clk_path, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap) const; + const EarlyLate *early_late) const; // InputDelay used to seed path root. InputDelay *pathInputDelay(const Path *path) const; void pathInputDelayRefPath(const Path *path, - const InputDelay *input_delay, - // Return value. - Path &ref_path) const; - const char *asRisingFalling(const RiseFall *rf) const; - const char *asRiseFall(const RiseFall *rf) const; - Delay delayIncr(Delay time, - Delay prev, - const MinMax *min_max) const; + const InputDelay *input_delay, + // Return value. + Path &ref_path) const; + std::string_view asRisingFalling(const RiseFall *rf) const; + std::string_view asRiseFall(const RiseFall *rf) const; + Delay delayIncr(const Delay &time, + const Delay &prev, + const MinMax *min_max) const; // Path options. - ReportPathFormat format_; + ReportPathFormat format_{ReportPathFormat::full}; ReportFieldSeq fields_; bool report_input_pin_; bool report_hier_pins_; bool report_net_; - bool dedup_by_word_; - bool dedup_same_delay_; + bool no_split_{false}; + bool dedup_by_word_{false}; + bool dedup_same_delay_{false}; std::optional silimate_dedup_endpoints_rx_; - bool no_split_; + int digits_; - bool report_sigmas_; - int start_end_pt_width_; + size_t start_end_pt_width_{80}; ReportField *field_description_; ReportField *field_total_; @@ -491,48 +508,50 @@ protected: ReportField *field_capacitance_; ReportField *field_slew_; ReportField *field_fanout_; + ReportField *field_variation_; ReportField *field_src_attr_; ReportField *field_edge_; ReportField *field_case_; - const char *plus_zero_; - const char *minus_zero_; + std::string plus_zero_; + std::string minus_zero_; - static const float field_blank_; + int field_width_extra_{5}; + static constexpr float field_blank_ = -1; static const float field_skip_; }; class ReportField { public: - ReportField(const char *name, - const char *title, - int width, - bool left_justify, - Unit *unit, - bool enabled); + ReportField(std::string_view name, + std::string_view title, + size_t width, + bool left_justify, + Unit *unit, + bool enabled); ~ReportField(); - void setProperties(const char *title, - int width, - bool left_justify); - const char *name() const { return name_; } - const char *title() const { return title_; } - int width() const { return width_; } - void setWidth(int width); + void setProperties(std::string_view title, + size_t width, + bool left_justify); + const std::string &name() const { return name_; } + const std::string &title() const { return title_; } + size_t width() const { return width_; } + void setWidth(size_t width); bool leftJustify() const { return left_justify_; } Unit *unit() const { return unit_; } - const char *blank() const { return blank_; } + const std::string &blank() const { return blank_; } void setEnabled(bool enabled); bool enabled() const { return enabled_; } protected: - const char *name_; - const char *title_; - int width_; + std::string name_; + std::string title_; + size_t width_; bool left_justify_; Unit *unit_; bool enabled_; - char *blank_; + std::string blank_; }; -} // namespace +} // namespace sta diff --git a/search/Scene.cc b/search/Scene.cc new file mode 100644 index 000000000..404ac409c --- /dev/null +++ b/search/Scene.cc @@ -0,0 +1,187 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#include "Scene.hh" + +#include "ContainerHelpers.hh" +#include "Mode.hh" +#include "Parasitics.hh" +#include "Sdc.hh" + +namespace sta { + +Scene::Scene(const std::string &name, + size_t index, + Mode *mode, + Parasitics *parasitics_min, + Parasitics *parasitics_max) : + name_(name), + index_(index), + mode_(mode), + parasitics_{parasitics_min, parasitics_max} +{ +} + +Scene::Scene(const std::string &name, + size_t index, + Mode *mode, + Parasitics *parasitics) : + name_(name), + index_(index), + mode_(mode) +{ + for (size_t mm_index : MinMax::rangeIndex()) + parasitics_[mm_index] = parasitics; +} + +size_t +Scene::pathIndex(const MinMax *min_max) const +{ + return index_ * MinMax::index_count + min_max->index(); +} + +void +Scene::setMode(Mode *mode) +{ + mode_ = mode; +} + +Sdc * +Scene::sdc() +{ + return mode_->sdc(); +} + +Sdc * +Scene::sdc() const +{ + return mode_->sdc(); +} + +Parasitics * +Scene::parasitics(const MinMax *min_max) const +{ + return parasitics_[min_max->index()]; +} + +void +Scene::setParasitics(Parasitics *parasitics, + const MinMaxAll *min_max) +{ + for (size_t mm : min_max->rangeIndex()) + parasitics_[mm] = parasitics; +} + +DcalcAPIndex +Scene::dcalcAnalysisPtIndex(const MinMax *min_max) const +{ + return index_ * MinMax::index_count + min_max->index(); +} + +const MinMax * +Scene::checkClkSlewMinMax(const MinMax *min_max) const +{ + switch (mode_->sdc()->analysisType()) { + case AnalysisType::single: + return MinMax::min(); + case AnalysisType::bc_wc: + return min_max; + case AnalysisType::ocv: + return min_max->opposite(); + default: + // suppress gcc warning + return min_max; + } +} + +DcalcAPIndex +Scene::checkClkSlewIndex(const MinMax *min_max) const +{ + return dcalcAnalysisPtIndex(checkClkSlewMinMax(min_max)); +} + +void +Scene::addLiberty(LibertyLibrary *lib, + const MinMax *min_max) +{ + liberty_[min_max->index()].push_back(lib); +} + +const LibertySeq & +Scene::libertyLibraries(const MinMax *min_max) const +{ + return liberty_[min_max->index()]; +} + +size_t +Scene::libertyIndex(const MinMax *min_max) const +{ + return index_ * MinMax::index_count + min_max->index(); +} + +//////////////////////////////////////////////////////////////// + +SceneSet +Scene::sceneSet(const SceneSeq &scenes) +{ + SceneSet scenes_set; + for (Scene *scene : scenes) + scenes_set.insert(scene); + return scenes_set; +} + +ModeSeq +Scene::modes(const SceneSeq &scenes) +{ + ModeSet mode_set; + for (const Scene *scene : scenes) + mode_set.insert(scene->mode()); + + ModeSeq modes; + for (Mode *mode : mode_set) + modes.push_back(mode); + return modes; +} + +ModeSet +Scene::modeSet(const SceneSeq &scenes) +{ + ModeSet modes; + for (const Scene *scene : scenes) + modes.insert(scene->mode()); + return modes; +} + +ModeSeq +Scene::modesSorted(const SceneSeq &scenes) +{ + ModeSeq modes = Scene::modes(scenes); + sort(modes, [] (const Mode *mode1, + const Mode *mode2) { + return mode1->name() < mode2->name(); + }); + return modes; +} + +} // namespace sta diff --git a/search/Search.cc b/search/Search.cc index c6ec18093..d8ffb7123 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -1,85 +1,85 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Search.hh" #include -#include // abs +#include +#include -#include "Mutex.hh" -#include "Report.hh" +#include "Bfs.hh" +#include "ClkInfo.hh" +#include "Clock.hh" +#include "ContainerHelpers.hh" +#include "Crpr.hh" +#include "DataCheck.hh" #include "Debug.hh" -#include "Stats.hh" +#include "Delay.hh" +#include "ExceptionPath.hh" #include "Fuzzy.hh" -#include "TimingRole.hh" -#include "FuncExpr.hh" -#include "TimingArc.hh" -#include "Sequential.hh" -#include "Units.hh" +#include "GatedClk.hh" +#include "Genclks.hh" +#include "Graph.hh" +#include "GraphClass.hh" +#include "Latches.hh" +#include "Levelize.hh" #include "Liberty.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "Mode.hh" +#include "Mutex.hh" #include "Network.hh" -#include "PortDirection.hh" -#include "Graph.hh" -#include "GraphCmp.hh" +#include "NetworkClass.hh" +#include "Path.hh" +#include "PathEnd.hh" +#include "PathGroup.hh" #include "PortDelay.hh" -#include "Clock.hh" -#include "CycleAccting.hh" -#include "ExceptionPath.hh" -#include "DataCheck.hh" +#include "PortDirection.hh" +#include "Report.hh" +#include "RiseFallMinMaxDelay.hh" +#include "Scene.hh" #include "Sdc.hh" -#include "DcalcAnalysisPt.hh" +#include "SdcClass.hh" +#include "SearchClass.hh" #include "SearchPred.hh" -#include "Levelize.hh" -#include "Bfs.hh" -#include "Corner.hh" -#include "Sim.hh" -#include "Path.hh" -#include "ClkInfo.hh" +#include "Stats.hh" +#include "StringUtil.hh" #include "Tag.hh" #include "TagGroup.hh" -#include "PathEnd.hh" -#include "PathGroup.hh" -#include "PathAnalysisPt.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Variables.hh" +#include "VertexVisitor.hh" #include "VisitPathEnds.hh" -#include "GatedClk.hh" #include "WorstSlack.hh" -#include "Latches.hh" -#include "Crpr.hh" -#include "Genclks.hh" -#include "Variables.hh" namespace sta { -using std::min; -using std::max; -using std::abs; - //////////////////////////////////////////////////////////////// EvalPred::EvalPred(const StaState *sta) : - SearchPred0(sta), - search_thru_latches_(true) + SearchPred0(sta) { } @@ -90,171 +90,188 @@ EvalPred::setSearchThruLatches(bool thru_latches) } bool -EvalPred::searchThru(Edge *edge) +EvalPred::searchThru(Edge *edge, + const Mode *mode) const { const TimingRole *role = edge->role(); - return SearchPred0::searchThru(edge) - && (sta_->variables()->dynamicLoopBreaking() - || !edge->isDisabledLoop()) - && !role->isTimingCheck() - && (search_thru_latches_ - || role != TimingRole::latchDtoQ() - || sta_->latches()->latchDtoQState(edge) == LatchEnableState::open); + return SearchPred0::searchThru(edge, mode) + && (sta_->variables()->dynamicLoopBreaking() || !edge->isDisabledLoop()) + && (search_thru_latches_ || role->isLatchDtoQ() + || sta_->latches()->latchDtoQState(edge, mode) == LatchEnableState::open); } bool -EvalPred::searchTo(const Vertex *to_vertex) +EvalPred::searchTo(const Vertex *to_vertex, + const Mode *mode) const { - const Sdc *sdc = sta_->sdc(); - const Pin *pin = to_vertex->pin(); - return SearchPred0::searchTo(to_vertex) - && !(sdc->isLeafPinClock(pin) - && !sdc->isPathDelayInternalTo(pin)); + const Pin *to_pin = to_vertex->pin(); + const Sdc *sdc = mode->sdc(); + return SearchPred0::searchTo(to_vertex, mode) + && !(sdc->isLeafPinClock(to_pin) && !sdc->isPathDelayInternalTo(to_pin)) + // Fanin paths are broken by path delay internal pin startpoints. + && !sdc->isPathDelayInternalFromBreak(to_pin); } //////////////////////////////////////////////////////////////// -DynLoopSrchPred::DynLoopSrchPred(TagGroupBldr *tag_bldr) : - tag_bldr_(tag_bldr) +// EvalPred unless +// latch D->Q edge +class SearchThru : public EvalPred { -} +public: + SearchThru(const StaState *sta); + bool searchThru(Edge *edge, + const Mode *mode) const override; +}; -bool -DynLoopSrchPred::loopEnabled(Edge *edge, - bool dynamic_loop_breaking_enabled, - const Graph *graph, - Search *search) +SearchThru::SearchThru(const StaState *sta) : + EvalPred(sta) { - return !edge->isDisabledLoop() - || (dynamic_loop_breaking_enabled - && hasPendingLoopPaths(edge, graph, search)); } bool -DynLoopSrchPred::hasPendingLoopPaths(Edge *edge, - const Graph *graph, - Search *search) +SearchThru::searchThru(Edge *edge, + const Mode *mode) const { - if (tag_bldr_ - && tag_bldr_->hasLoopTag()) { - Corners *corners = search->corners(); - Vertex *from_vertex = edge->from(graph); - TagGroup *prev_tag_group = search->tagGroup(from_vertex); - for (auto const [from_tag, path_index] : tag_bldr_->pathIndexMap()) { - if (from_tag->isLoop()) { - // Loop false path exceptions apply to rise/fall edges so to_rf - // does not matter. - PathAPIndex path_ap_index = from_tag->pathAPIndex(); - PathAnalysisPt *path_ap = corners->findPathAnalysisPt(path_ap_index); - Tag *to_tag = search->thruTag(from_tag, edge, RiseFall::rise(), - path_ap->pathMinMax(), path_ap, nullptr); - if (to_tag - && (prev_tag_group == nullptr - || !prev_tag_group->hasTag(from_tag))) - return true; - } - } - } - return false; + return EvalPred::searchThru(edge, mode) && !edge->role()->isLatchDtoQ(); } -// EvalPred unless +//////////////////////////////////////////////////////////////// + +// SearchAdj is mode independent. Search unless +// disabled to break combinational loop // latch D->Q edge -class SearchThru : public EvalPred, public DynLoopSrchPred +// timing check edge +// dynamic loop breaking pending tags +class SearchAdj : public SearchPred { public: - SearchThru(TagGroupBldr *tag_bldr, - const StaState *sta); - virtual bool searchThru(Edge *edge); + SearchAdj(TagGroupBldr *tag_bldr, + const StaState *sta); + bool searchFrom(const Vertex *from_vertex, + const Mode *mode) const override; + bool searchThru(Edge *edge, + const Mode *mode) const override; + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; + +protected: + bool loopEnabled(Edge *edge) const; + bool hasPendingLoopPaths(Edge *edge) const; + + TagGroupBldr *tag_bldr_; + const StaState *sta_; }; -SearchThru::SearchThru(TagGroupBldr *tag_bldr, - const StaState *sta) : - EvalPred(sta), - DynLoopSrchPred(tag_bldr) +SearchAdj::SearchAdj(TagGroupBldr *tag_bldr, + const StaState *sta) : + SearchPred(sta), + tag_bldr_(tag_bldr), + sta_(sta) { } bool -SearchThru::searchThru(Edge *edge) +SearchAdj::searchFrom(const Vertex * /* from_vertex */, + const Mode *) const { - const Graph *graph = sta_->graph(); - Search *search = sta_->search(); - return EvalPred::searchThru(edge) - // Only search thru latch D->Q if it is always open. - // Enqueue thru latches is handled explicitly by search. - && (edge->role() != TimingRole::latchDtoQ() - || sta_->latches()->latchDtoQState(edge) == LatchEnableState::open) - && loopEnabled(edge, sta_->variables()->dynamicLoopBreaking(), - graph, search); + return true; } -ClkArrivalSearchPred::ClkArrivalSearchPred(const StaState *sta) : - EvalPred(sta) +bool +SearchAdj::searchThru(Edge *edge, + const Mode *) const { + const TimingRole *role = edge->role(); + const Variables *variables = sta_->variables(); + return !role->isTimingCheck() + && !role->isLatchDtoQ() + // Register/latch preset/clr edges are disabled by default. + && !(role == TimingRole::regSetClr() && !variables->presetClrArcsEnabled()) + && !(edge->isBidirectInstPath() && !variables->bidirectInstPathsEnabled()) + && (!edge->isDisabledLoop() + || (variables->dynamicLoopBreaking() && hasPendingLoopPaths(edge))); } bool -ClkArrivalSearchPred::searchThru(Edge *edge) +SearchAdj::loopEnabled(Edge *edge) const { - const TimingRole *role = edge->role(); - return (role->isWire() - || role == TimingRole::combinational()) - && EvalPred::searchThru(edge); + return !edge->isDisabledLoop() + || (sta_->variables()->dynamicLoopBreaking() && hasPendingLoopPaths(edge)); } -//////////////////////////////////////////////////////////////// +bool +SearchAdj::hasPendingLoopPaths(Edge *edge) const +{ + if (tag_bldr_ && tag_bldr_->hasLoopTag()) { + const Graph *graph = sta_->graph(); + Search *search = sta_->search(); + Vertex *from_vertex = edge->from(graph); + TagGroup *prev_tag_group = search->tagGroup(from_vertex); + for (auto const [from_tag, path_index] : tag_bldr_->pathIndexMap()) { + if (from_tag->isLoop()) { + // Loop false path exceptions apply to rise/fall edges so to_rf + // does not matter. + Tag *to_tag = search->thruTag(from_tag, edge, RiseFall::rise(), nullptr); + if (to_tag + && (prev_tag_group == nullptr || !prev_tag_group->hasTag(from_tag))) + return true; + } + } + } + return false; +} -Search::Search(StaState *sta) : - StaState(sta) +bool +SearchAdj::searchTo(const Vertex * /* to_vertex */, + const Mode *) const { - init(sta); + return true; } -void -Search::init(StaState *sta) +//////////////////////////////////////////////////////////////// + +Search::Search(StaState *sta) : + StaState(sta), + + search_thru_(new SearchThru(this)), + search_adj_(new SearchAdj(nullptr, + this)), + eval_pred_(new EvalPred(this)), + + invalid_arrivals_(makeVertexSet(this)), + arrival_iter_(new BfsFwdIterator(BfsIndex::arrival, + nullptr, + this)), + arrival_visitor_(new ArrivalVisitor(this)), + + invalid_requireds_(makeVertexSet(this)), + required_iter_(new BfsBkwdIterator(BfsIndex::required, + search_adj_, + this)), + + invalid_tns_(makeVertexSet(this)), + clk_info_set_(new ClkInfoSet(ClkInfoLess(this))), + + tags_(new Tag *[tag_capacity_]), + tag_set_(new TagSet(tag_capacity_, + TagHash(this), + TagEqual(this))), + tag_group_capacity_(tag_capacity_), + tag_groups_(new TagGroup *[tag_group_capacity_]), + tag_group_set_(new TagGroupSet(tag_group_capacity_)), + pending_latch_outputs_(makeVertexSet(this)), + pending_clk_endpoints_(makeVertexSet(this)), + endpoints_(makeVertexSet(this)), + invalid_endpoints_(makeVertexSet(this)), + + filtered_arrivals_(makeVertexSet(this)), + + visit_path_ends_(new VisitPathEnds(this)), + gated_clk_(new GatedClk(this)), + check_crpr_(new CheckCrpr(this)) { initVars(); - - search_adj_ = new SearchThru(nullptr, sta); - eval_pred_ = new EvalPred(sta); - check_crpr_ = new CheckCrpr(sta); - genclks_ = new Genclks(sta); - arrival_visitor_ = new ArrivalVisitor(sta); - clk_arrivals_valid_ = false; - arrivals_exist_ = false; - arrivals_at_endpoints_exist_ = false; - arrivals_seeded_ = false; - requireds_exist_ = false; - requireds_seeded_ = false; - invalid_arrivals_ = new VertexSet(graph_); - invalid_requireds_ = new VertexSet(graph_); - invalid_tns_ = new VertexSet(graph_); - tns_exists_ = false; - worst_slacks_ = nullptr; - arrival_iter_ = new BfsFwdIterator(BfsIndex::arrival, nullptr, sta); - required_iter_ = new BfsBkwdIterator(BfsIndex::required, search_adj_, sta); - tag_capacity_ = 128; - tag_set_ = new TagSet(tag_capacity_, TagHash(sta), TagEqual(sta)); - clk_info_set_ = new ClkInfoSet(ClkInfoLess(sta)); - tag_next_ = 0; - tags_ = new Tag*[tag_capacity_]; - tag_group_capacity_ = tag_capacity_; - tag_groups_ = new TagGroup*[tag_group_capacity_]; - tag_group_next_ = 0; - tag_group_set_ = new TagGroupSet(tag_group_capacity_); - pending_latch_outputs_ = new VertexSet(graph_); - visit_path_ends_ = new VisitPathEnds(this); - gated_clk_ = new GatedClk(this); - path_groups_ = nullptr; - endpoints_ = nullptr; - invalid_endpoints_ = nullptr; - filter_ = nullptr; - filter_from_ = nullptr; - filter_to_ = nullptr; - filtered_arrivals_ = new VertexSet(graph_); - found_downstream_clk_pins_ = false; - postpone_latch_outputs_ = false; } // Init "options". @@ -273,26 +290,19 @@ Search::~Search() deleteTags(); delete tag_set_; delete clk_info_set_; - delete [] tags_; - delete [] tag_groups_; + delete[] tags_; + delete[] tag_groups_; delete tag_group_set_; + delete search_thru_; delete search_adj_; delete eval_pred_; delete arrival_visitor_; delete arrival_iter_; delete required_iter_; - delete endpoints_; - delete invalid_arrivals_; - delete invalid_requireds_; - delete invalid_tns_; - delete invalid_endpoints_; - delete pending_latch_outputs_; delete visit_path_ends_; delete gated_clk_; delete worst_slacks_; delete check_crpr_; - delete genclks_; - delete filtered_arrivals_; deleteFilter(); } @@ -301,28 +311,33 @@ Search::clear() { initVars(); - clk_arrivals_valid_ = false; - arrivals_at_endpoints_exist_ = false; arrivals_seeded_ = false; requireds_exist_ = false; requireds_seeded_ = false; tns_exists_ = false; clearWorstSlack(); - invalid_arrivals_->clear(); + invalid_arrivals_.clear(); arrival_iter_->clear(); - invalid_requireds_->clear(); - invalid_tns_->clear(); + invalid_requireds_.clear(); + invalid_tns_.clear(); required_iter_->clear(); endpointsInvalid(); deletePathGroups(); deletePaths(); deleteTags(); clearPendingLatchOutputs(); + pending_clk_endpoints_.clear(); deleteFilter(); - genclks_->clear(); found_downstream_clk_pins_ = false; } +void +Search::deletePathGroups() +{ + for (Mode *mode : modes_) + mode->deletePathGroups(); +} + bool Search::crprPathPruningEnabled() const { @@ -359,26 +374,29 @@ Search::deleteTags() tag_group_free_indices_.clear(); tag_next_ = 0; - tag_set_->deleteContentsClear(); - tag_free_indices_.clear(); + deleteContents(tag_set_); - clk_info_set_->deleteContentsClear(); + deleteContents(clk_info_set_); deleteTagsPrev(); } void Search::deleteFilter() { - if (filter_) { - sdc_->deleteException(filter_); - filter_ = nullptr; - filter_from_ = nullptr; + if (have_filter_) { + for (const Mode *mode : modes_) + mode->sdc()->deleteFilter(); + have_filter_ = false; } - else { - // Filter owns filter_from_ if it exists. - delete filter_from_; - filter_from_ = nullptr; + delete filter_from_; + filter_from_ = nullptr; + + if (filter_thrus_) { + deleteContents(*filter_thrus_); + delete filter_thrus_; + filter_thrus_ = nullptr; } + delete filter_to_; filter_to_ = nullptr; } @@ -391,10 +409,12 @@ Search::copyState(const StaState *sta) arrival_iter_->copyState(sta); required_iter_->copyState(sta); arrival_visitor_->copyState(sta); + eval_pred_->copyState(sta); + search_thru_->copyState(sta); + search_adj_->copyState(sta); visit_path_ends_->copyState(sta); gated_clk_->copyState(sta); check_crpr_->copyState(sta); - genclks_->copyState(sta); } //////////////////////////////////////////////////////////////// @@ -410,11 +430,9 @@ Search::deletePaths() deletePaths(vertex); } - for (Path *path : enum_paths_) - delete path; - enum_paths_.clear(); + deleteContents(enum_paths_); - filtered_arrivals_->clear(); + filtered_arrivals_.clear(); arrivals_exist_ = false; } } @@ -438,11 +456,11 @@ Search::deletePathsIncr(Vertex *vertex) void Search::deletePaths(Vertex *vertex) { - debugPrint(debug_, "search", 4, "delete paths %s", - vertex->name(network_)); + debugPrint(debug_, "search", 4, "delete paths {}", + vertex->to_string(this)); TagGroup *tag_group = tagGroup(vertex); if (tag_group) { - graph_->deletePaths(vertex); + vertex->deletePaths(); tag_group->decrRefCount(); } } @@ -454,42 +472,49 @@ Search::deletePaths(Vertex *vertex) // PathEnds are owned by Search PathGroups and deleted on next call. PathEndSeq Search::findPathEnds(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool unconstrained, - const Corner *corner, - const MinMaxAll *min_max, - size_t group_path_count, - size_t endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - bool sort_by_slack, - PathGroupNameSet *group_names, - bool setup, - bool hold, - bool recovery, - bool removal, - bool clk_gating_setup, - bool clk_gating_hold) + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool unconstrained, + const SceneSeq &scenes, + const MinMaxAll *min_max, + int group_path_count, + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + bool sort_by_slack, + StringSeq &group_names, + bool setup, + bool hold, + bool recovery, + bool removal, + bool clk_gating_setup, + bool clk_gating_hold) { findFilteredArrivals(from, thrus, to, unconstrained, true); if (!variables_->recoveryRemovalChecksEnabled()) recovery = removal = false; if (!variables_->gatedClkChecksEnabled()) clk_gating_setup = clk_gating_hold = false; - makePathGroups(group_path_count, endpoint_path_count, - unique_pins, unique_edges, - slack_min, slack_max, - group_names, setup, hold, - recovery, removal, - clk_gating_setup, clk_gating_hold); ensureDownstreamClkPins(); - PathEndSeq path_ends = path_groups_->makePathEnds(to, unconstrained_paths_, - corner, min_max, - sort_by_slack); - sdc_->reportClkToClkMaxCycleWarnings(); + const ModeSeq modes = Scene::modesSorted(scenes); + PathEndSeq path_ends; + for (Mode *mode : modes) { + PathGroups *path_groups = mode->makePathGroups( + group_path_count, endpoint_path_count, unique_pins, unique_edges, slack_min, + slack_max, group_names, setup, hold, recovery, removal, clk_gating_setup, + clk_gating_hold, unconstrained_paths_); + SceneSeq mode_scenes; + for (Scene *scene : scenes) { + if (scene->mode() == mode) + mode_scenes.push_back(scene); + } + path_groups->makePathEnds(to, mode_scenes, min_max, sort_by_slack, + unconstrained_paths_, path_ends); + } + for (const Mode *mode : modes) + mode->sdc()->reportClkToClkMaxCycleWarnings(); return path_ends; } @@ -503,45 +528,22 @@ Search::findFilteredArrivals(ExceptionFrom *from, unconstrained_paths_ = unconstrained; checkFromThrusTo(from, thrus, to); filter_from_ = from; + filter_thrus_ = thrus; filter_to_ = to; - if ((from - && (from->pins() - || from->instances())) - || thrus) { - filter_ = sdc_->makeFilterPath(from, thrus, nullptr); + if ((from && (from->pins() || from->instances())) || thrus) { + for (const Mode *mode : modes_) { + Sdc *sdc = mode->sdc(); + sdc->makeFilter(from ? from->clone(network_) : nullptr, + thrus ? exceptionThrusClone(thrus, network_) : nullptr); + } + have_filter_ = true; findFilteredArrivals(thru_latches); } else // These cases do not require filtered arrivals. // -from clocks // -to - findAllArrivals(thru_latches); -} - -void -Search::makePathGroups(int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - PathGroupNameSet *group_names, - bool setup, - bool hold, - bool recovery, - bool removal, - bool clk_gating_setup, - bool clk_gating_hold) -{ - path_groups_ = new PathGroups(group_path_count, endpoint_path_count, - unique_pins, unique_edges, - slack_min, slack_max, - group_names, - setup, hold, - recovery, removal, - clk_gating_setup, clk_gating_hold, - unconstrained_paths_, - this); + findAllArrivals(thru_latches, false); } // From/thrus/to are used to make a filter exception. If the last @@ -551,16 +553,11 @@ Search::makePathGroups(int group_path_count, void Search::deleteFilteredArrivals() { - if (filter_) { - ExceptionFrom *from = filter_->from(); - ExceptionThruSeq *thrus = filter_->thrus(); - if ((from - && (from->pins() - || from->instances())) - || thrus) { - for (Vertex *vertex : *filtered_arrivals_) { - if (isClock(vertex)) - clk_arrivals_valid_ = false; + if (have_filter_) { + ExceptionThruSeq *thrus = filter_thrus_; + if ((filter_from_ && (filter_from_->pins() || filter_from_->instances())) + || thrus) { + for (Vertex *vertex : filtered_arrivals_) { deletePathsIncr(vertex); arrivalInvalid(vertex); requiredInvalid(vertex); @@ -571,23 +568,23 @@ Search::deleteFilteredArrivals() while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); TagGroup *tag_group = tagGroup(vertex); - if (tag_group - && tag_group->hasFilterTag()) - filtered_arrivals_->erase(vertex); + if (tag_group && tag_group->hasFilterTag()) + filtered_arrivals_.erase(vertex); } - if (!filtered_arrivals_->empty()) { - report_->reportLine("Filtered verticies mismatch"); - for (Vertex *vertex : *filtered_arrivals_) - report_->reportLine(" %s", vertex->to_string(this).c_str()); + if (!filtered_arrivals_.empty()) { + report_->report("Filtered verticies mismatch"); + for (Vertex *vertex : filtered_arrivals_) + report_->report(" {}", vertex->to_string(this)); } } - filtered_arrivals_->clear(); + filtered_arrivals_.clear(); deleteFilterTagGroups(); deleteFilterTags(); deleteFilterClkInfos(); } - deleteFilter(); } + // Delete filter_from/thru/to even if there is no filter_. + deleteFilter(); } void @@ -595,8 +592,7 @@ Search::deleteFilterTagGroups() { for (TagGroupIndex i = 0; i < tag_group_next_; i++) { TagGroup *group = tag_groups_[i]; - if (group - && group->hasFilterTag()) + if (group && group->hasFilterTag()) deleteTagGroup(group); } } @@ -615,13 +611,10 @@ Search::deleteFilterTags() { for (TagIndex i = 0; i < tag_next_; i++) { Tag *tag = tags_[i]; - if (tag - && (tag->isFilter() - || tag->clkInfo()->crprPathRefsFilter())) { + if (tag && (tag->isFilter() || tag->clkInfo()->crprPathRefsFilter())) { tags_[i] = nullptr; tag_set_->erase(tag); delete tag; - tag_free_indices_.push_back(i); } } } @@ -629,7 +622,7 @@ Search::deleteFilterTags() void Search::deleteFilterClkInfos() { - for (auto itr = clk_info_set_->cbegin(); itr != clk_info_set_->cend(); ) { + for (auto itr = clk_info_set_->cbegin(); itr != clk_info_set_->cend();) { const ClkInfo *clk_info = *itr; if (clk_info->crprPathRefsFilter()) { itr = clk_info_set_->erase(itr); @@ -643,22 +636,24 @@ Search::deleteFilterClkInfos() void Search::findFilteredArrivals(bool thru_latches) { - filtered_arrivals_->clear(); + 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); + arrival_visitor_->init(true, false, eval_pred_); // Iterate until data arrivals at all latches stop changing. postpone_latch_outputs_ = true; - for (int pass = 1; pass == 1 || (thru_latches && havePendingLatchOutputs()) ; pass++) { + enqueuePendingClkFanouts(); + for (int pass = 1; pass == 1 || (thru_latches && havePendingLatchOutputs()); + pass++) { if (thru_latches) enqueuePendingLatchOutputs(); - debugPrint(debug_, "search", 1, "find arrivals pass %d", pass); + debugPrint(debug_, "search", 1, "find arrivals pass {}", pass); int arrival_count = arrival_iter_->visitParallel(max_level, arrival_visitor_); deleteTagsPrev(); - debugPrint(debug_, "search", 1, "found %d arrivals", arrival_count); + debugPrint(debug_, "search", 1, "found {} arrivals", arrival_count); postpone_latch_outputs_ = false; } arrivals_exist_ = true; @@ -668,12 +663,12 @@ Search::findFilteredArrivals(bool thru_latches) void Search::deleteTagsPrev() { - for (Tag** tags: tags_prev_) - delete [] tags; + for (Tag **tags : tags_prev_) + delete[] tags; tags_prev_.clear(); - for (TagGroup** tag_groups: tag_groups_prev_) - delete [] tag_groups; + for (TagGroup **tag_groups : tag_groups_prev_) + delete[] tag_groups; tag_groups_prev_.clear(); } @@ -691,7 +686,7 @@ VertexSeq Search::filteredEndpoints() { VertexSeq ends; - for (Vertex *vertex : *filtered_arrivals_) { + for (Vertex *vertex : filtered_arrivals_) { if (isEndpoint(vertex)) ends.push_back(vertex); } @@ -702,19 +697,17 @@ class SeedFaninsThruHierPin : public HierPinThruVisitor { public: SeedFaninsThruHierPin(Graph *graph, - Search *search); + Search *search); + void visit(const Pin *drvr, + const Pin *load) override; protected: - virtual void visit(const Pin *drvr, - const Pin *load); - Graph *graph_; Search *search_; }; SeedFaninsThruHierPin::SeedFaninsThruHierPin(Graph *graph, - Search *search) : - HierPinThruVisitor(), + Search *search) : graph_(graph), search_(search) { @@ -722,19 +715,23 @@ SeedFaninsThruHierPin::SeedFaninsThruHierPin(Graph *graph, void SeedFaninsThruHierPin::visit(const Pin *drvr, - const Pin *) + const Pin *) { Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(drvr, vertex, bidirect_drvr_vertex); - search_->seedArrival(vertex); + search_->arrivalIterator()->enqueue(vertex); if (bidirect_drvr_vertex) - search_->seedArrival(bidirect_drvr_vertex); + search_->arrivalIterator()->enqueue(bidirect_drvr_vertex); } void Search::seedFilterStarts() { - ExceptionPt *first_pt = filter_->firstPt(); + ExceptionPt *first_pt = nullptr; + if (filter_from_) + first_pt = filter_from_; + else if (filter_thrus_) + first_pt = (*filter_thrus_)[0]; if (first_pt) { PinSet first_pins = first_pt->allPins(network_); for (const Pin *pin : first_pins) { @@ -746,9 +743,9 @@ Search::seedFilterStarts() Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); if (vertex) - seedArrival(vertex); + arrival_iter_->enqueue(vertex); if (bidirect_drvr_vertex) - seedArrival(bidirect_drvr_vertex); + arrival_iter_->enqueue(bidirect_drvr_vertex); } } } @@ -762,18 +759,17 @@ Search::deleteVertexBefore(Vertex *vertex) if (arrivals_exist_) { deletePathsIncr(vertex); arrival_iter_->deleteVertexBefore(vertex); - invalid_arrivals_->erase(vertex); - filtered_arrivals_->erase(vertex); + invalid_arrivals_.erase(vertex); + filtered_arrivals_.erase(vertex); } if (requireds_exist_) { required_iter_->deleteVertexBefore(vertex); - invalid_requireds_->erase(vertex); - invalid_tns_->erase(vertex); + invalid_requireds_.erase(vertex); + invalid_tns_.erase(vertex); } - if (endpoints_) - endpoints_->erase(vertex); - if (invalid_endpoints_) - invalid_endpoints_->erase(vertex); + if (endpoints_initialized_) + endpoints_.erase(vertex); + invalid_endpoints_.erase(vertex); } void @@ -793,8 +789,7 @@ Search::deleteEdgeBefore(Edge *edge) bool Search::arrivalsValid() { - return arrivals_exist_ - && invalid_arrivals_->empty(); + return arrivals_exist_ && invalid_arrivals_.empty(); } void @@ -808,9 +803,9 @@ Search::arrivalsInvalid() deletePathGroups(); deletePaths(); deleteTags(); - genclks_->clear(); + for (const Mode *mode : modes_) + mode->genclks()->clear(); deleteFilter(); - arrivals_at_endpoints_exist_ = false; arrivals_seeded_ = false; requireds_exist_ = false; requireds_seeded_ = false; @@ -819,11 +814,11 @@ Search::arrivalsInvalid() arrival_iter_->clear(); required_iter_->clear(); // No need to keep track of incremental updates any more. - invalid_arrivals_->clear(); - invalid_requireds_->clear(); + invalid_arrivals_.clear(); + invalid_requireds_.clear(); tns_exists_ = false; clearWorstSlack(); - invalid_tns_->clear(); + invalid_tns_.clear(); } } @@ -833,22 +828,22 @@ Search::requiredsInvalid() debugPrint(debug_, "search", 1, "requireds invalid"); requireds_exist_ = false; requireds_seeded_ = false; - invalid_requireds_->clear(); + invalid_requireds_.clear(); tns_exists_ = false; clearWorstSlack(); - invalid_tns_->clear(); + invalid_tns_.clear(); } void Search::arrivalInvalid(Vertex *vertex) { if (arrivals_exist_) { - debugPrint(debug_, "search", 2, "arrival invalid %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 2, "arrival invalid {}", + vertex->to_string(this)); if (!arrival_iter_->inQueue(vertex)) { // Lock for StaDelayCalcObserver called by delay calc threads. LockGuard lock(invalid_arrivals_lock_); - invalid_arrivals_->insert(vertex); + invalid_arrivals_.insert(vertex); } tnsInvalid(vertex); } @@ -922,12 +917,12 @@ void Search::requiredInvalid(Vertex *vertex) { if (requireds_exist_) { - debugPrint(debug_, "search", 2, "required invalid %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 2, "required invalid {}", + vertex->to_string(this)); if (!required_iter_->inQueue(vertex)) { // Lock for StaDelayCalcObserver called by delay calc threads. LockGuard lock(invalid_arrivals_lock_); - invalid_requireds_->insert(vertex); + invalid_requireds_.insert(vertex); } tnsInvalid(vertex); } @@ -939,18 +934,7 @@ void Search::findClkArrivals() { if (!clk_arrivals_valid_) { - genclks_->ensureInsertionDelays(); - Stats stats(debug_, report_); - debugPrint(debug_, "search", 1, "find clk arrivals"); - arrival_iter_->clear(); - clk_gated_.assign(graph_->vertexCount() + 1, 0); - seedClkVertexArrivals(); - ClkArrivalSearchPred search_clk(this); - arrival_visitor_->init(false, &search_clk); - arrival_iter_->visitParallel(levelize_->maxLevel(), arrival_visitor_); - deleteTagsPrev(); - arrivals_exist_ = true; - stats.report("Find clk arrivals"); + findAllArrivals(false, true); } clk_arrivals_valid_ = true; } @@ -987,9 +971,9 @@ Search::updateClkGates(Vertex *vertex) // Find and log instance name being evaluated. Instance *inst = network_->instance(vertex->pin()); - const char *inst_name = inst != nullptr + std::string inst_name = inst != nullptr ? network_->cellName(inst) : "unknown"; - debugPrint(debug_, "clkgates", 1, "updating clk gates for %s (cell %s)", + debugPrint(debug_, "clkgates", 1, "updating clk gates for {} (cell {})", network_->pathName(vertex->pin()), inst_name); // Check for clock-gate instance and initialize booleans for evaluation. @@ -1013,20 +997,21 @@ Search::updateClkGates(Vertex *vertex) // Log source cell name. Instance *from_inst = network_->instance(from_pin); - const char *from_cell_name = from_inst != nullptr + std::string from_cell_name = from_inst != nullptr ? network_->cellName(from_inst) : "unknown"; - debugPrint(debug_, "clkgates", 1, " checking edge %s (cell %s)", + debugPrint(debug_, "clkgates", 1, " checking edge {} (cell {})", network_->pathName(from_pin), from_cell_name); // Do not consider any non-clock edges or invalid timing arcs. - if (!isClock(from)) { + TagGroup *from_tg = tagGroup(from); + if (!(from_tg && from_tg->hasClkTag())) { debugPrint(debug_, "clkgates", 1, - " edge %s is not a clock - skipped", + " edge {} is not a clock - skipped", network_->pathName(from_pin)); continue; } if (!clk_tree_pred.searchThru(edge)) { - debugPrint(debug_, "clkgates", 1, " edge %s skipped (not a live arc)", + debugPrint(debug_, "clkgates", 1, " edge {} skipped (not a live arc)", network_->pathName(from_pin)); continue; } @@ -1034,14 +1019,14 @@ Search::updateClkGates(Vertex *vertex) // Check if the vertex itself is gated or if the input edge is gated. if (is_gate || clk_gated_[graph_->id(from)]) { - debugPrint(debug_, "clkgates", 1, " edge %s live, %s", + debugPrint(debug_, "clkgates", 1, " edge {} live, {}", network_->pathName(from_pin), is_gate ? "cell gates" : "source gated"); continue; } // If the input edge is not gated, all_gated is now false. - debugPrint(debug_, "clkgates", 1, " edge %s live, source NOT gated", + debugPrint(debug_, "clkgates", 1, " edge {} live, source NOT gated", network_->pathName(from_pin)); all_gated = false; break; @@ -1049,7 +1034,7 @@ Search::updateClkGates(Vertex *vertex) // Determine if the vertex is gated based on cell type and input edges. bool gated = any_live && (is_gate || all_gated); - debugPrint(debug_, "clkgates", 1, " final verdict: %s", + debugPrint(debug_, "clkgates", 1, " final verdict: {}", gated ? "gated" : "not gated"); clk_gated_[id] = gated; } @@ -1062,117 +1047,52 @@ Search::seedClkVertexArrivals() for (const Pin *pin : clk_pins) { Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - seedClkVertexArrivals(pin, vertex); + arrival_iter_->enqueue(vertex); if (bidirect_drvr_vertex) - seedClkVertexArrivals(pin, bidirect_drvr_vertex); + arrival_iter_->enqueue(bidirect_drvr_vertex); } } -void -Search::seedClkVertexArrivals(const Pin *pin, - Vertex *vertex) -{ - TagGroupBldr tag_bldr(true, this); - tag_bldr.init(vertex); - genclks_->copyGenClkSrcPaths(vertex, &tag_bldr); - seedClkArrivals(pin, vertex, &tag_bldr); - setVertexArrivals(vertex, &tag_bldr); -} - Arrival Search::clockInsertion(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap) const + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + const EarlyLate *early_late, + const Mode *mode) const { float insert; bool exists; - sdc_->clockInsertion(clk, pin, rf, min_max, early_late, insert, exists); + mode->sdc()->clockInsertion(clk, pin, rf, min_max, early_late, insert, exists); if (exists) return insert; else if (clk->isGeneratedWithPropagatedMaster()) - return genclks_->insertionDelay(clk, pin, rf, early_late, path_ap); + return mode->genclks()->insertionDelay(clk, pin, rf, early_late); else return 0.0; } //////////////////////////////////////////////////////////////// -void -Search::visitStartpoints(VertexVisitor *visitor) -{ - Instance *top_inst = network_->topInstance(); - InstancePinIterator *pin_iter = network_->pinIterator(top_inst); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - if (network_->direction(pin)->isAnyInput()) { - Vertex *vertex = graph_->pinDrvrVertex(pin); - visitor->visit(vertex); - } - } - delete pin_iter; - - for (const auto [pin, input_delays] : sdc_->inputDelayPinMap()) { - // Already hit these. - if (!network_->isTopLevelPort(pin)) { - Vertex *vertex = graph_->pinDrvrVertex(pin); - if (vertex) - visitor->visit(vertex); - } - } - - for (const Clock *clk : sdc_->clks()) { - for (const Pin *pin : clk->leafPins()) { - // Already hit these. - if (!network_->isTopLevelPort(pin)) { - Vertex *vertex = graph_->pinDrvrVertex(pin); - visitor->visit(vertex); - } - } - } - - // Register clk pins. - for (Vertex *vertex : *graph_->regClkVertices()) - visitor->visit(vertex); - - const PinSet &startpoints = sdc_->pathDelayInternalFrom(); - for (const Pin *pin : startpoints) { - Vertex *vertex = graph_->pinDrvrVertex(pin); - visitor->visit(vertex); - } -} - -void -Search::visitEndpoints(VertexVisitor *visitor) -{ - for (Vertex *end : *endpoints()) { - Pin *pin = end->pin(); - // Filter register clock pins (fails on set_max_delay -from clk_src). - if (!network_->isRegClkPin(pin) - || sdc_->isPathDelayInternalTo(pin)) - visitor->visit(end); - } -} - -//////////////////////////////////////////////////////////////// - void Search::findAllArrivals() { - findAllArrivals(true); + findAllArrivals(true, false); } void -Search::findAllArrivals(bool thru_latches) +Search::findAllArrivals(bool thru_latches, + bool clks_only) { - arrival_visitor_->init(false); + if (!clks_only) + enqueuePendingClkFanouts(); + arrival_visitor_->init(false, clks_only, eval_pred_); // Iterate until data arrivals at all latches stop changing. postpone_latch_outputs_ = true; - for (int pass = 1; pass == 1 || (thru_latches && havePendingLatchOutputs()); pass++) { + for (int pass = 1; pass == 1 || (thru_latches && havePendingLatchOutputs()); + pass++) { enqueuePendingLatchOutputs(); - debugPrint(debug_, "search", 1, "find arrivals pass %d", pass); + debugPrint(debug_, "search", 1, "find arrivals pass {}", pass); findArrivals1(levelize_->maxLevel()); if (pass > 2) postpone_latch_outputs_ = false; @@ -1182,26 +1102,44 @@ Search::findAllArrivals(bool thru_latches) bool Search::havePendingLatchOutputs() { - return !pending_latch_outputs_->empty(); + return !pending_latch_outputs_.empty(); } void Search::clearPendingLatchOutputs() { - pending_latch_outputs_->clear(); + pending_latch_outputs_.clear(); } void Search::enqueuePendingLatchOutputs() { - for (Vertex *latch_vertex : *pending_latch_outputs_) { - debugPrint(debug_, "search", 2, "enqueue latch output %s", - latch_vertex->to_string(this).c_str()); + for (Vertex *latch_vertex : pending_latch_outputs_) { + debugPrint(debug_, "search", 2, "enqueue latch output {}", + latch_vertex->to_string(this)); arrival_iter_->enqueue(latch_vertex); } clearPendingLatchOutputs(); } +void +Search::enqueuePendingClkFanouts() +{ + for (Vertex *vertex : pending_clk_endpoints_) { + debugPrint(debug_, "search", 2, "enqueue clk fanout {}", + vertex->to_string(this)); + arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_); + } + pending_clk_endpoints_.clear(); +} + +void +Search::postponeClkFanouts(Vertex *vertex) +{ + LockGuard lock(pending_clk_endpoints_lock_); + pending_clk_endpoints_.insert(vertex); +} + void Search::findArrivals() { @@ -1211,7 +1149,7 @@ Search::findArrivals() void Search::findArrivals(Level level) { - arrival_visitor_->init(false); + arrival_visitor_->init(false, false, eval_pred_); findArrivals1(level); } @@ -1229,7 +1167,8 @@ Search::computeClkGates() VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); - if (isClock(vertex)) { + TagGroup *tg = tagGroup(vertex); + if (tg && tg->hasClkTag()) { Level level = vertex->level(); if (level >= 0) by_level[level].push_back(vertex); @@ -1245,7 +1184,7 @@ Search::computeClkGates() void Search::findArrivals1(Level level) { - debugPrint(debug_, "search", 1, "find arrivals to level %d", level); + debugPrint(debug_, "search", 1, "find arrivals to level {}", level); findArrivalsSeed(); Stats stats(debug_, report_); int arrival_count = arrival_iter_->visitParallel(level, arrival_visitor_); @@ -1253,13 +1192,8 @@ Search::findArrivals1(Level level) if (arrival_count > 0) deleteUnusedTagGroups(); stats.report("Find arrivals"); - if (arrival_iter_->empty() - && invalid_arrivals_->empty()) { - clk_arrivals_valid_ = true; - arrivals_at_endpoints_exist_ = true; - } arrivals_exist_ = true; - debugPrint(debug_, "search", 1, "found %d arrivals", arrival_count); + debugPrint(debug_, "search", 1, "found {} arrivals", arrival_count); // Compute clock gated registers after all arrivals are found. if (arrival_count > 0) @@ -1270,7 +1204,8 @@ void Search::findArrivalsSeed() { if (!arrivals_seeded_) { - genclks_->ensureInsertionDelays(); + for (const Mode *mode : modes_) + mode->genclks()->ensureInsertionDelays(); arrival_iter_->clear(); required_iter_->clear(); seedArrivals(); @@ -1289,17 +1224,17 @@ ArrivalVisitor::ArrivalVisitor(const StaState *sta) : PathVisitor(nullptr, false, sta) { init0(); - init(true); + init(true, false, nullptr); } // Copy constructor. ArrivalVisitor::ArrivalVisitor(bool always_to_endpoints, - SearchPred *pred, - const StaState *sta) : + SearchPred *pred, + const StaState *sta) : PathVisitor(pred, true, sta) { init0(); - init(always_to_endpoints, pred); + init(always_to_endpoints, false, pred); } void @@ -1307,31 +1242,33 @@ ArrivalVisitor::init0() { tag_bldr_ = new TagGroupBldr(true, this); tag_bldr_no_crpr_ = new TagGroupBldr(false, this); - adj_pred_ = new SearchThru(tag_bldr_, this); -} - -void -ArrivalVisitor::init(bool always_to_endpoints) -{ - init(always_to_endpoints, search_ ? search_->evalPred() : nullptr); + adj_pred_ = new SearchAdj(tag_bldr_, this); } void ArrivalVisitor::init(bool always_to_endpoints, - SearchPred *pred) + bool clks_only, + SearchPred *pred) { always_to_endpoints_ = always_to_endpoints; + clks_only_ = clks_only; pred_ = pred; - crpr_active_ = crprActive(); + crpr_active_ = variables_->crprEnabled(); } - VertexVisitor * ArrivalVisitor::copy() const { return new ArrivalVisitor(always_to_endpoints_, pred_, this); } +void +ArrivalVisitor::copyState(const StaState *sta) +{ + StaState::copyState(sta); + adj_pred_->copyState(sta); +} + ArrivalVisitor::~ArrivalVisitor() { delete tag_bldr_; @@ -1348,69 +1285,89 @@ ArrivalVisitor::setAlwaysToEndpoints(bool to_endpoints) void ArrivalVisitor::visit(Vertex *vertex) { - debugPrint(debug_, "search", 2, "find arrivals %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 2, "find arrivals {}", + vertex->to_string(this)); Pin *pin = vertex->pin(); tag_bldr_->init(vertex); has_fanin_one_ = graph_->hasFaninOne(vertex); - if (crpr_active_ - && !has_fanin_one_) + if (crpr_active_ && !has_fanin_one_) tag_bldr_no_crpr_->init(vertex); - // Fanin paths are broken by path delays internal pin startpoints. - if (!sdc_->isPathDelayInternalFromBreak(pin)) { - visitFaninPaths(vertex); - if (crpr_active_ - && search_->crprPathPruningEnabled() - // No crpr for ideal clocks. - && tag_bldr_->hasPropagatedClk() - && !has_fanin_one_) - pruneCrprArrivals(); - } + visitFaninPaths(vertex); + if (crpr_active_ + && search_->crprPathPruningEnabled() + // No crpr for ideal clocks. + && tag_bldr_->hasPropagatedClk() && !has_fanin_one_) + pruneCrprArrivals(); // Insert paths that originate here. - if (!network_->isTopLevelPort(pin) - && sdc_->hasInputDelay(pin)) - // set_input_delay on internal pin. - search_->seedInputSegmentArrival(pin, vertex, tag_bldr_); - if (sdc_->isPathDelayInternalFrom(pin)) - // set_min/max_delay -from internal pin. - search_->makeUnclkedPaths(vertex, false, true, tag_bldr_); - if (sdc_->isLeafPinClock(pin)) - // set_min/max_delay -to internal pin also a clock src. Bizzaroland. - // Re-seed the clock arrivals on top of the propagated paths. - search_->seedClkArrivals(pin, vertex, tag_bldr_); - // Register/latch clock pin that is not connected to a declared clock. - // Seed with unclocked tag, zero arrival and allow search thru reg - // clk->q edges. - // These paths are required to report path delays from unclocked registers - // For example, "set_max_delay -to" from an unclocked source register. - bool is_clk = tag_bldr_->hasClkTag(); - if (vertex->isRegClk() && !is_clk) { - debugPrint(debug_, "search", 2, "arrival seed unclked reg clk %s", - network_->pathName(pin)); - search_->makeUnclkedPaths(vertex, true, false, tag_bldr_); - } + seedArrivals(vertex); + bool is_clk = tag_bldr_->hasClkTag(); bool arrivals_changed = search_->arrivalsChanged(vertex, tag_bldr_); // If vertex is a latch data input arrival that changed from the // previous eval pass enqueue the latch outputs to be re-evaled on the // next pass. - if (arrivals_changed - && network_->isLatchData(pin)) + if (arrivals_changed && network_->isLatchData(pin)) search_->enqueueLatchDataOutputs(vertex); - if (!search_->arrivalsAtEndpointsExist() - || always_to_endpoints_ - || arrivals_changed) - search_->arrivalIterator()->enqueueAdjacentVertices(vertex, adj_pred_); + if ((always_to_endpoints_ || arrivals_changed)) { + if (clks_only_ && vertex->isRegClk()) { + debugPrint(debug_, "search", 3, "postponing clk fanout"); + search_->postponeClkFanouts(vertex); + } + else + search_->arrivalIterator()->enqueueAdjacentVertices(vertex, adj_pred_); + } if (arrivals_changed) { debugPrint(debug_, "search", 4, "arrivals changed"); search_->setVertexArrivals(vertex, tag_bldr_); search_->tnsInvalid(vertex); constrainedRequiredsInvalid(vertex, is_clk); } - enqueueRefPinInputDelays(pin); +} + +void +ArrivalVisitor::seedArrivals(Vertex *vertex) +{ + const Pin *pin = vertex->pin(); + bool is_clk = tag_bldr_->hasClkTag(); + for (const Mode *mode : modes_) { + const Sdc *sdc = mode->sdc(); + mode->genclks()->copyGenClkSrcPaths(vertex, tag_bldr_); + if (sdc->isLeafPinClock(pin)) + search_->seedClkArrivals(pin, mode, tag_bldr_); + if (search_->isInputArrivalSrchStart(vertex)) + search_->seedInputArrival(pin, vertex, mode, tag_bldr_); + // Do not apply input delay to bidir load vertices. + if (!(network_->direction(pin)->isBidirect() && !vertex->isBidirectDriver()) + && !network_->isTopLevelPort(pin) && sdc->hasInputDelay(pin)) + search_->seedInputSegmentArrival(pin, vertex, mode, tag_bldr_); + if (sdc->isPathDelayInternalFrom(pin) && !sdc->isLeafPinClock(pin)) + // set_min/max_delay -from internal pin. + search_->makeUnclkedPaths(vertex, false, true, tag_bldr_, mode); + if (search_->isSrchRoot(vertex, mode)) { + bool is_reg_clk = vertex->isRegClk(); + if (is_reg_clk + // Internal roots isolated by disabled pins are seeded with no clock. + || (search_->unconstrainedPaths() && !network_->isTopLevelPort(pin))) { + debugPrint(debug_, "search", 2, "arrival seed unclked root {}", + network_->pathName(pin)); + search_->makeUnclkedPaths(vertex, is_reg_clk, false, tag_bldr_, mode); + } + } + // Register/latch clock pin that is not connected to a declared clock. + // Seed with unclocked tag, zero arrival and allow search thru reg + // clk->q edges. + // These paths are required to report path delays from unclocked registers + // For example, "set_max_delay -to" from an unclocked source register. + if (vertex->isRegClk() && !is_clk) { + debugPrint(debug_, "search", 2, "arrival seed unclked reg clk {}", + network_->pathName(pin)); + search_->makeUnclkedPaths(vertex, true, false, tag_bldr_, mode); + } + enqueueRefPinInputDelays(pin, sdc); + } } // When a clock arrival changes, the required time changes for any @@ -1418,55 +1375,56 @@ ArrivalVisitor::visit(Vertex *vertex) // by the clock pin. void ArrivalVisitor::constrainedRequiredsInvalid(Vertex *vertex, - bool is_clk) + bool is_clk) { Pin *pin = vertex->pin(); - if (network_->isLoad(pin) - && search_->requiredsExist()) { + if (network_->isLoad(pin) && search_->requiredsExist()) { if (is_clk && network_->isCheckClk(pin)) { VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->role()->isTimingCheck()) { - Vertex *to_vertex = edge->to(graph_); - search_->requiredInvalid(to_vertex); - } + Edge *edge = edge_iter.next(); + if (edge->role()->isTimingCheck()) { + Vertex *to_vertex = edge->to(graph_); + search_->requiredInvalid(to_vertex); + } } } // Data checks (vertex does not need to be a clk). - DataCheckSet *data_checks = sdc_->dataChecksFrom(pin); - if (data_checks) { - for (DataCheck *data_check : *data_checks) { - Pin *to = data_check->to(); - search_->requiredInvalid(to); + for (const Mode *mode : modes_) { + const Sdc *sdc = mode->sdc(); + DataCheckSet *data_checks = sdc->dataChecksFrom(pin); + if (data_checks) { + for (DataCheck *data_check : *data_checks) { + Pin *to = data_check->to(); + search_->requiredInvalid(to); + } + } + + // Gated clocks. + if (is_clk && variables_->gatedClkChecksEnabled()) { + PinSet enable_pins = search_->gatedClk()->gatedClkEnables(vertex, mode); + for (const Pin *enable : enable_pins) + search_->requiredInvalid(enable); } - } - // Gated clocks. - if (is_clk && variables_->gatedClkChecksEnabled()) { - PinSet enable_pins(network_); - search_->gatedClk()->gatedClkEnables(vertex, enable_pins); - for (const Pin *enable : enable_pins) - search_->requiredInvalid(enable); } } } bool Search::arrivalsChanged(Vertex *vertex, - TagGroupBldr *tag_bldr) + TagGroupBldr *tag_bldr) { - Path *paths1 = graph_->paths(vertex); + Path *paths1 = vertex->paths(); if (paths1) { TagGroup *tag_group = tagGroup(vertex); - if (tag_group == nullptr - || tag_group->pathCount() != tag_bldr->pathCount()) + if (tag_group == nullptr || tag_group->pathCount() != tag_bldr->pathCount()) return true; for (auto const [tag1, path_index1] : *tag_group->pathIndexMap()) { Path *path1 = &paths1[path_index1]; Path *path2 = tag_bldr->tagMatchPath(tag1); if (path2 == nullptr || path1->tag(this) != path2->tag(this) - || !delayEqual(path1->arrival(), path2->arrival()) + || !delayEqual(path1->arrival(), path2->arrival(), this) || path1->prevEdge(this) != path2->prevEdge(this) || path1->prevArc(this) != path2->prevArc(this) || path1->prevPath() != path2->prevPath()) @@ -1475,36 +1433,31 @@ Search::arrivalsChanged(Vertex *vertex, return false; } else - return true; + return !tag_bldr->empty(); } bool ArrivalVisitor::visitFromToPath(const Pin * /* from_pin */, - Vertex *from_vertex, - const RiseFall *from_rf, - Tag *from_tag, - Path *from_path, + Vertex *from_vertex, + const RiseFall *from_rf, + Tag *from_tag, + Path *from_path, const Arrival &from_arrival, - Edge *edge, - TimingArc *arc, - ArcDelay arc_delay, - Vertex * /* to_vertex */, - const RiseFall *to_rf, - Tag *to_tag, - Arrival &to_arrival, - const MinMax *min_max, - const PathAnalysisPt *) -{ - debugPrint(debug_, "search", 3, " %s", - from_vertex->to_string(this).c_str()); - debugPrint(debug_, "search", 3, " %s -> %s %s", - from_rf->to_string().c_str(), - to_rf->to_string().c_str(), - min_max->to_string().c_str()); - debugPrint(debug_, "search", 3, " from tag: %s", - from_tag->to_string(this).c_str()); - debugPrint(debug_, "search", 3, " to tag : %s", - to_tag->to_string(this).c_str()); + Edge *edge, + TimingArc *arc, + ArcDelay arc_delay, + Vertex * /* to_vertex */, + const RiseFall *to_rf, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max) +{ + debugPrint(debug_, "search", 3, " {}", from_vertex->to_string(this)); + debugPrint(debug_, "search", 3, " {} -> {} {}", from_rf->shortName(), + to_rf->shortName(), min_max->to_string()); + debugPrint(debug_, "search", 3, " from tag: {}", + from_tag->to_string(this)); + debugPrint(debug_, "search", 3, " to tag : {}", to_tag->to_string(this)); const ClkInfo *to_clk_info = to_tag->clkInfo(); bool to_is_clk = to_tag->isClock(); Path *match; @@ -1512,21 +1465,18 @@ ArrivalVisitor::visitFromToPath(const Pin * /* from_pin */, tag_bldr_->tagMatchPath(to_tag, match, path_index); if (match == nullptr || delayGreater(to_arrival, match->arrival(), min_max, this)) { - debugPrint(debug_, "search", 3, " %s + %s = %s %s %s", - delayAsString(from_arrival, this), - delayAsString(arc_delay, this), - delayAsString(to_arrival, this), - min_max == MinMax::max() ? ">" : "<", + debugPrint(debug_, "search", 3, " {} + {} = {} {} {}", + delayAsString(from_arrival, this), delayAsString(arc_delay, this), + delayAsString(to_arrival, this), min_max == MinMax::max() ? ">" : "<", match ? delayAsString(match->arrival(), this) : "MIA"); - tag_bldr_->setMatchPath(match, path_index, to_tag, to_arrival, from_path, edge, arc); - if (crpr_active_ - && !has_fanin_one_ - && to_clk_info->hasCrprClkPin() - && !to_is_clk) { + tag_bldr_->setMatchPath(match, path_index, to_tag, to_arrival, from_path, edge, + arc); + if (crpr_active_ && !has_fanin_one_ && to_clk_info->hasCrprClkPin() + && !to_is_clk) { tag_bldr_no_crpr_->tagMatchPath(to_tag, match, path_index); if (match == nullptr - || delayGreater(to_arrival, match->arrival(), min_max, this)) { - tag_bldr_no_crpr_->setMatchPath(match, path_index, to_tag, to_arrival, + || delayGreater(to_arrival, match->arrival(), min_max, this)) { + tag_bldr_no_crpr_->setMatchPath(match, path_index, to_tag, to_arrival, from_path, edge, arc); } } @@ -1539,39 +1489,37 @@ ArrivalVisitor::pruneCrprArrivals() { CheckCrpr *crpr = search_->checkCrpr(); PathIndexMap &path_index_map = tag_bldr_->pathIndexMap(); - for (auto path_itr = path_index_map.cbegin(); path_itr != path_index_map.cend(); ) { + for (auto path_itr = path_index_map.cbegin(); path_itr != path_index_map.cend();) { Tag *tag = path_itr->first; size_t path_index = path_itr->second; const ClkInfo *clk_info = tag->clkInfo(); bool deleted_tag = false; - if (!tag->isClock() - && clk_info->hasCrprClkPin()) { - PathAnalysisPt *path_ap = tag->pathAnalysisPt(this); - const MinMax *min_max = path_ap->pathMinMax(); + if (!tag->isClock() && clk_info->hasCrprClkPin()) { + const MinMax *min_max = tag->minMax(); Path *path_no_crpr = tag_bldr_no_crpr_->tagMatchPath(tag); if (path_no_crpr) { Arrival max_arrival = path_no_crpr->arrival(); - const ClkInfo *clk_info_no_crpr = path_no_crpr->clkInfo(this); - Arrival max_crpr = crpr->maxCrpr(clk_info_no_crpr); - Arrival max_arrival_max_crpr = (min_max == MinMax::max()) - ? max_arrival - max_crpr - : max_arrival + max_crpr; - debugPrint(debug_, "search", 4, " cmp %s %s - %s = %s", - tag->to_string(this).c_str(), + const ClkInfo *clk_info_no_crpr = path_no_crpr->clkInfo(this); + Arrival max_crpr = crpr->maxCrpr(clk_info_no_crpr); + Arrival max_arrival_max_crpr = (min_max == MinMax::max()) + ? delayDiff(max_arrival, max_crpr, this) + : delaySum(max_arrival, max_crpr, this); + debugPrint(debug_, "search", 4, " cmp {} {} - {} = {}", + tag->to_string(this), delayAsString(max_arrival, this), delayAsString(max_crpr, this), delayAsString(max_arrival_max_crpr, this)); Arrival arrival = tag_bldr_->arrival(path_index); - // Latch D->Q path uses enable min so crpr clk path min/max - // does not match the path min/max. - if (delayGreater(max_arrival_max_crpr, arrival, min_max, this) - && clk_info_no_crpr->crprClkPath(this)->minMax(this) - == clk_info->crprClkPath(this)->minMax(this)) { - debugPrint(debug_, "search", 3, " pruned %s", - tag->to_string(this).c_str()); + // Latch D->Q path uses enable min so crpr clk path min/max + // does not match the path min/max. + if (delayGreater(max_arrival_max_crpr, arrival, min_max, this) + && clk_info_no_crpr->crprClkPath(this)->minMax(this) + == clk_info->crprClkPath(this)->minMax(this)) { + debugPrint(debug_, "search", 3, " pruned {}", + tag->to_string(this)); path_itr = path_index_map.erase(path_itr); deleted_tag = true; - } + } } } if (!deleted_tag) @@ -1583,46 +1531,33 @@ ArrivalVisitor::pruneCrprArrivals() // reference pin as if there is a timing arc from the reference pin to // the input delay pin. void -ArrivalVisitor::enqueueRefPinInputDelays(const Pin *ref_pin) +ArrivalVisitor::enqueueRefPinInputDelays(const Pin *ref_pin, + const Sdc *sdc) { - InputDelaySet *input_delays = sdc_->refPinInputDelays(ref_pin); + InputDelaySet *input_delays = sdc->refPinInputDelays(ref_pin); if (input_delays) { + BfsFwdIterator *arrival_iter = search_->arrivalIterator(); for (InputDelay *input_delay : *input_delays) { const Pin *pin = input_delay->pin(); Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - seedInputDelayArrival(pin, vertex, input_delay); + arrival_iter->enqueue(vertex); if (bidirect_drvr_vertex) - seedInputDelayArrival(pin, bidirect_drvr_vertex, input_delay); + arrival_iter->enqueue(bidirect_drvr_vertex); } } } -void -ArrivalVisitor::seedInputDelayArrival(const Pin *pin, - Vertex *vertex, - InputDelay *input_delay) -{ - TagGroupBldr tag_bldr(true, this); - tag_bldr.init(vertex); - search_->genclks()->copyGenClkSrcPaths(vertex, &tag_bldr); - search_->seedInputDelayArrival(pin, vertex, input_delay, - !network_->isTopLevelPort(pin), &tag_bldr); - search_->setVertexArrivals(vertex, &tag_bldr); - search_->arrivalIterator()->enqueueAdjacentVertices(vertex, - search_->searchAdj()); -} - void Search::enqueueLatchDataOutputs(Vertex *vertex) { - VertexOutEdgeIterator out_edge_iter(vertex, graph_); - while (out_edge_iter.hasNext()) { - Edge *out_edge = out_edge_iter.next(); - if (latches_->isLatchDtoQ(out_edge)) { - Vertex *out_vertex = out_edge->to(graph_); + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role() == TimingRole::latchDtoQ()) { + Vertex *out_vertex = edge->to(graph_); LockGuard lock(pending_latch_outputs_lock_); - pending_latch_outputs_->insert(out_vertex); + pending_latch_outputs_.insert(out_vertex); } } } @@ -1631,31 +1566,34 @@ void Search::enqueueLatchOutput(Vertex *vertex) { LockGuard lock(pending_latch_outputs_lock_); - pending_latch_outputs_->insert(vertex); + pending_latch_outputs_.insert(vertex); } void Search::seedArrivals() { - VertexSet vertices(graph_); + VertexSet vertices = makeVertexSet(this); findClockVertices(vertices); findRootVertices(vertices); findInputDrvrVertices(vertices); for (Vertex *vertex : vertices) - seedArrival(vertex); + arrival_iter_->enqueue(vertex); } void Search::findClockVertices(VertexSet &vertices) { - for (const Clock *clk : sdc_->clks()) { - for (const Pin *pin : clk->leafPins()) { - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - vertices.insert(vertex); - if (bidirect_drvr_vertex) - vertices.insert(bidirect_drvr_vertex); + for (const Mode *mode : modes_) { + const Sdc *sdc = mode->sdc(); + for (const Clock *clk : sdc->clocks()) { + for (const Pin *pin : clk->leafPins()) { + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + vertices.insert(vertex); + if (bidirect_drvr_vertex) + vertices.insert(bidirect_drvr_vertex); + } } } } @@ -1663,190 +1601,140 @@ Search::findClockVertices(VertexSet &vertices) void Search::seedInvalidArrivals() { - for (Vertex *vertex : *invalid_arrivals_) - seedArrival(vertex); - invalid_arrivals_->clear(); -} - -void -Search::seedArrival(Vertex *vertex) -{ - const Pin *pin = vertex->pin(); - if (sdc_->isLeafPinClock(pin)) { - TagGroupBldr tag_bldr(true, this); - tag_bldr.init(vertex); - genclks_->copyGenClkSrcPaths(vertex, &tag_bldr); - seedClkArrivals(pin, vertex, &tag_bldr); - // Clock pin may also have input arrivals from other clocks. - seedInputArrival(pin, vertex, &tag_bldr); - setVertexArrivals(vertex, &tag_bldr); - } - else if (isInputArrivalSrchStart(vertex)) { - TagGroupBldr tag_bldr(true, this); - tag_bldr.init(vertex); - genclks_->copyGenClkSrcPaths(vertex, &tag_bldr); - seedInputArrival(pin, vertex, &tag_bldr); - setVertexArrivals(vertex, &tag_bldr); - if (!tag_bldr.empty()) - // Only search downstream if there were non-false paths from here. - arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_); - } - else if (levelize_->isRoot(vertex)) { - bool is_reg_clk = vertex->isRegClk(); - if (is_reg_clk - // Internal roots isolated by disabled pins are seeded with no clock. - || (unconstrained_paths_ - && !network_->isTopLevelPort(pin))) { - debugPrint(debug_, "search", 2, "arrival seed unclked root %s", - network_->pathName(pin)); - TagGroupBldr tag_bldr(true, this); - tag_bldr.init(vertex); - genclks_->copyGenClkSrcPaths(vertex, &tag_bldr); - if (makeUnclkedPaths(vertex, is_reg_clk, false, &tag_bldr)) - // Only search downstream if there are no false paths from here. - arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_); - setVertexArrivals(vertex, &tag_bldr); - } - else { - deletePathsIncr(vertex); - if (search_adj_->searchFrom(vertex)) - arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_); - } - } - else { - debugPrint(debug_, "search", 4, "arrival enqueue %s %u", - network_->pathName(pin), - vertex->level()); + for (Vertex *vertex : invalid_arrivals_) arrival_iter_->enqueue(vertex); - } + invalid_arrivals_.clear(); } // Find all of the clock leaf pins. void Search::findClkVertexPins(PinSet &clk_pins) { - for (const Clock *clk : sdc_->clks()) { - for (const Pin *pin : clk->leafPins()) { - clk_pins.insert(pin); + for (Scene *scene : scenes_) { + const Sdc *sdc = scene->sdc(); + for (const Clock *clk : sdc->clocks()) { + for (const Pin *pin : clk->leafPins()) { + clk_pins.insert(pin); + } } } } void Search::seedClkArrivals(const Pin *pin, - Vertex *vertex, - TagGroupBldr *tag_bldr) -{ - for (const Clock *clk : *sdc_->findLeafPinClocks(pin)) { - debugPrint(debug_, "search", 2, "arrival seed clk %s pin %s", - clk->name(), network_->pathName(pin)); - for (PathAnalysisPt *path_ap : corners_->pathAnalysisPts()) { - const MinMax *min_max = path_ap->pathMinMax(); - for (const RiseFall *rf : RiseFall::range()) { - const ClockEdge *clk_edge = clk->edge(rf); - const EarlyLate *early_late = min_max; - if (clk->isGenerated() - && clk->masterClk() == nullptr) - seedClkDataArrival(pin, rf, clk, clk_edge, min_max, path_ap, - 0.0, tag_bldr); - else { - Arrival insertion = clockInsertion(clk, pin, rf, min_max, - early_late, path_ap); - seedClkArrival(pin, rf, clk, clk_edge, min_max, path_ap, - insertion, tag_bldr); - } + const Mode *mode, + TagGroupBldr *tag_bldr) +{ + const Sdc *sdc = mode->sdc(); + ClockSet *clks = sdc->findLeafPinClocks(pin); + if (clks) { + for (const Clock *clk : *clks) { + debugPrint(debug_, "search", 2, "arrival seed clk {}/{} pin {}", + mode->name(), clk->name(), network_->pathName(pin)); + for (Scene *scene : mode->scenes()) { + for (const MinMax *min_max : MinMax::range()) { + for (const RiseFall *rf : RiseFall::range()) { + const ClockEdge *clk_edge = clk->edge(rf); + const EarlyLate *early_late = min_max; + if (clk->isGenerated() && clk->masterClk() == nullptr) + seedClkDataArrival(pin, rf, clk, clk_edge, min_max, 0.0, scene, + tag_bldr); + else { + Arrival insertion = + clockInsertion(clk, pin, rf, min_max, early_late, mode); + seedClkArrival(pin, rf, clk, clk_edge, min_max, insertion, scene, + tag_bldr); + } + } + } } } - arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_); } } void Search::seedClkArrival(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const ClockEdge *clk_edge, - const MinMax *min_max, - const PathAnalysisPt *path_ap, - Arrival insertion, - TagGroupBldr *tag_bldr) -{ + const RiseFall *rf, + const Clock *clk, + const ClockEdge *clk_edge, + const MinMax *min_max, + Arrival insertion, + Scene *scene, + TagGroupBldr *tag_bldr) +{ + Sdc *sdc = scene->sdc(); bool is_propagated = false; float latency = 0.0; bool latency_exists; // Check for clk pin latency. - sdc_->clockLatency(clk, pin, rf, min_max, - latency, latency_exists); + sdc->clockLatency(clk, pin, rf, min_max, latency, latency_exists); if (!latency_exists) { // Check for clk latency (lower priority). - sdc_->clockLatency(clk, rf, min_max, - latency, latency_exists); + sdc->clockLatency(clk, rf, min_max, latency, latency_exists); if (latency_exists) { // Propagated pin overrides latency on clk. - if (sdc_->isPropagatedClock(pin)) { - latency = 0.0; - latency_exists = false; - is_propagated = true; + if (sdc->isPropagatedClock(pin)) { + latency = 0.0; + is_propagated = true; } } else - is_propagated = sdc_->isPropagatedClock(pin) - || clk->isPropagated(); + is_propagated = sdc->isPropagatedClock(pin) || clk->isPropagated(); } - ClockUncertainties *uncertainties = sdc_->clockUncertainties(pin); + const ClockUncertainties *uncertainties = sdc->clockUncertainties(pin); if (uncertainties == nullptr) - uncertainties = clk->uncertainties(); + uncertainties = &clk->uncertainties(); // Propagate liberty "pulse_clock" transition to transitive fanout. LibertyPort *port = network_->libertyPort(pin); const RiseFall *pulse_clk_sense = (port ? port->pulseClkSense() : nullptr); - const ClkInfo *clk_info = findClkInfo(clk_edge, pin, is_propagated, nullptr, false, - pulse_clk_sense, insertion, latency, - uncertainties, path_ap, nullptr); + const ClkInfo *clk_info = findClkInfo(scene, clk_edge, pin, is_propagated, nullptr, + false, pulse_clk_sense, insertion, latency, + uncertainties, min_max, nullptr); // Only false_paths -from apply to clock tree pins. ExceptionStateSet *states = nullptr; - sdc_->exceptionFromClkStates(pin,rf,clk,rf,min_max,states); - Tag *tag = findTag(rf, path_ap, clk_info, true, nullptr, false, states, - true, nullptr); - Arrival arrival(clk_edge->time() + insertion); + sdc->exceptionFromClkStates(pin,rf,clk,rf,min_max,states); + Tag *tag = findTag(scene, rf, min_max, clk_info, true, nullptr, false, + states, true, nullptr); + Arrival arrival = delaySum(insertion, clk_edge->time(), this); tag_bldr->setArrival(tag, arrival); } void Search::seedClkDataArrival(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const ClockEdge *clk_edge, - const MinMax *min_max, - const PathAnalysisPt *path_ap, - Arrival insertion, - TagGroupBldr *tag_bldr) -{ - Tag *tag = clkDataTag(pin, clk, rf, clk_edge, insertion, min_max, path_ap); + const RiseFall *rf, + const Clock *clk, + const ClockEdge *clk_edge, + const MinMax *min_max, + Arrival insertion, + Scene *scene, + TagGroupBldr *tag_bldr) +{ + Tag *tag = clkDataTag(pin, clk, rf, clk_edge, insertion, min_max, scene); if (tag) { // Data arrivals include insertion delay. - Arrival arrival(clk_edge->time() + insertion); + Arrival arrival = delaySum(insertion, clk_edge->time(), this); tag_bldr->setArrival(tag, arrival); } } Tag * Search::clkDataTag(const Pin *pin, - const Clock *clk, - const RiseFall *rf, - const ClockEdge *clk_edge, - Arrival insertion, - const MinMax *min_max, - const PathAnalysisPt *path_ap) -{ + const Clock *clk, + const RiseFall *rf, + const ClockEdge *clk_edge, + Arrival insertion, + const MinMax *min_max, + Scene *scene) +{ + Sdc *sdc = scene->sdc(); ExceptionStateSet *states = nullptr; - if (sdc_->exceptionFromStates(pin, rf, clk, rf, min_max, states)) { - bool is_propagated = (clk->isPropagated() - || sdc_->isPropagatedClock(pin)); - const ClkInfo *clk_info = findClkInfo(clk_edge, pin, is_propagated, - insertion, path_ap); - return findTag(rf, path_ap, clk_info, false, nullptr, false, states, - true, nullptr); + if (sdc->exceptionFromStates(pin, rf, clk, rf, min_max, states)) { + bool is_propagated = (clk->isPropagated() || sdc->isPropagatedClock(pin)); + const ClkInfo *clk_info = + findClkInfo(scene, clk_edge, pin, is_propagated, insertion, min_max); + return findTag(scene, rf, min_max, clk_info, false, nullptr, false, states, true, + nullptr); } else return nullptr; @@ -1856,39 +1744,61 @@ Search::clkDataTag(const Pin *pin, bool Search::makeUnclkedPaths(Vertex *vertex, - bool is_segment_start, - bool require_exception, - TagGroupBldr *tag_bldr) + bool is_segment_start, + bool require_exception, + TagGroupBldr *tag_bldr, + const Mode *mode) { bool search_from = false; const Pin *pin = vertex->pin(); - for (PathAnalysisPt *path_ap : corners_->pathAnalysisPts()) { - const MinMax *min_max = path_ap->pathMinMax(); - for (const RiseFall *rf : RiseFall::range()) { - Tag *tag = fromUnclkedInputTag(pin, rf, min_max, path_ap, - is_segment_start, - require_exception); - if (tag) { - tag_bldr->setArrival(tag, delay_zero); - search_from = true; + for (Scene *scene : mode->scenes()) { + for (const MinMax *min_max : MinMax::range()) { + for (const RiseFall *rf : RiseFall::range()) { + Tag *tag = fromUnclkedInputTag(pin, rf, min_max, is_segment_start, + require_exception, scene); + if (tag) { + tag_bldr->setArrival(tag, delay_zero); + search_from = true; + } } } } return search_from; } -// Find graph roots and input ports that do NOT have arrivals. void Search::findRootVertices(VertexSet &vertices) { - for (Vertex *vertex : levelize_->roots()) { - const Pin *pin = vertex->pin(); - if (!sdc_->isLeafPinClock(pin) - && !sdc_->hasInputDelay(pin) - && !vertex->isConstant()) { - vertices.insert(vertex); + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + for (Mode *mode : modes_) { + if (isSrchRoot(vertex, mode)) { + vertices.insert(vertex); + break; + } + } + } +} + +bool +Search::isSrchRoot(Vertex *vertex, + const Mode *mode) const +{ + if (!eval_pred_->searchFrom(vertex, mode)) + return false; + else { + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *from_vertex = edge->from(graph_); + if (!edge->role()->isTimingCheck() + && (eval_pred_->searchFrom(from_vertex, mode) + && eval_pred_->searchThru(edge, mode))) + return false; } } + return true; } void @@ -1904,13 +1814,6 @@ Search::findInputDrvrVertices(VertexSet &vertices) delete pin_iter; } -bool -Search::isSegmentStart(const Pin *pin) -{ - return sdc_->isInputDelayInternal(pin) - && !sdc_->isLeafPinClock(pin); -} - bool Search::isInputArrivalSrchStart(Vertex *vertex) { @@ -1918,151 +1821,110 @@ Search::isInputArrivalSrchStart(Vertex *vertex) PortDirection *dir = network_->direction(pin); bool is_top_level_port = network_->isTopLevelPort(pin); return (is_top_level_port - && (dir->isInput() - || (dir->isBidirect() && vertex->isBidirectDriver()))) ; -} - -// Seed input arrivals clocked by clks. -void -Search::seedInputArrivals(ClockSet *clks) -{ - // Input arrivals can be on internal pins, so iterate over the pins - // that have input arrivals rather than the top level input pins. - for (const auto [pin, input_delays] : sdc_->inputDelayPinMap()) { - if (!sdc_->isLeafPinClock(pin)) { - Vertex *vertex = graph_->pinDrvrVertex(pin); - seedInputArrival(pin, vertex, clks); - } - } -} - -void -Search::seedInputArrival(const Pin *pin, - Vertex *vertex, - ClockSet *wrt_clks) -{ - bool has_arrival = false; - // There can be multiple arrivals for a pin with wrt different clocks. - TagGroupBldr tag_bldr(true, this); - tag_bldr.init(vertex); - genclks_->copyGenClkSrcPaths(vertex, &tag_bldr); - InputDelaySet *input_delays = sdc_->inputDelaysLeafPin(pin); - if (input_delays) { - for (InputDelay *input_delay : *input_delays) { - Clock *input_clk = input_delay->clock(); - ClockSet *pin_clks = sdc_->findLeafPinClocks(pin); - if (input_clk && wrt_clks->hasKey(input_clk) - // Input arrivals wrt a clock source pin is the insertion - // delay (source latency), but arrivals wrt other clocks - // propagate. - && (pin_clks == nullptr - || !pin_clks->hasKey(input_clk))) { - seedInputDelayArrival(pin, vertex, input_delay, false, &tag_bldr); - has_arrival = true; - } - } - if (has_arrival) - setVertexArrivals(vertex, &tag_bldr); - } + && (dir->isInput() || (dir->isBidirect() && vertex->isBidirectDriver()))); } void Search::seedInputArrival(const Pin *pin, - Vertex *vertex, - TagGroupBldr *tag_bldr) + Vertex *vertex, + const Mode *mode, + TagGroupBldr *tag_bldr) { - if (sdc_->hasInputDelay(pin)) - seedInputArrival1(pin, vertex, false, tag_bldr); - else if (!sdc_->isLeafPinClock(pin)) + const Sdc *sdc = mode->sdc(); + if (sdc->hasInputDelay(pin)) + seedInputArrival1(pin, vertex, false, mode, tag_bldr); + else if (!sdc->isLeafPinClock(pin)) // Seed inputs without set_input_delays. - seedInputDelayArrival(pin, vertex, nullptr, false, tag_bldr); + seedInputDelayArrival(pin, vertex, nullptr, false, mode, tag_bldr); } void Search::seedInputSegmentArrival(const Pin *pin, - Vertex *vertex, - TagGroupBldr *tag_bldr) + Vertex *vertex, + const Mode *mode, + TagGroupBldr *tag_bldr) { - seedInputArrival1(pin, vertex, true, tag_bldr); + seedInputArrival1(pin, vertex, true, mode, tag_bldr); } void Search::seedInputArrival1(const Pin *pin, - Vertex *vertex, - bool is_segment_start, - TagGroupBldr *tag_bldr) + Vertex *vertex, + bool is_segment_start, + const Mode *mode, + TagGroupBldr *tag_bldr) { // There can be multiple arrivals for a pin with wrt different clocks. - InputDelaySet *input_delays = sdc_->inputDelaysLeafPin(pin); + const Sdc *sdc = mode->sdc(); + InputDelaySet *input_delays = sdc->inputDelaysLeafPin(pin); if (input_delays) { for (InputDelay *input_delay : *input_delays) { Clock *input_clk = input_delay->clock(); - ClockSet *pin_clks = sdc_->findLeafPinClocks(pin); + ClockSet *pin_clks = sdc->findLeafPinClocks(pin); // Input arrival wrt a clock source pin is the clock insertion // delay (source latency), but arrivals wrt other clocks // propagate. - if (pin_clks == nullptr - || !pin_clks->hasKey(input_clk)) - seedInputDelayArrival(pin, vertex, input_delay, is_segment_start, - tag_bldr); + if (pin_clks == nullptr || !pin_clks->contains(input_clk)) + seedInputDelayArrival(pin, vertex, input_delay, is_segment_start, mode, + tag_bldr); } } } void Search::seedInputDelayArrival(const Pin *pin, - Vertex *vertex, - InputDelay *input_delay, - bool is_segment_start, - TagGroupBldr *tag_bldr) + Vertex *vertex, + InputDelay *input_delay, + bool is_segment_start, + const Mode *mode, + TagGroupBldr *tag_bldr) { debugPrint(debug_, "search", 2, - input_delay - ? "arrival seed input arrival %s" - : "arrival seed input %s", - vertex->to_string(this).c_str()); + input_delay ? "arrival seed input arrival {}" : "arrival seed input {}", + vertex->to_string(this)); const ClockEdge *clk_edge = nullptr; const Pin *ref_pin = nullptr; + const Sdc *sdc = mode->sdc(); if (input_delay) { clk_edge = input_delay->clkEdge(); - if (clk_edge == nullptr - && variables_->useDefaultArrivalClock()) - clk_edge = sdc_->defaultArrivalClockEdge(); + if (clk_edge == nullptr && variables_->useDefaultArrivalClock()) + clk_edge = sdc->defaultArrivalClockEdge(); ref_pin = input_delay->refPin(); } else if (variables_->useDefaultArrivalClock()) - clk_edge = sdc_->defaultArrivalClockEdge(); + clk_edge = sdc->defaultArrivalClockEdge(); if (ref_pin) { Vertex *ref_vertex = graph_->pinLoadVertex(ref_pin); - for (PathAnalysisPt *path_ap : corners_->pathAnalysisPts()) { - const MinMax *min_max = path_ap->pathMinMax(); - const RiseFall *ref_rf = input_delay->refTransition(); - const Clock *clk = input_delay->clock(); - VertexPathIterator ref_path_iter(ref_vertex, ref_rf, path_ap, this); - while (ref_path_iter.hasNext()) { - Path *ref_path = ref_path_iter.next(); - if (ref_path->isClock(this) - && (clk == nullptr - || ref_path->clock(this) == clk)) { - float ref_arrival, ref_insertion, ref_latency; - inputDelayRefPinArrival(ref_path, ref_path->clkEdge(this), min_max, - ref_arrival, ref_insertion, ref_latency); - seedInputDelayArrival(pin, input_delay, ref_path->clkEdge(this), - ref_arrival, ref_insertion, ref_latency, - is_segment_start, min_max, path_ap, tag_bldr); - } + for (Scene *scene : mode->scenes()) { + for (const MinMax *min_max : MinMax::range()) { + const RiseFall *ref_rf = input_delay->refTransition(); + const Clock *clk = input_delay->clock(); + VertexPathIterator ref_path_iter(ref_vertex, scene, min_max, ref_rf, this); + while (ref_path_iter.hasNext()) { + Path *ref_path = ref_path_iter.next(); + if (ref_path->isClock(this) + && (clk == nullptr || ref_path->clock(this) == clk)) { + float ref_arrival, ref_insertion, ref_latency; + inputDelayRefPinArrival(ref_path, ref_path->clkEdge(this), min_max, sdc, + ref_arrival, ref_insertion, ref_latency); + seedInputDelayArrival(pin, input_delay, ref_path->clkEdge(this), + ref_arrival, ref_insertion, ref_latency, + is_segment_start, min_max, scene, tag_bldr); + } + } } } } else { - for (PathAnalysisPt *path_ap : corners_->pathAnalysisPts()) { - const MinMax *min_max = path_ap->pathMinMax(); + for (const MinMax *min_max : MinMax::range()) { float clk_arrival, clk_insertion, clk_latency; - inputDelayClkArrival(input_delay, clk_edge, min_max, path_ap, - clk_arrival, clk_insertion, clk_latency); - seedInputDelayArrival(pin, input_delay, clk_edge, - clk_arrival, clk_insertion, clk_latency, - is_segment_start, min_max, path_ap, tag_bldr); + inputDelayClkArrival(input_delay, clk_edge, min_max, mode, clk_arrival, + clk_insertion, clk_latency); + for (Scene *scene : mode->scenes()) { + seedInputDelayArrival(pin, input_delay, clk_edge, clk_arrival, clk_insertion, + clk_latency, is_segment_start, min_max, scene, + tag_bldr); + } } } } @@ -2071,18 +1933,19 @@ Search::seedInputDelayArrival(const Pin *pin, // from the clock source to the reference pin. void Search::inputDelayRefPinArrival(Path *ref_path, - const ClockEdge *clk_edge, - const MinMax *min_max, - // Return values. - float &ref_arrival, - float &ref_insertion, - float &ref_latency) + const ClockEdge *clk_edge, + const MinMax *min_max, + const Sdc *sdc, + // Return values. + float &ref_arrival, + float &ref_insertion, + float &ref_latency) { Clock *clk = clk_edge->clock(); if (clk->isPropagated()) { const ClkInfo *clk_info = ref_path->clkInfo(this); - ref_arrival = delayAsFloat(ref_path->arrival()); - ref_insertion = delayAsFloat(clk_info->insertion()); + ref_arrival = delayAsFloat(ref_path->arrival(), min_max, this); + ref_insertion = delayAsFloat(clk_info->insertion(), min_max, this); ref_latency = clk_info->latency(); } else { @@ -2090,7 +1953,7 @@ Search::inputDelayRefPinArrival(Path *ref_path, const EarlyLate *early_late = min_max; // Input delays from ideal clk reference pins include clock // insertion delay but not latency. - ref_insertion = sdc_->clockInsertion(clk, clk_rf, min_max, early_late); + ref_insertion = sdc->clockInsertion(clk, clk_rf, min_max, early_late); ref_arrival = clk_edge->time() + ref_insertion; ref_latency = 0.0; } @@ -2098,15 +1961,15 @@ Search::inputDelayRefPinArrival(Path *ref_path, void Search::seedInputDelayArrival(const Pin *pin, - InputDelay *input_delay, - const ClockEdge *clk_edge, - float clk_arrival, - float clk_insertion, - float clk_latency, - bool is_segment_start, - const MinMax *min_max, - PathAnalysisPt *path_ap, - TagGroupBldr *tag_bldr) + InputDelay *input_delay, + const ClockEdge *clk_edge, + float clk_arrival, + float clk_insertion, + float clk_latency, + bool is_segment_start, + const MinMax *min_max, + Scene *scene, + TagGroupBldr *tag_bldr) { for (const RiseFall *rf : RiseFall::range()) { if (input_delay) { @@ -2114,45 +1977,44 @@ Search::seedInputDelayArrival(const Pin *pin, bool exists; input_delay->delays()->value(rf, min_max, delay, exists); if (exists) - seedInputDelayArrival(pin, rf, clk_arrival + delay, - input_delay, clk_edge, - clk_insertion, clk_latency, is_segment_start, - min_max, path_ap, tag_bldr); + seedInputDelayArrival(pin, rf, clk_arrival + delay, input_delay, clk_edge, + clk_insertion, clk_latency, is_segment_start, min_max, + scene, tag_bldr); } else - seedInputDelayArrival(pin, rf, 0.0, nullptr, clk_edge, - clk_insertion, clk_latency, is_segment_start, - min_max, path_ap, tag_bldr); + seedInputDelayArrival(pin, rf, 0.0, nullptr, clk_edge, clk_insertion, + clk_latency, is_segment_start, min_max, scene, tag_bldr); } } void Search::seedInputDelayArrival(const Pin *pin, - const RiseFall *rf, - float arrival, - InputDelay *input_delay, - const ClockEdge *clk_edge, - float clk_insertion, - float clk_latency, - bool is_segment_start, - const MinMax *min_max, - PathAnalysisPt *path_ap, - TagGroupBldr *tag_bldr) + const RiseFall *rf, + float arrival, + InputDelay *input_delay, + const ClockEdge *clk_edge, + float clk_insertion, + float clk_latency, + bool is_segment_start, + const MinMax *min_max, + Scene *scene, + TagGroupBldr *tag_bldr) { Tag *tag = inputDelayTag(pin, rf, clk_edge, clk_insertion, clk_latency, - input_delay, is_segment_start, min_max, path_ap); + input_delay, is_segment_start, min_max, scene); if (tag) tag_bldr->setArrival(tag, arrival); } void Search::inputDelayClkArrival(InputDelay *input_delay, - const ClockEdge *clk_edge, - const MinMax *min_max, - const PathAnalysisPt *path_ap, - // Return values. - float &clk_arrival, float &clk_insertion, - float &clk_latency) + const ClockEdge *clk_edge, + const MinMax *min_max, + const Mode *mode, + // Return values. + float &clk_arrival, + float &clk_insertion, + float &clk_latency) { clk_arrival = 0.0; clk_insertion = 0.0; @@ -2163,14 +2025,12 @@ Search::inputDelayClkArrival(InputDelay *input_delay, const RiseFall *clk_rf = clk_edge->transition(); if (!input_delay->sourceLatencyIncluded()) { const EarlyLate *early_late = min_max; - clk_insertion = delayAsFloat(clockInsertion(clk, clk->defaultPin(), - clk_rf, min_max, early_late, - path_ap)); + clk_insertion = delayAsFloat( + clockInsertion(clk, clk->defaultPin(), clk_rf, min_max, early_late, mode)); clk_arrival += clk_insertion; } - if (!clk->isPropagated() - && !input_delay->networkLatencyIncluded()) { - clk_latency = sdc_->clockLatency(clk, clk_rf, min_max); + if (!clk->isPropagated() && !input_delay->networkLatencyIncluded()) { + clk_latency = mode->sdc()->clockLatency(clk, clk_rf, min_max); clk_arrival += clk_latency; } } @@ -2178,44 +2038,44 @@ Search::inputDelayClkArrival(InputDelay *input_delay, Tag * Search::inputDelayTag(const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - float clk_insertion, - float clk_latency, - InputDelay *input_delay, - bool is_segment_start, - const MinMax *min_max, - const PathAnalysisPt *path_ap) + const RiseFall *rf, + const ClockEdge *clk_edge, + float clk_insertion, + float clk_latency, + InputDelay *input_delay, + bool is_segment_start, + const MinMax *min_max, + Scene *scene) { Clock *clk = nullptr; const Pin *clk_pin = nullptr; const RiseFall *clk_rf = nullptr; bool is_propagated = false; - ClockUncertainties *clk_uncertainties = nullptr; + const ClockUncertainties *clk_uncertainties = nullptr; if (clk_edge) { clk = clk_edge->clock(); clk_rf = clk_edge->transition(); clk_pin = clk->defaultPin(); is_propagated = clk->isPropagated(); - clk_uncertainties = clk->uncertainties(); + clk_uncertainties = &clk->uncertainties(); } + Sdc *sdc = scene->sdc(); ExceptionStateSet *states = nullptr; Tag *tag = nullptr; - if (sdc_->exceptionFromStates(pin,rf,clk,clk_rf,min_max,states)) { - const ClkInfo *clk_info = findClkInfo(clk_edge, clk_pin, is_propagated, nullptr, - false, nullptr, clk_insertion, clk_latency, - clk_uncertainties, path_ap, nullptr); - tag = findTag(rf, path_ap, clk_info, false, input_delay, is_segment_start, - states, true, nullptr); + if (sdc->exceptionFromStates(pin, rf, clk, clk_rf, min_max, states)) { + const ClkInfo *clk_info = + findClkInfo(scene, clk_edge, clk_pin, is_propagated, nullptr, false, nullptr, + clk_insertion, clk_latency, clk_uncertainties, min_max, nullptr); + tag = findTag(scene, rf, min_max, clk_info, false, input_delay, is_segment_start, + states, true, nullptr); } if (tag) { const ClkInfo *clk_info = tag->clkInfo(); // Check for state changes on existing tag exceptions (pending -thru pins). - tag = mutateTag(tag, pin, rf, false, clk_info, - pin, rf, false, false, is_segment_start, clk_info, - input_delay, min_max, path_ap, nullptr); + tag = mutateTag(tag, pin, rf, false, clk_info, pin, rf, false, false, + is_segment_start, clk_info, input_delay, nullptr); } return tag; } @@ -2226,42 +2086,37 @@ PathVisitor::PathVisitor(const StaState *sta) : StaState(sta), pred_(sta->search()->evalPred()), - tag_cache_( nullptr) + tag_cache_(nullptr) { } PathVisitor::PathVisitor(SearchPred *pred, - bool make_tag_cache, - const StaState *sta) : + bool make_tag_cache, + const StaState *sta) : StaState(sta), pred_(pred), - tag_cache_(make_tag_cache - ? new TagSet(128, TagSet::hasher(sta), TagSet::key_equal(sta)) - : nullptr) + tag_cache_(make_tag_cache ? new TagSet(128, + TagSet::hasher(sta), + TagSet::key_equal(sta)) + : nullptr) { } -PathVisitor::~PathVisitor() -{ - delete tag_cache_; -} +PathVisitor::~PathVisitor() { delete tag_cache_; } void PathVisitor::visitFaninPaths(Vertex *to_vertex) { - if (pred_->searchTo(to_vertex)) { - VertexInEdgeIterator edge_iter(to_vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); + VertexInEdgeIterator edge_iter(to_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (!edge->role()->isTimingCheck()) { Vertex *from_vertex = edge->from(graph_); const Pin *from_pin = from_vertex->pin(); - if (pred_->searchFrom(from_vertex) - && pred_->searchThru(edge)) { - const Pin *to_pin = to_vertex->pin(); - if (!visitEdge(from_pin, from_vertex, edge, to_pin, to_vertex)) - break; - } + const Pin *to_pin = to_vertex->pin(); + if (!visitEdge(from_pin, from_vertex, edge, to_pin, to_vertex)) + break; } } } @@ -2270,49 +2125,47 @@ void PathVisitor::visitFanoutPaths(Vertex *from_vertex) { const Pin *from_pin = from_vertex->pin(); - if (pred_->searchFrom(from_vertex)) { - VertexOutEdgeIterator edge_iter(from_vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - Vertex *to_vertex = edge->to(graph_); - const Pin *to_pin = to_vertex->pin(); - if (pred_->searchTo(to_vertex) - && pred_->searchThru(edge)) { - debugPrint(debug_, "search", 3, " %s", - to_vertex->to_string(this).c_str()); - if (!visitEdge(from_pin, from_vertex, edge, to_pin, to_vertex)) - break; - } - } + VertexOutEdgeIterator edge_iter(from_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + const Pin *to_pin = to_vertex->pin(); + debugPrint(debug_, "search", 3, " {}", to_vertex->to_string(this)); + if (!visitEdge(from_pin, from_vertex, edge, to_pin, to_vertex)) + break; } } bool PathVisitor::visitEdge(const Pin *from_pin, - Vertex *from_vertex, - Edge *edge, - const Pin *to_pin, - Vertex *to_vertex) + Vertex *from_vertex, + Edge *edge, + const Pin *to_pin, + Vertex *to_vertex) { TagGroup *from_tag_group = search_->tagGroup(from_vertex); if (from_tag_group) { TimingArcSet *arc_set = edge->timingArcSet(); VertexPathIterator from_iter(from_vertex, search_); + const Mode *prev_mode = nullptr; while (from_iter.hasNext()) { Path *from_path = from_iter.next(); - PathAnalysisPt *path_ap = from_path->pathAnalysisPt(this); - const MinMax *min_max = path_ap->pathMinMax(); - const RiseFall *from_rf = from_path->transition(this); - TimingArc *arc1, *arc2; - arc_set->arcsFrom(from_rf, arc1, arc2); - if (!visitArc(from_pin, from_vertex, from_rf, from_path, - edge, arc1, to_pin, to_vertex, - min_max, path_ap)) - return false; - if (!visitArc(from_pin, from_vertex, from_rf, from_path, - edge, arc2, to_pin, to_vertex, - min_max, path_ap)) - return false; + const Mode *mode = from_path->mode(this); + if (mode == prev_mode + || (pred_->searchFrom(from_vertex, mode) && pred_->searchThru(edge, mode) + && pred_->searchTo(to_vertex, mode))) { + prev_mode = mode; + const MinMax *min_max = from_path->minMax(this); + const RiseFall *from_rf = from_path->transition(this); + TimingArc *arc1, *arc2; + arc_set->arcsFrom(from_rf, arc1, arc2); + if (!visitArc(from_pin, from_vertex, from_rf, from_path, edge, arc1, to_pin, + to_vertex, min_max, mode)) + return false; + if (!visitArc(from_pin, from_vertex, from_rf, from_path, edge, arc2, to_pin, + to_vertex, min_max, mode)) + return false; + } } } return true; @@ -2320,88 +2173,89 @@ PathVisitor::visitEdge(const Pin *from_pin, bool PathVisitor::visitArc(const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - Path *from_path, - Edge *edge, - TimingArc *arc, - const Pin *to_pin, - Vertex *to_vertex, - const MinMax *min_max, - PathAnalysisPt *path_ap) + Vertex *from_vertex, + const RiseFall *from_rf, + Path *from_path, + Edge *edge, + TimingArc *arc, + const Pin *to_pin, + Vertex *to_vertex, + const MinMax *min_max, + const Mode *mode) { if (arc) { const RiseFall *to_rf = arc->toEdge()->asRiseFall(); - if (searchThru(from_vertex, from_rf, edge, to_vertex, to_rf)) - return visitFromPath(from_pin, from_vertex, from_rf, from_path, - edge, arc, to_pin, to_vertex, to_rf, - min_max, path_ap); + if (searchThru(from_vertex, from_rf, edge, to_vertex, to_rf, mode)) + return visitFromPath(from_pin, from_vertex, from_rf, from_path, edge, arc, + to_pin, to_vertex, to_rf, min_max); } return true; } bool PathVisitor::visitFromPath(const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - Path *from_path, - Edge *edge, - TimingArc *arc, - const Pin *to_pin, - Vertex *to_vertex, - const RiseFall *to_rf, - const MinMax *min_max, - const PathAnalysisPt *path_ap) + Vertex *from_vertex, + const RiseFall *from_rf, + Path *from_path, + Edge *edge, + TimingArc *arc, + const Pin *to_pin, + Vertex *to_vertex, + const RiseFall *to_rf, + const MinMax *min_max) { const TimingRole *role = edge->role(); Tag *from_tag = from_path->tag(this); + Scene *scene = from_tag->scene(); + const Mode *mode = scene->mode(); + const Sdc *sdc = scene->sdc(); const ClkInfo *from_clk_info = from_tag->clkInfo(); Tag *to_tag = nullptr; const ClockEdge *clk_edge = from_clk_info->clkEdge(); const Clock *clk = from_clk_info->clock(); Arrival from_arrival = from_path->arrival(); ArcDelay arc_delay = 0.0; + DcalcAPIndex dcalc_ap = from_path->dcalcAnalysisPtIndex(this); Arrival to_arrival; if (from_clk_info->isGenClkSrcPath()) { - if (!sdc_->clkStopPropagation(clk,from_pin,from_rf,to_pin,to_rf) - && (variables_->clkThruTristateEnabled() - || !(role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable()))) { - const Clock *gclk = from_tag->genClkSrcPathClk(this); + if (!sdc->clkStopPropagation(clk, from_pin, from_rf, to_pin, to_rf) + && (variables_->clkThruTristateEnabled() + || !(role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable()))) { + const Clock *gclk = from_tag->genClkSrcPathClk(); if (gclk) { - Genclks *genclks = search_->genclks(); - VertexSet *fanins = genclks->fanins(gclk); - // Note: encountering a latch d->q edge means find the - // latch feedback edges, but they are referenced for - // other edges in the gen clk fanout. - if (role == TimingRole::latchDtoQ()) - genclks->findLatchFdbkEdges(gclk); - EdgeSet *fdbk_edges = genclks->latchFdbkEdges(gclk); - if ((role == TimingRole::combinational() - || role == TimingRole::wire() - || !gclk->combinational()) - && fanins->hasKey(to_vertex) - && !(fdbk_edges && fdbk_edges->hasKey(edge))) { - arc_delay = search_->deratedDelay(from_vertex, arc, edge, - true, path_ap); - const PathAnalysisPt *path_ap_opp = - path_ap->corner()->findPathAnalysisPt(min_max->opposite()); + Genclks *genclks = mode->genclks(); + VertexSet *fanins = genclks->fanins(gclk); + // Note: encountering a latch d->q edge means find the + // latch feedback edges, but they are referenced for + // other edges in the gen clk fanout. + if (role == TimingRole::latchDtoQ()) + genclks->findLatchFdbkEdges(gclk); + EdgeSet &fdbk_edges = genclks->latchFdbkEdges(gclk); + if ((role == TimingRole::combinational() || role == TimingRole::wire() + || !gclk->combinational()) + && fanins->contains(to_vertex) && !fdbk_edges.contains(edge)) { + arc_delay = search_->deratedDelay(from_vertex, arc, edge, true, min_max, + dcalc_ap, sdc); + DcalcAPIndex dcalc_ap = scene->dcalcAnalysisPtIndex(min_max->opposite()); Delay arc_delay_opp = search_->deratedDelay(from_vertex, arc, edge, - true, path_ap_opp); - bool arc_delay_min_max_eq = - fuzzyEqual(delayAsFloat(arc_delay), delayAsFloat(arc_delay_opp)); - to_tag = search_->thruClkTag(from_path, from_vertex, from_tag, true, + true, min_max, + dcalc_ap, + sdc); + bool arc_delay_min_max_eq = delayEqual(arc_delay, arc_delay_opp, this); + to_tag = search_->thruClkTag(from_path, from_vertex, from_tag, true, edge, to_rf, arc_delay_min_max_eq, - min_max, path_ap); - to_arrival = from_arrival + arc_delay; - } + min_max, scene); + if (to_tag) + to_arrival = delaySum(from_arrival, arc_delay, this); + } } } } else if (role->genericRole() == TimingRole::regClkToQ()) { - if (clk == nullptr - || !sdc_->clkStopPropagation(from_pin, clk)) { - arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, path_ap); + if (clk == nullptr || !sdc->clkStopPropagation(from_pin, clk)) { + arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, min_max, + dcalc_ap, sdc); // Remove clock network delay for macros created with propagated // clocks when used in a context with ideal clocks. @@ -2410,42 +2264,40 @@ PathVisitor::visitFromPath(const Pin *from_pin, const LibertyCell *inst_cell = clk_port->libertyCell(); if (inst_cell->isMacro()) { float slew = delayAsFloat(from_path->slew(this)); - arc_delay -= clk_port->clkTreeDelay(slew, from_rf, min_max); + arc_delay = delayDiff(arc_delay, + clk_port->clkTreeDelay(slew, from_rf, min_max), + this); } } // Propagate from unclocked reg/latch clk pins, which have no // clk but are distinguished with a segment_start flag. - if ((clk_edge == nullptr - && from_tag->isSegmentStart()) - // Do not propagate paths from input ports with default - // input arrival clk thru CLK->Q edges. - || (clk != sdc_->defaultArrivalClock() - // Only propagate paths from clocks that have not - // passed thru reg/latch D->Q edges. - && from_tag->isClock())) { - const RiseFall *clk_rf = clk_edge ? clk_edge->transition() : nullptr; - const ClkInfo *to_clk_info = from_clk_info; - if (from_clk_info->crprClkPath(this) == nullptr + if ((clk_edge == nullptr && from_tag->isSegmentStart()) + // Do not propagate paths from input ports with default + // input arrival clk thru CLK->Q edges. + || (clk != sdc->defaultArrivalClock() + // Only propagate paths from clocks that have not + // passed thru reg/latch D->Q edges. + && from_tag->isClock())) { + const RiseFall *clk_rf = clk_edge ? clk_edge->transition() : nullptr; + const ClkInfo *to_clk_info = from_clk_info; + if (from_clk_info->crprClkPath(this) == nullptr || network_->direction(to_pin)->isInternal()) - to_clk_info = search_->clkInfoWithCrprClkPath(from_clk_info, - from_path, path_ap); - to_tag = search_->fromRegClkTag(from_pin, from_rf, clk, clk_rf, - to_clk_info, to_pin, to_rf, min_max, - path_ap); - if (to_tag) - to_tag = search_->thruTag(to_tag, edge, to_rf, min_max, path_ap, tag_cache_); + to_clk_info = search_->clkInfoWithCrprClkPath(from_clk_info, from_path); + to_tag = search_->fromRegClkTag(from_pin, from_rf, clk, clk_rf, to_clk_info, + to_pin, to_rf, min_max, from_tag->scene()); + if (to_tag) + to_tag = search_->thruTag(to_tag, edge, to_rf, tag_cache_); from_arrival = search_->clkPathArrival(from_path, from_clk_info, - clk_edge, min_max, path_ap); - to_arrival = from_arrival + arc_delay; + clk_edge, min_max); + to_arrival = delaySum(from_arrival, arc_delay, this); } else - to_tag = nullptr; + to_tag = nullptr; } } else if (edge->role() == TimingRole::latchDtoQ()) { - if (min_max == MinMax::max() - && clk) { + if (min_max == MinMax::max() && clk) { bool postponed = false; if (search_->postponeLatchOutputs()) { const Path *from_clk_path = from_clk_info->crprClkPath(this); @@ -2457,73 +2309,70 @@ PathVisitor::visitFromPath(const Pin *from_pin, // Crpr clk path on latch data input is required to find Q // arrival. If the data clk path level is >= Q level the // crpr clk path prev_path pointers are not complete. - debugPrint(debug_, "search", 3, "postponed latch eval %d %s -> %s %d", - d_clk_level, - d_clk_vertex->to_string(this).c_str(), - edge->to_string(this).c_str(), - q_level); + debugPrint(debug_, "search", 3, "postponed latch eval {} {} -> {} {}", + d_clk_level, d_clk_vertex->to_string(this), + edge->to_string(this), q_level); postponed = true; search_->enqueueLatchOutput(to_vertex); } } } if (!postponed) { - arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, path_ap); - latches_->latchOutArrival(from_path, arc, edge, path_ap, - to_tag, arc_delay, to_arrival); + arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, min_max, + dcalc_ap, sdc); + latches_->latchOutArrival(from_path, arc, edge, to_tag, arc_delay, + to_arrival); if (to_tag) - to_tag = search_->thruTag(to_tag, edge, to_rf, min_max, path_ap, tag_cache_); + to_tag = search_->thruTag(to_tag, edge, to_rf, tag_cache_); } } } else if (from_tag->isClock()) { - ClockSet *clks = sdc_->findLeafPinClocks(from_pin); + ClockSet *clks = sdc->findLeafPinClocks(from_pin); // Disable edges from hierarchical clock source pins that do // not go thru the hierarchical pin and edges from clock source pins // that traverse a hierarchical source pin of a different clock. // Clock arrivals used as data also need to be disabled. if (!(role == TimingRole::wire() - && sdc_->clkDisabledByHpinThru(clk, from_pin, to_pin)) + && sdc->clkDisabledByHpinThru(clk, from_pin, to_pin)) // Generated clock source pins have arrivals for the source clock. // Do not propagate them past the generated clock source pin. - && !(clks - && !clks->hasKey(const_cast(from_tag->clock())))) { + && !(clks && !clks->contains(const_cast(from_tag->clock())))) { // Propagate arrival as non-clock at the end of the clock tree. bool to_propagates_clk = - !sdc_->clkStopPropagation(clk,from_pin,from_rf,to_pin,to_rf) - && (variables_->clkThruTristateEnabled() - || !(role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable())); - arc_delay = search_->deratedDelay(from_vertex, arc, edge, - to_propagates_clk, path_ap); - const PathAnalysisPt *path_ap_opp = - path_ap->corner()->findPathAnalysisPt(min_max->opposite()); - Delay arc_delay_opp = search_->deratedDelay(from_vertex, arc, edge, - to_propagates_clk, path_ap_opp); + !sdc->clkStopPropagation(clk, from_pin, from_rf, to_pin, to_rf) + && (variables_->clkThruTristateEnabled() + || !(role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable())); + arc_delay = search_->deratedDelay(from_vertex, arc, edge, to_propagates_clk, + min_max, dcalc_ap, sdc); + DcalcAPIndex dcalc_ap_opp = scene->dcalcAnalysisPtIndex(min_max->opposite()); + Delay arc_delay_opp = search_->deratedDelay( + from_vertex, arc, edge, to_propagates_clk, min_max, dcalc_ap_opp, sdc); bool arc_delay_min_max_eq = fuzzyEqual(delayAsFloat(arc_delay), delayAsFloat(arc_delay_opp)); to_tag = search_->thruClkTag(from_path, from_vertex, from_tag, to_propagates_clk, edge, to_rf, arc_delay_min_max_eq, - min_max, path_ap); - to_arrival = from_arrival + arc_delay; + min_max, scene); + to_arrival = delaySum(from_arrival, arc_delay, this); } } else { - if (!(sdc_->isPathDelayInternalFromBreak(to_pin) - || sdc_->isPathDelayInternalToBreak(from_pin))) { - to_tag = search_->thruTag(from_tag, edge, to_rf, min_max, path_ap, tag_cache_); - arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, path_ap); - if (!delayInf(arc_delay)) - to_arrival = from_arrival + arc_delay; + if (!(sdc->isPathDelayInternalFromBreak(to_pin) + || sdc->isPathDelayInternalToBreak(from_pin))) { + arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, + min_max, dcalc_ap, sdc); + if (!delayInf(arc_delay, this)) { + to_arrival = delaySum(from_arrival, arc_delay, this); + to_tag = search_->thruTag(from_tag, edge, to_rf, tag_cache_); + } } } if (to_tag) - return visitFromToPath(from_pin, from_vertex, from_rf, - from_tag, from_path, from_arrival, - edge, arc, arc_delay, - to_vertex, to_rf, to_tag, to_arrival, - min_max, path_ap); + return visitFromToPath(from_pin, from_vertex, from_rf, from_tag, from_path, + from_arrival, edge, arc, arc_delay, to_vertex, to_rf, + to_tag, to_arrival, min_max); else return true; } @@ -2533,30 +2382,26 @@ Search::clkPathArrival(const Path *clk_path) const { const ClkInfo *clk_info = clk_path->clkInfo(this); const ClockEdge *clk_edge = clk_info->clkEdge(); - const PathAnalysisPt *path_ap = clk_path->pathAnalysisPt(this); - const MinMax *min_max = path_ap->pathMinMax(); - return clkPathArrival(clk_path, clk_info, clk_edge, min_max, path_ap); + const MinMax *min_max = clk_path->minMax(this); + return clkPathArrival(clk_path, clk_info, clk_edge, min_max); } Arrival Search::clkPathArrival(const Path *clk_path, - const ClkInfo *clk_info, - const ClockEdge *clk_edge, - const MinMax *min_max, - const PathAnalysisPt *path_ap) const -{ - if (clk_path->vertex(this)->isRegClk() - && clk_path->isClock(this) - && clk_edge + const ClkInfo *clk_info, + const ClockEdge *clk_edge, + const MinMax *min_max) const +{ + const Scene *scene = clk_path->scene(this); + if (clk_path->vertex(this)->isRegClk() && clk_path->isClock(this) && clk_edge && !clk_info->isPropagated()) { // Ideal clock, apply ideal insertion delay and latency. const EarlyLate *early_late = min_max; - return clk_edge->time() - + clockInsertion(clk_edge->clock(), - clk_info->clkSrc(), - clk_edge->transition(), - min_max, early_late, path_ap) - + clk_info->latency(); + Arrival insertion = clockInsertion(clk_edge->clock(), clk_info->clkSrc(), + clk_edge->transition(), min_max, + early_late, scene->mode()); + return delaySum(delaySum(insertion, clk_edge->time(), this), + clk_info->latency(), this); } else return clk_path->arrival(); @@ -2592,12 +2437,12 @@ Search::pathClkPathArrival1(const Path *path) const if (prev_edge) { const TimingRole *prev_role = prev_edge->role(); if (prev_role == TimingRole::regClkToQ() - || prev_role == TimingRole::latchEnToQ()) { - return p->prevPath(); + || prev_role == TimingRole::latchEnToQ()) { + return p->prevPath(); } else if (prev_role == TimingRole::latchDtoQ()) { - Path *enable_path = latches_->latchEnablePath(p, prev_edge); - return enable_path; + Path *enable_path = latches_->latchEnablePath(p, prev_edge); + return enable_path; } } p = prev_path; @@ -2611,40 +2456,42 @@ Search::pathClkPathArrival1(const Path *path) const // Return nullptr if a false path starts at pin/clk_edge. Tag * Search::fromUnclkedInputTag(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - const PathAnalysisPt *path_ap, - bool is_segment_start, - bool require_exception) + const RiseFall *rf, + const MinMax *min_max, + bool is_segment_start, + bool require_exception, + Scene *scene) { + Sdc *sdc = scene->sdc(); ExceptionStateSet *states = nullptr; - if (sdc_->exceptionFromStates(pin, rf, nullptr, nullptr, min_max, states) + if (sdc->exceptionFromStates(pin, rf, nullptr, nullptr, min_max, states) && (!require_exception || states)) { - const ClkInfo *clk_info = findClkInfo(nullptr, nullptr, false, 0.0, path_ap); - return findTag(rf, path_ap, clk_info, false, nullptr, - is_segment_start, states, true, nullptr); + const ClkInfo *clk_info = + findClkInfo(scene, nullptr, nullptr, false, 0.0, min_max); + return findTag(scene, rf, min_max, clk_info, false, nullptr, is_segment_start, + states, true, nullptr); } return nullptr; } Tag * Search::fromRegClkTag(const Pin *from_pin, - const RiseFall *from_rf, - const Clock *clk, - const RiseFall *clk_rf, - const ClkInfo *clk_info, - const Pin *to_pin, - const RiseFall *to_rf, - const MinMax *min_max, - const PathAnalysisPt *path_ap) -{ + const RiseFall *from_rf, + const Clock *clk, + const RiseFall *clk_rf, + const ClkInfo *clk_info, + const Pin *to_pin, + const RiseFall *to_rf, + const MinMax *min_max, + Scene *scene) +{ + Sdc *sdc = scene->sdc(); ExceptionStateSet *states = nullptr; - if (sdc_->exceptionFromStates(from_pin, from_rf, clk, clk_rf, - min_max, states)) { + if (sdc->exceptionFromStates(from_pin, from_rf, clk, clk_rf, min_max, states)) { // Hack for filter -from reg/Q. - sdc_->filterRegQStates(to_pin, to_rf, min_max, states); - return findTag(to_rf, path_ap, clk_info, false, nullptr, false, states, - true, nullptr); + sdc->filterRegQStates(to_pin, to_rf, min_max, states); + return findTag(scene, to_rf, min_max, clk_info, false, nullptr, false, states, + true, nullptr); } else return nullptr; @@ -2653,20 +2500,16 @@ Search::fromRegClkTag(const Pin *from_pin, // Insert from_path as ClkInfo crpr_clk_path. const ClkInfo * Search::clkInfoWithCrprClkPath(const ClkInfo *from_clk_info, - Path *from_path, - const PathAnalysisPt *path_ap) -{ - if (crprActive()) - return findClkInfo(from_clk_info->clkEdge(), - from_clk_info->clkSrc(), - from_clk_info->isPropagated(), - from_clk_info->genClkSrc(), - from_clk_info->isGenClkSrcPath(), - from_clk_info->pulseClkSense(), - from_clk_info->insertion(), - from_clk_info->latency(), - from_clk_info->uncertainties(), - path_ap, from_path); + Path *from_path) +{ + Scene *scene = from_clk_info->scene(); + if (crprActive(scene->mode())) + return findClkInfo(scene, from_clk_info->clkEdge(), from_clk_info->clkSrc(), + from_clk_info->isPropagated(), from_clk_info->genClkSrc(), + from_clk_info->isGenClkSrcPath(), + from_clk_info->pulseClkSense(), from_clk_info->insertion(), + from_clk_info->latency(), from_clk_info->uncertainties(), + from_clk_info->minMax(), from_path); else return from_clk_info; } @@ -2677,8 +2520,6 @@ Tag * Search::thruTag(Tag *from_tag, Edge *edge, const RiseFall *to_rf, - const MinMax *min_max, - const PathAnalysisPt *path_ap, TagSet *tag_cache) { const Pin *from_pin = edge->from(graph_)->pin(); @@ -2687,11 +2528,10 @@ Search::thruTag(Tag *from_tag, const RiseFall *from_rf = from_tag->transition(); const ClkInfo *from_clk_info = from_tag->clkInfo(); bool to_is_reg_clk = to_vertex->isRegClk(); - Tag *to_tag = mutateTag(from_tag, from_pin, from_rf, false, from_clk_info, - to_pin, to_rf, false, to_is_reg_clk, false, + Tag *to_tag = mutateTag(from_tag, from_pin, from_rf, false, from_clk_info, to_pin, + to_rf, false, to_is_reg_clk, false, // input delay is not propagated. - from_clk_info, nullptr, min_max, path_ap, - tag_cache); + from_clk_info, nullptr, tag_cache); return to_tag; } @@ -2699,13 +2539,13 @@ Search::thruTag(Tag *from_tag, Tag * Search::thruClkTag(Path *from_path, Vertex *from_vertex, - Tag *from_tag, - bool to_propagates_clk, - Edge *edge, - const RiseFall *to_rf, + Tag *from_tag, + bool to_propagates_clk, + Edge *edge, + const RiseFall *to_rf, bool arc_delay_min_max_eq, - const MinMax *min_max, - const PathAnalysisPt *path_ap) + const MinMax *min_max, + Scene *scene) { const Pin *from_pin = edge->from(graph_)->pin(); Vertex *to_vertex = edge->to(graph_); @@ -2715,41 +2555,40 @@ Search::thruClkTag(Path *from_path, bool from_is_clk = from_tag->isClock(); bool to_is_reg_clk = to_vertex->isRegClk(); const TimingRole *role = edge->role(); - bool to_is_clk = (from_is_clk - && to_propagates_clk - && (role->isWire() - || role == TimingRole::combinational())); - const ClkInfo *to_clk_info = thruClkInfo(from_path, from_vertex, - from_clk_info, from_is_clk, - edge, to_vertex, to_pin, to_is_clk, - arc_delay_min_max_eq, min_max, path_ap); - Tag *to_tag = mutateTag(from_tag,from_pin,from_rf,from_is_clk,from_clk_info, - to_pin, to_rf, to_is_clk, to_is_reg_clk, false, - to_clk_info, nullptr, min_max, path_ap, nullptr); + bool to_is_clk = (from_is_clk && to_propagates_clk + && (role->isWire() || role == TimingRole::combinational())); + const ClkInfo *to_clk_info = thruClkInfo( + from_path, from_vertex, from_clk_info, from_is_clk, edge, to_vertex, to_pin, + to_is_clk, arc_delay_min_max_eq, min_max, scene); + Tag *to_tag = mutateTag(from_tag, from_pin, from_rf, from_is_clk, from_clk_info, + to_pin, to_rf, to_is_clk, to_is_reg_clk, false, + to_clk_info, nullptr, nullptr); return to_tag; } const ClkInfo * Search::thruClkInfo(Path *from_path, - Vertex *from_vertex, - const ClkInfo *from_clk_info, + Vertex *from_vertex, + const ClkInfo *from_clk_info, bool from_is_clk, - Edge *edge, - Vertex *to_vertex, - const Pin *to_pin, + Edge *edge, + Vertex *to_vertex, + const Pin *to_pin, bool to_is_clk, bool arc_delay_min_max_eq, - const MinMax *min_max, - const PathAnalysisPt *path_ap) + const MinMax *min_max, + Scene *scene) { + const ClkInfo *to_clk_info = from_clk_info; + const Sdc *sdc = scene->sdc(); + const Mode *mode = scene->mode(); bool changed = false; const ClockEdge *from_clk_edge = from_clk_info->clkEdge(); const RiseFall *clk_rf = from_clk_edge->transition(); bool from_clk_prop = from_clk_info->isPropagated(); bool to_clk_prop = from_clk_prop; - if (!from_clk_prop - && sdc_->isPropagatedClock(to_pin)) { + if (!from_clk_prop && sdc->isPropagatedClock(to_pin)) { to_clk_prop = true; changed = true; } @@ -2758,21 +2597,17 @@ Search::thruClkInfo(Path *from_path, // so that generated clock crpr info can be (later) safely set on // the clkinfo. const Pin *gen_clk_src = nullptr; - if (from_clk_info->isGenClkSrcPath() - && crprActive() - && sdc_->isClock(to_pin)) { + if (from_clk_info->isGenClkSrcPath() && crprActive(mode) && sdc->isClock(to_pin)) { // Don't care that it could be a regular clock root. gen_clk_src = to_pin; changed = true; } Path *to_crpr_clk_path = nullptr; - if (crprActive() + if (crprActive(mode) // Update crpr clk path for combinational paths leaving the clock // network (ie, tristate en->out) and buffer driving reg clk. - && ((from_is_clk - && !to_is_clk - && !from_vertex->isRegClk()) + && ((from_is_clk && !to_is_clk && !from_vertex->isRegClk()) || (to_vertex->isRegClk() // If the wire delay to the reg clk pin is zero, // leave the crpr_clk_path null to indicate that @@ -2790,8 +2625,8 @@ Search::thruClkInfo(Path *from_path, to_pulse_sense = port->pulseClkSense(); changed = true; } - else if (from_pulse_sense && - edge->timingArcSet()->sense() == TimingSense::negative_unate) { + else if (from_pulse_sense + && edge->timingArcSet()->sense() == TimingSense::negative_unate) { to_pulse_sense = from_pulse_sense->opposite(); changed = true; } @@ -2801,8 +2636,7 @@ Search::thruClkInfo(Path *from_path, float to_latency = from_clk_info->latency(); float latency; bool exists; - sdc_->clockLatency(from_clk, to_pin, clk_rf, min_max, - latency, exists); + sdc->clockLatency(from_clk, to_pin, clk_rf, min_max, latency, exists); if (exists) { // Latency on pin has precedence over fanin or hierarchical // pin latency. @@ -2812,8 +2646,7 @@ Search::thruClkInfo(Path *from_path, } else { // Check for hierarchical pin latency thru edge. - sdc_->clockLatency(edge, clk_rf, min_max, - latency, exists); + sdc->clockLatency(edge, clk_rf, min_max, latency, exists); if (exists) { to_latency = latency; to_clk_prop = false; @@ -2821,24 +2654,26 @@ Search::thruClkInfo(Path *from_path, } } - ClockUncertainties *to_uncertainties = from_clk_info->uncertainties(); - ClockUncertainties *uncertainties = sdc_->clockUncertainties(to_pin); + const ClockUncertainties *to_uncertainties = from_clk_info->uncertainties(); + const ClockUncertainties *uncertainties = sdc->clockUncertainties(to_pin); if (uncertainties) { to_uncertainties = uncertainties; changed = true; } - if (changed) { - const ClkInfo *to_clk_info = findClkInfo(from_clk_edge, - from_clk_info->clkSrc(), - to_clk_prop, gen_clk_src, - from_clk_info->isGenClkSrcPath(), - to_pulse_sense, to_insertion, to_latency, - to_uncertainties, path_ap, - to_crpr_clk_path); - return to_clk_info; - } - return from_clk_info; + if (changed) + to_clk_info = findClkInfo( + scene, from_clk_edge, from_clk_info->clkSrc(), to_clk_prop, gen_clk_src, + from_clk_info->isGenClkSrcPath(), to_pulse_sense, to_insertion, to_latency, + to_uncertainties, min_max, to_crpr_clk_path); + return to_clk_info; +} + +static size_t +tagsTableRfIndex(size_t tag_index, + const RiseFall *rf) +{ + return (tag_index / RiseFall::index_count) * RiseFall::index_count + rf->index(); } // Find the tag for a path going from from_tag thru edge to to_pin. @@ -2855,22 +2690,23 @@ Search::mutateTag(Tag *from_tag, bool to_is_segment_start, const ClkInfo *to_clk_info, InputDelay *to_input_delay, - const MinMax *min_max, - const PathAnalysisPt *path_ap, TagSet *tag_cache) { ExceptionStateSet *new_states = nullptr; ExceptionStateSet *from_states = from_tag->states(); + Scene *scene = from_tag->scene(); + Sdc *sdc = scene->sdc(); + const MinMax *min_max = from_tag->minMax(); if (from_states) { // Check for state changes in from_tag (but postpone copying state set). bool state_change = false; for (ExceptionState *state : *from_states) { ExceptionPath *exception = state->exception(); // One edge may traverse multiple hierarchical thru pins. - while (state->matchesNextThru(from_pin,to_pin,to_rf,min_max,network_)) { + while (state->matchesNextThru(from_pin, to_pin, to_rf, min_max, network_)) { // Found a -thru that we've been waiting for. state = state->nextState(); - state_change = true; + state_change = true; break; } if (state_change) @@ -2879,80 +2715,71 @@ Search::mutateTag(Tag *from_tag, // Don't propagate a completed false path -thru unless it is a // clock. Clocks carry the completed false path to disable // downstream paths that use the clock as data. - if ((state->isComplete() - && exception->isFalse() - && !from_is_clk) + if ((state->isComplete() && exception->isFalse() && !from_is_clk) // to_pin/edge completes a loop path. - || (exception->isLoop() - && state->isComplete())) - return nullptr; + || (exception->isLoop() && state->isComplete())) + return nullptr; // Kill path delay tags past the -to pin. if ((exception->isPathDelay() - && sdc_->isCompleteTo(state, to_pin, to_rf, min_max)) + && sdc->isCompleteTo(state, to_pin, to_rf, min_max)) // Kill loop tags at register clock pins. - || (exception->isLoop() - && to_is_reg_clk)) { - state_change = true; + || (exception->isLoop() && to_is_reg_clk)) { + state_change = true; break; } } // Get the set of -thru exceptions starting at to_pin/edge. - sdc_->exceptionThruStates(from_pin, to_pin, to_rf, min_max, new_states); + sdc->exceptionThruStates(from_pin, to_pin, to_rf, min_max, new_states); if (new_states || state_change) { // Second pass to apply state changes and add updated existing // states to new states. if (new_states == nullptr) - new_states = new ExceptionStateSet(); + new_states = new ExceptionStateSet(); for (auto state : *from_states) { - ExceptionPath *exception = state->exception(); - // One edge may traverse multiple hierarchical thru pins. - while (state->matchesNextThru(from_pin,to_pin,to_rf,min_max,network_)) - // Found a -thru that we've been waiting for. - state = state->nextState(); + ExceptionPath *exception = state->exception(); + // One edge may traverse multiple hierarchical thru pins. + while (state->matchesNextThru(from_pin, to_pin, to_rf, min_max, network_)) + // Found a -thru that we've been waiting for. + state = state->nextState(); // Don't propagate a completed false path -thru unless it is a // clock. Clocks carry the completed false path to disable // downstream paths that use the clock as data. - if ((state->isComplete() - && exception->isFalse() - && !from_is_clk) + if ((state->isComplete() && exception->isFalse() && !from_is_clk) // to_pin/edge completes a loop path. - || (exception->isLoop() - && state->isComplete())) { + || (exception->isLoop() && state->isComplete())) { delete new_states; return nullptr; } // Kill path delay tags past the -to pin. - if (!((exception->isPathDelay() - && sdc_->isCompleteTo(state, from_pin, from_rf, min_max)) + if (!((exception->isPathDelay() + && sdc->isCompleteTo(state, from_pin, from_rf, min_max)) // Kill loop tags at register clock pins. - || (to_is_reg_clk - && exception->isLoop()))) - new_states->insert(state); + || (to_is_reg_clk && exception->isLoop()))) + new_states->insert(state); } } } else // Get the set of -thru exceptions starting at to_pin/edge. - sdc_->exceptionThruStates(from_pin, to_pin, to_rf, min_max, new_states); + sdc->exceptionThruStates(from_pin, to_pin, to_rf, min_max, new_states); if (new_states) - return findTag(to_rf, path_ap, to_clk_info, to_is_clk, - from_tag->inputDelay(), to_is_segment_start, new_states, - true, tag_cache); + return findTag(scene, to_rf, min_max, to_clk_info, to_is_clk, + from_tag->inputDelay(), to_is_segment_start, new_states, true, + tag_cache); else { // No state change. - if (to_clk_info == from_clk_info - && to_rf == from_rf - && to_is_clk == from_is_clk - && from_tag->isSegmentStart() == to_is_segment_start - && from_tag->inputDelay() == to_input_delay) - return from_tag; + if (to_clk_info == from_clk_info && to_is_clk == from_is_clk + && from_tag->isSegmentStart() == to_is_segment_start + && from_tag->inputDelay() == to_input_delay) { + return tags_[tagsTableRfIndex(from_tag->index(), to_rf)]; + } else - return findTag(to_rf, path_ap, to_clk_info, to_is_clk, to_input_delay, + return findTag(scene, to_rf, min_max, to_clk_info, to_is_clk, to_input_delay, to_is_segment_start, from_states, false, tag_cache); } } @@ -2962,7 +2789,7 @@ Search::findTagGroup(TagGroupBldr *tag_bldr) { TagGroup probe(tag_bldr, this); LockGuard lock(tag_group_lock_); - TagGroup *tag_group = tag_group_set_->findKey(&probe); + TagGroup *tag_group = findKey(*tag_group_set_, &probe); if (tag_group == nullptr) { TagGroupIndex tag_group_index; if (tag_group_free_indices_.empty()) @@ -2979,9 +2806,8 @@ Search::findTagGroup(TagGroupBldr *tag_bldr) // can use Search::tagGroup(TagGroupIndex) without returning gubbish. if (tag_group_next_ == tag_group_capacity_) { TagGroupIndex tag_capacity = tag_group_capacity_ * 2; - TagGroup **tag_groups = new TagGroup*[tag_capacity]; - memcpy(tag_groups, tag_groups_, - tag_group_capacity_ * sizeof(TagGroup*)); + TagGroup **tag_groups = new TagGroup *[tag_capacity]; + std::copy_n(tag_groups_.load(), tag_group_capacity_, tag_groups); tag_groups_prev_.push_back(tag_groups_); tag_groups_ = tag_groups; tag_group_capacity_ = tag_capacity; @@ -2995,13 +2821,13 @@ Search::findTagGroup(TagGroupBldr *tag_bldr) void Search::setVertexArrivals(Vertex *vertex, - TagGroupBldr *tag_bldr) + TagGroupBldr *tag_bldr) { if (tag_bldr->empty()) deletePathsIncr(vertex); else { TagGroup *prev_tag_group = tagGroup(vertex); - Path *prev_paths = graph_->paths(vertex); + Path *prev_paths = vertex->paths(); TagGroup *tag_group = findTagGroup(tag_bldr); if (tag_group == prev_tag_group) { tag_bldr->copyPaths(tag_group, prev_paths); @@ -3009,19 +2835,19 @@ Search::setVertexArrivals(Vertex *vertex, } else { if (prev_tag_group) { - graph_->deletePaths(vertex); - prev_tag_group->decrRefCount(); + vertex->deletePaths(); + prev_tag_group->decrRefCount(); requiredInvalid(vertex); } size_t path_count = tag_group->pathCount(); - Path *paths = graph_->makePaths(vertex, path_count); + Path *paths = vertex->makePaths(path_count); tag_bldr->copyPaths(tag_group, paths); vertex->setTagGroupIndex(tag_group->index()); tag_group->incrRefCount(); } if (tag_group->hasFilterTag()) { LockGuard lock(filtered_arrivals_lock_); - filtered_arrivals_->insert(vertex); + filtered_arrivals_.insert(vertex); } } } @@ -3045,13 +2871,12 @@ class ReportPathLess public: ReportPathLess(const StaState *sta); bool operator()(const Path *path1, - const Path *path2) const; + const Path *path2) const; private: const StaState *sta_; }; - ReportPathLess::ReportPathLess(const StaState *sta) : sta_(sta) { @@ -3066,53 +2891,53 @@ ReportPathLess::operator()(const Path *path1, void Search::reportArrivals(Vertex *vertex, - bool report_tag_index) const + bool report_tag_index) const { - report_->reportLine("Vertex %s", vertex->to_string(this).c_str()); + report_->report("Vertex {}", vertex->to_string(this)); TagGroup *tag_group = tagGroup(vertex); if (tag_group) { if (report_tag_index) - report_->reportLine("Group %u", tag_group->index()); - std::vector paths; + report_->report("Group {}", tag_group->index()); + std::vector paths; VertexPathIterator path_iter(vertex, this); while (path_iter.hasNext()) { const Path *path = path_iter.next(); paths.push_back(path); } - sort(paths.begin(), paths.end(), ReportPathLess(this)); + sort(paths, ReportPathLess(this)); for (const Path *path : paths) { const Tag *tag = path->tag(this); - const PathAnalysisPt *path_ap = tag->pathAnalysisPt(this); const RiseFall *rf = tag->transition(); - const char *req = delayAsString(path->required(), this); + std::string req = delayAsString(path->required(), this); + bool report_prev = false; std::string prev_str; - Path *prev_path = path->prevPath(); - if (prev_path) { - prev_str += prev_path->to_string(this); - prev_str += " "; - const Edge *prev_edge = path->prevEdge(this); - TimingArc *arc = path->prevArc(this); - prev_str += prev_edge->from(graph_)->to_string(this); - prev_str += " "; - prev_str += arc->fromEdge()->to_string(); - prev_str += " -> "; - prev_str += prev_edge->to(graph_)->to_string(this); - prev_str += " "; - prev_str += arc->toEdge()->to_string(); + if (report_prev) { + prev_str = "prev "; + Path *prev_path = path->prevPath(); + if (prev_path) { + prev_str += prev_path->to_string(this); + prev_str += " "; + const Edge *prev_edge = path->prevEdge(this); + TimingArc *arc = path->prevArc(this); + prev_str += prev_edge->from(graph_)->to_string(this); + prev_str += " "; + prev_str += arc->fromEdge()->to_string(); + prev_str += " -> "; + prev_str += prev_edge->to(graph_)->to_string(this); + prev_str += " "; + prev_str += arc->toEdge()->to_string(); + } + else + prev_str += "NULL"; } - else - prev_str = "NULL"; - report_->reportLine(" %s %s %s / %s %s prev %s", - rf->to_string().c_str(), - path_ap->pathMinMax()->to_string().c_str(), - delayAsString(path->arrival(), this), - req, - tag->to_string(report_tag_index, false, this).c_str(), - prev_str.c_str()); + report_->report(" {} {} {} / {} {}{}", rf->shortName(), + path->minMax(this)->to_string(), + delayAsString(path->arrival(), this), req, + tag->to_string(report_tag_index, false, this), prev_str); } } else - report_->reportLine(" no arrivals"); + report_->report(" no arrivals"); } TagGroup * @@ -3143,10 +2968,8 @@ Search::reportTagGroups() const for (TagGroupIndex i = 0; i < tag_group_next_; i++) { TagGroup *tag_group = tag_groups_[i]; if (tag_group) { - report_->reportLine("Group %4u hash = %4lu (%4lu)", - i, - tag_group->hash(), - tag_group->hash() % tag_group_set_->bucket_count()); + report_->report("Group {:4} hash = {:4} ({:4})", i, tag_group->hash(), + tag_group->hash() % tag_group_set_->bucket_count()); tag_group->reportArrivalMap(this); } } @@ -3155,15 +2978,14 @@ Search::reportTagGroups() const if (tag_group_set_->bucket_size(i) > long_hash) long_hash = i; } - report_->reportLine("Longest hash bucket length %zu hash=%zu", - tag_group_set_->bucket_size(long_hash), - long_hash); + report_->report("Longest hash bucket length {} hash={}", + tag_group_set_->bucket_size(long_hash), long_hash); } void Search::reportPathCountHistogram() const { - Vector vertex_counts(10); + std::vector vertex_counts(10); VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); @@ -3171,7 +2993,7 @@ Search::reportPathCountHistogram() const if (tag_group) { size_t path_count = tag_group->pathCount(); if (path_count >= vertex_counts.size()) - vertex_counts.resize(path_count * 2); + vertex_counts.resize(path_count * 2); vertex_counts[path_count]++; } } @@ -3179,7 +3001,7 @@ Search::reportPathCountHistogram() const for (size_t path_count = 0; path_count < vertex_counts.size(); path_count++) { int vertex_count = vertex_counts[path_count]; if (vertex_count > 0) - report_->reportLine("%6lu %6d",path_count, vertex_count); + report_->report("{:6} {:6}", path_count, vertex_count); } } @@ -3198,8 +3020,9 @@ Search::tagCount() const } Tag * -Search::findTag(const RiseFall *rf, - const PathAnalysisPt *path_ap, +Search::findTag(Scene *scene, + const RiseFall *rf, + const MinMax *min_max, const ClkInfo *clk_info, bool is_clk, InputDelay *input_delay, @@ -3208,54 +3031,54 @@ Search::findTag(const RiseFall *rf, bool own_states, TagSet *tag_cache) { - Tag probe(0, rf->index(), path_ap->index(), clk_info, is_clk, input_delay, - is_segment_start, states, false, this); + Tag probe(scene, 0, rf, min_max, clk_info, is_clk, input_delay, is_segment_start, + states, false); if (tag_cache) { - Tag *tag = tag_cache->findKey(&probe); + Tag *tag = findKey(*tag_cache, &probe); if (tag) return tag; } + LockGuard lock(tag_lock_); - Tag *tag = tag_set_->findKey(&probe); + Tag *tag = findKey(*tag_set_, &probe); if (tag == nullptr) { - ExceptionStateSet *new_states = !own_states && states - ? new ExceptionStateSet(*states) : states; - TagIndex tag_index; - if (tag_free_indices_.empty()) - tag_index = tag_next_++; - else { - tag_index = tag_free_indices_.back(); - tag_free_indices_.pop_back(); + // Make rise/fall versions of the tag to avoid tag_set lookups when the + // only change is the rise/fall edge. + for (const RiseFall *rf1 : RiseFall::range()) { + ExceptionStateSet *new_states = + !own_states && states ? new ExceptionStateSet(*states) : states; + TagIndex tag_index = tag_next_++; + Tag *tag1 = new Tag(scene, tag_index, rf1, min_max, clk_info, is_clk, + input_delay, is_segment_start, new_states, true); + own_states = false; + // Make sure tag can be indexed in tags_ before it is visible to + // other threads via tag_set_. + tags_[tagsTableRfIndex(tag_index, rf1)] = tag1; + tag_set_->insert(tag1); + if (tag_cache) + tag_cache->insert(tag1); + if (rf1 == rf) + tag = tag1; + + if (tag_next_ == tag_index_max) + report_->critical(1511, "max tag index exceeded"); } - tag = new Tag(tag_index, rf->index(), path_ap->index(), - clk_info, is_clk, input_delay, is_segment_start, - new_states, true, this); - own_states = false; - // Make sure tag can be indexed in tags_ before it is visible to - // other threads via tag_set_. - tags_[tag_index] = tag; - tag_set_->insert(tag); + // If tags_ needs to grow make the new array and copy the // contents into it before updating tags_ so that other threads // can use Search::tag(TagIndex) without returning gubbish. if (tag_next_ == tag_capacity_) { TagIndex tag_capacity = tag_capacity_ * 2; - Tag **tags = new Tag*[tag_capacity]; - memcpy(tags, tags_, tag_capacity_ * sizeof(Tag*)); + Tag **tags = new Tag *[tag_capacity]; + std::copy_n(tags_.load(), tag_capacity_, tags); tags_prev_.push_back(tags_); tags_ = tags; tag_capacity_ = tag_capacity; tag_set_->reserve(tag_capacity); } - if (tag_next_ == tag_index_max) - report_->critical(1511, "max tag index exceeded"); } if (own_states) delete states; - - if (tag_cache) - tag_cache->insert(tag); - return tag; } @@ -3265,68 +3088,68 @@ Search::reportTags() const for (TagIndex i = 0; i < tag_next_; i++) { Tag *tag = tags_[i]; if (tag) - report_->reportLine("%s", tag->to_string(this).c_str()) ; + report_->report("{}", tag->to_string(this)); } size_t long_hash = 0; for (size_t i = 0; i < tag_set_->bucket_count(); i++) { if (tag_set_->bucket_size(i) > long_hash) long_hash = i; } - report_->reportLine("Longest hash bucket length %zu hash=%zu", - tag_set_->bucket_size(long_hash), - long_hash); + report_->report("Longest hash bucket length {} hash={}", + tag_set_->bucket_size(long_hash), long_hash); } void Search::reportClkInfos() const { - Vector clk_infos; + std::vector clk_infos; // set -> vector for sorting. for (const ClkInfo *clk_info : *clk_info_set_) clk_infos.push_back(clk_info); sort(clk_infos, ClkInfoLess(this)); for (const ClkInfo *clk_info : clk_infos) - report_->reportLine("%s", clk_info->to_string(this).c_str()); - report_->reportLine("%zu clk infos", clk_info_set_->size()); + report_->report("{}", clk_info->to_string(this)); + report_->report("{} clk infos", clk_info_set_->size()); } const ClkInfo * -Search::findClkInfo(const ClockEdge *clk_edge, - const Pin *clk_src, - bool is_propagated, +Search::findClkInfo(Scene *scene, + const ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, const Pin *gen_clk_src, - bool gen_clk_src_path, - const RiseFall *pulse_clk_sense, - Arrival insertion, - float latency, - ClockUncertainties *uncertainties, - const PathAnalysisPt *path_ap, - Path *crpr_clk_path) -{ - ClkInfo probe(clk_edge, clk_src, is_propagated, gen_clk_src, gen_clk_src_path, - pulse_clk_sense, insertion, latency, uncertainties, - path_ap->index(), crpr_clk_path, this); + bool gen_clk_src_path, + const RiseFall *pulse_clk_sense, + Arrival insertion, + float latency, + const ClockUncertainties *uncertainties, + const MinMax *min_max, + Path *crpr_clk_path) +{ + const ClkInfo probe(scene, clk_edge, clk_src, is_propagated, gen_clk_src, + gen_clk_src_path, pulse_clk_sense, insertion, latency, + uncertainties, min_max, crpr_clk_path, this); LockGuard lock(clk_info_lock_); - const ClkInfo *clk_info = clk_info_set_->findKey(&probe); + const ClkInfo *clk_info = findKey(*clk_info_set_, &probe); if (clk_info == nullptr) { - clk_info = new ClkInfo(clk_edge, clk_src, - is_propagated, gen_clk_src, gen_clk_src_path, - pulse_clk_sense, insertion, latency, uncertainties, - path_ap->index(), crpr_clk_path, this); + clk_info = new ClkInfo(scene, clk_edge, clk_src, is_propagated, gen_clk_src, + gen_clk_src_path, pulse_clk_sense, insertion, latency, + uncertainties, min_max, crpr_clk_path, this); clk_info_set_->insert(clk_info); } return clk_info; } const ClkInfo * -Search::findClkInfo(const ClockEdge *clk_edge, - const Pin *clk_src, - bool is_propagated, - Arrival insertion, - const PathAnalysisPt *path_ap) +Search::findClkInfo(Scene *scene, + const ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, + Arrival insertion, + const MinMax *min_max) { - return findClkInfo(clk_edge, clk_src, is_propagated, nullptr, false, nullptr, - insertion, 0.0, nullptr, path_ap, nullptr); + return findClkInfo(scene, clk_edge, clk_src, is_propagated, nullptr, false, + nullptr, insertion, 0.0, nullptr, min_max, nullptr); } int @@ -3337,33 +3160,32 @@ Search::clkInfoCount() const ArcDelay Search::deratedDelay(const Vertex *from_vertex, - const TimingArc *arc, - const Edge *edge, - bool is_clk, - const PathAnalysisPt *path_ap) + const TimingArc *arc, + const Edge *edge, + bool is_clk, + const MinMax *min_max, + DcalcAPIndex dcalc_ap, + const Sdc *sdc) { - const DcalcAnalysisPt *dcalc_ap = path_ap->dcalcAnalysisPt(); - DcalcAPIndex ap_index = dcalc_ap->index(); - float derate = timingDerate(from_vertex, arc, edge, is_clk, path_ap); - ArcDelay delay = graph_->arcDelay(edge, arc, ap_index); - return delay * derate; + float derate = timingDerate(from_vertex, arc, edge, is_clk, sdc, min_max); + const ArcDelay &delay = graph_->arcDelay(edge, arc, dcalc_ap); + return delayProduct(delay, derate, this);; } float Search::timingDerate(const Vertex *from_vertex, - const TimingArc *arc, - const Edge *edge, - bool is_clk, - const PathAnalysisPt *path_ap) + const TimingArc *arc, + const Edge *edge, + bool is_clk, + const Sdc *sdc, + const MinMax *min_max) { - PathClkOrData derate_clk_data = - is_clk ? PathClkOrData::clk : PathClkOrData::data; + PathClkOrData derate_clk_data = is_clk ? PathClkOrData::clk : PathClkOrData::data; const TimingRole *role = edge->role(); const Pin *pin = from_vertex->pin(); if (role->isWire()) { const RiseFall *rf = arc->toEdge()->asRiseFall(); - return sdc_->timingDerateNet(pin, derate_clk_data, rf, - path_ap->pathMinMax()); + return sdc->timingDerateNet(pin, derate_clk_data, rf, min_max); } else { TimingDerateCellType derate_type; @@ -3373,103 +3195,89 @@ Search::timingDerate(const Vertex *from_vertex, rf = arc->toEdge()->asRiseFall(); } else { - derate_type = TimingDerateCellType::cell_delay; - rf = arc->fromEdge()->asRiseFall(); + derate_type = TimingDerateCellType::cell_delay; + rf = arc->fromEdge()->asRiseFall(); } - return sdc_->timingDerateInstance(pin, derate_type, derate_clk_data, rf, - path_ap->pathMinMax()); + return sdc->timingDerateInstance(pin, derate_type, derate_clk_data, rf, min_max); } } ClockSet -Search::clockDomains(const Vertex *vertex) const +Search::clockDomains(const Vertex *vertex, + const Mode *mode) const + { ClockSet clks; - clockDomains(vertex, clks); + clockDomains(vertex, mode, clks); return clks; } void Search::clockDomains(const Vertex *vertex, + const Mode *mode, // Return value. ClockSet &clks) const { - VertexPathIterator path_iter(const_cast(vertex), this); + VertexPathIterator path_iter(const_cast(vertex), this); while (path_iter.hasNext()) { Path *path = path_iter.next(); const Clock *clk = path->clock(this); - if (clk) + if (clk && path->mode(this) == mode) clks.insert(const_cast(clk)); } } ClockSet -Search::clockDomains(const Pin *pin) const +Search::clockDomains(const Pin *pin, + const Mode *mode) const { ClockSet clks; Vertex *vertex; Vertex *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); if (vertex) - clockDomains(vertex, clks); + clockDomains(vertex, mode, clks); if (bidirect_drvr_vertex) - clockDomains(bidirect_drvr_vertex, clks); + clockDomains(bidirect_drvr_vertex, mode, clks); return clks; } ClockSet -Search::clocks(const Vertex *vertex) const -{ - ClockSet clks; - clocks(vertex, clks); - return clks; -} - -void -Search::clocks(const Vertex *vertex, - // Return value. - ClockSet &clks) const -{ - VertexPathIterator path_iter(const_cast(vertex), this); - while (path_iter.hasNext()) { - Path *path = path_iter.next(); - if (path->isClock(this)) - clks.insert(const_cast(path->clock(this))); - } -} - -ClockSet -Search::clocks(const Pin *pin) const +Search::clocks(const Pin *pin, + const Mode *mode) const { ClockSet clks; Vertex *vertex; Vertex *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); if (vertex) - clocks(vertex, clks); + clocks(vertex, mode, clks); if (bidirect_drvr_vertex) - clocks(bidirect_drvr_vertex, clks); + clocks(bidirect_drvr_vertex, mode, clks); return clks; } -bool -Search::isClock(const Vertex *vertex) const +ClockSet +Search::clocks(const Vertex *vertex, + const Mode *mode) const { - TagGroup *tag_group = tagGroup(vertex); - if (tag_group) - return tag_group->hasClkTag(); - else - return false; + ClockSet clks; + clocks(vertex, mode, clks); + return clks; } -bool -Search::isGenClkSrc(const Vertex *vertex) const +void +Search::clocks(const Vertex *vertex, + const Mode *mode, + // Return value. + ClockSet &clks) const { - TagGroup *tag_group = tagGroup(vertex); - if (tag_group) - return tag_group->hasGenClkSrcTag(); - else - return false; + VertexPathIterator path_iter(const_cast(vertex), this); + while (path_iter.hasNext()) { + Path *path = path_iter.next(); + if (path->isClock(this) && path->mode(this) == mode) + clks.insert(const_cast(path->clock(this))); + } } //////////////////////////////////////////////////////////////// @@ -3484,7 +3292,7 @@ void Search::findRequireds(Level level) { Stats stats(debug_, report_); - debugPrint(debug_, "search", 1, "find requireds to level %d", level); + debugPrint(debug_, "search", 1, "find requireds to level {}", level); RequiredVisitor req_visitor(this); if (!requireds_seeded_) seedRequireds(); @@ -3492,7 +3300,7 @@ Search::findRequireds(Level level) int required_count = required_iter_->visitParallel(level, &req_visitor); deleteTagsPrev(); requireds_exist_ = true; - debugPrint(debug_, "search", 1, "found %d requireds", required_count); + debugPrint(debug_, "search", 1, "found {} requireds", required_count); stats.report("Find requireds"); } @@ -3500,44 +3308,41 @@ void Search::seedRequireds() { ensureDownstreamClkPins(); - for (Vertex *vertex : *endpoints()) + for (Vertex *vertex : endpoints()) seedRequired(vertex); requireds_seeded_ = true; requireds_exist_ = true; } -VertexSet * +VertexSet & Search::endpoints() { - if (endpoints_ == nullptr) { - endpoints_ = new VertexSet(graph_); - invalid_endpoints_ = new VertexSet(graph_); + if (!endpoints_initialized_) { VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); if (isEndpoint(vertex)) { - debugPrint(debug_, "endpoint", 2, "insert %s", - vertex->to_string(this).c_str()); - endpoints_->insert(vertex); + debugPrint(debug_, "endpoint", 2, "insert {}", + vertex->to_string(this)); + endpoints_.insert(vertex); } } + endpoints_initialized_ = true; } - if (invalid_endpoints_) { - for (Vertex *vertex : *invalid_endpoints_) { + if (!invalid_endpoints_.empty()) { + for (Vertex *vertex : invalid_endpoints_) { if (isEndpoint(vertex)) { - debugPrint(debug_, "endpoint", 2, "insert %s", - vertex->to_string(this).c_str()); - endpoints_->insert(vertex); + debugPrint(debug_, "endpoint", 2, "insert {}", + vertex->to_string(this)); + endpoints_.insert(vertex); } else { - if (debug_->check("endpoint", 2) - && endpoints_->hasKey(vertex)) - report_->reportLine("endpoint: remove %s", - vertex->to_string(this).c_str()); - endpoints_->erase(vertex); + if (debug_->check("endpoint", 2) && endpoints_.contains(vertex)) + report_->report("endpoint: remove {}", vertex->to_string(this)); + endpoints_.erase(vertex); } } - invalid_endpoints_->clear(); + invalid_endpoints_.clear(); } return endpoints_; } @@ -3545,44 +3350,63 @@ Search::endpoints() void Search::endpointInvalid(Vertex *vertex) { - if (invalid_endpoints_) { - debugPrint(debug_, "endpoint", 2, "invalid %s", - vertex->to_string(this).c_str()); - invalid_endpoints_->insert(vertex); - } + debugPrint(debug_, "endpoint", 2, "invalid {}", vertex->to_string(this)); + invalid_endpoints_.insert(vertex); } bool Search::isEndpoint(Vertex *vertex) const { - return isEndpoint(vertex, search_adj_); + for (const Mode *mode : modes_) { + if (isEndpoint(vertex, search_thru_, mode)) + return true; + } + return false; } bool Search::isEndpoint(Vertex *vertex, - SearchPred *pred) const + const ModeSeq &modes) const { - Pin *pin = vertex->pin(); - return hasFanin(vertex, pred, graph_) - && ((vertex->hasChecks() - && hasEnabledChecks(vertex)) - || (variables_->gatedClkChecksEnabled() - && gated_clk_->isGatedClkEnable(vertex)) - || vertex->isConstrained() - || sdc_->isPathDelayInternalTo(pin) - || !hasFanout(vertex, pred, graph_) - // Unconstrained paths at register clk pins. - || (unconstrained_paths_ - && vertex->isRegClk())); + for (const Mode *mode : modes) { + if (isEndpoint(vertex, search_thru_, mode)) + return true; + } + return false; } bool -Search::hasEnabledChecks(Vertex *vertex) const +Search::isEndpoint(Vertex *vertex, + const Mode *mode) const +{ + return isEndpoint(vertex, search_thru_, mode); +} + +bool +Search::isEndpoint(Vertex *vertex, + SearchPred *pred, + const Mode *mode) const +{ + const Pin *pin = vertex->pin(); + const Sdc *sdc = mode->sdc(); + return hasFanin(vertex, pred, graph_, mode) + && ((vertex->hasChecks() && hasEnabledChecks(vertex, mode)) + || sdc->isConstrainedEnd(pin) || !hasFanout(vertex, pred, graph_, mode) + || sdc->isPathDelayInternalTo(pin) + // Unconstrained paths at register clk pins. + || (unconstrained_paths_ && vertex->isRegClk()) + || (variables_->gatedClkChecksEnabled() + && gated_clk_->isGatedClkEnable(vertex, mode))); +} + +bool +Search::hasEnabledChecks(Vertex *vertex, + const Mode *mode) const { VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); - if (visit_path_ends_->checkEdgeEnabled(edge)) + if (visit_path_ends_->checkEdgeEnabled(edge, mode)) return true; } return false; @@ -3591,18 +3415,17 @@ Search::hasEnabledChecks(Vertex *vertex) const void Search::endpointsInvalid() { - delete endpoints_; - delete invalid_endpoints_; - endpoints_ = nullptr; - invalid_endpoints_ = nullptr; + endpoints_.clear(); + endpoints_initialized_ = false; + invalid_endpoints_.clear(); } void Search::seedInvalidRequireds() { - for (Vertex *vertex : *invalid_requireds_) + for (Vertex *vertex : invalid_requireds_) required_iter_->enqueue(vertex); - invalid_requireds_->clear(); + invalid_requireds_.clear(); } //////////////////////////////////////////////////////////////// @@ -3612,11 +3435,11 @@ class FindEndRequiredVisitor : public PathEndVisitor { public: FindEndRequiredVisitor(RequiredCmp *required_cmp, - const StaState *sta); + const StaState *sta); FindEndRequiredVisitor(const StaState *sta); - virtual ~FindEndRequiredVisitor(); - virtual PathEndVisitor *copy() const; - virtual void visit(PathEnd *path_end); + ~FindEndRequiredVisitor() override; + PathEndVisitor *copy() const override; + void visit(PathEnd *path_end) override; protected: const StaState *sta_; @@ -3625,7 +3448,7 @@ class FindEndRequiredVisitor : public PathEndVisitor }; FindEndRequiredVisitor::FindEndRequiredVisitor(RequiredCmp *required_cmp, - const StaState *sta) : + const StaState *sta) : sta_(sta), required_cmp_(required_cmp), own_required_cmp_(false) @@ -3666,8 +3489,8 @@ FindEndRequiredVisitor::visit(PathEnd *path_end) void Search::seedRequired(Vertex *vertex) { - debugPrint(debug_, "search", 2, "required seed %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 2, "required seed {}", + vertex->to_string(this)); RequiredCmp required_cmp; FindEndRequiredVisitor seeder(&required_cmp, this); required_cmp.requiredsInit(vertex, this); @@ -3691,15 +3514,9 @@ Search::seedRequiredEnqueueFanin(Vertex *vertex) //////////////////////////////////////////////////////////////// -RequiredCmp::RequiredCmp() : - have_requireds_(false) -{ - requireds_.reserve(10); -} - void RequiredCmp::requiredsInit(Vertex *vertex, - const StaState *sta) + const StaState *sta) { Search *search = sta->search(); TagGroup *tag_group = search->tagGroup(vertex); @@ -3707,8 +3524,7 @@ RequiredCmp::requiredsInit(Vertex *vertex, size_t path_count = tag_group->pathCount(); requireds_.resize(path_count); for (auto const [tag, path_index] : *tag_group->pathIndexMap()) { - PathAnalysisPt *path_ap = tag->pathAnalysisPt(sta); - const MinMax *min_max = path_ap->pathMinMax(); + const MinMax *min_max = tag->minMax(); requireds_[path_index] = delayInitValue(min_max->opposite()); } } @@ -3719,9 +3535,9 @@ RequiredCmp::requiredsInit(Vertex *vertex, void RequiredCmp::requiredSet(size_t path_index, - Required &required, - const MinMax *min_max, - const StaState *sta) + Required &required, + const MinMax *min_max, + const StaState *sta) { if (delayGreater(required, requireds_[path_index], min_max, sta)) { requireds_[path_index] = required; @@ -3731,7 +3547,7 @@ RequiredCmp::requiredSet(size_t path_index, bool RequiredCmp::requiredsSave(Vertex *vertex, - const StaState *sta) + const StaState *sta) { bool requireds_changed = false; Debug *debug = sta->debug(); @@ -3739,11 +3555,11 @@ RequiredCmp::requiredsSave(Vertex *vertex, while (path_iter.hasNext()) { Path *path = path_iter.next(); size_t path_index = path->pathIndex(sta); - Required req = requireds_[path_index]; - Required &prev_req = path->required(); - bool changed = !delayEqual(prev_req, req); - debugPrint(debug, "search", 3, "required %s save %s -> %s%s", - path->to_string(sta).c_str(), + const Required req = requireds_[path_index]; + const Required &prev_req = path->required(); + bool changed = !delayEqual(prev_req, req, sta); + debugPrint(debug, "search", 3, "required {} save {} -> {}{}", + path->to_string(sta), delayAsString(prev_req, sta), delayAsString(req, sta), changed ? " changed" : ""); @@ -3769,8 +3585,10 @@ RequiredVisitor::RequiredVisitor(const StaState *sta) : } RequiredVisitor::RequiredVisitor(bool make_tag_cache, - const StaState *sta) : - PathVisitor(sta->search()->evalPred(), make_tag_cache, sta), + const StaState *sta) : + PathVisitor(sta->search()->evalPred(), + make_tag_cache, + sta), required_cmp_(new RequiredCmp), visit_path_ends_(new VisitPathEnds(sta)) { @@ -3791,8 +3609,8 @@ RequiredVisitor::copy() const void RequiredVisitor::visit(Vertex *vertex) { - debugPrint(debug_, "search", 2, "find required %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 2, "find required {}", + vertex->to_string(this)); required_cmp_->requiredsInit(vertex, this); // Back propagate requireds from fanout. visitFanoutPaths(vertex); @@ -3810,30 +3628,26 @@ RequiredVisitor::visit(Vertex *vertex) bool RequiredVisitor::visitFromToPath(const Pin *, - Vertex * /* from_vertex */, - const RiseFall *from_rf, - Tag *from_tag, - Path *from_path, + Vertex * /* from_vertex */, + const RiseFall *from_rf, + Tag *from_tag, + Path *from_path, const Arrival &, - Edge *edge, - TimingArc *, - ArcDelay arc_delay, - Vertex *to_vertex, - const RiseFall *to_rf, - Tag *to_tag, - Arrival &, - const MinMax *min_max, - const PathAnalysisPt *path_ap) + Edge *edge, + TimingArc *, + ArcDelay arc_delay, + Vertex *to_vertex, + const RiseFall *to_rf, + Tag *to_tag, + Arrival &, + const MinMax *min_max) { // Don't propagate required times through latch D->Q edges. - if (edge->role() != TimingRole::latchDtoQ()) { - debugPrint(debug_, "search", 3, " %s -> %s %s", - from_rf->to_string().c_str(), - to_rf->to_string().c_str(), - min_max->to_string().c_str()); - debugPrint(debug_, "search", 3, " from tag %2u: %s", - from_tag->index(), - from_tag->to_string(this).c_str()); + if (!edge->role()->isLatchDtoQ()) { + debugPrint(debug_, "search", 3, " {} -> {} {}", from_rf->shortName(), + to_rf->shortName(), min_max->to_string()); + debugPrint(debug_, "search", 3, " from tag {:2}: {}", from_tag->index(), + from_tag->to_string(this)); size_t path_index = from_path->pathIndex(this); const MinMax *req_min = min_max->opposite(); TagGroup *to_tag_group = search_->tagGroup(to_vertex); @@ -3841,12 +3655,12 @@ RequiredVisitor::visitFromToPath(const Pin *, if (to_tag_group && to_tag_group->hasTag(to_tag)) { size_t to_path_index = to_tag_group->pathIndex(to_tag); Path &to_path = to_vertex->paths()[to_path_index]; - Required &to_required = to_path.required(); - Required from_required = to_required - arc_delay; - debugPrint(debug_, "search", 3, " to tag %2u: %s", + const Required &to_required = to_path.required(); + Required from_required = delayDiff(to_required, arc_delay, this); + debugPrint(debug_, "search", 3, " to tag {:2}: {}", to_tag->index(), - to_tag->to_string(this).c_str()); - debugPrint(debug_, "search", 3, " %s - %s = %s %s %s", + to_tag->to_string(this)); + debugPrint(debug_, "search", 3, " {} - {} = {} {} {}", delayAsString(to_required, this), delayAsString(arc_delay, this), delayAsString(from_required, this), @@ -3856,30 +3670,30 @@ RequiredVisitor::visitFromToPath(const Pin *, } else { if (search_->crprApproxMissingRequireds()) { - // Arrival on to_vertex that differs by crpr_pin was pruned. - // Find an arrival that matches everything but the crpr_pin - // as an appromate required. - VertexPathIterator to_iter(to_vertex, to_rf, path_ap, this); - while (to_iter.hasNext()) { - Path *to_path = to_iter.next(); - Tag *to_path_tag = to_path->tag(this); - if (Tag::matchNoCrpr(to_path_tag, to_tag)) { - Required to_required = to_path->required(); - Required from_required = to_required - arc_delay; - debugPrint(debug_, "search", 3, " to tag %2u: %s", + // Arrival on to_vertex that differs by crpr_pin was pruned. + // Find an arrival that matches everything but the crpr_pin + // as an appromate required. + VertexPathIterator to_iter(to_vertex, from_path->scene(this), + from_path->minMax(this), to_rf, this); + while (to_iter.hasNext()) { + Path *to_path = to_iter.next(); + Tag *to_path_tag = to_path->tag(this); + if (Tag::matchNoCrpr(to_path_tag, to_tag)) { + Required to_required = to_path->required(); + Required from_required = delayDiff(to_required, arc_delay, this); + debugPrint(debug_, "search", 3, " to tag {:2}: {}", to_path_tag->index(), - to_path_tag->to_string(this).c_str()); - debugPrint(debug_, "search", 3, " %s - %s = %s %s %s", + to_path_tag->to_string(this)); + debugPrint(debug_, "search", 3, " {} - {} = {} {} {}", delayAsString(to_required, this), delayAsString(arc_delay, this), delayAsString(from_required, this), min_max == MinMax::max() ? "<" : ">", - delayAsString(required_cmp_->required(path_index), - this)); - required_cmp_->requiredSet(path_index, from_required, req_min, this); - break; - } - } + delayAsString(required_cmp_->required(path_index), this)); + required_cmp_->requiredSet(path_index, from_required, req_min, this); + break; + } + } } } } @@ -3896,10 +3710,8 @@ Search::ensureDownstreamClkPins() // as having downstream clk pins. ClkTreeSearchPred pred(this); BfsBkwdIterator iter(BfsIndex::other, &pred, this); - for (Vertex *vertex : *graph_->regClkVertices()) { - if (!vertex->isConstant()) - iter.enqueue(vertex); - } + for (Vertex *vertex : graph_->regClkVertices()) + iter.enqueue(vertex); while (iter.hasNext()) { Vertex *vertex = iter.next(); @@ -3914,42 +3726,36 @@ Search::ensureDownstreamClkPins() bool Search::matchesFilter(Path *path, - const ClockEdge *to_clk_edge) + const ClockEdge *to_clk_edge) { - if (filter_ == nullptr - && filter_from_ == nullptr - && filter_to_ == nullptr) + if (!have_filter_ && filter_from_ == nullptr && filter_to_ == nullptr) return true; - else if (filter_) { + else if (have_filter_) { // -from pins|inst // -thru // Path has to be tagged by traversing the filter exception points. ExceptionStateSet *states = path->tag(this)->states(); if (states) { for (auto state : *states) { - if (state->exception() == filter_ - && state->nextThru() == nullptr - && matchesFilterTo(path, to_clk_edge)) - return true; + if (state->exception()->isFilter() && state->nextThru() == nullptr + && matchesFilterTo(path, to_clk_edge)) + return true; } } return false; } - else if (filter_from_ - && filter_from_->pins() == nullptr - && filter_from_->instances() == nullptr - && filter_from_->clks()) { + else if (filter_from_ && filter_from_->pins() == nullptr + && filter_from_->instances() == nullptr && filter_from_->clks()) { // -from clks const ClockEdge *path_clk_edge = path->clkEdge(this); const Clock *path_clk = path_clk_edge ? path_clk_edge->clock() : nullptr; const RiseFall *path_clk_rf = - path_clk_edge ? path_clk_edge->transition() : nullptr; - return filter_from_->clks()->hasKey(const_cast(path_clk)) - && filter_from_->transition()->matches(path_clk_rf) - && matchesFilterTo(path, to_clk_edge); + path_clk_edge ? path_clk_edge->transition() : nullptr; + return filter_from_->clks()->contains(const_cast(path_clk)) + && filter_from_->transition()->matches(path_clk_rf) + && matchesFilterTo(path, to_clk_edge); } - else if (filter_from_ == nullptr - && filter_to_) + else if (filter_from_ == nullptr && filter_to_) // -to return matchesFilterTo(path, to_clk_edge); else { @@ -3958,14 +3764,14 @@ Search::matchesFilter(Path *path, } } -// Similar to Constraints::exceptionMatchesTo. +// Similar to Sdf::exceptionMatchesTo. bool Search::matchesFilterTo(Path *path, - const ClockEdge *to_clk_edge) const + const ClockEdge *to_clk_edge) const { return (filter_to_ == nullptr - || filter_to_->matchesFilter(path->pin(graph_), to_clk_edge, - path->transition(this), network_)); + || filter_to_->matchesFilter(path->pin(graph_), to_clk_edge, + path->transition(this), network_)); } //////////////////////////////////////////////////////////////// @@ -3974,13 +3780,14 @@ Search::matchesFilterTo(Path *path, // including exceptions that start at the end pin or target clock. ExceptionPath * Search::exceptionTo(ExceptionPathType type, - const Path *path, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - bool require_to_pin) const + const Path *path, + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + bool require_to_pin, + Sdc *sdc) const { // Find the highest priority exception carried by the path's tag. int hi_priority = -1; @@ -3990,23 +3797,20 @@ Search::exceptionTo(ExceptionPathType type, for (auto state : *states) { ExceptionPath *exception = state->exception(); int priority = exception->priority(min_max); - if ((type == ExceptionPathType::any - || exception->type() == type) - && sdc_->isCompleteTo(state, pin, rf, clk_edge, min_max, - match_min_max_exactly, require_to_pin) - && (hi_priority_exception == nullptr - || priority > hi_priority - || (priority == hi_priority - && exception->tighterThan(hi_priority_exception)))) { - hi_priority = priority; - hi_priority_exception = exception; + if ((type == ExceptionPathType::any || exception->type() == type) + && sdc->isCompleteTo(state, pin, rf, clk_edge, min_max, + match_min_max_exactly, require_to_pin) + && (hi_priority_exception == nullptr || priority > hi_priority + || (priority == hi_priority + && exception->tighterThan(hi_priority_exception)))) { + hi_priority = priority; + hi_priority_exception = exception; } } } // Check for -to exceptions originating at the end pin or target clock. - sdc_->exceptionTo(type, pin, rf, clk_edge, min_max, - match_min_max_exactly, - hi_priority_exception, hi_priority); + sdc->exceptionTo(type, pin, rf, clk_edge, min_max, match_min_max_exactly, + hi_priority_exception, hi_priority); return hi_priority_exception; } @@ -4020,21 +3824,22 @@ Search::groupPathsTo(const PathEnd *path_end) const const Path *path = path_end->path(); const Pin *pin = path->pin(this); const Tag *tag = path->tag(this); + Sdc *sdc = tag->scene()->sdc(); const RiseFall *rf = tag->transition(); const ClockEdge *clk_edge = path_end->targetClkEdge(this); - const MinMax *min_max = tag->minMax(this); + const MinMax *min_max = tag->minMax(); const ExceptionStateSet *states = path->tag(this)->states(); if (states) { for (auto state : *states) { ExceptionPath *exception = state->exception(); if (exception->isGroupPath() - && sdc_->exceptionMatchesTo(exception, pin, rf, clk_edge, min_max, - false, false)) - group_paths.push_back(exception); + && sdc->exceptionMatchesTo(exception, pin, rf, clk_edge, min_max, false, + false)) + group_paths.push_back(exception); } } // Check for group_path -to exceptions originating at the end pin or target clock. - sdc_->groupPathsTo(pin, rf, clk_edge, min_max, group_paths); + sdc->groupPathsTo(pin, rf, clk_edge, min_max, group_paths); return group_paths; } @@ -4044,32 +3849,32 @@ Slack Search::totalNegativeSlack(const MinMax *min_max) { tnsPreamble(); - Slack tns = 0.0; - for (Corner *corner : *corners_) { - PathAPIndex path_ap_index = corner->findPathAnalysisPt(min_max)->index(); - Slack tns1 = tns_[path_ap_index]; + DelayDbl tns = 0.0; + for (Scene *scene : scenes_) { + size_t path_index = scene->pathIndex(min_max); + DelayDbl tns1 = tns_[path_index]; if (delayLess(tns1, tns, this)) tns = tns1; } - return tns; + return delayDblAsDelay(tns); } Slack -Search::totalNegativeSlack(const Corner *corner, - const MinMax *min_max) +Search::totalNegativeSlack(const Scene *scene, + const MinMax *min_max) { tnsPreamble(); - PathAPIndex path_ap_index = corner->findPathAnalysisPt(min_max)->index(); - return tns_[path_ap_index]; + PathAPIndex path_ap_index = scene->pathIndex(min_max); + return delayDblAsDelay(tns_[path_ap_index]); } void Search::tnsPreamble() { wnsTnsPreamble(); - PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); - tns_.resize(path_ap_count); - tns_slacks_.resize(path_ap_count); + size_t path_count = scenePathCount(); + tns_.resize(path_count); + tns_slacks_.resize(path_count); if (tns_exists_) updateInvalidTns(); else @@ -4079,49 +3884,46 @@ Search::tnsPreamble() void Search::tnsInvalid(Vertex *vertex) { - if ((tns_exists_ || worst_slacks_) - && isEndpoint(vertex)) { - debugPrint(debug_, "tns", 2, "tns invalid %s", - vertex->to_string(this).c_str()); + if ((tns_exists_ || worst_slacks_) && isEndpoint(vertex)) { + debugPrint(debug_, "tns", 2, "tns invalid {}", vertex->to_string(this)); LockGuard lock(tns_lock_); - invalid_tns_->insert(vertex); + invalid_tns_.insert(vertex); } } void Search::updateInvalidTns() { - PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); - for (Vertex *vertex : *invalid_tns_) { + size_t path_count = scenePathCount(); + for (Vertex *vertex : invalid_tns_) { // Network edits can change endpointedness since tnsInvalid was called. if (isEndpoint(vertex)) { - debugPrint(debug_, "tns", 2, "update tns %s", - vertex->to_string(this).c_str()); - SlackSeq slacks(path_ap_count); + debugPrint(debug_, "tns", 2, "update tns {}", vertex->to_string(this)); + SlackSeq slacks(path_count); wnsSlacks(vertex, slacks); if (tns_exists_) - updateTns(vertex, slacks); + updateTns(vertex, slacks); if (worst_slacks_) - worst_slacks_->updateWorstSlacks(vertex, slacks); + worst_slacks_->updateWorstSlacks(vertex, slacks); } } - invalid_tns_->clear(); + invalid_tns_.clear(); } void Search::findTotalNegativeSlacks() { - PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); - for (PathAPIndex i = 0; i < path_ap_count; i++) { + size_t path_count = scenePathCount(); + for (size_t i = 0; i < path_count; i++) { tns_[i] = 0.0; tns_slacks_[i].clear(); } - for (Vertex *vertex : *endpoints()) { + for (Vertex *vertex : endpoints()) { // No locking required. - SlackSeq slacks(path_ap_count); + SlackSeq slacks(path_count); wnsSlacks(vertex, slacks); - for (PathAPIndex i = 0; i < path_ap_count; i++) + for (size_t i = 0; i < path_count; i++) tnsIncr(vertex, slacks[i], i); } tns_exists_ = true; @@ -4129,10 +3931,10 @@ Search::findTotalNegativeSlacks() void Search::updateTns(Vertex *vertex, - SlackSeq &slacks) + SlackSeq &slacks) { - PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); - for (PathAPIndex i = 0; i < path_ap_count; i++) { + size_t path_count = scenePathCount(); + for (size_t i = 0; i < path_count; i++) { tnsDecr(vertex, i); tnsIncr(vertex, slacks[i], i); } @@ -4140,15 +3942,15 @@ Search::updateTns(Vertex *vertex, void Search::tnsIncr(Vertex *vertex, - Slack slack, - PathAPIndex path_ap_index) + Slack slack, + PathAPIndex path_ap_index) { if (delayLess(slack, 0.0, this)) { - debugPrint(debug_, "tns", 3, "tns+ %s %s", + debugPrint(debug_, "tns", 3, "tns+ {} {}", delayAsString(slack, this), - vertex->to_string(this).c_str()); - tns_[path_ap_index] += slack; - if (tns_slacks_[path_ap_index].hasKey(vertex)) + vertex->to_string(this)); + delayIncr(tns_[path_ap_index], slack, this); + if (tns_slacks_[path_ap_index].contains(vertex)) report_->critical(1513, "tns incr existing vertex"); tns_slacks_[path_ap_index][vertex] = slack; } @@ -4156,17 +3958,17 @@ Search::tnsIncr(Vertex *vertex, void Search::tnsDecr(Vertex *vertex, - PathAPIndex path_ap_index) + PathAPIndex path_ap_index) { Slack slack; bool found; - tns_slacks_[path_ap_index].findKey(vertex, slack, found); + findKeyValue(tns_slacks_[path_ap_index], vertex, slack, found); if (found && delayLess(slack, 0.0, this)) { - debugPrint(debug_, "tns", 3, "tns- %s %s", + debugPrint(debug_, "tns", 3, "tns- {} {}", delayAsString(slack, this), - vertex->to_string(this).c_str()); - tns_[path_ap_index] -= slack; + vertex->to_string(this)); + delayDecr(tns_[path_ap_index], slack, this); tns_slacks_[path_ap_index].erase(vertex); } } @@ -4175,10 +3977,9 @@ Search::tnsDecr(Vertex *vertex, void Search::tnsNotifyBefore(Vertex *vertex) { - if (tns_exists_ - && isEndpoint(vertex)) { - int ap_count = corners_->pathAnalysisPtCount(); - for (int i = 0; i < ap_count; i++) { + if (tns_exists_ && isEndpoint(vertex)) { + size_t path_count = scenePathCount(); + for (size_t i = 0; i < path_count; i++) { tnsDecr(vertex, i); } } @@ -4188,23 +3989,23 @@ Search::tnsNotifyBefore(Vertex *vertex) void Search::worstSlack(const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex) + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { worstSlackPreamble(); worst_slacks_->worstSlack(min_max, worst_slack, worst_vertex); } void -Search::worstSlack(const Corner *corner, - const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex) +Search::worstSlack(const Scene *scene, + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { worstSlackPreamble(); - worst_slacks_->worstSlack(corner, min_max, worst_slack, worst_vertex); + worst_slacks_->worstSlack(scene, min_max, worst_slack, worst_vertex); } void @@ -4223,19 +4024,19 @@ Search::wnsTnsPreamble() findAllArrivals(); // Required times are only needed at endpoints. if (requireds_seeded_) { - for (auto itr = invalid_requireds_->begin(); itr != invalid_requireds_->end(); ) { + for (auto itr = invalid_requireds_.begin(); itr != invalid_requireds_.end();) { Vertex *vertex = *itr; - debugPrint(debug_, "search", 2, "tns update required %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 2, "tns update required {}", + vertex->to_string(this)); if (isEndpoint(vertex)) { - seedRequired(vertex); - // If the endpoint has fanout it's required time - // depends on downstream checks, so enqueue it to - // force required propagation to it's level if - // the required time is requested later. - if (hasFanout(vertex, search_adj_, graph_)) - required_iter_->enqueue(vertex); - itr = invalid_requireds_->erase(itr); + seedRequired(vertex); + // If the endpoint has fanout it's required time + // depends on downstream checks, so enqueue it to + // force required propagation to it's level if + // the required time is requested later. + if (vertex->hasFanout()) + required_iter_->enqueue(vertex); + itr = invalid_requireds_.erase(itr); } else itr++; @@ -4261,10 +4062,10 @@ class FindEndSlackVisitor : public PathEndVisitor { public: FindEndSlackVisitor(SlackSeq &slacks, - const StaState *sta); + const StaState *sta); FindEndSlackVisitor(const FindEndSlackVisitor &) = default; - virtual PathEndVisitor *copy() const; - virtual void visit(PathEnd *path_end); + PathEndVisitor *copy() const override; + void visit(PathEnd *path_end) override; protected: SlackSeq &slacks_; @@ -4272,7 +4073,7 @@ class FindEndSlackVisitor : public PathEndVisitor }; FindEndSlackVisitor::FindEndSlackVisitor(SlackSeq &slacks, - const StaState *sta) : + const StaState *sta) : slacks_(slacks), sta_(sta) { @@ -4298,14 +4099,14 @@ FindEndSlackVisitor::visit(PathEnd *path_end) void Search::wnsSlacks(Vertex *vertex, - // Return values. - SlackSeq &slacks) + // Return values. + SlackSeq &slacks) { Slack slack_init = MinMax::min()->initValue(); - PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); - for (PathAPIndex i = 0; i < path_ap_count; i++) + size_t path_count = scenePathCount(); + for (size_t i = 0; i < path_count; i++) slacks[i] = slack_init; - if (hasFanout(vertex, search_adj_, graph_)) { + if (vertex->hasFanout()) { // If the vertex has fanout the path slacks include downstream // PathEnd slacks so find the endpoint slack directly. FindEndSlackVisitor end_visitor(slacks, this); @@ -4318,64 +4119,53 @@ Search::wnsSlacks(Vertex *vertex, PathAPIndex path_ap_index = path->pathAnalysisPtIndex(this); const Slack path_slack = path->slack(this); if (!path->tag(this)->isFilter() - && delayLess(path_slack, slacks[path_ap_index], this)) - slacks[path_ap_index] = path_slack; + && delayLess(path_slack, slacks[path_ap_index], this)) + slacks[path_ap_index] = path_slack; } } } Slack Search::wnsSlack(Vertex *vertex, - PathAPIndex path_ap_index) + PathAPIndex path_ap_index) { - PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); - SlackSeq slacks(path_ap_count); + size_t path_count = scenePathCount(); + SlackSeq slacks(path_count); wnsSlacks(vertex, slacks); return slacks[path_ap_index]; } //////////////////////////////////////////////////////////////// -void -Search::deletePathGroups() +DelaysWrtClks +Search::arrivalsWrtClks(Vertex *vertex, + const Scene *scene) { - delete path_groups_; - path_groups_ = nullptr; + return delaysWrtClks(vertex, scene, + [] (const Path *path) { + return path->arrival(); + }); } -PathGroupSeq -Search::pathGroups(const PathEnd *path_end) const +DelaysWrtClks +Search::delaysWrtClks(Vertex *vertex, + const Scene *scene, + const PathDelayFunc &get_path_delay) { - if (path_groups_) - return path_groups_->pathGroups(path_end); - else - return PathGroupSeq(); -} - -bool -Search::havePathGroups() const -{ - return path_groups_ != nullptr; -} - -PathGroup * -Search::findPathGroup(const char *name, - const MinMax *min_max) const -{ - if (path_groups_) - return path_groups_->findPathGroup(name, min_max); - else - return nullptr; -} - -PathGroup * -Search::findPathGroup(const Clock *clk, - const MinMax *min_max) const -{ - if (path_groups_) - return path_groups_->findPathGroup(clk, min_max); - else - return nullptr; + DelaysWrtClks delays_wrt_clks; + VertexPathIterator path_iter(vertex, scene, nullptr, nullptr, this); + while (path_iter.hasNext()) { + Path *path = path_iter.next(); + Delay delay = get_path_delay(path); + if (!delayInf(delay, this)) { + const RiseFall *rf = path->transition(this); + const MinMax *min_max = path->minMax(this); + const ClockEdge *clk_edge = path->clkEdge(this); + RiseFallMinMaxDelay &delays = delays_wrt_clks[clk_edge]; + delays.mergeValue(rf, min_max, delay, this); + } + } + return delays_wrt_clks; } -} // namespace +} // namespace sta diff --git a/search/Search.i b/search/Search.i index 66d6bc7ea..2ec870afa 100644 --- a/search/Search.i +++ b/search/Search.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,18 +22,23 @@ // // This notice may not be removed or altered from any source distribution. -%module search +%include "std_string.i" %{ +#include + #include "Units.hh" #include "PathGroup.hh" #include "Search.hh" #include "search/Levelize.hh" #include "search/ReportPath.hh" +#include "search/Tag.hh" #include "PathExpanded.hh" #include "Bfs.hh" +#include "Scene.hh" #include "Sta.hh" +#include "StaConfig.hh" #include "liberty/LibertyParser.hh" using namespace sta; @@ -68,36 +73,10 @@ private: ~PathEnd(); }; -class MinPulseWidthCheck -{ -private: - MinPulseWidthCheck(); - ~MinPulseWidthCheck(); -}; - -class MinPulseWidthCheckSeq -{ -private: - MinPulseWidthCheckSeq(); - ~MinPulseWidthCheckSeq(); -}; - -class MinPulseWidthCheckSeqIterator -{ -private: - MinPulseWidthCheckSeqIterator(); - ~MinPulseWidthCheckSeqIterator(); -}; - -class Corner -{ -private: - Corner(); - ~Corner(); -}; - %inline %{ +using std::string; + int group_path_count_max = PathGroup::group_path_count_max; //////////////////////////////////////////////////////////////// @@ -159,12 +138,6 @@ arrivals_invalid() sta->arrivalsInvalid(); } -PinSet -startpoints() -{ - return Sta::sta()->startpointPins(); -} - PinSet endpoints() { @@ -172,7 +145,7 @@ endpoints() } size_t -endpoint_path_count() +endpoint_count() { return Sta::sta()->endpointPins().size(); } @@ -183,26 +156,29 @@ find_requireds() Sta::sta()->findRequireds(); } -Slack +float total_negative_slack_cmd(const MinMax *min_max) { - return Sta::sta()->totalNegativeSlack(min_max); + Sta *sta = Sta::sta(); + return delayAsFloat(sta->totalNegativeSlack(min_max), min_max, sta); } -Slack -total_negative_slack_corner_cmd(const Corner *corner, - const MinMax *min_max) +float +total_negative_slack_scene_cmd(const Scene *scene, + const MinMax *min_max) { - return Sta::sta()->totalNegativeSlack(corner, min_max); + Sta *sta = Sta::sta(); + return delayAsFloat(sta->totalNegativeSlack(scene, min_max), min_max, sta); } -Slack +float worst_slack_cmd(const MinMax *min_max) { + Sta *sta = Sta::sta(); Slack worst_slack; Vertex *worst_vertex; - Sta::sta()->worstSlack(min_max, worst_slack, worst_vertex); - return worst_slack; + sta->worstSlack(min_max, worst_slack, worst_vertex); + return delayAsFloat(worst_slack, min_max, sta); } Vertex * @@ -214,19 +190,20 @@ worst_slack_vertex(const MinMax *min_max) return worst_vertex;; } -Slack -worst_slack_corner(const Corner *corner, - const MinMax *min_max) +float +worst_slack_scene(const Scene *scene, + const MinMax *min_max) { + Sta *sta = Sta::sta(); Slack worst_slack; Vertex *worst_vertex; - Sta::sta()->worstSlack(corner, min_max, worst_slack, worst_vertex); - return worst_slack; + sta->worstSlack(scene, min_max, worst_slack, worst_vertex); + return delayAsFloat(worst_slack, min_max, sta); } Path * vertex_worst_arrival_path(Vertex *vertex, - const MinMax *min_max) + const MinMax *min_max) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); @@ -235,8 +212,8 @@ vertex_worst_arrival_path(Vertex *vertex, Path * vertex_worst_arrival_path_rf(Vertex *vertex, - const RiseFall *rf, - MinMax *min_max) + const RiseFall *rf, + MinMax *min_max) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); @@ -245,36 +222,37 @@ vertex_worst_arrival_path_rf(Vertex *vertex, Path * vertex_worst_slack_path(Vertex *vertex, - const MinMax *min_max) + const MinMax *min_max) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); return sta->vertexWorstSlackPath(vertex, min_max); } -Slack +float endpoint_slack(const Pin *pin, - const char *path_group_name, - const MinMax *min_max) + const std::string &path_group_name, + const MinMax *min_max) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); - if (sta->isGroupPathName(path_group_name)) { - Slack slack = sta->endpointSlack(pin, std::string(path_group_name), min_max); - return sta->units()->timeUnit()->staToUser(delayAsFloat(slack)); + if (!path_group_name.empty() + && !sta->isGroupPathName(path_group_name, sta->cmdSdc())) { + sta->report()->error(1590, "{} is not a known path group name.", + path_group_name); + return INF; } else { - sta->report()->error(1577, "%s is not a known path group name.", - path_group_name); - return INF; + Slack slack = sta->endpointSlack(pin, path_group_name, min_max); + return sta->units()->timeUnit()->staToUser(delayAsFloat(slack, min_max, sta)); } } -StdStringSeq +StringSeq path_group_names() { Sta *sta = Sta::sta(); - return sta->pathGroupNames(); + return sta->pathGroupNames(sta->cmdSdc()); } int @@ -291,7 +269,7 @@ report_tag_groups() void report_tag_arrivals_cmd(Vertex *vertex, - bool report_tag_index) + bool report_tag_index) { Sta::sta()->search()->reportArrivals(vertex, report_tag_index); } @@ -345,14 +323,16 @@ report_loops() Report *report = sta->report(); for (GraphLoop *loop : sta->graphLoops()) { loop->report(sta); - report->reportLineString(""); + report->reportLine(""); } } char pin_sim_logic_value(const Pin *pin) { - return logicValueString(Sta::sta()->simLogicValue(pin)); + Sta *sta = Sta::sta(); + const Mode *sdc = sta->cmdMode(); + return logicValueString(sta->simLogicValue(pin, sdc)); } InstanceSeq @@ -361,59 +341,53 @@ slow_drivers(int count) return Sta::sta()->slowDrivers(count); } +bool +is_ideal_clock(const Pin *pin) +{ + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + return sta->isIdealClock(pin, mode); +} + //////////////////////////////////////////////////////////////// PathEndSeq find_path_ends(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool unconstrained, - Corner *corner, - const MinMaxAll *delay_min_max, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - bool sort_by_slack, - PathGroupNameSet *groups, - bool setup, - bool hold, - bool recovery, - bool removal, - bool clk_gating_setup, - bool clk_gating_hold) + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool unconstrained, + SceneSeq scenes, + const MinMaxAll *delay_min_max, + int group_path_count, + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + bool sort_by_slack, + StringSeq path_groups, + bool setup, + bool hold, + bool recovery, + bool removal, + bool clk_gating_setup, + bool clk_gating_hold) { Sta *sta = Sta::sta(); PathEndSeq ends = sta->findPathEnds(from, thrus, to, unconstrained, - corner, delay_min_max, + scenes, delay_min_max, group_path_count, endpoint_path_count, - unique_pins, unique_edges, + unique_pins, unique_edges, slack_min, slack_max, - sort_by_slack, - groups->size() ? groups : nullptr, + sort_by_slack, path_groups, setup, hold, recovery, removal, clk_gating_setup, clk_gating_hold); - delete groups; return ends; } //////////////////////////////////////////////////////////////// -void -report_path_end_header() -{ - Sta::sta()->reportPathEndHeader(); -} - -void -report_path_end_footer() -{ - Sta::sta()->reportPathEndFooter(); -} - void report_path_end(PathEnd *end) { @@ -422,9 +396,10 @@ report_path_end(PathEnd *end) void report_path_end2(PathEnd *end, - PathEnd *prev_end) + PathEnd *prev_end, + bool last) { - Sta::sta()->reportPathEnd(end, prev_end); + Sta::sta()->reportPathEnd(end, prev_end, last); } void @@ -434,54 +409,43 @@ set_report_path_format(ReportPathFormat format) } void -set_report_path_field_order(StringSeq *field_names) +set_report_path_field_order(const StringSeq &field_names) { Sta::sta()->setReportPathFieldOrder(field_names); - delete field_names; } void set_report_path_fields(bool report_input_pin, bool report_hier_pins, - bool report_net, - bool report_cap, - bool report_slew, - bool report_fanout, - bool report_src_attr) + bool report_net, + bool report_cap, + bool report_slew, + bool report_fanout, + bool report_variation, + bool report_src_attr) { Sta::sta()->setReportPathFields(report_input_pin, report_hier_pins, - report_net, - report_cap, - report_slew, - report_fanout, - report_src_attr); + report_net, + report_cap, + report_slew, + report_fanout, + report_variation, + report_src_attr); } void set_report_path_field_properties(const char *field_name, - const char *title, - int width, - bool left_justify) + const char *title, + int width, + bool left_justify) { Sta *sta = Sta::sta(); ReportField *field = sta->findReportPathField(field_name); if (field) field->setProperties(title, width, left_justify); else - sta->report()->warn(1575, "unknown report path field %s", field_name); -} - -void -set_report_path_field_width(const char *field_name, - int width) -{ - Sta *sta = Sta::sta(); - ReportField *field = sta->findReportPathField(field_name); - if (field) - field->setWidth(width); - else - sta->report()->warn(1576, "unknown report path field %s", field_name); + sta->report()->warn(1591, "unknown report path field {}", field_name); } void @@ -514,12 +478,6 @@ set_report_path_silimate_dedup_endpoints_rx(const char *silimate_dedup_endpoints Sta::sta()->setSilimateDedupEndpointRegex(silimate_dedup_endpoints_rx); } -void -set_report_path_sigmas(bool report_sigmas) -{ - Sta::sta()->setReportPathSigmas(report_sigmas); -} - void report_path_cmd(Path *path) { @@ -536,134 +494,105 @@ report_path_ends(PathEndSeq *ends) //////////////////////////////////////////////////////////////// void -report_clk_skew(ConstClockSeq clks, - const Corner *corner, - const SetupHold *setup_hold, - bool include_internal_latency, - int digits) +report_arrival_wrt_clks(const Pin *pin, + const Scene *scene, + bool report_variance, + int digits) { - Sta::sta()->reportClkSkew(clks, corner, setup_hold, - include_internal_latency, digits); + Sta::sta()->reportArrivalWrtClks(pin, scene, report_variance, digits); } void -report_clk_latency(ConstClockSeq clks, - const Corner *corner, - bool include_internal_latency, - int digits) +report_required_wrt_clks(const Pin *pin, + const Scene *scene, + bool report_variance, + int digits) { - Sta::sta()->reportClkLatency(clks, corner, include_internal_latency, digits); + Sta::sta()->reportRequiredWrtClks(pin, scene, report_variance, digits); } -float -worst_clk_skew_cmd(const SetupHold *setup_hold, - bool include_internal_latency) +void +report_slack_wrt_clks(const Pin *pin, + const Scene *scene, + bool report_variance, + int digits) { - return Sta::sta()->findWorstClkSkew(setup_hold, include_internal_latency); + Sta::sta()->reportSlackWrtClks(pin, scene, report_variance, digits); } //////////////////////////////////////////////////////////////// -MinPulseWidthCheckSeq & -min_pulse_width_violations(const Corner *corner) +void +report_clk_skew(ConstClockSeq clks, + const SceneSeq scenes, + const SetupHold *setup_hold, + bool include_internal_latency, + int digits) { - return Sta::sta()->minPulseWidthViolations(corner); + Sta::sta()->reportClkSkew(clks, scenes, setup_hold, + include_internal_latency, digits); } -MinPulseWidthCheckSeq & -min_pulse_width_check_pins(PinSeq *pins, - const Corner *corner) +float +worst_clk_skew_cmd(const SetupHold *setup_hold, + bool include_internal_latency) { Sta *sta = Sta::sta(); - MinPulseWidthCheckSeq &checks = sta->minPulseWidthChecks(pins, corner); - return checks; + Delay skew = sta->findWorstClkSkew(setup_hold, include_internal_latency); + return delayAsFloat(skew, MinMax::max(), sta); } -MinPulseWidthCheckSeq & -min_pulse_width_checks(const Corner *corner) -{ - return Sta::sta()->minPulseWidthChecks(corner); -} - -MinPulseWidthCheck * -min_pulse_width_check_slack(const Corner *corner) -{ - return Sta::sta()->minPulseWidthSlack(corner); -} - -void -report_mpw_checks(MinPulseWidthCheckSeq *checks, - bool verbose) -{ - Sta::sta()->reportMpwChecks(checks, verbose); -} +//////////////////////////////////////////////////////////////// void -report_mpw_check(MinPulseWidthCheck *check, - bool verbose) +report_clk_latency(ConstClockSeq clks, + const SceneSeq scenes, + bool include_internal_latency, + int digits) { - Sta::sta()->reportMpwCheck(check, verbose); + Sta::sta()->reportClkLatency(clks, scenes, include_internal_latency, digits); } //////////////////////////////////////////////////////////////// -MinPeriodCheckSeq & -min_period_violations() -{ - return Sta::sta()->minPeriodViolations(); -} - -MinPeriodCheck * -min_period_check_slack() -{ - return Sta::sta()->minPeriodSlack(); -} - -void -report_min_period_checks(MinPeriodCheckSeq *checks, - bool verbose) -{ - Sta::sta()->reportChecks(checks, verbose); -} - void -report_min_period_check(MinPeriodCheck *check, - bool verbose) +report_min_pulse_width_checks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq scenes) { - Sta::sta()->reportCheck(check, verbose); + return Sta::sta()->reportMinPulseWidthChecks(net, max_count, violators, + verbose, scenes); } //////////////////////////////////////////////////////////////// -MaxSkewCheckSeq & -max_skew_violations() -{ - return Sta::sta()->maxSkewViolations(); -} - -MaxSkewCheck * -max_skew_check_slack() -{ - return Sta::sta()->maxSkewSlack(); -} - void -report_max_skew_checks(MaxSkewCheckSeq *checks, - bool verbose) +report_min_period_checks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq scenes) { - Sta::sta()->reportChecks(checks, verbose); + Sta::sta()->reportMinPeriodChecks(net, max_count, violators, verbose, scenes); } +//////////////////////////////////////////////////////////////// + void -report_max_skew_check(MaxSkewCheck *check, - bool verbose) +report_max_skew_checks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq scenes) { - Sta::sta()->reportCheck(check, verbose); + Sta::sta()->reportMaxSkewChecks(net, max_count, violators, verbose, scenes); } //////////////////////////////////////////////////////////////// -Slack +float find_clk_min_period(const Clock *clk, bool ignore_port_paths) { @@ -672,19 +601,22 @@ find_clk_min_period(const Clock *clk, //////////////////////////////////////////////////////////////// -PinSeq -check_slew_limits(Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max) +void +report_slew_checks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq scenes, + const MinMax *min_max) { - return Sta::sta()->checkSlewLimits(net, violators, corner, min_max); + return Sta::sta()->reportSlewChecks(net, max_count, violators, verbose, + scenes, min_max); } size_t max_slew_violation_count() { - return Sta::sta()->checkSlewLimits(nullptr, true, nullptr, MinMax::max()).size(); + return Sta::sta()->maxSlewViolationCount(); } float @@ -711,103 +643,82 @@ max_slew_check_limit() return sta->units()->timeUnit()->staToUser(limit); } -void -report_slew_limit_short_header() -{ - Sta::sta()->reportSlewLimitShortHeader(); -} - -void -report_slew_limit_short(Pin *pin, - const Corner *corner, - const MinMax *min_max) -{ - Sta::sta()->reportSlewLimitShort(pin, corner, min_max); -} - -void -report_slew_limit_verbose(Pin *pin, - const Corner *corner, - const MinMax *min_max) -{ - Sta::sta()->reportSlewLimitVerbose(pin, corner, min_max); -} - //////////////////////////////////////////////////////////////// -PinSeq -check_fanout_limits(Net *net, - bool violators, - const MinMax *min_max) +void +report_fanout_checks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq scenes, + const MinMax *min_max) { - return Sta::sta()->checkFanoutLimits(net, violators, min_max); + Sta *sta = Sta::sta(); + return sta->reportFanoutChecks(net, max_count, violators, verbose, + scenes, min_max); } size_t max_fanout_violation_count() { - return Sta::sta()->checkFanoutLimits(nullptr, true, MinMax::max()).size(); + Sta *sta = Sta::sta(); + return sta->fanoutViolationCount(MinMax::max(), sta->modes()); } float -max_fanout_check_slack() +max_fanout_min_slack() { Sta *sta = Sta::sta(); const Pin *pin; - float fanout; - float slack; - float limit; - sta->maxFanoutCheck(pin, fanout, slack, limit); + float fanout, limit, slack; + const Mode *mode; + sta->maxFanoutMinSlackPin(sta->modes(), pin, fanout, limit, slack, mode); return slack;; } +// Deprecated 11/16/2025 float -max_fanout_check_limit() -{ - Sta *sta = Sta::sta(); - const Pin *pin; - float fanout; - float slack; - float limit; - sta->maxFanoutCheck(pin, fanout, slack, limit); - return limit;; -} - -void -report_fanout_limit_short_header() +max_fanout_check_slack() { - Sta::sta()->reportFanoutLimitShortHeader(); + return max_fanout_min_slack(); } -void -report_fanout_limit_short(Pin *pin, - const MinMax *min_max) +float +max_fanout_min_slack_limit() { - Sta::sta()->reportFanoutLimitShort(pin, min_max); + Sta *sta = Sta::sta(); + const Pin *pin; + float fanout, limit, slack; + const Mode *mode; + sta->maxFanoutMinSlackPin(sta->modes(), pin, fanout, limit, slack, mode); + return limit;; } -void -report_fanout_limit_verbose(Pin *pin, - const MinMax *min_max) +// Deprecated 11/16/2025 +float +max_fanout_check_limit() { - Sta::sta()->reportFanoutLimitVerbose(pin, min_max); + return max_fanout_min_slack_limit(); } //////////////////////////////////////////////////////////////// -PinSeq -check_capacitance_limits(Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max) +void +report_capacitance_checks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq scenes, + const MinMax *min_max) { - return Sta::sta()->checkCapacitanceLimits(net, violators, corner, min_max); + Sta::sta()->reportCapacitanceChecks(net, max_count, violators, verbose, + scenes, min_max); } size_t max_capacitance_violation_count() { - return Sta::sta()->checkCapacitanceLimits(nullptr, true,nullptr,MinMax::max()).size(); + return Sta::sta()->maxCapacitanceViolationCount(); } float @@ -834,167 +745,215 @@ max_capacitance_check_limit() return sta->units()->capacitanceUnit()->staToUser(limit); } +//////////////////////////////////////////////////////////////// + void -report_capacitance_limit_short_header() +write_timing_model_cmd(const char *lib_name, + const char *cell_name, + const char *filename, + const Scene *scene, + const bool scalar) { - Sta::sta()->reportCapacitanceLimitShortHeader(); + Sta::sta()->writeTimingModel(lib_name, cell_name, filename, scene, scalar); } +//////////////////////////////////////////////////////////////// + void -report_capacitance_limit_short(Pin *pin, - const Corner *corner, - const MinMax *min_max) +define_scene_cmd(const char *name, + const char *mode_name, + StringSeq liberty_min_files, + StringSeq liberty_max_files, + const char *spef_min_file, + const char *spef_max_file) { - Sta::sta()->reportCapacitanceLimitShort(pin, corner, min_max); + Sta *sta = Sta::sta(); + sta->makeScene(name, mode_name, + liberty_min_files, liberty_max_files, + spef_min_file, spef_max_file); } void -report_capacitance_limit_verbose(Pin *pin, - const Corner *corner, - const MinMax *min_max) +define_scenes_cmd(StringSeq scene_names) { - Sta::sta()->reportCapacitanceLimitVerbose(pin, corner, min_max); + Sta *sta = Sta::sta(); + sta->makeScenes(scene_names); } -//////////////////////////////////////////////////////////////// +Scene * +cmd_scene() +{ + return Sta::sta()->cmdScene(); +} void -write_timing_model_cmd(const char *lib_name, - const char *cell_name, - const char *filename, - const Corner *corner, - const bool scalar) +set_cmd_scene(Scene *scene) { - Sta::sta()->writeTimingModel(lib_name, cell_name, filename, corner, scalar); + Sta::sta()->setCmdScene(scene); } -//////////////////////////////////////////////////////////////// - -void -define_corners_cmd(StringSet *corner_names) +const SceneSeq +scenes() { Sta *sta = Sta::sta(); - sta->makeCorners(corner_names); - delete corner_names; + return sta->scenes(); } -Corner * -cmd_corner() +Scene * +find_scene(const char *scene_name) { - return Sta::sta()->cmdCorner(); + return Sta::sta()->findScene(scene_name); } -void -set_cmd_corner(Corner *corner) +SceneSeq +find_scenes_matching(std::string scene_name) { - Sta::sta()->setCmdCorner(corner); + return Sta::sta()->findScenes(scene_name); } -Corner * -find_corner(const char *corner_name) +SceneSeq +find_mode_scenes_matching(std::string scene_name, + ModeSeq modes) { - return Sta::sta()->findCorner(corner_name); + return Sta::sta()->findScenes(scene_name, modes); } -Corners * -corners() +bool +multi_scene() { - return Sta::sta()->corners(); + return Sta::sta()->multiScene(); } -bool -multi_corner() +ClockSeq +get_scene_clocks(SceneSeq scenes) { - return Sta::sta()->multiCorner(); + ClockSeq clks; + ModeSet modes = Scene::modeSet(scenes); + for (const Mode *mode : modes) { + for (Clock *clk : mode->sdc()->clocks()) + clks.push_back(clk); + } + return clks; +} + +//////////////////////////////////////////////////////////////// + +std::string +cmd_mode_name() +{ + return Sta::sta()->cmdMode()->name(); +} + +void +set_mode_cmd(std::string mode_name) +{ + Sta::sta()->setCmdMode(mode_name); +} + +ModeSeq +find_modes(std::string mode_name) +{ + return Sta::sta()->findModes(mode_name); } //////////////////////////////////////////////////////////////// CheckErrorSeq & check_timing_cmd(bool no_input_delay, - bool no_output_delay, - bool reg_multiple_clks, - bool reg_no_clks, - bool unconstrained_endpoints, - bool loops, - bool generated_clks) + bool no_output_delay, + bool reg_multiple_clks, + bool reg_no_clks, + bool unconstrained_endpoints, + bool loops, + bool generated_clks) { - return Sta::sta()->checkTiming(no_input_delay, no_output_delay, - reg_multiple_clks, reg_no_clks, - unconstrained_endpoints, - loops, generated_clks); + Sta *sta = Sta::sta(); + const Mode *sdc = sta->cmdMode(); + return sta->checkTiming(sdc, no_input_delay, no_output_delay, + reg_multiple_clks, reg_no_clks, + unconstrained_endpoints, + loops, generated_clks); } //////////////////////////////////////////////////////////////// PinSet find_fanin_pins(PinSeq *to, - bool flat, - bool startpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants) + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants) { Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); PinSet fanin = sta->findFaninPins(to, flat, startpoints_only, inst_levels, pin_levels, - thru_disabled, thru_constants); + thru_disabled, thru_constants, mode); + delete to; return fanin; } InstanceSet find_fanin_insts(PinSeq *to, - bool flat, - bool startpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants) + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants) { Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); InstanceSet fanin = sta->findFaninInstances(to, flat, startpoints_only, inst_levels, pin_levels, - thru_disabled, thru_constants); + thru_disabled, thru_constants, mode); + delete to; return fanin; } PinSet find_fanout_pins(PinSeq *from, - bool flat, - bool endpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants) + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants) { Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); PinSet fanout = sta->findFanoutPins(from, flat, endpoints_only, inst_levels, pin_levels, - thru_disabled, thru_constants); + thru_disabled, thru_constants, mode); + delete from; return fanout; } InstanceSet find_fanout_insts(PinSeq *from, - bool flat, - bool endpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants) + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants) { Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); InstanceSet fanout = sta->findFanoutInstances(from, flat, endpoints_only, inst_levels, pin_levels, - thru_disabled, thru_constants); + thru_disabled, thru_constants, mode); + delete from; return fanout; } InstanceSeq clock_gated_registers() { - return Sta::sta()->clockGatedRegisters(); + Sta *sta = Sta::sta(); + return sta->clockGatedRegisters(); } //////////////////////////////////////////////////////////////// @@ -1029,43 +988,41 @@ crpr_mode() } void -set_crpr_mode(const char *mode) +set_crpr_mode(std::string mode) { Sta *sta = Sta::sta(); - if (stringEq(mode, "same_pin")) - Sta::sta()->setCrprMode(CrprMode::same_pin); - else if (stringEq(mode, "same_transition")) - Sta::sta()->setCrprMode(CrprMode::same_transition); + if (stringEqual(mode, "same_pin")) + sta->setCrprMode(CrprMode::same_pin); + else if (stringEqual(mode, "same_transition")) + sta->setCrprMode(CrprMode::same_transition); else - sta->report()->critical(1573, "unknown common clk pessimism mode."); + sta->report()->error(1592, "unknown common clk pessimism mode."); } -bool -pocv_enabled() +const std::string & +pocv_mode() { - return Sta::sta()->pocvEnabled(); + return pocvModeName(Sta::sta()->pocvMode()); } void -set_pocv_enabled(bool enabled) +set_pocv_mode(const char *mode_name) { -#if !SSTA - if (enabled) - Sta::sta()->report()->error(1574, "POCV support requires compilation with SSTA=1."); -#endif - return Sta::sta()->setPocvEnabled(enabled); + Sta *sta = Sta::sta(); + PocvMode mode = findPocvMode(mode_name); + sta->setPocvMode(mode); } float -pocv_sigma_factor() +pocv_quantile() { - return Sta::sta()->sigmaFactor(); + return Sta::sta()->pocvQuantile(); } void -set_pocv_sigma_factor(float factor) +set_pocv_quantile(float quantile) { - Sta::sta()->setSigmaFactor(factor); + Sta::sta()->setPocvQuantile(quantile); } bool @@ -1116,18 +1073,6 @@ set_bidirect_inst_paths_enabled(bool enabled) Sta::sta()->setBidirectInstPathsEnabled(enabled); } -bool -bidirect_net_paths_enabled() -{ - return Sta::sta()->bidirectNetPathsEnabled(); -} - -void -set_bidirect_net_paths_enabled(bool enabled) -{ - Sta::sta()->setBidirectNetPathsEnabled(enabled); -} - bool recovery_removal_checks_enabled() { @@ -1199,7 +1144,7 @@ void levelize() { Sta *sta = Sta::sta(); - sta->levelize()->levelize(); + sta->levelize()->findLevels(); } @@ -1309,58 +1254,83 @@ Vertex *vertex() { return self->vertex(Sta::sta()); } Path *path() { return self->path(); } RiseFall *end_transition() { return const_cast(self->path()->transition(Sta::sta())); } -Slack slack() { return self->slack(Sta::sta()); } -ArcDelay margin() { return self->margin(Sta::sta()); } -Required data_required_time() { return self->requiredTimeOffset(Sta::sta()); } -Arrival data_arrival_time() { return self->dataArrivalTimeOffset(Sta::sta()); } + +float +slack() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->slack(sta), self->minMax(sta), sta); +} + +float +margin() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->margin(sta), self->minMax(sta), sta); +} + +float +data_required_time() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->requiredTimeOffset(sta), self->minMax(sta), sta); +} + +float +data_arrival_time() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->dataArrivalTimeOffset(sta), self->minMax(sta), sta); +} + +float +target_clk_delay() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->targetClkDelay(sta), self->minMax(sta), sta); +} + +float +source_clk_latency() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->sourceClkLatency(sta), self->minMax(sta), sta); +} + +float +clk_skew() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->clkSkew(sta), self->minMax(sta), sta); +} + const TimingRole *check_role() { return self->checkRole(Sta::sta()); } MinMax *min_max() { return const_cast(self->minMax(Sta::sta())); } -float source_clk_offset() { return self->sourceClkOffset(Sta::sta()); } -Arrival source_clk_latency() { return self->sourceClkLatency(Sta::sta()); } -Arrival source_clk_insertion_delay() -{ return self->sourceClkInsertionDelay(Sta::sta()); } const Clock *target_clk() { return self->targetClk(Sta::sta()); } const ClockEdge *target_clk_edge() { return self->targetClkEdge(Sta::sta()); } Path *target_clk_path() { return self->targetClkPath(); } -float target_clk_time() { return self->targetClkTime(Sta::sta()); } -float target_clk_offset() { return self->targetClkOffset(Sta::sta()); } -float target_clk_mcp_adjustment() -{ return self->targetClkMcpAdjustment(Sta::sta()); } -Arrival target_clk_delay() { return self->targetClkDelay(Sta::sta()); } -Arrival target_clk_insertion_delay() -{ return self->targetClkInsertionDelay(Sta::sta()); } -float target_clk_uncertainty() -{ return self->targetNonInterClkUncertainty(Sta::sta()); } -float inter_clk_uncertainty() -{ return self->interClkUncertainty(Sta::sta()); } -Arrival target_clk_arrival() { return self->targetClkArrival(Sta::sta()); } -bool path_delay_margin_is_external() -{ return self->pathDelayMarginIsExternal();} -Crpr check_crpr() { return self->checkCrpr(Sta::sta()); } -RiseFall *target_clk_end_trans() -{ return const_cast(self->targetClkEndTrans(Sta::sta())); } -Delay clk_skew() { return self->clkSkew(Sta::sta()); } - } %extend Path { float arrival() { - return delayAsFloat(self->arrival()); + Sta *sta = Sta::sta(); + return delayAsFloat(self->arrival(), self->minMax(sta), sta); } float required() { - return delayAsFloat(self->required()); + Sta *sta = Sta::sta(); + return delayAsFloat(self->required(), self->minMax(sta), sta); } float slack() { Sta *sta = Sta::sta(); - return delayAsFloat(self->slack(sta)); + return delayAsFloat(self->slack(sta), self->minMax(sta), sta); } const Pin * @@ -1376,7 +1346,7 @@ edge() return self->transition(Sta::sta()); } -string +std::string tag() { Sta *sta = Sta::sta(); @@ -1416,13 +1386,3 @@ next() void finish() { delete self; } } - -%extend MinPulseWidthCheckSeqIterator { -bool has_next() { return self->hasNext(); } -MinPulseWidthCheck *next() { return self->next(); } -void finish() { delete self; } -} // MinPulseWidthCheckSeqIterator methods - -%extend Corner { -const char *name() { return self->name(); } -} diff --git a/search/Search.tcl b/search/Search.tcl index 1b8fd0bed..e9dd569d1 100644 --- a/search/Search.tcl +++ b/search/Search.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -54,7 +54,7 @@ proc check_setup_cmd { cmd cmd_args } { } else { parse_key_args $cmd cmd_args keys {} \ flags {-no_input_delay -no_output_delay -multiple_clock -no_clock \ - -unconstrained_endpoints -loops -generated_clocks} + -unconstrained_endpoints -loops -generated_clocks} set no_input_delay [info exists flags(-no_input_delay)] set no_output_delay [info exists flags(-no_output_delay)] set multiple_clock [info exists flags(-multiple_clock)] @@ -65,15 +65,15 @@ proc check_setup_cmd { cmd cmd_args } { } set verbose [info exists flags(-verbose)] set errors [check_timing_cmd $no_input_delay $no_output_delay \ - $multiple_clock $no_clock \ - $unconstrained_endpoints $loops \ - $generated_clocks] + $multiple_clock $no_clock \ + $unconstrained_endpoints $loops \ + $generated_clocks] foreach error $errors { # First line is the error msg. report_line [lindex $error 0] if { $verbose } { foreach obj [lrange $error 1 end] { - report_line " $obj" + report_line " $obj" } } } @@ -98,7 +98,7 @@ define_cmd_args "find_timing_paths" \ [-to to_list|-rise_to to_list|-fall_to to_list]\ [-path_delay min|min_rise|min_fall|max|max_rise|max_fall|min_max]\ [-unconstrained] - [-corner corner]\ + [-scenes scenes]\ [-group_path_count path_count] \ [-endpoint_path_count path_count]\ [-unique_paths_to_endpoint]\ @@ -119,12 +119,12 @@ proc find_timing_paths_cmd { cmd args_var } { parse_key_args $cmd args \ keys {-from -rise_from -fall_from -to -rise_to -fall_to \ - -path_delay -corner -group_count -endpoint_count \ - -group_path_count -endpoint_path_count \ - -slack_max -slack_min -path_group} \ + -path_delay -corner -scenes -group_count -endpoint_count \ + -group_path_count -endpoint_path_count \ + -slack_max -slack_min -path_group} \ flags {-unconstrained -sort_by_slack \ - -unique_paths_to_endpoint \ - -unique_edges_to_endpoint} 0 + -unique_paths_to_endpoint \ + -unique_edges_to_endpoint} 0 set min_max "max" set end_rf "rise_fall" @@ -168,7 +168,7 @@ proc find_timing_paths_cmd { cmd args_var } { set unconstrained 0 } - set corner [parse_corner_or_all keys] + set scenes [parse_scenes_or_all keys] set endpoint_path_count 1 if { [info exists keys(-endpoint_count)] } { @@ -232,111 +232,20 @@ proc find_timing_paths_cmd { cmd args_var } { } set path_ends [find_path_ends $from $thrus $to $unconstrained \ - $corner $min_max \ - $group_path_count $endpoint_path_count \ - $unique_pins $unique_edges \ - $slack_min $slack_max \ - $sort_by_slack $groups \ - 1 1 1 1 1 1] + $scenes $min_max \ + $group_path_count $endpoint_path_count \ + $unique_pins $unique_edges \ + $slack_min $slack_max \ + $sort_by_slack $groups \ + 1 1 1 1 1 1] return $path_ends } ################################################################ -define_cmd_args "report_arrival" {pin} - -proc report_arrival { pin } { - report_delays_wrt_clks $pin "arrivals_clk_delays" -} - -proc report_delays_wrt_clks { pin_arg what } { - set pin [get_port_pin_error "pin" $pin_arg] - foreach vertex [$pin vertices] { - if { $vertex != "NULL" } { - report_delays_wrt_clk $vertex $what "NULL" "rise" - report_delays_wrt_clk $vertex $what [default_arrival_clock] "rise" - foreach clk [all_clocks] { - report_delays_wrt_clk $vertex $what $clk "rise" - report_delays_wrt_clk $vertex $what $clk "fall" - } - } - } -} - -proc report_delays_wrt_clk { vertex what clk clk_rf } { - global sta_report_default_digits - - set rise [$vertex $what rise $clk $clk_rf $sta_report_default_digits] - set fall [$vertex $what fall $clk $clk_rf $sta_report_default_digits] - # Filter INF/-INF arrivals. - if { !([delays_are_inf $rise] && [delays_are_inf $fall]) } { - set rise_fmt [format_delays $rise] - set fall_fmt [format_delays $fall] - if {$clk != "NULL"} { - set clk_str " ([get_name $clk] [rf_short_name $clk_rf])" - } else { - set clk_str "" - } - report_line "$clk_str r $rise_fmt f $fall_fmt" - } -} - -proc report_wrt_clks { pin_arg what } { - set pin [get_port_pin_error "pin" $pin_arg] - foreach vertex [$pin vertices] { - if { $vertex != "NULL" } { - report_wrt_clk $vertex $what "NULL" "rise" - report_wrt_clk $vertex $what [default_arrival_clock] "rise" - foreach clk [all_clocks] { - report_wrt_clk $vertex $what $clk "rise" - report_wrt_clk $vertex $what $clk "fall" - } - } - } -} - -proc report_wrt_clk { vertex what clk clk_rf } { - global sta_report_default_digits - - set rise [$vertex $what rise $clk $clk_rf] - set fall [$vertex $what fall $clk $clk_rf] - # Filter INF/-INF arrivals. - if { !([times_are_inf $rise] && [times_are_inf $fall]) } { - set rise_fmt [format_times $rise $sta_report_default_digits] - set fall_fmt [format_times $fall $sta_report_default_digits] - if {$clk != "NULL"} { - set clk_str " ([get_name $clk] [rf_short_name $clk_rf])" - } else { - set clk_str "" - } - report_line "$clk_str r $rise_fmt f $fall_fmt" - } -} - -proc times_are_inf { times } { - foreach time $times { - if { $time < 1e+10 && $time > -1e+10 } { - return 0 - } - } - return 1 -} - -proc delays_are_inf { delays } { - foreach delay $delays { - if { !([string match "INF*" $delay] \ - || [string match "-INF*" $delay]) } { - return 0 - } - } - return 1 -} - -################################################################ - define_cmd_args "report_clock_skew" {[-setup|-hold]\ - [-clock clocks]\ - [-corner corner]\ + [-clocks clocks]\ + [-scenes scenes]\ [-include_internal_latency] [-digits digits]} @@ -344,7 +253,7 @@ proc_redirect report_clock_skew { global sta_report_default_digits parse_key_args "report_clock_skew" args \ - keys {-clock -corner -digits} \ + keys {-clocks -corner -scenes -digits} \ flags {-setup -hold -include_internal_latency} check_argc_eq0 "report_clock_skew" $args @@ -358,12 +267,13 @@ proc_redirect report_clock_skew { set setup_hold "setup" } - if [info exists keys(-clock)] { - set clks [get_clocks_warn "-clocks" $keys(-clock)] + set scenes [parse_scenes_or_all keys] + if [info exists keys(-clocks)] { + puts "clks1 = [get_object_names $clks]" } else { - set clks [all_clocks] + set clks [get_scene_clocks $scenes] } - set corner [parse_corner_or_all keys] + set include_internal_latency [info exists flags(-include_internal_latency)] if [info exists keys(-digits)] { set digits $keys(-digits) @@ -372,14 +282,14 @@ proc_redirect report_clock_skew { set digits $sta_report_default_digits } if { $clks != {} } { - report_clk_skew $clks $corner $setup_hold $include_internal_latency $digits + report_clk_skew $clks $scenes $setup_hold $include_internal_latency $digits } } ################################################################ -define_cmd_args "report_clock_latency" {[-clock clocks]\ - [-corner corner]\ +define_cmd_args "report_clock_latency" {[-clocks clocks]\ + [-scenes scene]\ [-include_internal_latency] [-digits digits]} @@ -387,16 +297,16 @@ proc_redirect report_clock_latency { global sta_report_default_digits parse_key_args "report_clock_" args \ - keys {-clock -corner -digits} \ + keys {-clocks -scenes -digits} \ flags {-include_internal_latency} check_argc_eq0 "report_clock_latency" $args - if [info exists keys(-clock)] { - set clks [get_clocks_warn "-clocks" $keys(-clock)] + set scenes [parse_scenes_or_all keys] + if [info exists keys(-clocks)] { + set clks [get_clocks_warn "-clocks" $keys(-clocks)] } else { - set clks [all_clocks] + set clks [get_scene_clocks $scenes] } - set corner [parse_corner_or_all keys] set include_internal_latency [info exists flags(-include_internal_latency)] if [info exists keys(-digits)] { set digits $keys(-digits) @@ -405,7 +315,7 @@ proc_redirect report_clock_latency { set digits $sta_report_default_digits } if { $clks != {} } { - report_clk_latency $clks $corner $include_internal_latency $digits + report_clk_latency $clks $scenes $include_internal_latency $digits } } @@ -417,7 +327,7 @@ define_cmd_args "report_checks" \ [-to to_list|-rise_to to_list|-fall_to to_list]\ [-unconstrained]\ [-path_delay min|min_rise|min_fall|max|max_rise|max_fall|min_max]\ - [-corner corner]\ + [-scenes scenes]\ [-group_path_count path_count] \ [-endpoint_path_count path_count]\ [-unique_paths_to_endpoint]\ @@ -444,8 +354,7 @@ proc_redirect report_checks { ################################################################ define_cmd_args "report_check_types" \ - {[-violators] [-verbose]\ - [-corner corner]\ + {[-scenes scenes] [-violators] [-verbose]\ [-format slack_only|end]\ [-max_delay] [-min_delay]\ [-recovery] [-removal]\ @@ -455,13 +364,17 @@ define_cmd_args "report_check_types" \ [-max_capacitance] [-min_capacitance]\ [-min_pulse_width] [-min_period] [-max_skew]\ [-net net]\ + [-max_count max_count]\ [-digits digits] [-no_line_splits]\ [> filename] [>> filename]} proc_redirect report_check_types { + variable float_inf + variable group_path_count_max variable path_options - parse_key_args "report_check_types" args keys {-net -corner}\ + parse_key_args "report_check_types" args \ + keys {-scenes -corner -net -max_count}\ flags {-violators -verbose -no_line_splits} 0 set violators [info exists flags(-violators)] @@ -480,13 +393,17 @@ proc_redirect report_check_types { set min_max "max" } - set corner [parse_corner_or_all keys] - set net "NULL" if { [info exists keys(-net)] } { set net [get_net_arg "-net" $keys(-net)] } + set max_count 1 + if { [info exists keys(-max_count)] } { + set max_count $keys(-max_count) + check_positive_integer "-max_count" $max_count + } + if { $args == {} } { if { $min_max == "max" || $min_max == "min_max" } { set setup 1 @@ -525,12 +442,12 @@ proc_redirect report_check_types { } else { parse_key_args "report_check_types" args keys {} \ flags {-max_delay -min_delay -recovery -removal \ - -clock_gating_setup -clock_gating_hold \ - -max_slew -min_slew \ - -max_fanout -min_fanout \ - -max_capacitance -min_capacitance \ - -min_pulse_width \ - -min_period -max_skew} 1 + -clock_gating_setup -clock_gating_hold \ + -max_slew -min_slew \ + -max_fanout -min_fanout \ + -max_capacitance -min_capacitance \ + -min_pulse_width \ + -min_period -max_skew} 1 set setup [info exists flags(-max_delay)] set hold [info exists flags(-min_delay)] @@ -548,24 +465,23 @@ proc_redirect report_check_types { set min_period [info exists flags(-min_period)] set max_skew [info exists flags(-max_skew)] if { [operating_condition_analysis_type] == "single" \ - && (($setup && $hold) \ - || ($recovery && $removal) \ - || ($clk_gating_setup && $clk_gating_hold)) } { + && (($setup && $hold) \ + || ($recovery && $removal) \ + || ($clk_gating_setup && $clk_gating_hold)) } { sta_error 520 "analysis type single is not consistent with doing both setup/max and hold/min checks." } } + set scenes [parse_scenes_or_all keys] if { $args != {} } { sta_error 521 "positional arguments not supported." } - set corner [parse_corner_or_all keys] - if { $setup || $hold || $recovery || $removal \ - || $clk_gating_setup || $clk_gating_hold } { + || $clk_gating_setup || $clk_gating_hold } { if { ($setup && $hold) \ - || ($recovery && $removal) \ - || ($clk_gating_setup && $clk_gating_hold) } { + || ($recovery && $removal) \ + || ($clk_gating_setup && $clk_gating_hold) } { set path_min_max "min_max" } elseif { $setup || $recovery || $clk_gating_setup } { set path_min_max "max" @@ -573,134 +489,51 @@ proc_redirect report_check_types { set path_min_max "min" } if { $violators } { - set group_path_count $sta::group_path_count_max - set slack_min [expr -$sta::float_inf] + set group_path_count $group_path_count_max + set slack_min [expr -$float_inf] set slack_max 0.0 } else { set group_path_count 1 - set slack_min [expr -$sta::float_inf] - set slack_max $sta::float_inf + set slack_min [expr -$float_inf] + set slack_max $float_inf } + set path_ends [find_path_ends "NULL" {} "NULL" 0 \ - $corner $path_min_max $group_path_count 1 1 0 \ - $slack_min $slack_max \ - 0 {} \ - $setup $hold \ - $recovery $removal \ - $clk_gating_setup $clk_gating_hold] + $scenes $path_min_max $group_path_count 1 1 0 \ + $slack_min $slack_max \ + 0 {} \ + $setup $hold \ + $recovery $removal \ + $clk_gating_setup $clk_gating_hold] report_path_ends $path_ends } if { $max_slew } { - report_slew_limits $net $corner "max" $violators $verbose $nosplit + report_slew_checks $net $max_count $violators $verbose $scenes "max" } if { $min_slew } { - report_slew_limits $net $corner "min" $violators $verbose $nosplit + report_slew_checks $net $max_count $violators $verbose $scenes "min" } if { $max_fanout } { - report_fanout_limits $net "max" $violators $verbose $nosplit + report_fanout_checks $net $max_count $violators $verbose $scenes "max" } if { $min_fanout } { - report_fanout_limits $net "min" $violators $verbose $nosplit + report_fanout_checks $net $max_count $violators $verbose $scenes "min" } if { $max_capacitance } { - report_capacitance_limits $net $corner "max" $violators $verbose $nosplit + report_capacitance_checks $net $max_count $violators $verbose $scenes "max" } if { $min_capacitance } { - report_capacitance_limits $net $corner "min" $violators $verbose $nosplit + report_capacitance_checks $net $max_count $violators $verbose $scenes "min" } if { $min_pulse_width } { - if { $violators } { - set checks [min_pulse_width_violations $corner] - report_mpw_checks $checks $verbose - } else { - set check [min_pulse_width_check_slack $corner] - if { $check != "NULL" } { - report_mpw_check $check $verbose - } - } + report_min_pulse_width_checks $net $max_count $violators $verbose $scenes } if { $min_period } { - if { $violators } { - set checks [min_period_violations] - report_min_period_checks $checks $verbose - } else { - set check [min_period_check_slack] - if { $check != "NULL" } { - report_min_period_check $check $verbose - } - } + report_min_period_checks $net $max_count $violators $verbose $scenes } if { $max_skew } { - if { $violators } { - set checks [max_skew_violations] - report_max_skew_checks $checks $verbose - } else { - set check [max_skew_check_slack] - if { $check != "NULL" } { - report_max_skew_check $check $verbose - } - } - } -} - -proc report_slew_limits { net corner min_max violators verbose nosplit } { - set pins [check_slew_limits $net $violators $corner $min_max] - if { $pins != {} } { - report_line "${min_max} slew" - report_line "" - if { $verbose } { - foreach pin $pins { - report_slew_limit_verbose $pin $corner $min_max - report_line "" - } - } else { - report_slew_limit_short_header - foreach pin $pins { - report_slew_limit_short $pin $corner $min_max - } - report_line "" - } - } -} - -proc report_fanout_limits { net min_max violators verbose nosplit } { - set pins [check_fanout_limits $net $violators $min_max] - if { $pins != {} } { - report_line "${min_max} fanout" - report_line "" - if { $verbose } { - foreach pin $pins { - report_fanout_limit_verbose $pin $min_max - report_line "" - } - } else { - report_fanout_limit_short_header - foreach pin $pins { - report_fanout_limit_short $pin $min_max - } - report_line "" - } - } -} - -proc report_capacitance_limits { net corner min_max violators verbose nosplit } { - set pins [check_capacitance_limits $net $violators $corner $min_max] - if { $pins != {} } { - report_line "${min_max} capacitance" - report_line "" - if { $verbose } { - foreach pin $pins { - report_capacitance_limit_verbose $pin $corner $min_max - report_line "" - } - } else { - report_capacitance_limit_short_header - foreach pin $pins { - report_capacitance_limit_short $pin $corner $min_max - } - report_line "" - } + report_max_skew_checks $net $max_count $violators $verbose $scenes } } @@ -783,33 +616,6 @@ proc_redirect report_worst_slack { ################################################################ -define_cmd_args "report_pulse_width_checks" \ - {[-verbose] [-corner corner] [-digits digits] [-no_line_splits] [pins]\ - [> filename] [>> filename]} - -proc_redirect report_pulse_width_checks { - variable path_options - - parse_key_args "report_pulse_width_checks" args keys {-corner} \ - flags {-verbose} 0 - # Only -digits and -no_line_splits are respected. - parse_report_path_options "report_pulse_width_checks" args "full" 0 - check_argc_eq0or1 "report_pulse_width_checks" $args - set corner [parse_corner_or_all keys] - set verbose [info exists flags(-verbose)] - if { [llength $args] == 1 } { - set pins [get_port_pins_error "pins" [lindex $args 0]] - set checks [min_pulse_width_check_pins $pins $corner] - } else { - set checks [min_pulse_width_checks $corner] - } - if { $checks != {} } { - report_mpw_checks $checks $verbose - } -} - -################################################################ - # Note that -all and -tags are intentionally "hidden". define_cmd_args "report_path" \ {[-min|-max]\ @@ -848,41 +654,40 @@ proc_redirect report_path { } else { foreach vertex [$pin vertices] { if { $vertex != "NULL" } { - if { $report_all } { - set first 1 - set path_iter [$vertex path_iterator $rf $min_max] - while {[$path_iter has_next]} { - set path [$path_iter next] - if { $first } { - report_line "Tag group: [$vertex tag_group_index]" - } else { - report_line "" - } - if { $report_tags } { - report_line "Tag: [$path tag]" - } - report_path_cmd $path - set first 0 - } - $path_iter finish - } else { - set worst_path [vertex_worst_arrival_path_rf $vertex $rf $min_max] - if { $worst_path != "NULL" } { - if { $report_tags } { - report_line "Tag: [$worst_path tag]" - } - report_path_cmd $worst_path - } - } + if { $report_all } { + set first 1 + set path_iter [$vertex path_iterator $rf $min_max] + while {[$path_iter has_next]} { + set path [$path_iter next] + if { $first } { + report_line "Tag group: [$vertex tag_group_index]" + } else { + report_line "" + } + if { $report_tags } { + report_line "Tag: [$path tag]" + } + report_path_cmd $path + set first 0 + } + $path_iter finish + } else { + set worst_path [vertex_worst_arrival_path_rf $vertex $rf $min_max] + if { $worst_path != "NULL" } { + if { $report_tags } { + report_line "Tag: [$worst_path tag]" + } + report_path_cmd $worst_path + } + } } } } } proc parse_report_path_options { cmd args_var default_format - unknown_key_is_error } { + unknown_key_is_error } { variable path_options - variable report_path_field_width_extra global sta_report_default_digits upvar 1 $args_var args @@ -896,7 +701,7 @@ proc parse_report_path_options { cmd args_var default_format if [info exists path_options(-format)] { set format $path_options(-format) set formats {full full_clock full_clock_expanded short \ - end slack_only summary json} + end slack_only summary json} if { [lsearch $formats $format] == -1 } { sta_error 524 "-format $format not recognized." } @@ -911,24 +716,8 @@ proc parse_report_path_options { cmd args_var default_format check_positive_integer "-digits" $digits } - set report_sigmas [info exists path_options(-report_sigmas)] - set_report_path_sigmas $report_sigmas - set path_options(num_fmt) "%.${digits}f" set_report_path_digits $digits - # Numeric field width expands with digits. - set field_width [expr $digits + $report_path_field_width_extra] - if { $report_sigmas } { - set delay_field_width [expr $field_width * 3 + $report_path_field_width_extra] - } else { - set delay_field_width $field_width - } - foreach field {total incr} { - set_report_path_field_width $field $delay_field_width - } - foreach field {capacitance slew} { - set_report_path_field_width $field $field_width - } set report_input_pin 0 set report_hier_pins 0 @@ -936,6 +725,7 @@ proc parse_report_path_options { cmd args_var default_format set report_net 0 set report_slew 0 set report_fanout 0 + set report_variation 0 set report_src_attr 0 if { [info exists path_options(-fields)] } { foreach field $path_options(-fields) { @@ -951,6 +741,8 @@ proc parse_report_path_options { cmd args_var default_format set report_slew 1 } elseif { [string match "fanout" $field] } { set report_fanout 1 + } elseif { [string match "variation" $field] } { + set report_variation 1 } elseif { [string match "src*" $field] } { set report_src_attr 1 } else { @@ -958,8 +750,9 @@ proc parse_report_path_options { cmd args_var default_format } } } + set_report_path_fields $report_input_pin $report_hier_pins $report_net \ - $report_cap $report_slew $report_fanout $report_src_attr + $report_cap $report_slew $report_fanout $report_variation $report_src_attr set_report_path_no_split [info exists path_options(-no_line_splits)] @@ -978,18 +771,68 @@ proc parse_report_path_options { cmd args_var default_format ################################################################ -define_cmd_args "report_required" {pin} +define_cmd_args "report_arrival" {[-scene scene] [-report_variance] [-digits digits] pin} + +proc report_arrival { args } { + global sta_report_default_digits -proc report_required { pin } { - report_delays_wrt_clks $pin "requireds_clk_delays" + parse_key_args "report_arrival" args keys {-scene -digits} flags {-report_variance} + check_argc_eq1 "report_arrival" $args + + set pin [get_port_pin_error "pin" [lindex $args 0]] + set scene [parse_scene keys] + if [info exists keys(-digits)] { + set digits $keys(-digits) + check_positive_integer "-digits" $digits + } else { + set digits $sta_report_default_digits + } + set report_variance [info exists flags(-report_variance)] + report_arrival_wrt_clks $pin $scene $report_variance $digits } ################################################################ -define_cmd_args "report_slack" {pin} +define_cmd_args "report_required" {[-scene scene] [-report_variance] [-digits digits] pin} + +proc report_required { args } { + global sta_report_default_digits + + parse_key_args "report_required" args keys {-scene -digits} flags {-report_variance} + check_argc_eq1 "report_required" $args -proc report_slack { pin } { - report_delays_wrt_clks $pin "slacks_clk_delays" + set pin [get_port_pin_error "pin" [lindex $args 0]] + set scene [parse_scene keys] + if [info exists keys(-digits)] { + set digits $keys(-digits) + check_positive_integer "-digits" $digits + } else { + set digits $sta_report_default_digits + } + set report_variance [info exists flags(-report_variance)] + report_required_wrt_clks $pin $scene $report_variance $digits +} + +################################################################ + +define_cmd_args "report_slack" {[-scene scene] [-report_variance] [-digits digits] pin} + +proc report_slack { args } { + global sta_report_default_digits + + parse_key_args "report_slack" args keys {-scene -digits} flags {-report_variance} + check_argc_eq1 "report_slack" $args + + set pin [get_port_pin_error "pin" [lindex $args 0]] + set scene [parse_scene keys] + if [info exists keys(-digits)] { + set digits $keys(-digits) + check_positive_integer "-digits" $digits + } else { + set digits $sta_report_default_digits + } + set report_variance [info exists flags(-report_variance)] + report_slack_wrt_clks $pin $scene $report_variance $digits } ################################################################ @@ -1005,16 +848,17 @@ proc report_tag_arrivals { pin } { ################################################################ define_hidden_cmd_args "total_negative_slack" \ - {[-corner corner] [-min]|[-max]} + {[-scene scene] [-min]|[-max]} proc total_negative_slack { args } { parse_key_args "total_negative_slack" args \ - keys {-corner} flags {-min -max} + keys {-scene -corner} flags {-min -max} check_argc_eq0 "total_negative_slack" $args set min_max [parse_min_max_flags flags] - if { [info exists keys(-corner)] } { - set corner [parse_corner_required keys] - set tns [total_negative_slack_corner_cmd $corner $min_max] + # compabibility 05/29/2025 + if { [info exists keys(-scene)] || [info exists keys(-corner)]} { + set scene [parse_scene_required keys] + set tns [total_negative_slack_scene_cmd $scene $min_max] } else { set tns [total_negative_slack_cmd $min_max] } @@ -1024,7 +868,7 @@ proc total_negative_slack { args } { ################################################################ define_hidden_cmd_args "worst_negative_slack" \ - {[-corner corner] [-min]|[-max]} + {[-scene scene] [-min]|[-max]} proc worst_negative_slack { args } { set worst_slack [worst_slack1 "worst_negative_slack" $args] @@ -1038,7 +882,7 @@ proc worst_negative_slack { args } { ################################################################ define_hidden_cmd_args "worst_slack" \ - {[-corner corner] [-min]|[-max]} + {[-scene scene] [-min]|[-max]} proc worst_slack { args } { return [worst_slack1 "worst_slack" $args] @@ -1047,12 +891,13 @@ proc worst_slack { args } { # arg parsing common to worst_slack/worst_negative_slack proc worst_slack1 { cmd args1 } { parse_key_args $cmd args1 \ - keys {-corner} flags {-min -max} + keys {-corner -scene} flags {-min -max} check_argc_eq0 $cmd $args1 set min_max [parse_min_max_flags flags] - if { [info exists keys(-corner)] } { - set corner [parse_corner_required keys] - set worst_slack [worst_slack_corner $corner $min_max] + # compabibility 05/29/2025 + if { [info exists keys(-scene)] || [info exists keys(-corner)]} { + set scene [parse_scene_required keys] + set worst_slack [worst_slack_scene $scene $min_max] } else { set worst_slack [worst_slack_cmd $min_max] } @@ -1082,15 +927,15 @@ proc worst_clock_skew { args } { ################################################################ -define_cmd_args "write_timing_model" {[-scalar] \ - [-corner corner] \ +define_cmd_args "write_timing_model" {[-scalar] + [-scene scene] \ [-library_name lib_name]\ [-cell_name cell_name]\ filename} proc write_timing_model { args } { parse_key_args "write_timing_model" args \ - keys {-library_name -cell_name -corner} flags {-scalar} + keys {-library_name -cell_name -scene} flags {-scalar} check_argc_eq1 "write_timing_model" $args set filename [file nativename [lindex $args 0]] @@ -1105,8 +950,8 @@ proc write_timing_model { args } { } else { set lib_name $cell_name } - set corner [parse_corner keys] - write_timing_model_cmd $lib_name $cell_name $filename $corner $scalar + set scene [parse_scene keys] + write_timing_model_cmd $lib_name $cell_name $filename $scene $scalar } ################################################################ @@ -1144,7 +989,7 @@ proc_redirect report_clock_min_period { set include_port_paths [info exists flags(-include_port_paths)] foreach clk $clks { - set min_period [sta::find_clk_min_period $clk $include_port_paths] + set min_period [find_clk_min_period $clk $include_port_paths] if { $min_period == 0.0 } { set min_period 0 set fmax "INF" @@ -1152,7 +997,7 @@ proc_redirect report_clock_min_period { # max frequency in MHz set fmax [expr 1.0e-6 / $min_period] } - report_line "[get_name $clk] period_min = [sta::format_time $min_period 2] fmax = [format %.2f $fmax]" + report_line "[get_name $clk] period_min = [format_time $min_period 2] fmax = [format %.2f $fmax]" } } @@ -1240,17 +1085,17 @@ proc unset_disable_inferred_clock_gating_cmd { objects } { # max slew slack / limit proc max_slew_check_slack_limit {} { - return [expr "[sta::max_slew_check_slack] / [sta::max_slew_check_limit]"] + return [expr "[max_slew_check_slack] / [max_slew_check_limit]"] } # max cap slack / limit proc max_capacitance_check_slack_limit {} { - return [expr [sta::max_capacitance_check_slack] / [sta::max_capacitance_check_limit]] + return [expr [max_capacitance_check_slack] / [max_capacitance_check_limit]] } # max fanout slack / limit proc max_fanout_check_slack_limit {} { - return [expr [sta::max_fanout_check_slack] / [sta::max_fanout_check_limit]] + return [expr [max_fanout_check_slack] / [max_fanout_check_limit]] } ################################################################ diff --git a/search/SearchPred.cc b/search/SearchPred.cc index 9cb0401ea..772793742 100644 --- a/search/SearchPred.cc +++ b/search/SearchPred.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,192 +24,233 @@ #include "SearchPred.hh" -#include "TimingArc.hh" -#include "TimingRole.hh" +#include "Graph.hh" +#include "Latches.hh" +#include "Levelize.hh" #include "Liberty.hh" +#include "Mode.hh" #include "Network.hh" -#include "Graph.hh" #include "Sdc.hh" -#include "Levelize.hh" #include "Search.hh" -#include "Latches.hh" +#include "Sim.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" #include "Variables.hh" namespace sta { static bool -searchThruSimEdge(const Vertex *vertex, const RiseFall *rf); +searchThruSimEdge(const Vertex *vertex, + const RiseFall *rf, + const Mode *mode); static bool -searchThruTimingSense(const Edge *edge, const RiseFall *from_rf, - const RiseFall *to_rf); +searchThruTimingSense(const Edge *edge, + const RiseFall *from_rf, + const RiseFall *to_rf, + const Mode *mode); -SearchPred0::SearchPred0(const StaState *sta) : +SearchPred::SearchPred(const StaState *sta) : sta_(sta) { } +void +SearchPred::copyState(const StaState *sta) +{ + sta_ = sta; +} + bool -SearchPred0::searchFrom(const Vertex *from_vertex) +SearchPred::searchFrom(const Vertex *from_vertex) const { - return !(from_vertex->isDisabledConstraint() - || from_vertex->isConstant()); + for (const Mode *mode : sta_->modes()) { + if (searchFrom(from_vertex, mode)) + return true; + } + return false; } bool -SearchPred0::searchThru(Edge *edge) +SearchPred::searchThru(Edge *edge) const { - const TimingRole *role = edge->role(); - const Sdc *sdc = sta_->sdc(); - const Variables *variables = sta_->variables(); - return !(edge->isDisabledConstraint() - // Constants disable edge cond expression. - || edge->isDisabledCond() - || sdc->isDisabledCondDefault(edge) - // Register/latch preset/clr edges are disabled by default. - || (role == TimingRole::regSetClr() - && !variables->presetClrArcsEnabled()) - // Constants on other pins disable this edge (ie, a mux select). - || edge->simTimingSense() == TimingSense::none - || (edge->isBidirectInstPath() - && !variables->bidirectInstPathsEnabled()) - || (edge->isBidirectNetPath() - && !variables->bidirectNetPathsEnabled()) - || (role == TimingRole::latchDtoQ() - && sta_->latches()->latchDtoQState(edge) - == LatchEnableState::closed)); + for (const Mode *mode : sta_->modes()) { + if (searchThru(edge, mode)) + return true; + } + return false; } bool -SearchPred0::searchTo(const Vertex *to_vertex) +SearchPred::searchTo(const Vertex *to_vertex) const { - return !to_vertex->isConstant(); + for (const Mode *mode : sta_->modes()) { + if (searchTo(to_vertex, mode)) + return true; + } + return false; } //////////////////////////////////////////////////////////////// -SearchPred1::SearchPred1(const StaState *sta) : - SearchPred0(sta) +SearchPred0::SearchPred0(const StaState *sta) : + SearchPred(sta) { } bool -SearchPred1::searchThru(Edge *edge) +SearchPred0::searchFrom(const Vertex *from_vertex, + const Mode *mode) const { - return SearchPred0::searchThru(edge) - && !edge->isDisabledLoop(); + const Pin *from_pin = from_vertex->pin(); + const Sdc *sdc = mode->sdc(); + const Sim *sim = mode->sim(); + return !(sdc->isDisabledConstraint(from_pin) + || sim->isConstant(from_vertex)); } -//////////////////////////////////////////////////////////////// - -SearchPred2::SearchPred2(const StaState *sta) : - SearchPred1(sta) +bool +SearchPred0::searchThru(Edge *edge, + const Mode *mode) const { + const TimingRole *role = edge->role(); + const Variables *variables = sta_->variables(); + const Sdc *sdc = mode->sdc(); + const Sim *sim = mode->sim(); + return !(role->isTimingCheck() + || sdc->isDisabledConstraint(edge) + // Constants disable edge cond expression. + || sim->isDisabledCond(edge) + || sdc->isDisabledCondDefault(edge) + // Register/latch preset/clr edges are disabled by default. + || (role == TimingRole::regSetClr() + && !variables->presetClrArcsEnabled()) + // Constants on other pins disable this edge (ie, a mux select). + || sim->simTimingSense(edge) == TimingSense::none + || (edge->isBidirectInstPath() + && !variables->bidirectInstPathsEnabled()) + || (role == TimingRole::latchDtoQ() + && sta_->latches()->latchDtoQState(edge, mode) + == LatchEnableState::closed)); } bool -SearchPred2::searchThru(Edge *edge) +SearchPred0::searchTo(const Vertex *to_vertex, + const Mode *mode) const { - return SearchPred1::searchThru(edge) - && !edge->role()->isTimingCheck(); + return !mode->sim()->isConstant(to_vertex); } //////////////////////////////////////////////////////////////// -SearchPredNonLatch2::SearchPredNonLatch2(const StaState *sta) : - SearchPred2(sta) +SearchPred1::SearchPred1(const StaState *sta) : + SearchPred0(sta) { } bool -SearchPredNonLatch2::searchThru(Edge *edge) +SearchPred1::searchThru(Edge *edge, + const Mode *mode) const { - return SearchPred2::searchThru(edge) - && !sta_->latches()->isLatchDtoQ(edge); + return SearchPred0::searchThru(edge, mode) + && !edge->isDisabledLoop(); } //////////////////////////////////////////////////////////////// -SearchPredNonReg2::SearchPredNonReg2(const StaState *sta) : - SearchPred2(sta) +ClkTreeSearchPred::ClkTreeSearchPred(const StaState *sta) : + SearchPred(sta) { } bool -SearchPredNonReg2::searchThru(Edge *edge) +ClkTreeSearchPred::searchFrom(const Vertex *from_vertex, + const Mode *mode) const { - const TimingRole *role = edge->role(); - return SearchPred2::searchThru(edge) - // Enqueue thru latches is handled explicitly by search. - && !sta_->latches()->isLatchDtoQ(edge) - && role->genericRole() != TimingRole::regClkToQ(); + const Pin *from_pin = from_vertex->pin(); + const Sdc *sdc = mode->sdc(); + return !sdc->isDisabledConstraint(from_pin); } -//////////////////////////////////////////////////////////////// - -ClkTreeSearchPred::ClkTreeSearchPred(const StaState *sta) : - SearchPred1(sta) +bool +ClkTreeSearchPred::searchThru(Edge *edge, + const Mode *mode) const { + const TimingRole *role = edge->role(); + const Sdc *sdc = mode->sdc(); + return searchThruAllow(role) + && !((role == TimingRole::tristateEnable() + && !sta_->variables()->clkThruTristateEnabled()) + || role == TimingRole::regSetClr() + || sdc->isDisabledConstraint(edge) + || sdc->isDisabledCondDefault(edge) + || edge->isBidirectInstPath() + || edge->isDisabledLoop()); } bool -ClkTreeSearchPred::searchThru(Edge *edge) +ClkTreeSearchPred::searchThruAllow(const TimingRole *role) const { - // Propagate clocks through constants. - const TimingRole *role = edge->role(); - return (role->isWire() - || role == TimingRole::combinational()) - && (sta_->variables()->clkThruTristateEnabled() - || !(role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable())) - && SearchPred1::searchThru(edge); + return role->isWire() + || role == TimingRole::combinational(); } bool isClkEnd(Vertex *vertex, - Graph *graph) + const Mode *mode) { + Graph *graph = mode->sta()->graph(); ClkTreeSearchPred pred(graph); VertexOutEdgeIterator edge_iter(vertex, graph); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); - if (pred.searchThru(edge)) + if (pred.searchThru(edge, mode)) return false; } return true; } +bool +ClkTreeSearchPred::searchTo(const Vertex *, + const Mode *) const +{ + return true; +} + //////////////////////////////////////////////////////////////// bool searchThru(const Edge *edge, - const TimingArc *arc, - const Graph *graph) + const TimingArc *arc, + const Mode *mode) { + const Graph *graph = mode->sta()->graph(); const RiseFall *from_rf = arc->fromEdge()->asRiseFall(); const RiseFall *to_rf = arc->toEdge()->asRiseFall(); // Ignore transitions other than rise/fall. return from_rf && to_rf - && searchThru(edge->from(graph), from_rf, edge, edge->to(graph), to_rf); + && searchThru(edge->from(graph), from_rf, edge, edge->to(graph), to_rf, mode); } bool searchThru(Vertex *from_vertex, - const RiseFall *from_rf, - const Edge *edge, - Vertex *to_vertex, - const RiseFall *to_rf) + const RiseFall *from_rf, + const Edge *edge, + Vertex *to_vertex, + const RiseFall *to_rf, + const Mode *mode) { - return searchThruTimingSense(edge, from_rf, to_rf) - && searchThruSimEdge(from_vertex, from_rf) - && searchThruSimEdge(to_vertex, to_rf); + return searchThruTimingSense(edge, from_rf, to_rf, mode) + && searchThruSimEdge(from_vertex, from_rf, mode) + && searchThruSimEdge(to_vertex, to_rf, mode); } // set_case_analysis rising/falling filters rise/fall edges during search. static bool searchThruSimEdge(const Vertex *vertex, - const RiseFall *rf) + const RiseFall *rf, + const Mode *mode) { - LogicValue sim_value = vertex->simValue(); + LogicValue sim_value = mode->sim()->simValue(vertex->pin()); switch (sim_value) { case LogicValue::rise: return rf == RiseFall::rise(); @@ -221,10 +262,12 @@ searchThruSimEdge(const Vertex *vertex, } static bool -searchThruTimingSense(const Edge *edge, const RiseFall *from_rf, - const RiseFall *to_rf) +searchThruTimingSense(const Edge *edge, + const RiseFall *from_rf, + const RiseFall *to_rf, + const Mode *mode) { - switch (edge->simTimingSense()) { + switch (mode->sim()->simTimingSense(edge)) { case TimingSense::unknown: return true; case TimingSense::positive_unate: @@ -244,17 +287,18 @@ searchThruTimingSense(const Edge *edge, const RiseFall *from_rf, bool hasFanin(Vertex *vertex, - SearchPred *pred, - const Graph *graph) + SearchPred *pred, + const Graph *graph, + const Mode *mode) { - if (pred->searchTo(vertex)) { + if (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 (pred->searchFrom(from_vertex) - && pred->searchThru(edge)) - return true; + if (pred->searchFrom(from_vertex, mode) + && pred->searchThru(edge, mode)) + return true; } } return false; @@ -262,20 +306,21 @@ hasFanin(Vertex *vertex, bool hasFanout(Vertex *vertex, - SearchPred *pred, - const Graph *graph) + SearchPred *pred, + const Graph *graph, + const Mode *mode) { - if (pred->searchFrom(vertex)) { + if (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 (pred->searchTo(to_vertex) - && pred->searchThru(edge)) - return true; + if (pred->searchTo(to_vertex, mode) + && pred->searchThru(edge, mode)) + return true; } } return false; } -} // namespace +} // namespace sta diff --git a/search/Sim.cc b/search/Sim.cc index 959b088bb..58f9adeac 100644 --- a/search/Sim.cc +++ b/search/Sim.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Sim.hh" @@ -27,6 +27,7 @@ // https://davidkebo.com/cudd #include "cudd.h" +#include "ContainerHelpers.hh" #include "Error.hh" #include "Mutex.hh" #include "Debug.hh" @@ -41,6 +42,7 @@ #include "Network.hh" #include "Sdc.hh" #include "Graph.hh" +#include "Mode.hh" namespace sta { @@ -48,37 +50,42 @@ static LogicValue logicNot(LogicValue value); static const Pin * findDrvrPin(const Pin *pin, - Network *network); + Network *network); Sim::Sim(StaState *sta) : StaState(sta), - observer_(nullptr), - valid_(false), - incremental_(false), const_func_pins_(network_), - const_func_pins_valid_(false), invalid_insts_(network_), invalid_drvr_pins_(network_), invalid_load_pins_(network_), - instances_with_const_pins_(network_), instances_to_annotate_(network_), bdd_(sta) { } -Sim::~Sim() +Sim::~Sim() { delete observer_; } + +void +Sim::copyState(const StaState *sta) { - delete observer_; + StaState::copyState(sta); + // Notify sub-components. + observer_->copyState(sta); +} + +void +Sim::setMode(Mode *mode) +{ + mode_ = mode; } TimingSense Sim::functionSense(const FuncExpr *expr, - const Pin *input_pin, - const Instance *inst) + const Pin *input_pin, + const Instance *inst) { - debugPrint(debug_, "sim", 4, "find sense pin %s %s", - network_->pathName(input_pin), - expr->to_string().c_str()); + debugPrint(debug_, "sim", 4, "find sense pin {} {}", network_->pathName(input_pin), + expr->to_string()); bool increasing, decreasing; { LockGuard lock(bdd_lock_); @@ -87,10 +94,10 @@ Sim::functionSense(const FuncExpr *expr, LibertyPort *input_port = network_->libertyPort(input_pin); DdNode *input_node = bdd_.ensureNode(input_port); unsigned int input_index = Cudd_NodeReadIndex(input_node); - increasing = (Cudd_Increasing(cudd_mgr, bdd, input_index) - == Cudd_ReadOne(cudd_mgr)); - decreasing = (Cudd_Decreasing(cudd_mgr, bdd, input_index) - == Cudd_ReadOne(cudd_mgr)); + increasing = + (Cudd_Increasing(cudd_mgr, bdd, input_index) == Cudd_ReadOne(cudd_mgr)); + decreasing = + (Cudd_Decreasing(cudd_mgr, bdd, input_index) == Cudd_ReadOne(cudd_mgr)); Cudd_RecursiveDeref(cudd_mgr, bdd); bdd_.clearVarMap(); } @@ -103,13 +110,13 @@ Sim::functionSense(const FuncExpr *expr, sense = TimingSense::negative_unate; else sense = TimingSense::non_unate; - debugPrint(debug_, "sim", 4, " %s", to_string(sense)); + debugPrint(debug_, "sim", 4, " {}", to_string(sense)); return sense; } LogicValue Sim::evalExpr(const FuncExpr *expr, - const Instance *inst) + const Instance *inst) { LockGuard lock(bdd_lock_); DdNode *bdd = funcBddSim(expr, inst); @@ -140,19 +147,20 @@ Sim::funcBddSim(const FuncExpr *expr, const LibertyPort *port = network_->libertyPort(pin); DdNode *port_node = bdd_.findNode(port); if (port_node) { - LogicValue value = logicValue(pin); + LogicValue value = simValue(pin); int var_index = Cudd_NodeReadIndex(port_node); switch (value) { - case LogicValue::zero: - bdd = Cudd_bddCompose(cudd_mgr, bdd, Cudd_ReadLogicZero(cudd_mgr), var_index); - Cudd_Ref(bdd); - break; - case LogicValue::one: - bdd = Cudd_bddCompose(cudd_mgr, bdd, Cudd_ReadOne(cudd_mgr), var_index); - Cudd_Ref(bdd); - break; - default: - break; + case LogicValue::zero: + bdd = Cudd_bddCompose(cudd_mgr, bdd, Cudd_ReadLogicZero(cudd_mgr), + var_index); + Cudd_Ref(bdd); + break; + case LogicValue::one: + bdd = Cudd_bddCompose(cudd_mgr, bdd, Cudd_ReadOne(cudd_mgr), var_index); + Cudd_Ref(bdd); + break; + default: + break; } } } @@ -164,9 +172,9 @@ static LogicValue logicNot(LogicValue value) { static LogicValue logic_not[5] = {LogicValue::one, LogicValue::zero, - LogicValue::unknown, LogicValue::unknown, - LogicValue::unknown}; - return logic_not[int(value)]; + LogicValue::unknown, LogicValue::unknown, + LogicValue::unknown}; + return logic_not[static_cast(value)]; } void @@ -176,11 +184,11 @@ Sim::clear() incremental_ = false; const_func_pins_.clear(); const_func_pins_valid_ = false; - instances_with_const_pins_.clear(); instances_to_annotate_.clear(); invalid_insts_.clear(); invalid_drvr_pins_.clear(); invalid_load_pins_.clear(); + clearSimValues(); } void @@ -190,6 +198,86 @@ Sim::setObserver(SimObserver *observer) observer_ = observer; } +SimObserver::SimObserver(StaState *sta) : + StaState(sta) +{ +} + +LogicValue +Sim::simValue(const Vertex *vertex) const +{ + if (vertex->hasSimValue()) { + LogicValue value; + bool exists; + findKeyValue(sim_value_map_, vertex->pin(), value, exists); + if (exists) + return value; + } + return LogicValue::unknown; +} + +LogicValue +Sim::simValue(const Pin *pin) const +{ + Vertex *vertex = graph_->pinLoadVertex(pin); + if (vertex) + return simValue(vertex); + LogicValue value; + bool exists; + findKeyValue(sim_value_map_, pin, value, exists); + if (exists) + return value; + else + return LogicValue::unknown; +} + +bool +Sim::isConstant(const Vertex *vertex) const +{ + LogicValue value = simValue(vertex); + return value == LogicValue::zero || value == LogicValue::one; +} + +bool +Sim::isConstant(const Pin *pin) const +{ + LogicValue value = simValue(pin); + return value == LogicValue::zero || value == LogicValue::one; +} + +TimingSense +Sim::simTimingSense(const Edge *edge) const +{ + if (edge->hasSimSense()) { + TimingSense sense; + bool exists; + findKeyValue(edge_timing_sense_map_, edge, sense, exists); + if (exists) + return sense; + } + return TimingSense::unknown; +} + +void +Sim::setSimTimingSense(Edge *edge, + TimingSense sense) +{ + if (sense == TimingSense::unknown) + edge_timing_sense_map_.erase(edge); + else { + edge_timing_sense_map_[edge] = sense; + edge->setHasSimSense(true); + } +} + +bool +Sim::isDisabledCond(const Edge *edge) const +{ + return edge->hasDisabledCond() && edge_disabled_cond_set_.contains(edge); +} + +//////////////////////////////////////////////////////////////// + void Sim::ensureConstantsPropagated() { @@ -208,7 +296,7 @@ Sim::ensureConstantsPropagated() } invalid_insts_.clear(); propagateConstants(false); - annotateGraphEdges(); + findDisabledEdges(); valid_ = true; incremental_ = true; @@ -246,7 +334,7 @@ Sim::propagateToInvalidLoads() else { const Pin *drvr_pin = findDrvrPin(load_pin, network_); if (drvr_pin) - propagateDrvrToLoad(drvr_pin, load_pin); + propagateDrvrToLoad(drvr_pin, load_pin); } } invalid_load_pins_.clear(); @@ -256,15 +344,14 @@ void Sim::propagateFromInvalidDrvrsToLoads() { for (const Pin *drvr_pin : invalid_drvr_pins_) { - LogicValue value = const_func_pins_.hasKey(drvr_pin) - ? pinConstFuncValue(drvr_pin) - : logicValue(drvr_pin); - PinConnectedPinIterator *load_iter=network_->connectedPinIterator(drvr_pin); + LogicValue value = const_func_pins_.contains(drvr_pin) + ? pinConstFuncValue(drvr_pin) + : simValue(drvr_pin); + PinConnectedPinIterator *load_iter = network_->connectedPinIterator(drvr_pin); while (load_iter->hasNext()) { const Pin *load_pin = load_iter->next(); - if (load_pin != drvr_pin - && network_->isLoad(load_pin)) - setPinValue(load_pin, value); + if (load_pin != drvr_pin && network_->isLoad(load_pin)) + setPinValue(load_pin, value); } delete load_iter; } @@ -273,9 +360,9 @@ Sim::propagateFromInvalidDrvrsToLoads() void Sim::propagateDrvrToLoad(const Pin *drvr_pin, - const Pin *load_pin) + const Pin *load_pin) { - LogicValue value = logicValue(drvr_pin); + LogicValue value = simValue(drvr_pin); setPinValue(load_pin, value); } @@ -295,8 +382,8 @@ Sim::ensureConstantFuncPins() Instance *inst = inst_iter->next(); InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - recordConstPinFunc(pin); + const Pin *pin = pin_iter->next(); + recordConstPinFunc(pin); } delete pin_iter; } @@ -312,10 +399,9 @@ Sim::recordConstPinFunc(const Pin *pin) if (port) { FuncExpr *expr = port->function(); if (expr - // Tristate outputs do not force the output to be constant. - && port->tristateEnable() == nullptr - && (expr->op() == FuncExpr::op_zero - || expr->op() == FuncExpr::op_one)) + // Tristate outputs do not force the output to be constant. + && port->tristateEnable() == nullptr + && (expr->op() == FuncExpr::Op::zero || expr->op() == FuncExpr::Op::one)) const_func_pins_.insert(pin); } } @@ -323,7 +409,6 @@ Sim::recordConstPinFunc(const Pin *pin) void Sim::deleteInstanceBefore(const Instance *inst) { - instances_with_const_pins_.erase(inst); invalid_insts_.erase(inst); } @@ -337,6 +422,7 @@ Sim::makePinAfter(const Pin *pin) void Sim::deletePinBefore(const Pin *pin) { + sim_value_map_.erase(pin); // Incrementally update const_func_pins_. const_func_pins_.erase(pin); invalid_load_pins_.erase(pin); @@ -373,12 +459,8 @@ Sim::disconnectPinBefore(const Pin *pin) void Sim::pinSetFuncAfter(const Pin *pin) { - if (incremental_) { - Instance *inst = network_->instance(pin); - if (instances_with_const_pins_.hasKey(inst)) - invalid_insts_.insert(inst); + if (incremental_) valid_ = false; - } // Incrementally update const_func_pins_. const_func_pins_.erase(pin); recordConstPinFunc(pin); @@ -387,12 +469,13 @@ Sim::pinSetFuncAfter(const Pin *pin) void Sim::seedConstants() { + const Sdc *sdc = mode_->sdc(); // Propagate constants from inputs tied hi/low in the network. enqueueConstantPinInputs(); - // Propagate set_LogicValue::zero, set_LogicValue::one, set_logic_dc constants. - setConstraintConstPins(sdc_->logicValues()); + // Propagate set_logic_zero/one/dc constants. + setConstraintConstPins(sdc->logicValues()); // Propagate set_case_analysis constants. - setConstraintConstPins(sdc_->caseLogicValues()); + setConstraintConstPins(sdc->caseLogicValues()); // Propagate 0/1 constant functions. setConstFuncPins(); } @@ -408,23 +491,21 @@ Sim::propagateConstants(bool thru_sequentials) } void -Sim::setConstraintConstPins(LogicValueMap &value_map) +Sim::setConstraintConstPins(const LogicValueMap &value_map) { for (const auto [pin, value] : value_map) { - debugPrint(debug_, "sim", 2, "case pin %s = %c", - network_->pathName(pin), + debugPrint(debug_, "sim", 2, "case pin {} = {}", network_->pathName(pin), logicValueString(value)); if (network_->isHierarchical(pin)) { // Set the logic value on pins inside the instance of a hierarchical pin. bool pin_is_output = network_->direction(pin)->isAnyOutput(); - PinConnectedPinIterator *pin_iter=network_->connectedPinIterator(pin); + PinConnectedPinIterator *pin_iter = network_->connectedPinIterator(pin); while (pin_iter->hasNext()) { - const Pin *pin1 = pin_iter->next(); - if (network_->isLeaf(pin1) - && network_->direction(pin1)->isAnyInput() - && ((pin_is_output && !network_->isInside(pin1, pin)) - || (!pin_is_output && network_->isInside(pin1, pin)))) - setPinValue(pin1, value); + const Pin *pin1 = pin_iter->next(); + if (network_->isLeaf(pin1) && network_->direction(pin1)->isAnyInput() + && ((pin_is_output && !network_->isInside(pin1, pin)) + || (!pin_is_output && network_->isInside(pin1, pin)))) + setPinValue(pin1, value); } delete pin_iter; } @@ -441,8 +522,7 @@ Sim::setConstFuncPins() for (const Pin *pin : const_func_pins_) { LogicValue value = pinConstFuncValue(pin); setPinValue(pin, value); - debugPrint(debug_, "sim", 2, "func pin %s = %c", - network_->pathName(pin), + debugPrint(debug_, "sim", 2, "func pin {} = {}", network_->pathName(pin), logicValueString(value)); } } @@ -453,9 +533,9 @@ Sim::pinConstFuncValue(const Pin *pin) LibertyPort *port = network_->libertyPort(pin); if (port) { FuncExpr *expr = port->function(); - if (expr->op() == FuncExpr::op_zero) + if (expr->op() == FuncExpr::Op::zero) return LogicValue::zero; - else if (expr->op() == FuncExpr::op_one) + else if (expr->op() == FuncExpr::Op::one) return LogicValue::one; } return LogicValue::unknown; @@ -469,9 +549,8 @@ Sim::enqueueConstantPinInputs() LogicValue value; const Pin *pin; const_iter->next(pin, value); - debugPrint(debug_, "sim", 2, "network constant pin %s = %c", - network_->pathName(pin), - logicValueString(value)); + debugPrint(debug_, "sim", 2, "network constant pin {} = {}", + network_->pathName(pin), logicValueString(value)); setPinValue(pin, value); } delete const_iter; @@ -481,81 +560,61 @@ void Sim::removePropagatedValue(const Pin *pin) { Instance *inst = network_->instance(pin); - if (instances_with_const_pins_.hasKey(inst)) { - invalid_insts_.insert(inst); - valid_ = false; + invalid_insts_.insert(inst); + valid_ = false; - LogicValue constraint_value; - bool exists; - sdc_->caseLogicValue(pin, constraint_value, exists); + const Sdc *sdc = mode_->sdc(); + LogicValue constraint_value; + bool exists; + sdc->caseLogicValue(pin, constraint_value, exists); + if (!exists) { + sdc->logicValue(pin, constraint_value, exists); if (!exists) { - sdc_->logicValue(pin, constraint_value, exists); - if (!exists) { - debugPrint(debug_, "sim", 2, "pin %s remove prop constant", - network_->pathName(pin)); - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - if (vertex) - setSimValue(vertex, LogicValue::unknown); - if (bidirect_drvr_vertex) - setSimValue(bidirect_drvr_vertex, LogicValue::unknown); - } + debugPrint(debug_, "sim", 2, "pin {} remove prop constant", + network_->pathName(pin)); + setSimValue(pin, LogicValue::unknown); } } } void Sim::setPinValue(const Pin *pin, - LogicValue value) + LogicValue value) { + const Sdc *sdc = mode_->sdc(); LogicValue constraint_value; bool exists; - sdc_->caseLogicValue(pin, constraint_value, exists); + sdc->caseLogicValue(pin, constraint_value, exists); if (!exists) - sdc_->logicValue(pin, constraint_value, exists); - if (exists - && value != constraint_value) { + sdc->logicValue(pin, constraint_value, exists); + if (exists && value != constraint_value) { if (value != LogicValue::unknown) - report_->warn(1521, "propagated logic value %c differs from constraint value of %c on pin %s.", - logicValueString(value), - logicValueString(constraint_value), - sdc_network_->pathName(pin)); + report_->warn( + 1521, + "propagated logic value {} differs from constraint value of {} on pin {}.", + logicValueString(value), logicValueString(constraint_value), + sdc_network_->pathName(pin)); } else { - debugPrint(debug_, "sim", 3, "pin %s = %c", - network_->pathName(pin), + debugPrint(debug_, "sim", 3, "pin {} = {}", network_->pathName(pin), logicValueString(value)); - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - // Set vertex constant flags. bool value_changed = false; - if (vertex) { - value_changed |= value != vertex->simValue(); - setSimValue(vertex, value); - } - if (bidirect_drvr_vertex) { - value_changed |= value != bidirect_drvr_vertex->simValue(); - setSimValue(bidirect_drvr_vertex, value); - } + value_changed |= value != simValue(pin); + setSimValue(pin, value); if (value_changed) { Instance *inst = network_->instance(pin); - if (logicValueZeroOne(value)) - instances_with_const_pins_.insert(inst); instances_to_annotate_.insert(inst); - if (network_->isLeaf(inst) - && network_->direction(pin)->isAnyInput()) { - if (eval_queue_.empty() - || (eval_queue_.back() != inst)) + if (network_->isLeaf(inst) && network_->direction(pin)->isAnyInput()) { + if (eval_queue_.empty() || (eval_queue_.back() != inst)) eval_queue_.push(inst); } else if (network_->isDriver(pin)) { // Enqueue instances with input pins connected to net. - PinConnectedPinIterator *pin_iter=network_->connectedPinIterator(pin); + PinConnectedPinIterator *pin_iter = network_->connectedPinIterator(pin); while (pin_iter->hasNext()) { const Pin *pin1 = pin_iter->next(); - if (pin1 != pin - && network_->isLoad(pin1)) + if (pin1 != pin && network_->isLoad(pin1)) setPinValue(pin1, value); } delete pin_iter; @@ -568,7 +627,7 @@ void Sim::evalInstance(const Instance *inst, bool thru_sequentials) { - debugPrint(debug_, "sim", 2, "eval %s", network_->pathName(inst)); + debugPrint(debug_, "sim", 2, "eval {}", network_->pathName(inst)); InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); @@ -577,49 +636,42 @@ Sim::evalInstance(const Instance *inst, PortDirection *dir = port->direction(); if (dir->isAnyOutput()) { LogicValue value = LogicValue::unknown; - FuncExpr *expr = port->function(); + FuncExpr *expr = port->function(); LibertyCell *cell = port->libertyCell(); - if (expr) { + if (expr) { FuncExpr *tri_en_expr = port->tristateEnable(); if (tri_en_expr) { if (evalExpr(tri_en_expr, inst) == LogicValue::one) { value = evalExpr(expr, inst); - debugPrint(debug_, "sim", 2, " %s tri_en=1 %s = %c", - port->name(), - expr->to_string().c_str(), - logicValueString(value)); + debugPrint(debug_, "sim", 2, " {} tri_en=1 {} = {}", port->name(), + expr->to_string(), logicValueString(value)); } } else { LibertyPort *expr_port = expr->port(); - Sequential *sequential = (thru_sequentials && expr_port) - ? cell->outputPortSequential(expr_port) - : nullptr; + Sequential *sequential = (thru_sequentials && expr_port) + ? cell->outputPortSequential(expr_port) + : nullptr; if (sequential) { value = evalExpr(sequential->data(), inst); if (expr_port == sequential->outputInv()) value = logicNot(value); - debugPrint(debug_, "sim", 2, " %s seq %s = %c", - port->name(), - expr->to_string().c_str(), - logicValueString(value)); + debugPrint(debug_, "sim", 2, " {} seq {} = {}", port->name(), + expr->to_string(), logicValueString(value)); } else { value = evalExpr(expr, inst); - debugPrint(debug_, "sim", 2, " %s %s = %c", - port->name(), - expr->to_string().c_str(), - logicValueString(value)); + debugPrint(debug_, "sim", 2, " {} {} = {}", port->name(), + expr->to_string(), logicValueString(value)); } } } else if (port->isClockGateOut()) { value = clockGateOutValue(inst); - debugPrint(debug_, "sim", 2, " %s gated_clk = %c", - port->name(), + debugPrint(debug_, "sim", 2, " {} gated_clk = {}", port->name(), logicValueString(value)); } - if (value != logicValue(pin)) + if (value != simValue(pin)) setPinValue(pin, value); } } @@ -634,36 +686,42 @@ Sim::clockGateOutValue(const Instance *inst) LibertyCellPortIterator port_iter(cell); while (port_iter.hasNext()) { LibertyPort *port = port_iter.next(); - if (port->isClockGateClock() - || port->isClockGateEnable()) { + if (port->isClockGateClock() || port->isClockGateEnable()) { Pin *gclk_pin = network_->findPin(inst, port); - if (gclk_pin) { - Vertex *gclk_vertex = graph_->pinLoadVertex(gclk_pin); - if (gclk_vertex->simValue() == LogicValue::zero) - return LogicValue::zero; - } + if (gclk_pin && simValue(gclk_pin) == LogicValue::zero) + return LogicValue::zero; } } return LogicValue::unknown; } void -Sim::setSimValue(Vertex *vertex, - LogicValue value) +Sim::setSimValue(const Pin *pin, + LogicValue value) { - if (value != vertex->simValue()) { - vertex->setSimValue(value); + if (value != simValue(pin)) { + if (value == LogicValue::unknown) + sim_value_map_.erase(pin); + else { + sim_value_map_[pin] = value; + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + if (vertex) + vertex->setHasSimValue(true); + if (bidirect_drvr_vertex) + bidirect_drvr_vertex->setHasSimValue(true); + } if (observer_) - observer_->valueChangeAfter(vertex); + observer_->valueChangeAfter(pin); } } TimingSense Sim::functionSense(const Instance *inst, - const Pin *from_pin, - const Pin *to_pin) + const Pin *from_pin, + const Pin *to_pin) { - if (logicZeroOne(from_pin)) + if (isConstant(from_pin)) return TimingSense::none; else { LibertyPort *from_port = network_->libertyPort(from_pin); @@ -671,217 +729,98 @@ Sim::functionSense(const Instance *inst, if (to_port) { const FuncExpr *func = to_port->function(); if (func) { - PortDirection *to_dir = to_port->direction(); - if (to_dir->isAnyTristate()) { - FuncExpr *tri_func = to_port->tristateEnable(); - if (tri_func) { - if (func->hasPort(from_port)) { - // from_pin is an input to the to_pin function. - LogicValue tri_enable = evalExpr(tri_func, inst); - if (tri_enable == LogicValue::zero) - // Tristate is disabled. - return TimingSense::none; - else - return functionSense(func, from_pin, inst); - } - } - else { - // Missing tristate enable function. - if (func->hasPort(from_port)) - // from_pin is an input to the to_pin function. - return functionSense(func, from_pin, inst); - } - } - else { - if (func->hasPort(from_port)) - // from_pin is an input to the to_pin function. - return functionSense(func, from_pin, inst); - } + PortDirection *to_dir = to_port->direction(); + if (to_dir->isAnyTristate()) { + FuncExpr *tri_func = to_port->tristateEnable(); + if (tri_func) { + if (func->hasPort(from_port)) { + // from_pin is an input to the to_pin function. + LogicValue tri_enable = evalExpr(tri_func, inst); + if (tri_enable == LogicValue::zero) + // Tristate is disabled. + return TimingSense::none; + else + return functionSense(func, from_pin, inst); + } + } + else { + // Missing tristate enable function. + if (func->hasPort(from_port)) + // from_pin is an input to the to_pin function. + return functionSense(func, from_pin, inst); + } + } + else { + if (func->hasPort(from_port)) + // from_pin is an input to the to_pin function. + return functionSense(func, from_pin, inst); + } } } return TimingSense::unknown; } } -LogicValue -Sim::logicValue(const Pin *pin) const -{ - Vertex *vertex = graph_->pinLoadVertex(pin); - if (vertex) - return vertex->simValue(); - else { - if (network_->isHierarchical(pin)) { - const Pin *drvr_pin = findDrvrPin(pin, network_); - if (drvr_pin) - return logicValue(drvr_pin); - } - return LogicValue::unknown; - } -} - static const Pin * findDrvrPin(const Pin *pin, - Network *network) + Network *network) { PinSet *drvrs = network->drivers(pin); - if (drvrs) { - PinSet::Iterator drvr_iter(drvrs); - if (drvr_iter.hasNext()) - return drvr_iter.next(); - } - return nullptr; -} - -bool -logicValueZeroOne(LogicValue value) -{ - return value == LogicValue::zero || value == LogicValue::one; -} - -bool -Sim::logicZeroOne(const Pin *pin) const -{ - return logicValueZeroOne(logicValue(pin)); -} - -bool -Sim::logicZeroOne(const Vertex *vertex) const -{ - return logicValueZeroOne(vertex->simValue()); + if (drvrs && !drvrs->empty()) + return *drvrs->begin(); + else + return nullptr; } void Sim::clearSimValues() { - for (const Instance *inst : instances_with_const_pins_) { - // Clear sim values on all pins before evaling functions. - clearInstSimValues(inst); - annotateVertexEdges(inst, false); - } - instances_with_const_pins_.clear(); -} - -void -Sim::clearInstSimValues(const Instance *inst) -{ - debugPrint(debug_, "sim", 4, "clear %s", - network_->pathName(inst)); - InstancePinIterator *pin_iter = network_->pinIterator(inst); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - if (vertex) - setSimValue(vertex, LogicValue::unknown); - if (bidirect_drvr_vertex) - setSimValue(bidirect_drvr_vertex, LogicValue::unknown); - } - delete pin_iter; -} - -// Annotate graph edges disabled by constant values. -void -Sim::annotateGraphEdges() -{ - for (const Instance *inst : instances_to_annotate_) - annotateVertexEdges(inst, true); + for (auto const [pin, value] : sim_value_map_) + observer_->valueChangeAfter(pin); + sim_value_map_.clear(); + edge_timing_sense_map_.clear(); + edge_disabled_cond_set_.clear(); } -void -Sim::annotateVertexEdges(const Instance *inst, - bool annotate) -{ - debugPrint(debug_, "sim", 4, "annotate %s %s", - network_->pathName(inst), - annotate ? "true" : "false"); - InstancePinIterator *pin_iter = network_->pinIterator(inst); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - Vertex *vertex = graph_->pinDrvrVertex(pin); - if (vertex) - annotateVertexEdges(inst, pin, vertex, annotate); - } - delete pin_iter; -} +//////////////////////////////////////////////////////////////// void -Sim::annotateVertexEdges(const Instance *inst, - const Pin *pin, - Vertex *vertex, - bool annotate) +Sim::setIsDisabledCond(Edge *edge, + bool disabled) { - bool fanin_disables_changed = false; - VertexInEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (!edge->role()->isWire()) { - Vertex *from_vertex = edge->from(graph_); - Pin *from_pin = from_vertex->pin(); - TimingSense sense = TimingSense::unknown; - bool is_disabled_cond = false; - if (annotate) { - // Set timing sense on edges in instances that have constant pins. - if (logicZeroOne(from_vertex)) - sense = TimingSense::none; - else - sense = functionSense(inst, from_pin, pin); - - if (sense != TimingSense::none) - // Disable conditional timing edges based on constant pins. - is_disabled_cond = isCondDisabled(edge, inst, from_pin, - pin, network_,sim_) - // Disable mode conditional timing - // edges based on constant pins. - || isModeDisabled(edge,inst,network_,sim_); - } - bool disables_changed = false; - if (sense != edge->simTimingSense()) { - edge->setSimTimingSense(sense); - disables_changed = true; - fanin_disables_changed = true; - } - if (is_disabled_cond != edge->isDisabledCond()) { - edge->setIsDisabledCond(is_disabled_cond); - disables_changed = true; - fanin_disables_changed = true; - } - if (observer_ && disables_changed) - observer_->fanoutEdgesChangeAfter(from_vertex); - } + if (!disabled) + edge_disabled_cond_set_.erase(edge); + else { + edge_disabled_cond_set_.insert(edge); + edge->setHasDisabledCond(true); } - if (observer_ && fanin_disables_changed) - observer_->faninEdgesChangeAfter(vertex); } bool -isCondDisabled(Edge *edge, - const Instance *inst, - const Pin *from_pin, - const Pin *to_pin, - const Network *network, - Sim *sim) +Sim::isDisabledCond(Edge *edge, + const Instance *inst, + const Pin *from_pin, + const Pin *to_pin) { bool is_disabled; FuncExpr *disable_cond; - isCondDisabled(edge, inst, from_pin, to_pin, network, sim, - is_disabled, disable_cond); + isDisabledCond(edge, inst, from_pin, to_pin, is_disabled, disable_cond); return is_disabled; } void -isCondDisabled(Edge *edge, - const Instance *inst, - const Pin *from_pin, - const Pin *to_pin, - const Network *network, - Sim *sim, - bool &is_disabled, - FuncExpr *&disable_cond) +Sim::isDisabledCond(Edge *edge, + const Instance *inst, + const Pin *from_pin, + const Pin *to_pin, + // Return values. + bool &is_disabled, + FuncExpr *&disable_cond) { TimingArcSet *arc_set = edge->timingArcSet(); FuncExpr *cond = arc_set->cond(); if (cond) { - LogicValue cond_value = sim->evalExpr(cond, inst); + LogicValue cond_value = evalExpr(cond, inst); disable_cond = cond; is_disabled = (cond_value == LogicValue::zero); } @@ -889,78 +828,143 @@ isCondDisabled(Edge *edge, // Unconditional "default" arc set is disabled if another // conditional arc from/to the same pins is enabled (condition // evals to logic one). - LibertyCell *cell = network->libertyCell(inst); - LibertyPort *from_port = network->libertyPort(from_pin); - LibertyPort *to_port = network->libertyPort(to_pin); + LibertyCell *cell = network_->libertyCell(inst); + LibertyPort *from_port = network_->libertyPort(from_pin); + LibertyPort *to_port = network_->libertyPort(to_pin); is_disabled = false; for (TimingArcSet *cond_set : cell->timingArcSets(from_port, to_port)) { FuncExpr *cond = cond_set->cond(); - if (cond && sim->evalExpr(cond, inst) == LogicValue::one) { - disable_cond = cond; - is_disabled = true; - break; + if (cond && evalExpr(cond, inst) == LogicValue::one) { + disable_cond = cond; + is_disabled = true; + break; } } } } bool -isModeDisabled(Edge *edge, - const Instance *inst, - const Network *network, - Sim *sim) +Sim::isDisabledMode(Edge *edge, + const Instance *inst) { bool is_disabled; FuncExpr *disable_cond; - isModeDisabled(edge, inst, network, sim, - is_disabled, disable_cond); + isDisabledMode(edge, inst, is_disabled, disable_cond); return is_disabled; } void -isModeDisabled(Edge *edge, - const Instance *inst, - const Network *network, - Sim *sim, - bool &is_disabled, - FuncExpr *&disable_cond) +Sim::isDisabledMode(Edge *edge, + const Instance *inst, + // Return values. + bool &is_disabled, + FuncExpr *&disable_cond) { // Default values. is_disabled = false; - disable_cond = 0; + disable_cond = nullptr; TimingArcSet *arc_set = edge->timingArcSet(); - const char *mode_name = arc_set->modeName(); - const char *mode_value = arc_set->modeValue(); - if (mode_name && mode_value) { - LibertyCell *cell = network->libertyCell(inst); - ModeDef *mode_def = cell->findModeDef(mode_name); + const std::string &mode_name = arc_set->modeName(); + const std::string &mode_value = arc_set->modeValue(); + if (!mode_name.empty() && !mode_value.empty()) { + LibertyCell *cell = network_->libertyCell(inst); + const ModeDef *mode_def = cell->findModeDef(mode_name); if (mode_def) { - ModeValueDef *value_def = mode_def->findValueDef(mode_value); + const ModeValueDef *value_def = mode_def->findValueDef(mode_value); if (value_def) { - FuncExpr *cond = value_def->cond(); - if (cond) { - LogicValue cond_value = sim->evalExpr(cond, inst); - if (cond_value == LogicValue::zero) { - // For a mode value to be disabled by having a value of - // logic zero one mode value must logic one. - for (const auto [name, value_def] : *mode_def->values()) { - if (value_def) { - FuncExpr *cond1 = value_def->cond(); - if (cond1) { - LogicValue cond_value1 = sim->evalExpr(cond1, inst); - if (cond_value1 == LogicValue::one) { - disable_cond = cond; - is_disabled = true; - break; - } - } - } - } - } - } + FuncExpr *cond = value_def->cond(); + if (cond) { + LogicValue cond_value = evalExpr(cond, inst); + if (cond_value == LogicValue::zero) { + // For a mode value to be disabled by having a value of + // logic zero one mode value must logic one. + for (const auto &[name, value_def] : mode_def->values()) { + FuncExpr *cond1 = value_def.cond(); + if (cond1) { + LogicValue cond_value1 = evalExpr(cond1, inst); + if (cond_value1 == LogicValue::one) { + disable_cond = cond; + is_disabled = true; + break; + } + } + } + } + } + } + } + } +} + +//////////////////////////////////////////////////////////////// + +// Find graph edges disabled by constant values. +void +Sim::findDisabledEdges() +{ + for (const Instance *inst : instances_to_annotate_) + findDisabledEdges(inst); + instances_to_annotate_.clear(); +} + +void +Sim::findDisabledEdges(const Instance *inst) +{ + debugPrint(debug_, "sim", 4, "annotate {}", network_->pathName(inst)); + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + Vertex *vertex = graph_->pinDrvrVertex(pin); + if (vertex) + findDisabledEdges(inst, pin, vertex); + } + delete pin_iter; +} + +void +Sim::findDisabledEdges(const Instance *inst, + const Pin *pin, + Vertex *vertex) +{ + bool fanin_disables_changed = false; + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (!edge->role()->isWire()) { + Vertex *from_vertex = edge->from(graph_); + Pin *from_pin = from_vertex->pin(); + TimingSense sense = TimingSense::unknown; + bool is_disabled_cond = false; + // Set timing sense on edges in instances that have constant pins. + if (isConstant(from_vertex)) + sense = TimingSense::none; + else + sense = functionSense(inst, from_pin, pin); + + if (sense != TimingSense::none) + // Disable conditional timing edges based on constant pins. + is_disabled_cond = isDisabledCond(edge, inst, from_pin, pin) + // Disable mode conditional timing + // edges based on constant pins. + || isDisabledMode(edge, inst); + + bool disables_changed = false; + if (sense != simTimingSense(edge)) { + setSimTimingSense(edge, sense); + disables_changed = true; + fanin_disables_changed = true; } + if (is_disabled_cond != isDisabledCond(edge)) { + setIsDisabledCond(edge, is_disabled_cond); + disables_changed = true; + fanin_disables_changed = true; + } + if (observer_ && disables_changed) + observer_->fanoutEdgesChangeAfter(from_vertex->pin()); } } + if (observer_ && fanin_disables_changed) + observer_->faninEdgesChangeAfter(vertex->pin()); } -} // namespace +} // namespace sta diff --git a/search/Sim.hh b/search/Sim.hh index 958e9d82b..5edd90f24 100644 --- a/search/Sim.hh +++ b/search/Sim.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,48 +24,92 @@ #pragma once -#include #include +#include +#include +#include -#include "StaConfig.hh" // CUDD -#include "Map.hh" -#include "NetworkClass.hh" +#include "Bdd.hh" #include "GraphClass.hh" +#include "LibertyClass.hh" +#include "Mode.hh" +#include "NetworkClass.hh" #include "SdcClass.hh" #include "StaState.hh" -#include "Bdd.hh" namespace sta { class SimObserver; -typedef Map PinValueMap; -typedef std::queue EvalQueue; +using SimValueMap = std::unordered_map; +using EdgeDisabledCondSet = std::unordered_set; +using EdgeTimingSenseMap = std::unordered_map; +using EvalQueue = std::queue; // Propagate constants from constraints and netlist tie high/low // connections thru gates. class Sim : public StaState { public: - explicit Sim(StaState *sta); - virtual ~Sim(); + Sim(StaState *sta); + ~Sim() override; + void copyState(const StaState *sta) override; void clear(); + void setMode(Mode *mode); // Set the observer for simulation value changes. void setObserver(SimObserver *observer); void ensureConstantsPropagated(); void constantsInvalid(); + + // Dertived by Sim from set_case_analysis or set_logic constant. + LogicValue simValue(const Pin *pin) const; + LogicValue simValue(const Vertex *vertex) const; + // Constant zero/one from simulation. + bool isConstant(const Pin *pin) const; + bool isConstant(const Vertex *vertex) const; + // Timing sense for the to_pin function after simplifying the + // function based constants on the instance pins. + TimingSense simTimingSense(const Edge *edge) const; + // Edge is disabled by constants in condition (when) function. + bool isDisabledCond(const Edge *edge) const; + LogicValue evalExpr(const FuncExpr *expr, - const Instance *inst); - LogicValue logicValue(const Pin *pin) const; - bool logicZeroOne(const Pin *pin) const; - bool logicZeroOne(const Vertex *vertex) const; + const Instance *inst); // Timing sense for the function between from_pin and to_pin // after simplifying the function based constants on the pins. - virtual TimingSense functionSense(const Instance *inst, - const Pin *from_pin, - const Pin *to_pin); + TimingSense functionSense(const Instance *inst, + const Pin *from_pin, + const Pin *to_pin); + // Propagate liberty constant functions and pins tied high/low through + // combinational logic and registers (ignores Sdc). + // Used by OpenROAD/Restructure.cpp void findLogicConstants(); + // Edge cond (liberty "when") function is disabled (evals to zero). + bool isDisabledCond(Edge *edge, + const Instance *inst, + const Pin *from_pin, + const Pin *to_pin); + // isDisabledCond but also return the cond expression that causes + // the disable. This can differ from the edge cond expression + // when the default timing edge is disabled by another edge between + // the same pins that is enabled. + void isDisabledCond(Edge *edge, + const Instance *inst, + const Pin *from_pin, + const Pin *to_pin, + // Return values. + bool &is_disabled, + FuncExpr *&disable_cond); + // Edge mode function is disabled (evals zero). + bool isDisabledMode(Edge *edge, + const Instance *inst); + void isDisabledMode(Edge *edge, + const Instance *inst, + // Return values. + bool &is_disabled, + FuncExpr *&disable_cond); + // Network edits. void deleteInstanceBefore(const Instance *inst); void makePinAfter(const Pin *pin); @@ -73,58 +117,67 @@ public: void connectPinAfter(const Pin *pin); void disconnectPinBefore(const Pin *pin); void pinSetFuncAfter(const Pin *pin); + const SimValueMap &simValues() const { return sim_value_map_; } protected: void ensureConstantFuncPins(); void recordConstPinFunc(const Pin *pin); - virtual void seedConstants(); + void seedConstants(); void seedInvalidConstants(); void propagateConstants(bool thru_sequentials); - void setConstraintConstPins(LogicValueMap &pin_value_map); + void setConstraintConstPins(const LogicValueMap &pin_value_map); void setConstFuncPins(); LogicValue pinConstFuncValue(const Pin *pin); void enqueueConstantPinInputs(); - virtual void setPinValue(const Pin *pin, - LogicValue value); + void setSimValue(const Pin *pin, + LogicValue value); + void setPinValue(const Pin *pin, + LogicValue value); + void setSimTimingSense(Edge *edge, + TimingSense sense); + void setIsDisabledCond(Edge *edge, + bool disabled); void enqueue(const Instance *inst); void evalInstance(const Instance *inst, bool thru_sequentials); LogicValue clockGateOutValue(const Instance *inst); TimingSense functionSense(const FuncExpr *expr, - const Pin *input_pin, - const Instance *inst); + const Pin *input_pin, + const Instance *inst); void functionSense(const FuncExpr *expr, - const Pin *input_pin, - const Instance *inst, - // return values - TimingSense &sense, - LogicValue &value) const; + const Pin *input_pin, + const Instance *inst, + // return values + TimingSense &sense, + LogicValue &value) const; void clearSimValues(); - virtual void clearInstSimValues(const Instance *inst); - void annotateGraphEdges(); - void annotateVertexEdges(const Instance *inst, - const Pin *pin, - Vertex *vertex, - bool annotate); - void annotateVertexEdges(const Instance *inst, - bool annotate); + + void findDisabledEdges(); + void findDisabledEdges(const Instance *inst); + void findDisabledEdges(const Instance *inst, + const Pin *pin, + Vertex *vertex); + void removePropagatedValue(const Pin *pin); void propagateFromInvalidDrvrsToLoads(); void propagateToInvalidLoads(); void propagateDrvrToLoad(const Pin *drvr_pin, - const Pin *load_pin); - void setSimValue(Vertex *vertex, - LogicValue value); + const Pin *load_pin); DdNode *funcBddSim(const FuncExpr *expr, const Instance *inst); - SimObserver *observer_; - bool valid_; - bool incremental_; + Mode *mode_{nullptr}; + SimObserver *observer_{nullptr}; + bool valid_{false}; + bool incremental_{false}; + // Constants propagated by Sim.cc + SimValueMap sim_value_map_; + EdgeDisabledCondSet edge_disabled_cond_set_; + EdgeTimingSenseMap edge_timing_sense_map_; // Cache of pins that have constant functions (tie high and tie low // cell instances). PinSet const_func_pins_; - bool const_func_pins_valid_; + bool const_func_pins_valid_{false}; // Instances that require incremental constant propagation. InstanceSet invalid_insts_; // Driver pins waiting to propagate constant to loads. @@ -132,63 +185,19 @@ protected: // Load pins that waiting for the driver constant to propagate. PinSet invalid_load_pins_; EvalQueue eval_queue_; - // Instances with constant pin values for annotateVertexEdges. - InstanceSet instances_with_const_pins_; InstanceSet instances_to_annotate_; Bdd bdd_; mutable std::mutex bdd_lock_; }; // Abstract base class for Sim value change observer. -class SimObserver +class SimObserver : public StaState { public: - SimObserver() {} - virtual ~SimObserver() {} - virtual void valueChangeAfter(Vertex *vertex) = 0; - virtual void faninEdgesChangeAfter(Vertex *vertex) = 0; - virtual void fanoutEdgesChangeAfter(Vertex *vertex) = 0; + SimObserver(StaState *sta); + virtual void valueChangeAfter(const Pin *pin) = 0; + virtual void faninEdgesChangeAfter(const Pin *pin) = 0; + virtual void fanoutEdgesChangeAfter(const Pin *pin) = 0; }; -bool -logicValueZeroOne(LogicValue value); - -// Edge cond (liberty "when") function is disabled (evals to zero). -bool -isCondDisabled(Edge *edge, - const Instance *inst, - const Pin *from_pin, - const Pin *to_pin, - const Network *network, - Sim *sim); - -// isCondDisabled but also return the cond expression that causes -// the disable. This can differ from the edge cond expression -// when the default timing edge is disabled by another edge between -// the same pins that is enabled. -void -isCondDisabled(Edge *edge, - const Instance *inst, - const Pin *from_pin, - const Pin *to_pin, - const Network *network, - Sim *sim, - bool &is_disabled, - FuncExpr *&disable_cond); - - -// Edge mode function is disabled (evals to zero). -bool -isModeDisabled(Edge *edge, - const Instance *inst, - const Network *network, - Sim *sim); -void -isModeDisabled(Edge *edge, - const Instance *inst, - const Network *network, - Sim *sim, - bool &is_disabled, - FuncExpr *&disable_cond); - -} // namespace +} // namespace sta diff --git a/search/Sta.cc b/search/Sta.cc index d34ffecbd..e84ca5c50 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -1,109 +1,127 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Sta.hh" -#include -#include +#include +#include +#include +#include -#include "Machine.hh" -#include "DispatchQueue.hh" -#include "ReportTcl.hh" +#include "ArcDelayCalc.hh" +#include "CheckCapacitances.hh" +#include "CheckFanouts.hh" +#include "CheckMaxSkews.hh" +#include "CheckMinPeriods.hh" +#include "CheckMinPulseWidths.hh" +#include "CheckSlews.hh" +#include "CheckTiming.hh" +#include "CircuitSim.hh" +#include "ClkInfo.hh" +#include "ClkLatency.hh" +#include "ClkNetwork.hh" +#include "ClkSkew.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" -#include "Stats.hh" -#include "Fuzzy.hh" -#include "Units.hh" -#include "TimingArc.hh" -#include "FuncExpr.hh" +#include "Delay.hh" +#include "DelayCalc.hh" +#include "DelayNormal.hh" +#include "DelayScalar.hh" +#include "DelaySkewNormal.hh" +#include "DispatchQueue.hh" #include "EquivCells.hh" +#include "ExceptionPath.hh" +#include "FindRegister.hh" +#include "Format.hh" +#include "FuncExpr.hh" +#include "Fuzzy.hh" +#include "Genclks.hh" +#include "Graph.hh" +#include "GraphClass.hh" +#include "GraphCmp.hh" +#include "GraphDelayCalc.hh" +#include "Latches.hh" +#include "Levelize.hh" #include "Liberty.hh" -#include "liberty/LibertyReader.hh" +#include "LibertyClass.hh" #include "LibertyWriter.hh" -#include "SdcNetwork.hh" +#include "Machine.hh" #include "MakeConcreteNetwork.hh" +#include "MakeTimingModel.hh" +#include "MinMax.hh" +#include "Mode.hh" +#include "Network.hh" +#include "NetworkClass.hh" +#include "Parasitics.hh" +#include "PathExpanded.hh" +#include "PathGroup.hh" +#include "PatternMatch.hh" +#include "PocvMode.hh" #include "PortDirection.hh" -#include "VerilogReader.hh" -#include "Graph.hh" -#include "GraphCmp.hh" +#include "PowerClass.hh" +#include "ReportPath.hh" +#include "ReportTcl.hh" +#include "RiseFallMinMaxDelay.hh" +#include "Scene.hh" #include "Sdc.hh" -#include "Variables.hh" -#include "WriteSdc.hh" -#include "ExceptionPath.hh" -#include "MakeConcreteParasitics.hh" -#include "Parasitics.hh" -#include "parasitics/SpefReader.hh" -#include "parasitics/ReportParasiticAnnotation.hh" -#include "DelayCalc.hh" -#include "ArcDelayCalc.hh" -#include "GraphDelayCalc.hh" -#include "sdf/SdfWriter.hh" -#include "Levelize.hh" +#include "SdcClass.hh" +#include "SdcNetwork.hh" +#include "Search.hh" +#include "SearchClass.hh" +#include "SearchPred.hh" #include "Sim.hh" -#include "ClkInfo.hh" +#include "Stats.hh" +#include "StringUtil.hh" #include "TagGroup.hh" -#include "PathAnalysisPt.hh" -#include "Corner.hh" -#include "Search.hh" -#include "GatedClk.hh" -#include "Latches.hh" -#include "PathGroup.hh" -#include "CheckTiming.hh" -#include "CheckSlewLimits.hh" -#include "CheckFanoutLimits.hh" -#include "CheckCapacitanceLimits.hh" -#include "CheckMinPulseWidths.hh" -#include "CheckMinPeriods.hh" -#include "CheckMaxSkews.hh" -#include "ClkSkew.hh" -#include "ClkLatency.hh" -#include "FindRegister.hh" -#include "ReportPath.hh" -#include "Genclks.hh" -#include "ClkNetwork.hh" -#include "power/Power.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Units.hh" +#include "Variables.hh" +#include "VerilogReader.hh" #include "VisitPathEnds.hh" -#include "PathExpanded.hh" -#include "MakeTimingModel.hh" +#include "Wireload.hh" +#include "liberty/LibertyReader.hh" +#include "parasitics/ConcreteParasitics.hh" +#include "parasitics/ReportParasiticAnnotation.hh" +#include "parasitics/SpefReader.hh" +#include "power/Power.hh" +#include "sdc/WriteSdc.hh" +#include "sdf/SdfReader.hh" +#include "sdf/SdfWriter.hh" #include "spice/WritePathSpice.hh" namespace sta { -using std::string; -using std::min; -using std::max; - -static const ClockEdge *clk_edge_wildcard = reinterpret_cast(1); - static bool libertyPortCapsEqual(const LibertyPort *port1, - const LibertyPort *port2); + const LibertyPort *port2); static bool hasDisabledArcs(Edge *edge, - Graph *graph); + const Mode *mode); static InstanceSet pinInstances(PinSet &pins, - const Network *network); + const Network *network); //////////////////////////////////////////////////////////////// // @@ -118,17 +136,16 @@ pinInstances(PinSet &pins, class StaDelayCalcObserver : public DelayCalcObserver { public: - explicit StaDelayCalcObserver(Search *search); - virtual void delayChangedFrom(Vertex *vertex); - virtual void delayChangedTo(Vertex *vertex); - virtual void checkDelayChangedTo(Vertex *vertex); + StaDelayCalcObserver(Search *search); + void delayChangedFrom(Vertex *vertex) override; + void delayChangedTo(Vertex *vertex) override; + void checkDelayChangedTo(Vertex *vertex) override; private: Search *search_; }; StaDelayCalcObserver::StaDelayCalcObserver(Search *search) : - DelayCalcObserver(), search_(search) { } @@ -156,26 +173,14 @@ StaDelayCalcObserver::checkDelayChangedTo(Vertex *vertex) class StaSimObserver : public SimObserver { public: - StaSimObserver(GraphDelayCalc *graph_delay_calc, - Levelize *levelize, - Search *search); - virtual void valueChangeAfter(Vertex *vertex); - virtual void faninEdgesChangeAfter(Vertex *vertex); - virtual void fanoutEdgesChangeAfter(Vertex *vertex); - -private: - GraphDelayCalc *graph_delay_calc_; - Levelize *levelize_; - Search *search_; + StaSimObserver(StaState *sta); + void valueChangeAfter(const Pin *pin) override; + void faninEdgesChangeAfter(const Pin *pin) override; + void fanoutEdgesChangeAfter(const Pin *pin) override; }; -StaSimObserver::StaSimObserver(GraphDelayCalc *graph_delay_calc, - Levelize *levelize, - Search *search) : - SimObserver(), - graph_delay_calc_(graph_delay_calc), - levelize_(levelize), - search_(search) +StaSimObserver::StaSimObserver(StaState *sta) : + SimObserver(sta) { } @@ -184,26 +189,28 @@ StaSimObserver::StaSimObserver(GraphDelayCalc *graph_delay_calc, // because the search predicate does not search through constants. // This observer makes sure the delays and arrivals are invalidated. void -StaSimObserver::valueChangeAfter(Vertex *vertex) +StaSimObserver::valueChangeAfter(const Pin *pin) { - graph_delay_calc_->delayInvalid(vertex); - search_->arrivalInvalid(vertex); - search_->requiredInvalid(vertex); - search_->endpointInvalid(vertex); - levelize_->invalidFrom(vertex); + Vertex *vertex = graph_->pinDrvrVertex(pin); + if (vertex) { + search_->arrivalInvalid(vertex); + search_->requiredInvalid(vertex); + search_->endpointInvalid(vertex); + } } void -StaSimObserver::faninEdgesChangeAfter(Vertex *vertex) +StaSimObserver::faninEdgesChangeAfter(const Pin *pin) { - graph_delay_calc_->delayInvalid(vertex); + Vertex *vertex = graph_->pinDrvrVertex(pin); search_->arrivalInvalid(vertex); search_->endpointInvalid(vertex); } void -StaSimObserver::fanoutEdgesChangeAfter(Vertex *vertex) +StaSimObserver::fanoutEdgesChangeAfter(const Pin *pin) { + Vertex *vertex = graph_->pinDrvrVertex(pin); search_->requiredInvalid(vertex); search_->endpointInvalid(vertex); } @@ -213,7 +220,8 @@ StaSimObserver::fanoutEdgesChangeAfter(Vertex *vertex) class StaLevelizeObserver : public LevelizeObserver { public: - StaLevelizeObserver(Search *search, GraphDelayCalc *graph_delay_calc); + StaLevelizeObserver(Search *search, + GraphDelayCalc *graph_delay_calc); void levelsChangedBefore() override; void levelChangedBefore(Vertex *vertex) override; @@ -271,33 +279,6 @@ deleteAllMemory() //////////////////////////////////////////////////////////////// -// Singleton used by TCL commands. -Sta *Sta::sta_; - -Sta::Sta() : - StaState(), - current_instance_(nullptr), - cmd_corner_(nullptr), - verilog_reader_(nullptr), - check_timing_(nullptr), - check_slew_limits_(nullptr), - check_fanout_limits_(nullptr), - check_capacitance_limits_(nullptr), - check_min_pulse_widths_(nullptr), - check_min_periods_(nullptr), - check_max_skews_(nullptr), - clk_skews_(nullptr), - report_path_(nullptr), - power_(nullptr), - update_genclks_(false), - equiv_cells_(nullptr), - graph_sdc_annotated_(false), - // Default to same parasitics for all corners. - parasitics_per_corner_(false), - properties_(this) -{ -} - void Sta::makeComponents() { @@ -306,35 +287,32 @@ Sta::makeComponents() makeDebug(); makeUnits(); makeNetwork(); - makeSdc(); makeLevelize(); - makeParasitics(); - makeCorners(); + makeDefaultScene(); makeArcDelayCalc(); makeGraphDelayCalc(); - makeSim(); makeSearch(); makeLatches(); - makeClkNetwork(); makeSdcNetwork(); makeReportPath(); makePower(); makeClkSkews(); + makeCheckTiming(); + delay_ops_ = new DelayOpsScalar(); setCmdNamespace1(CmdNamespace::sdc); setThreadCount1(defaultThreadCount()); updateComponentsState(); makeObservers(); - // This must follow updateComponentsState. - makeParasiticAnalysisPts(); } void Sta::makeObservers() { graph_delay_calc_->setObserver(new StaDelayCalcObserver(search_)); - sim_->setObserver(new StaSimObserver(graph_delay_calc_, levelize_, search_)); + for (Mode *mode : modes_) + mode->sim()->setObserver(new StaSimObserver(this)); levelize_->setObserver(new StaLevelizeObserver(search_, graph_delay_calc_)); } @@ -369,20 +347,17 @@ Sta::updateComponentsState() sdc_network_->copyState(this); if (graph_) graph_->copyState(this); - sdc_->copyState(this); - corners_->copyState(this); + for (Mode *mode : modes_) + mode->copyState(this); levelize_->copyState(this); - parasitics_->copyState(this); arc_delay_calc_->copyState(this); - sim_->copyState(this); search_->copyState(this); latches_->copyState(this); graph_delay_calc_->copyState(this); report_path_->copyState(this); - if (check_timing_) - check_timing_->copyState(this); - clk_network_->copyState(this); + check_timing_->copyState(this); clk_skews_->copyState(this); + if (power_) power_->copyState(this); } @@ -411,24 +386,12 @@ Sta::makeNetwork() network_ = makeConcreteNetwork(); } -void -Sta::makeSdc() -{ - sdc_ = new Sdc(this); -} - void Sta::makeLevelize() { levelize_ = new Levelize(this); } -void -Sta::makeParasitics() -{ - parasitics_ = makeConcreteParasitics(this); -} - void Sta::makeArcDelayCalc() { @@ -441,12 +404,6 @@ Sta::makeGraphDelayCalc() graph_delay_calc_ = new GraphDelayCalc(this); } -void -Sta::makeSim() -{ - sim_ = new Sim(this); -} - void Sta::makeSearch() { @@ -472,21 +429,21 @@ Sta::makeCheckTiming() } void -Sta::makeCheckSlewLimits() +Sta::makeCheckSlews() { - check_slew_limits_ = new CheckSlewLimits(this); + check_slews_ = new CheckSlews(this); } void -Sta::makeCheckFanoutLimits() +Sta::makeCheckFanouts() { - check_fanout_limits_ = new CheckFanoutLimits(this); + check_fanouts_ = new CheckFanouts(this); } void -Sta::makeCheckCapacitanceLimits() +Sta::makeCheckCapacitances() { - check_capacitance_limits_ = new CheckCapacitanceLimits(this); + check_capacitances_ = new CheckCapacitances(this); } void @@ -513,12 +470,6 @@ Sta::makeReportPath() report_path_ = new ReportPath(this); } -void -Sta::makeClkNetwork() -{ - clk_network_ = new ClkNetwork(this); -} - void Sta::makePower() { @@ -545,14 +496,13 @@ Sta::sta() Sta::~Sta() { - delete variables_; // Verilog modules refer to the network in the sta so it has // to deleted before the network. delete verilog_reader_; // Delete "top down" to minimize chance of referencing deleted memory. - delete check_slew_limits_; - delete check_fanout_limits_; - delete check_capacitance_limits_; + delete check_slews_; + delete check_fanouts_; + delete check_capacitances_; delete check_min_pulse_widths_; delete check_min_periods_; delete check_max_skews_; @@ -562,45 +512,54 @@ Sta::~Sta() // Sdc references search filter, so delete search first. delete search_; delete latches_; - delete parasitics_; delete arc_delay_calc_; delete graph_delay_calc_; - delete sim_; delete levelize_; - delete sdc_; - delete corners_; delete graph_; delete sdc_network_; delete network_; delete debug_; delete units_; delete report_; - delete clk_network_; delete power_; delete equiv_cells_; delete dispatch_queue_; + delete variables_; + delete delay_ops_; + deleteContents(parasitics_name_map_); + deleteContents(modes_); + deleteContents(scenes_); } void Sta::clear() { - clkPinsInvalid(); - // Constraints reference search filter, so clear search first. - search_->clear(); - sdc_->clear(); - graph_sdc_annotated_ = false; - // corners are NOT cleared because they are used to index liberty files. + clearNonSdc(); + for (Mode *mode : modes_) + mode->sdc()->clear(); +} + +void +Sta::clearNonSdc() +{ + // Sdc holds search filter, so clear search first. levelize_->clear(); - if (parasitics_) - parasitics_->clear(); + deleteParasitics(); graph_delay_calc_->clear(); - sim_->clear(); power_->clear(); if (check_min_pulse_widths_) check_min_pulse_widths_->clear(); if (check_min_periods_) check_min_periods_->clear(); clk_skews_->clear(); + + // scenes are NOT cleared because they are used to index liberty files. + for (Mode *mode : modes_) { + mode->clkNetwork()->clkPinsInvalid(); + mode->sim()->clear(); + } + search_->clear(); + delete graph_; graph_ = nullptr; current_instance_ = nullptr; @@ -608,26 +567,64 @@ Sta::clear() updateComponentsState(); } +Sdc * +Sta::cmdSdc() const +{ + return cmdMode()->sdc(); +} + +void +Sta::setCmdMode(std::string_view mode_name) +{ + if (!mode_name.empty()) { + if (!mode_name_map_.contains(mode_name)) { + if (modes_.size() == 1 && modes_[0]->name() == "default") { + // No need for default mode if one is defined. + delete modes_[0]; + mode_name_map_.clear(); + modes_.clear(); + } + Mode *mode = new Mode(mode_name, mode_name_map_.size(), this); + mode_name_map_[std::string(mode_name)] = mode; + modes_.push_back(mode); + mode->sim()->setMode(mode); + mode->sim()->setObserver(new StaSimObserver(this)); + + if (scenes_.size() == 1 && scenes_[0]->name() == "default") + scenes_[0]->setMode(mode); + updateComponentsState(); + } + } +} + +Mode * +Sta::findMode(std::string_view mode_name) const +{ + return findStringKey(mode_name_map_, mode_name); +} + +ModeSeq +Sta::findModes(const std::string &name) const +{ + ModeSeq matches; + PatternMatch pattern(name); + for (Mode *mode : modes_) { + if (pattern.match(mode->name())) + matches.push_back(mode); + } + return matches; +} + void Sta::networkChanged() { - // Everything else from clear(). - search_->clear(); - levelize_->clear(); - if (parasitics_) - parasitics_->clear(); - graph_delay_calc_->clear(); - sim_->clear(); - if (check_min_pulse_widths_) - check_min_pulse_widths_->clear(); - if (check_min_periods_) - check_min_periods_->clear(); - clk_skews_->clear(); - delete graph_; - graph_ = nullptr; - graph_sdc_annotated_ = false; - current_instance_ = nullptr; - updateComponentsState(); + clear(); +} + +void +Sta::networkChangedNonSdc() +{ + clearNonSdc(); } void @@ -661,12 +658,12 @@ Sta::setCmdNamespace1(CmdNamespace namespc) { cmd_namespace_ = namespc; switch (cmd_namespace_) { - case CmdNamespace::sta: - cmd_network_ = network_; - break; - case CmdNamespace::sdc: - cmd_network_ = sdc_network_; - break; + case CmdNamespace::sta: + cmd_network_ = network_; + break; + case CmdNamespace::sdc: + cmd_network_ = sdc_network_; + break; } } @@ -688,14 +685,13 @@ Sta::setCurrentInstance(Instance *inst) //////////////////////////////////////////////////////////////// LibertyLibrary * -Sta::readLiberty(const char *filename, - Corner *corner, - const MinMaxAll *min_max, - bool infer_latches) +Sta::readLiberty(std::string_view filename, + Scene *scene, + const MinMaxAll *min_max, + bool infer_latches) { Stats stats(debug_, report_); - LibertyLibrary *library = readLibertyFile(filename, corner, min_max, - infer_latches); + LibertyLibrary *library = readLibertyFile(filename, scene, min_max, infer_latches); if (library // The default library is the first library read. // This corresponds to a link_path of '*'. @@ -709,46 +705,33 @@ Sta::readLiberty(const char *filename, } LibertyLibrary * -Sta::readLibertyFile(const char *filename, - Corner *corner, - const MinMaxAll *min_max, - bool infer_latches) +Sta::readLibertyFile(std::string_view filename, + Scene *scene, + const MinMaxAll *min_max, + bool infer_latches) { - LibertyLibrary *liberty = sta::readLibertyFile(filename, infer_latches, - network_); + LibertyLibrary *liberty = sta::readLibertyFile(filename, infer_latches, network_); if (liberty) { // Don't map liberty cells if they are redefined by reading another // library with the same cell names. - if (min_max == MinMaxAll::all()) { - readLibertyAfter(liberty, corner, MinMax::min()); - readLibertyAfter(liberty, corner, MinMax::max()); - } - else - readLibertyAfter(liberty, corner, min_max->asMinMax()); + readLibertyAfter(liberty, scene, min_max); network_->readLibertyAfter(liberty); } return liberty; } -LibertyLibrary * -Sta::readLibertyFile(const char *filename, - bool infer_latches) -{ - return sta::readLibertyFile(filename, infer_latches, network_); -} - void Sta::readLibertyAfter(LibertyLibrary *liberty, - Corner *corner, - const MinMax *min_max) + Scene *scene, + const MinMaxAll *min_max) { - corner->addLiberty(liberty, min_max); - LibertyLibrary::makeCornerMap(liberty, corner->libertyIndex(min_max), - network_, report_); + for (const MinMax *mm : min_max->range()) + scene->addLiberty(liberty, mm); + LibertyLibrary::makeSceneMap(liberty, scene, min_max, network_, report_); } bool -Sta::readVerilog(const char *filename) +Sta::readVerilog(std::string_view filename) { NetworkReader *network = networkReader(); if (network) { @@ -776,18 +759,34 @@ Sta::linkDesign(const char *top_cell_name, { clear(); Stats stats(debug_, report_); - bool status = network_->linkNetwork(top_cell_name, - make_black_boxes, - report_); + bool status = network_->linkNetwork(top_cell_name, make_black_boxes, report_); stats.report("Link"); return status; } //////////////////////////////////////////////////////////////// +bool +Sta::readSdf(std::string_view filename, + std::string_view path, + Scene *scene, + bool unescaped_dividers, + bool incremental_only, + MinMaxAll *cond_use) +{ + ensureLibLinked(); + ensureGraph(); + bool success = sta::readSdf(filename, path, scene, unescaped_dividers, + incremental_only, cond_use, this); + search_->arrivalsInvalid(); + return success; +} + +//////////////////////////////////////////////////////////////// + void Sta::setDebugLevel(const char *what, - int level) + int level) { debug_->setLevel(what, level); } @@ -795,83 +794,90 @@ Sta::setDebugLevel(const char *what, //////////////////////////////////////////////////////////////// void -Sta::setAnalysisType(AnalysisType analysis_type) +Sta::setAnalysisType(AnalysisType analysis_type, + Sdc *sdc) { - if (analysis_type != sdc_->analysisType()) { - sdc_->setAnalysisType(analysis_type); + if (analysis_type != sdc->analysisType()) { + sdc->setAnalysisType(analysis_type); delaysInvalid(); search_->deletePathGroups(); - corners_->analysisTypeChanged(); if (graph_) - graph_->setDelayCount(corners_->dcalcAnalysisPtCount()); + graph_->setDelayCount(dcalcAnalysisPtCount()); } } OperatingConditions * -Sta::operatingConditions(const MinMax *min_max) const +Sta::operatingConditions(const MinMax *min_max, + const Sdc *sdc) const { - return sdc_->operatingConditions(min_max); + return sdc->operatingConditions(min_max); } void Sta::setOperatingConditions(OperatingConditions *op_cond, - const MinMaxAll *min_max) + const MinMaxAll *min_max, + Sdc *sdc) { - sdc_->setOperatingConditions(op_cond, min_max); - corners_->operatingConditionsChanged(); + sdc->setOperatingConditions(op_cond, min_max); delaysInvalid(); } const Pvt * Sta::pvt(Instance *inst, - const MinMax *min_max) + const MinMax *min_max, + Sdc *sdc) { - return sdc_->pvt(inst, min_max); + return sdc->pvt(inst, min_max); } void Sta::setPvt(Instance *inst, - const MinMaxAll *min_max, - float process, - float voltage, - float temperature) + const MinMaxAll *min_max, + float process, + float voltage, + float temperature, + Sdc *sdc) { Pvt pvt(process, voltage, temperature); - setPvt(inst, min_max, pvt); + setPvt(inst, min_max, pvt, sdc); } void Sta::setPvt(const Instance *inst, - const MinMaxAll *min_max, - const Pvt &pvt) + const MinMaxAll *min_max, + const Pvt &pvt, + Sdc *sdc) { - sdc_->setPvt(inst, min_max, pvt); + sdc->setPvt(inst, min_max, pvt); delaysInvalidFrom(inst); } void Sta::setVoltage(const MinMax *min_max, - float voltage) + float voltage, + Sdc *sdc) { - sdc_->setVoltage(min_max, voltage); + sdc->setVoltage(min_max, voltage); } void Sta::setVoltage(const Net *net, const MinMax *min_max, - float voltage) + float voltage, + Sdc *sdc) { - sdc_->setVoltage(net, min_max, voltage); + sdc->setVoltage(net, min_max, voltage); } void Sta::setTimingDerate(TimingDerateType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate, + Sdc *sdc) { - sdc_->setTimingDerate(type, clk_data, rf, early_late, derate); + sdc->setTimingDerate(type, clk_data, rf, early_late, derate); // Delay calculation results are still valid. // The search derates delays while finding arrival times. search_->arrivalsInvalid(); @@ -879,12 +885,13 @@ Sta::setTimingDerate(TimingDerateType type, void Sta::setTimingDerate(const Net *net, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate, + Sdc *sdc) { - sdc_->setTimingDerate(net, clk_data, rf, early_late, derate); + sdc->setTimingDerate(net, clk_data, rf, early_late, derate); // Delay calculation results are still valid. // The search derates delays while finding arrival times. search_->arrivalsInvalid(); @@ -892,13 +899,14 @@ Sta::setTimingDerate(const Net *net, void Sta::setTimingDerate(const Instance *inst, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) -{ - sdc_->setTimingDerate(inst, type, clk_data, rf, early_late, derate); + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate, + Sdc *sdc) +{ + sdc->setTimingDerate(inst, type, clk_data, rf, early_late, derate); // Delay calculation results are still valid. // The search derates delays while finding arrival times. search_->arrivalsInvalid(); @@ -906,22 +914,23 @@ Sta::setTimingDerate(const Instance *inst, void Sta::setTimingDerate(const LibertyCell *cell, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) -{ - sdc_->setTimingDerate(cell, type, clk_data, rf, early_late, derate); + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate, + Sdc *sdc) +{ + sdc->setTimingDerate(cell, type, clk_data, rf, early_late, derate); // Delay calculation results are still valid. // The search derates delays while finding arrival times. search_->arrivalsInvalid(); } void -Sta::unsetTimingDerate() +Sta::unsetTimingDerate(Sdc *sdc) { - sdc_->unsetTimingDerate(); + sdc->unsetTimingDerate(); // Delay calculation results are still valid. // The search derates delays while finding arrival times. search_->arrivalsInvalid(); @@ -929,300 +938,334 @@ Sta::unsetTimingDerate() void Sta::setInputSlew(const Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew) + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew, + Sdc *sdc) { - sdc_->setInputSlew(port, rf, min_max, slew); + sdc->setInputSlew(port, rf, min_max, slew); delaysInvalidFrom(port); } void Sta::setDriveCell(const LibertyLibrary *library, - const LibertyCell *cell, - const Port *port, - const LibertyPort *from_port, - float *from_slews, - const LibertyPort *to_port, - const RiseFallBoth *rf, - const MinMaxAll *min_max) -{ - sdc_->setDriveCell(library, cell, port, from_port, from_slews, to_port, - rf, min_max); + const LibertyCell *cell, + const Port *port, + const LibertyPort *from_port, + const DriveCellSlews &from_slews, + const LibertyPort *to_port, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + Sdc *sdc) +{ + sdc->setDriveCell(library, cell, port, from_port, from_slews, to_port, rf, + min_max); delaysInvalidFrom(port); } void Sta::setDriveResistance(const Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float res) + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float res, + Sdc *sdc) { - sdc_->setDriveResistance(port, rf, min_max, res); + sdc->setDriveResistance(port, rf, min_max, res); delaysInvalidFrom(port); } void Sta::setLatchBorrowLimit(const Pin *pin, - float limit) + float limit, + Sdc *sdc) { - sdc_->setLatchBorrowLimit(pin, limit); + sdc->setLatchBorrowLimit(pin, limit); search_->requiredInvalid(pin); } void Sta::setLatchBorrowLimit(const Instance *inst, - float limit) + float limit, + Sdc *sdc) { - sdc_->setLatchBorrowLimit(inst, limit); + sdc->setLatchBorrowLimit(inst, limit); search_->requiredInvalid(inst); } void Sta::setLatchBorrowLimit(const Clock *clk, - float limit) + float limit, + Sdc *sdc) { - sdc_->setLatchBorrowLimit(clk, limit); + sdc->setLatchBorrowLimit(clk, limit); search_->arrivalsInvalid(); } void Sta::setMinPulseWidth(const RiseFallBoth *rf, - float min_width) + float min_width, + Sdc *sdc) { - sdc_->setMinPulseWidth(rf, min_width); + sdc->setMinPulseWidth(rf, min_width); } void Sta::setMinPulseWidth(const Pin *pin, - const RiseFallBoth *rf, - float min_width) + const RiseFallBoth *rf, + float min_width, + Sdc *sdc) { - sdc_->setMinPulseWidth(pin, rf, min_width); + sdc->setMinPulseWidth(pin, rf, min_width); } void Sta::setMinPulseWidth(const Instance *inst, - const RiseFallBoth *rf, - float min_width) + const RiseFallBoth *rf, + float min_width, + Sdc *sdc) { - sdc_->setMinPulseWidth(inst, rf, min_width); + sdc->setMinPulseWidth(inst, rf, min_width); } void Sta::setMinPulseWidth(const Clock *clk, - const RiseFallBoth *rf, - float min_width) + const RiseFallBoth *rf, + float min_width, + Sdc *sdc) { - sdc_->setMinPulseWidth(clk, rf, min_width); + sdc->setMinPulseWidth(clk, rf, min_width); } void -Sta::setWireloadMode(WireloadMode mode) +Sta::setWireloadMode(WireloadMode mode, + Sdc *sdc) { - sdc_->setWireloadMode(mode); + sdc->setWireloadMode(mode); delaysInvalid(); } void Sta::setWireload(Wireload *wireload, - const MinMaxAll *min_max) + const MinMaxAll *min_max, + Sdc *sdc) { - sdc_->setWireload(wireload, min_max); + sdc->setWireload(wireload, min_max); delaysInvalid(); } void Sta::setWireloadSelection(WireloadSelection *selection, - const MinMaxAll *min_max) + const MinMaxAll *min_max, + Sdc *sdc) { - sdc_->setWireloadSelection(selection, min_max); + sdc->setWireloadSelection(selection, min_max); delaysInvalid(); } void Sta::setSlewLimit(Clock *clk, - const RiseFallBoth *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - float slew) + const RiseFallBoth *rf, + PathClkOrData clk_data, + const MinMax *min_max, + float slew, + Sdc *sdc) { - sdc_->setSlewLimit(clk, rf, clk_data, min_max, slew); + sdc->setSlewLimit(clk, rf, clk_data, min_max, slew); } void Sta::setSlewLimit(Port *port, - const MinMax *min_max, - float slew) + const MinMax *min_max, + float slew, + Sdc *sdc) { - sdc_->setSlewLimit(port, min_max, slew); + sdc->setSlewLimit(port, min_max, slew); } void Sta::setSlewLimit(Cell *cell, - const MinMax *min_max, - float slew) + const MinMax *min_max, + float slew, + Sdc *sdc) { - sdc_->setSlewLimit(cell, min_max, slew); + sdc->setSlewLimit(cell, min_max, slew); } void Sta::setCapacitanceLimit(Cell *cell, - const MinMax *min_max, - float cap) + const MinMax *min_max, + float cap, + Sdc *sdc) { - sdc_->setCapacitanceLimit(cell, min_max, cap); + sdc->setCapacitanceLimit(cell, min_max, cap); } void Sta::setCapacitanceLimit(Port *port, - const MinMax *min_max, - float cap) + const MinMax *min_max, + float cap, + Sdc *sdc) { - sdc_->setCapacitanceLimit(port, min_max, cap); + sdc->setCapacitanceLimit(port, min_max, cap); } void Sta::setCapacitanceLimit(Pin *pin, - const MinMax *min_max, - float cap) + const MinMax *min_max, + float cap, + Sdc *sdc) { - sdc_->setCapacitanceLimit(pin, min_max, cap); + sdc->setCapacitanceLimit(pin, min_max, cap); } void Sta::setFanoutLimit(Cell *cell, - const MinMax *min_max, - float fanout) + const MinMax *min_max, + float fanout, + Sdc *sdc) { - sdc_->setFanoutLimit(cell, min_max, fanout); + sdc->setFanoutLimit(cell, min_max, fanout); } void Sta::setFanoutLimit(Port *port, - const MinMax *min_max, - float fanout) + const MinMax *min_max, + float fanout, + Sdc *sdc) { - sdc_->setFanoutLimit(port, min_max, fanout); + sdc->setFanoutLimit(port, min_max, fanout); } void -Sta::setMaxArea(float area) +Sta::setMaxArea(float area, + Sdc *sdc) { - sdc_->setMaxArea(area); + sdc->setMaxArea(area); } void -Sta::setMaxDynamicPower(float power) +Sta::setMaxDynamicPower(float power, + Sdc *sdc) { - sdc_->setMaxDynamicPower(power); + sdc->setMaxDynamicPower(power); } void -Sta::setMaxLeakagePower(float power) +Sta::setMaxLeakagePower(float power, + Sdc *sdc) { - sdc_->setMaxLeakagePower(power); + sdc->setMaxLeakagePower(power); } void -Sta::makeClock(const char *name, - PinSet *pins, - bool add_to_pins, - float period, - FloatSeq *waveform, - char *comment) +Sta::makeClock(std::string_view name, + const PinSet &pins, + bool add_to_pins, + float period, + const FloatSeq &waveform, + std::string_view comment, + const Mode *mode) { - sdc_->makeClock(name, pins, add_to_pins, period, waveform, comment); + mode->sdc()->makeClock(name, pins, add_to_pins, period, waveform, comment); update_genclks_ = true; search_->arrivalsInvalid(); power_->activitiesInvalid(); -} - -void -Sta::makeGeneratedClock(const char *name, - PinSet *pins, - bool add_to_pins, - Pin *src_pin, - Clock *master_clk, - int divide_by, - int multiply_by, - float duty_cycle, - bool invert, - bool combinational, - IntSeq *edges, - FloatSeq *edge_shifts, - char *comment) -{ - sdc_->makeGeneratedClock(name, pins, add_to_pins, - src_pin, master_clk, - divide_by, multiply_by, duty_cycle, - invert, combinational, - edges, edge_shifts, comment); + mode->clkNetwork()->clkPinsInvalid(); +} + +void +Sta::makeGeneratedClock(std::string_view name, + const PinSet &pins, + bool add_to_pins, + Pin *src_pin, + Clock *master_clk, + int divide_by, + int multiply_by, + float duty_cycle, + bool invert, + bool combinational, + const IntSeq &edges, + const FloatSeq &edge_shifts, + std::string_view comment, + const Mode *mode) +{ + mode->sdc()->makeGeneratedClock(name, pins, add_to_pins, src_pin, master_clk, + divide_by, multiply_by, duty_cycle, invert, + combinational, edges, edge_shifts, comment); update_genclks_ = true; search_->arrivalsInvalid(); power_->activitiesInvalid(); + mode->clkNetwork()->clkPinsInvalid(); } void -Sta::removeClock(Clock *clk) +Sta::removeClock(Clock *clk, + Sdc *sdc) { - sdc_->removeClock(clk); + sdc->removeClock(clk); search_->arrivalsInvalid(); power_->activitiesInvalid(); } bool -Sta::isClockSrc(const Pin *pin) const +Sta::isClockSrc(const Pin *pin, + const Sdc *sdc) const { - return sdc_->isClock(pin); + return sdc->isClock(pin); } void -Sta::setPropagatedClock(Clock *clk) +Sta::setPropagatedClock(Clock *clk, + const Mode *mode) { - sdc_->setPropagatedClock(clk); + mode->sdc()->setPropagatedClock(clk); delaysInvalid(); - clkPinsInvalid(); + mode->clkNetwork()->clkPinsInvalid(); } void -Sta::removePropagatedClock(Clock *clk) +Sta::removePropagatedClock(Clock *clk, + const Mode *mode) { - sdc_->removePropagatedClock(clk); + mode->sdc()->removePropagatedClock(clk); delaysInvalid(); - clkPinsInvalid(); + mode->clkNetwork()->clkPinsInvalid(); } void -Sta::setPropagatedClock(Pin *pin) +Sta::setPropagatedClock(Pin *pin, + const Mode *mode) { - sdc_->setPropagatedClock(pin); + mode->sdc()->setPropagatedClock(pin); delaysInvalid(); - clkPinsInvalid(); + mode->clkNetwork()->clkPinsInvalid(); } void -Sta::removePropagatedClock(Pin *pin) +Sta::removePropagatedClock(Pin *pin, + const Mode *mode) { - sdc_->removePropagatedClock(pin); + mode->sdc()->removePropagatedClock(pin); delaysInvalid(); - clkPinsInvalid(); + mode->clkNetwork()->clkPinsInvalid(); } void Sta::setClockSlew(Clock *clk, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew) + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew, + Sdc *sdc) { - sdc_->setClockSlew(clk, rf, min_max, slew); + sdc->setClockSlew(clk, rf, min_max, slew); clockSlewChanged(clk); } void -Sta::removeClockSlew(Clock *clk) +Sta::removeClockSlew(Clock *clk, + Sdc *sdc) { - sdc_->removeClockSlew(clk); + sdc->removeClockSlew(clk); clockSlewChanged(clk); } @@ -1236,66 +1279,51 @@ Sta::clockSlewChanged(Clock *clk) void Sta::setClockLatency(Clock *clk, - Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float delay) + Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float delay, + Sdc *sdc) { - sdcChangedGraph(); - sdc_->setClockLatency(clk, pin, rf, min_max, delay); + sdc->setClockLatency(clk, pin, rf, min_max, delay); search_->arrivalsInvalid(); } -void -Sta::sdcChangedGraph() -{ - if (graph_sdc_annotated_) - sdc_->removeGraphAnnotations(); - graph_sdc_annotated_ = false; -} - -void -Sta::ensureGraphSdcAnnotated() -{ - if (!graph_sdc_annotated_) { - sdc_->annotateGraph(); - graph_sdc_annotated_ = true; - } -} - void Sta::removeClockLatency(const Clock *clk, - const Pin *pin) + const Pin *pin, + Sdc *sdc) { - sdcChangedGraph(); - sdc_->removeClockLatency(clk, pin); + sdc->removeClockLatency(clk, pin); search_->arrivalsInvalid(); } void Sta::setClockInsertion(const Clock *clk, - const Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - const EarlyLateAll *early_late, - float delay) -{ - sdc_->setClockInsertion(clk, pin, rf, min_max, early_late, delay); + const Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay, + Sdc *sdc) +{ + sdc->setClockInsertion(clk, pin, rf, min_max, early_late, delay); search_->arrivalsInvalid(); } void Sta::removeClockInsertion(const Clock *clk, - const Pin *pin) + const Pin *pin, + Sdc *sdc) { - sdc_->removeClockInsertion(clk, pin); + sdc->removeClockInsertion(clk, pin); search_->arrivalsInvalid(); } void Sta::setClockUncertainty(Clock *clk, - const SetupHoldAll *setup_hold, - float uncertainty) + const SetupHoldAll *setup_hold, + float uncertainty) { clk->setUncertainty(setup_hold, uncertainty); search_->arrivalsInvalid(); @@ -1303,7 +1331,7 @@ Sta::setClockUncertainty(Clock *clk, void Sta::removeClockUncertainty(Clock *clk, - const SetupHoldAll *setup_hold) + const SetupHoldAll *setup_hold) { clk->removeUncertainty(setup_hold); search_->arrivalsInvalid(); @@ -1311,97 +1339,126 @@ Sta::removeClockUncertainty(Clock *clk, void Sta::setClockUncertainty(Pin *pin, - const SetupHoldAll *setup_hold, - float uncertainty) + const SetupHoldAll *setup_hold, + float uncertainty, + Sdc *sdc) { - sdc_->setClockUncertainty(pin, setup_hold, uncertainty); + sdc->setClockUncertainty(pin, setup_hold, uncertainty); search_->arrivalsInvalid(); } void Sta::removeClockUncertainty(Pin *pin, - const SetupHoldAll *setup_hold) + const SetupHoldAll *setup_hold, + Sdc *sdc) { - sdc_->removeClockUncertainty(pin, setup_hold); + sdc->removeClockUncertainty(pin, setup_hold); search_->arrivalsInvalid(); } void Sta::setClockUncertainty(Clock *from_clk, - const RiseFallBoth *from_rf, - Clock *to_clk, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold, - float uncertainty) -{ - sdc_->setClockUncertainty(from_clk, from_rf, to_clk, to_rf, - setup_hold, uncertainty); + const RiseFallBoth *from_rf, + Clock *to_clk, + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold, + float uncertainty, + Sdc *sdc) +{ + sdc->setClockUncertainty(from_clk, from_rf, to_clk, to_rf, setup_hold, + uncertainty); search_->arrivalsInvalid(); } void Sta::removeClockUncertainty(Clock *from_clk, - const RiseFallBoth *from_rf, - Clock *to_clk, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold) + const RiseFallBoth *from_rf, + Clock *to_clk, + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold, + Sdc *sdc) { - sdc_->removeClockUncertainty(from_clk, from_rf, to_clk, to_rf, setup_hold); + sdc->removeClockUncertainty(from_clk, from_rf, to_clk, to_rf, setup_hold); search_->arrivalsInvalid(); } ClockGroups * -Sta::makeClockGroups(const char *name, - bool logically_exclusive, - bool physically_exclusive, - bool asynchronous, - bool allow_paths, - const char *comment) -{ - ClockGroups *groups = sdc_->makeClockGroups(name, - logically_exclusive, - physically_exclusive, - asynchronous, - allow_paths, - comment); +Sta::makeClockGroups(std::string_view name, + bool logically_exclusive, + bool physically_exclusive, + bool asynchronous, + bool allow_paths, + std::string_view comment, + Sdc *sdc) +{ + ClockGroups *groups = sdc->makeClockGroups(name, logically_exclusive, + physically_exclusive, + asynchronous, allow_paths, + comment); search_->requiredsInvalid(); return groups; } void -Sta::removeClockGroupsLogicallyExclusive(const char *name) +Sta::removeClockGroupsLogicallyExclusive(Sdc *sdc) +{ + sdc->removeClockGroupsLogicallyExclusive(); + search_->requiredsInvalid(); +} + +void +Sta::removeClockGroupsLogicallyExclusive(const std::string &name, + Sdc *sdc) +{ + sdc->removeClockGroupsLogicallyExclusive(name); + search_->requiredsInvalid(); +} + +void +Sta::removeClockGroupsPhysicallyExclusive(Sdc *sdc) +{ + sdc->removeClockGroupsPhysicallyExclusive(); + search_->requiredsInvalid(); +} + +void +Sta::removeClockGroupsPhysicallyExclusive(const std::string &name, + Sdc *sdc) { - sdc_->removeClockGroupsLogicallyExclusive(name); + sdc->removeClockGroupsPhysicallyExclusive(name); search_->requiredsInvalid(); } void -Sta::removeClockGroupsPhysicallyExclusive(const char *name) +Sta::removeClockGroupsAsynchronous(Sdc *sdc) { - sdc_->removeClockGroupsPhysicallyExclusive(name); + sdc->removeClockGroupsAsynchronous(); search_->requiredsInvalid(); } void -Sta::removeClockGroupsAsynchronous(const char *name) +Sta::removeClockGroupsAsynchronous(const std::string &name, + Sdc *sdc) { - sdc_->removeClockGroupsAsynchronous(name); + sdc->removeClockGroupsAsynchronous(name); search_->requiredsInvalid(); } void Sta::makeClockGroup(ClockGroups *clk_groups, - ClockSet *clks) + ClockSet *clks, + Sdc *sdc) { - sdc_->makeClockGroup(clk_groups, clks); + sdc->makeClockGroup(clk_groups, clks); } void Sta::setClockSense(PinSet *pins, - ClockSet *clks, - ClockSense sense) + ClockSet *clks, + ClockSense sense, + Sdc *sdc) { - sdc_->setClockSense(pins, clks, sense); + sdc->setClockSense(pins, clks, sense); search_->arrivalsInvalid(); } @@ -1409,104 +1466,104 @@ Sta::setClockSense(PinSet *pins, void Sta::setClockGatingCheck(const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin) + const SetupHold *setup_hold, + float margin, + Sdc *sdc) { - sdc_->setClockGatingCheck(rf, setup_hold, margin); + sdc->setClockGatingCheck(rf, setup_hold, margin); search_->arrivalsInvalid(); } void Sta::setClockGatingCheck(Clock *clk, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin) + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + Sdc *sdc) { - sdc_->setClockGatingCheck(clk, rf, setup_hold, margin); + sdc->setClockGatingCheck(clk, rf, setup_hold, margin); search_->arrivalsInvalid(); } void Sta::setClockGatingCheck(Instance *inst, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value) + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value, + Sdc *sdc) { - sdc_->setClockGatingCheck(inst, rf, setup_hold, margin,active_value); + sdc->setClockGatingCheck(inst, rf, setup_hold, margin, active_value); search_->arrivalsInvalid(); } void Sta::setClockGatingCheck(Pin *pin, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value) + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value, + Sdc *sdc) { - sdc_->setClockGatingCheck(pin, rf, setup_hold, margin,active_value); + sdc->setClockGatingCheck(pin, rf, setup_hold, margin, active_value); search_->arrivalsInvalid(); } void Sta::setDataCheck(Pin *from, - const RiseFallBoth *from_rf, - Pin *to, - const RiseFallBoth *to_rf, - Clock *clk, - const SetupHoldAll *setup_hold, - float margin) -{ - sdcChangedGraph(); - sdc_->setDataCheck(from, from_rf, to, to_rf, clk, setup_hold,margin); + const RiseFallBoth *from_rf, + Pin *to, + const RiseFallBoth *to_rf, + Clock *clk, + const SetupHoldAll *setup_hold, + float margin, + Sdc *sdc) +{ + sdc->setDataCheck(from, from_rf, to, to_rf, clk, setup_hold, margin); search_->requiredInvalid(to); } void Sta::removeDataCheck(Pin *from, - const RiseFallBoth *from_rf, - Pin *to, - const RiseFallBoth *to_rf, - Clock *clk, - const SetupHoldAll *setup_hold) -{ - sdc_->removeDataCheck(from, from_rf, to, to_rf, clk, setup_hold); + const RiseFallBoth *from_rf, + Pin *to, + const RiseFallBoth *to_rf, + Clock *clk, + const SetupHoldAll *setup_hold, + Sdc *sdc) +{ + sdc->removeDataCheck(from, from_rf, to, to_rf, clk, setup_hold); search_->requiredInvalid(to); } //////////////////////////////////////////////////////////////// void -Sta::disable(Pin *pin) +Sta::disable(Pin *pin, + Sdc *sdc) { - sdcChangedGraph(); - sdc_->disable(pin); - // Levelization respects disabled edges. - levelize_->invalid(); + sdc->disable(pin); graph_delay_calc_->delayInvalid(pin); search_->arrivalsInvalid(); } void -Sta::removeDisable(Pin *pin) +Sta::removeDisable(Pin *pin, + Sdc *sdc) { - sdcChangedGraph(); - sdc_->removeDisable(pin); + sdc->removeDisable(pin); disableAfter(); - // Levelization respects disabled edges. - levelize_->invalid(); graph_delay_calc_->delayInvalid(pin); search_->arrivalsInvalid(); } void Sta::disable(Instance *inst, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to, + Sdc *sdc) { - sdcChangedGraph(); - sdc_->disable(inst, from, to); - + sdc->disable(inst, from, to); if (from) { Pin *from_pin = network_->findPin(inst, from); graph_delay_calc_->delayInvalid(from_pin); @@ -1523,19 +1580,16 @@ Sta::disable(Instance *inst, } delete pin_iter; } - // Levelization respects disabled edges. - levelize_->invalid(); search_->arrivalsInvalid(); } void Sta::removeDisable(Instance *inst, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to, + Sdc *sdc) { - sdcChangedGraph(); - sdc_->removeDisable(inst, from, to); - + sdc->removeDisable(inst, from, to); if (from) { Pin *from_pin = network_->findPin(inst, from); graph_delay_calc_->delayInvalid(from_pin); @@ -1552,100 +1606,105 @@ Sta::removeDisable(Instance *inst, } delete pin_iter; } - // Levelization respects disabled edges. - levelize_->invalid(); search_->arrivalsInvalid(); } void Sta::disable(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to, + Sdc *sdc) { - sdc_->disable(cell, from, to); + sdc->disable(cell, from, to); disableAfter(); } void Sta::removeDisable(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to, + Sdc *sdc) { - sdc_->removeDisable(cell, from, to); + sdc->removeDisable(cell, from, to); disableAfter(); } void -Sta::disable(LibertyPort *port) +Sta::disable(LibertyPort *port, + Sdc *sdc) { - sdcChangedGraph(); - sdc_->disable(port); + sdc->disable(port); disableAfter(); } void -Sta::removeDisable(LibertyPort *port) +Sta::removeDisable(LibertyPort *port, + Sdc *sdc) { - sdcChangedGraph(); - sdc_->removeDisable(port); + sdc->removeDisable(port); disableAfter(); } void -Sta::disable(Port *port) +Sta::disable(Port *port, + Sdc *sdc) { - sdc_->disable(port); + sdc->disable(port); disableAfter(); } void -Sta::removeDisable(Port *port) +Sta::removeDisable(Port *port, + Sdc *sdc) { - sdc_->removeDisable(port); + sdc->removeDisable(port); disableAfter(); } void -Sta::disable(Edge *edge) +Sta::disable(Edge *edge, + Sdc *sdc) { - sdc_->disable(edge); + sdc->disable(edge); disableAfter(); } void -Sta::removeDisable(Edge *edge) +Sta::removeDisable(Edge *edge, + Sdc *sdc) { - sdc_->removeDisable(edge); + sdc->removeDisable(edge); disableAfter(); } void -Sta::disable(TimingArcSet *arc_set) +Sta::disable(TimingArcSet *arc_set, + Sdc *sdc) { - sdc_->disable(arc_set); + sdc->disable(arc_set); disableAfter(); } void -Sta::removeDisable(TimingArcSet *arc_set) +Sta::removeDisable(TimingArcSet *arc_set, + Sdc *sdc) { - sdc_->removeDisable(arc_set); + sdc->removeDisable(arc_set); disableAfter(); } void Sta::disableAfter() { - // Levelization respects disabled edges. - levelize_->invalid(); delaysInvalid(); } //////////////////////////////////////////////////////////////// EdgeSeq -Sta::disabledEdges() +Sta::disabledEdges(const Mode *mode) { + const Sdc *sdc = mode->sdc(); ensureLevelized(); EdgeSeq disabled_edges; VertexIterator vertex_iter(graph_); @@ -1654,65 +1713,67 @@ Sta::disabledEdges() VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); - if (isDisabledConstant(edge) - || isDisabledCondDefault(edge) - || isDisabledConstraint(edge) - || edge->isDisabledLoop() - || isDisabledPresetClr(edge)) - disabled_edges.push_back(edge); + if (isDisabledConstant(edge, mode) || isDisabledCondDefault(edge) + || isDisabledConstraint(edge, sdc) || edge->isDisabledLoop() + || isDisabledPresetClr(edge)) + disabled_edges.push_back(edge); } } return disabled_edges; } - EdgeSeq -Sta::disabledEdgesSorted() +Sta::disabledEdgesSorted(const Mode *mode) { - EdgeSeq disabled_edges = disabledEdges(); + EdgeSeq disabled_edges = disabledEdges(mode); sortEdges(&disabled_edges, network_, graph_); return disabled_edges; } bool -Sta::isDisabledConstraint(Edge *edge) +Sta::isDisabledConstraint(Edge *edge, + const Sdc *sdc) { Pin *from_pin = edge->from(graph_)->pin(); Pin *to_pin = edge->to(graph_)->pin(); - const Instance *inst = network_->instance(from_pin); - TimingArcSet *arc_set = edge->timingArcSet(); - return sdc_->isDisabled(from_pin) - || sdc_->isDisabled(to_pin) - || sdc_->isDisabled(inst, from_pin, to_pin, edge->role()) - || sdc_->isDisabled(edge) - || sdc_->isDisabled(arc_set); + return sdc->isDisabledConstraint(from_pin) || sdc->isDisabledConstraint(to_pin) + || sdc->isDisabledConstraint(edge); } bool -Sta::isDisabledConstant(Edge *edge) +Sta::isConstant(const Pin *pin, + const Mode *mode) const { - sim_->ensureConstantsPropagated(); + Sim *sim = mode->sim(); + sim->ensureConstantsPropagated(); + return sim->isConstant(pin); +} + +bool +Sta::isDisabledConstant(Edge *edge, + const Mode *mode) +{ + Sim *sim = mode->sim(); + sim->ensureConstantsPropagated(); const TimingRole *role = edge->role(); Vertex *from_vertex = edge->from(graph_); Pin *from_pin = from_vertex->pin(); Vertex *to_vertex = edge->to(graph_); Pin *to_pin = to_vertex->pin(); const Instance *inst = network_->instance(from_pin); - return sim_->logicZeroOne(from_vertex) - || sim_->logicZeroOne(to_vertex) - || (!role->isWire() - && (isCondDisabled(edge, inst, from_pin, to_pin, network_, sim_) - || isModeDisabled(edge, inst, network_, sim_) - || hasDisabledArcs(edge, graph_))); + return sim->isConstant(from_vertex) || sim->isConstant(to_vertex) + || (!role->isWire() + && (sim->isDisabledCond(edge, inst, from_pin, to_pin) + || sim->isDisabledMode(edge, inst) || hasDisabledArcs(edge, mode))); } static bool hasDisabledArcs(Edge *edge, - Graph *graph) + const Mode *mode) { TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *arc : arc_set->arcs()) { - if (!searchThru(edge, arc, graph)) + if (!searchThru(edge, arc, mode)) return true; } return false; @@ -1725,39 +1786,38 @@ Sta::isDisabledLoop(Edge *edge) const } PinSet -Sta::disabledConstantPins(Edge *edge) +Sta::disabledConstantPins(Edge *edge, + const Mode *mode) { - sim_->ensureConstantsPropagated(); + Sim *sim = mode->sim(); + sim->ensureConstantsPropagated(); PinSet pins(network_); Vertex *from_vertex = edge->from(graph_); Pin *from_pin = from_vertex->pin(); Vertex *to_vertex = edge->to(graph_); const Pin *to_pin = to_vertex->pin(); - if (sim_->logicZeroOne(from_vertex)) + if (sim->isConstant(from_vertex)) pins.insert(from_pin); if (edge->role()->isWire()) { - if (sim_->logicZeroOne(to_vertex)) + if (sim->isConstant(to_vertex)) pins.insert(to_pin); } else { const Instance *inst = network_->instance(to_pin); bool is_disabled; FuncExpr *disable_cond; - isCondDisabled(edge, inst, from_pin, to_pin, network_, sim_, - is_disabled, disable_cond); + sim->isDisabledCond(edge, inst, from_pin, to_pin, is_disabled, disable_cond); if (is_disabled) - exprConstantPins(disable_cond, inst, pins); - isModeDisabled(edge, inst, network_, sim_, - is_disabled, disable_cond); + exprConstantPins(disable_cond, inst, mode, pins); + sim->isDisabledMode(edge, inst, is_disabled, disable_cond); if (is_disabled) - exprConstantPins(disable_cond, inst, pins); - if (hasDisabledArcs(edge, graph_)) { + exprConstantPins(disable_cond, inst, mode, pins); + if (hasDisabledArcs(edge, mode)) { LibertyPort *to_port = network_->libertyPort(to_pin); if (to_port) { - FuncExpr *func = to_port->function(); - if (func - && sim_->functionSense(inst, from_pin, to_pin) != edge->sense()) - exprConstantPins(func, inst, pins); + FuncExpr *func = to_port->function(); + if (func && sim->functionSense(inst, from_pin, to_pin) != edge->sense()) + exprConstantPins(func, inst, mode, pins); } } } @@ -1765,27 +1825,29 @@ Sta::disabledConstantPins(Edge *edge) } TimingSense -Sta::simTimingSense(Edge *edge) +Sta::simTimingSense(Edge *edge, + const Mode *mode) { Pin *from_pin = edge->from(graph_)->pin(); Pin *to_pin = edge->to(graph_)->pin(); Instance *inst = network_->instance(from_pin); - return sim_->functionSense(inst, from_pin, to_pin); + return mode->sim()->functionSense(inst, from_pin, to_pin); } void Sta::exprConstantPins(FuncExpr *expr, const Instance *inst, + const Mode *mode, + // Return value. PinSet &pins) { - FuncExprPortIterator port_iter(expr); - while (port_iter.hasNext()) { - LibertyPort *port = port_iter.next(); + LibertyPortSet ports = expr->ports(); + for (LibertyPort *port : ports) { Pin *pin = network_->findPin(inst, port); if (pin) { - LogicValue value = sim_->logicValue(pin); + LogicValue value = mode->sim()->simValue(pin); if (value != LogicValue::unknown) - pins.insert(pin); + pins.insert(pin); } } } @@ -1793,75 +1855,71 @@ Sta::exprConstantPins(FuncExpr *expr, bool Sta::isDisabledBidirectInstPath(Edge *edge) const { - return !variables_->bidirectInstPathsEnabled() - && edge->isBidirectInstPath(); -} - -bool -Sta::isDisabledBidirectNetPath(Edge *edge) const -{ - return !variables_->bidirectNetPathsEnabled() - && edge->isBidirectNetPath(); + return !variables_->bidirectInstPathsEnabled() && edge->isBidirectInstPath(); } bool Sta::isDisabledPresetClr(Edge *edge) const { return !variables_->presetClrArcsEnabled() - && edge->role() == TimingRole::regSetClr(); + && edge->role() == TimingRole::regSetClr(); } void -Sta::disableClockGatingCheck(Instance *inst) +Sta::disableClockGatingCheck(Instance *inst, + Sdc *sdc) { - sdc_->disableClockGatingCheck(inst); + sdc->disableClockGatingCheck(inst); search_->endpointsInvalid(); } void -Sta::disableClockGatingCheck(Pin *pin) +Sta::disableClockGatingCheck(Pin *pin, + Sdc *sdc) { - sdc_->disableClockGatingCheck(pin); + sdc->disableClockGatingCheck(pin); search_->endpointsInvalid(); } void -Sta::disableClockGatingCheck(LibertyCell *cell) +Sta::disableClockGatingCheck(LibertyCell *cell, + Sdc *sdc) { - sdc_->disableClockGatingCheck(cell); + sdc->disableClockGatingCheck(cell); search_->endpointsInvalid(); } void -Sta::removeDisableClockGatingCheck(Instance *inst) +Sta::removeDisableClockGatingCheck(Instance *inst, + Sdc *sdc) { - sdc_->removeDisableClockGatingCheck(inst); + sdc->removeDisableClockGatingCheck(inst); search_->endpointsInvalid(); } void -Sta::removeDisableClockGatingCheck(Pin *pin) +Sta::removeDisableClockGatingCheck(Pin *pin, + Sdc *sdc) { - sdc_->removeDisableClockGatingCheck(pin); + sdc->removeDisableClockGatingCheck(pin); search_->endpointsInvalid(); } void -Sta::removeDisableClockGatingCheck(LibertyCell *cell) +Sta::removeDisableClockGatingCheck(LibertyCell *cell, + Sdc *sdc) { - sdc_->removeDisableClockGatingCheck(cell); + sdc->removeDisableClockGatingCheck(cell); search_->endpointsInvalid(); } void Sta::setLogicValue(Pin *pin, - LogicValue value) + LogicValue value, + Mode *mode) { - sdc_->setLogicValue(pin, value); - // Levelization respects constant disabled edges. - levelize_->invalid(); - power_->activitiesInvalid(); - sim_->constantsInvalid(); + mode->sdc()->setLogicValue(pin, value); + mode->sim()->constantsInvalid(); // Constants disable edges which isolate downstream vertices of the // graph from the delay calculator's BFS search. This means that // simply invaldating the delays downstream from the constant pin @@ -1869,32 +1927,32 @@ Sta::setLogicValue(Pin *pin, // calculator searched thru disabled edges but ignored their // results. delaysInvalid(); + power_->activitiesInvalid(); } void Sta::setCaseAnalysis(Pin *pin, - LogicValue value) + LogicValue value, + Mode *mode) { - sdc_->setCaseAnalysis(pin, value); - power_->activitiesInvalid(); // Levelization respects constant disabled edges. - levelize_->invalid(); - sim_->constantsInvalid(); + mode->sdc()->setCaseAnalysis(pin, value); + mode->sim()->constantsInvalid(); // Constants disable edges which isolate downstream vertices of the // graph from the delay calculator's BFS search. This means that // simply invaldating the delays downstream from the constant pin // fails. This could be handled incrementally by invalidating delays // on the output of gates one level downstream. delaysInvalid(); + power_->activitiesInvalid(); } void -Sta::removeCaseAnalysis(Pin *pin) +Sta::removeCaseAnalysis(Pin *pin, + Mode *mode) { - sdc_->removeCaseAnalysis(pin); - // Levelization respects constant disabled edges. - levelize_->invalid(); - sim_->constantsInvalid(); + mode->sdc()->removeCaseAnalysis(pin); + mode->sim()->constantsInvalid(); // Constants disable edges which isolate downstream vertices of the // graph from the delay calculator's BFS search. This means that // simply invaldating the delays downstream from the constant pin @@ -1905,190 +1963,196 @@ Sta::removeCaseAnalysis(Pin *pin) void Sta::setInputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const Pin *ref_pin, - bool source_latency_included, - bool network_latency_included, - const MinMaxAll *min_max, - bool add, - float delay) -{ - sdc_->setInputDelay(pin, rf, clk, clk_rf, ref_pin, - source_latency_included, network_latency_included, - min_max, add, delay); + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, + float delay, + Sdc *sdc) +{ + sdc->setInputDelay(pin, rf, clk, clk_rf, ref_pin, source_latency_included, + network_latency_included, min_max, add, delay); search_->arrivalInvalid(pin); } -void +void Sta::removeInputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMaxAll *min_max) + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMaxAll *min_max, + Sdc *sdc) { - sdc_->removeInputDelay(pin, rf, clk, clk_rf, min_max); + sdc->removeInputDelay(pin, rf, clk, clk_rf, min_max); search_->arrivalInvalid(pin); } void Sta::setOutputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const Pin *ref_pin, - bool source_latency_included, - bool network_latency_included, - const MinMaxAll *min_max, - bool add, - float delay) -{ - sdc_->setOutputDelay(pin, rf, clk, clk_rf, ref_pin, - source_latency_included,network_latency_included, - min_max, add, delay); - sdcChangedGraph(); + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, + float delay, + Sdc *sdc) +{ + sdc->setOutputDelay(pin, rf, clk, clk_rf, ref_pin, source_latency_included, + network_latency_included, min_max, add, delay); search_->requiredInvalid(pin); } -void +void Sta::removeOutputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMaxAll *min_max) + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMaxAll *min_max, + Sdc *sdc) { - sdc_->removeOutputDelay(pin, rf, clk, clk_rf, min_max); - sdcChangedGraph(); + sdc->removeOutputDelay(pin, rf, clk, clk_rf, min_max); search_->arrivalInvalid(pin); } void Sta::makeFalsePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - const char *comment) + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + std::string_view comment, + Sdc *sdc) { - sdc_->makeFalsePath(from, thrus, to, min_max, comment); + sdc->makeFalsePath(from, thrus, to, min_max, comment); search_->arrivalsInvalid(); } void Sta::makeMulticyclePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool use_end_clk, - int path_multiplier, - const char *comment) -{ - sdc_->makeMulticyclePath(from, thrus, to, min_max, - use_end_clk, path_multiplier, - comment); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, + std::string_view comment, + Sdc *sdc) +{ + sdc->makeMulticyclePath(from, thrus, to, min_max, use_end_clk, path_multiplier, + comment); search_->arrivalsInvalid(); } void Sta::makePathDelay(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMax *min_max, - bool ignore_clk_latency, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMax *min_max, + bool ignore_clk_latency, bool break_path, - float delay, - const char *comment) + float delay, + std::string_view comment, + Sdc *sdc) { - sdc_->makePathDelay(from, thrus, to, min_max, - ignore_clk_latency, break_path, - delay, comment); + sdc->makePathDelay(from, thrus, to, min_max, ignore_clk_latency, break_path, delay, + comment); search_->endpointsInvalid(); search_->arrivalsInvalid(); } void Sta::resetPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max) + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + Sdc *sdc) { - sdc_->resetPath(from, thrus, to, min_max); + sdc->resetPath(from, thrus, to, min_max); search_->arrivalsInvalid(); } void -Sta::makeGroupPath(const char *name, - bool is_default, - ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const char *comment) +Sta::makeGroupPath(std::string_view name, + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + std::string_view comment, + Sdc *sdc) { - sdc_->makeGroupPath(name, is_default, from, thrus, to, comment); + sdc->makeGroupPath(name, is_default, from, thrus, to, comment); search_->arrivalsInvalid(); } bool -Sta::isGroupPathName(const char *group_name) +Sta::isGroupPathName(std::string_view group_name, + const Sdc *sdc) { - return isPathGroupName(group_name); + return isPathGroupName(group_name, sdc); } bool -Sta::isPathGroupName(const char *group_name) const +Sta::isPathGroupName(std::string_view group_name, + const Sdc *sdc) const { - return sdc_->findClock(group_name) - || sdc_->isGroupPathName(group_name) - || stringEq(group_name, PathGroups::asyncPathGroupName()) - || stringEq(group_name, PathGroups::pathDelayGroupName()) - || stringEq(group_name, PathGroups::gatedClkGroupName()) - || stringEq(group_name, PathGroups::unconstrainedGroupName()); + return sdc->findClock(group_name) || sdc->isGroupPathName(group_name) + || group_name == PathGroups::asyncPathGroupName() + || group_name == PathGroups::pathDelayGroupName() + || group_name == PathGroups::gatedClkGroupName() + || group_name == PathGroups::unconstrainedGroupName(); } -StdStringSeq -Sta::pathGroupNames() const +StringSeq +Sta::pathGroupNames(const Sdc *sdc) const { - StdStringSeq names; - for (const Clock *clk : *sdc_->clocks()) + StringSeq names; + for (const Clock *clk : sdc->clocks()) names.push_back(clk->name()); - for (auto const &[name, group] : sdc_->groupPaths()) + for (auto const &[name, group] : sdc->groupPaths()) names.push_back(name); - names.push_back(PathGroups::asyncPathGroupName()); - names.push_back(PathGroups::pathDelayGroupName()); - names.push_back(PathGroups::gatedClkGroupName()); - names.push_back(PathGroups::unconstrainedGroupName()); + names.emplace_back(PathGroups::asyncPathGroupName()); + names.emplace_back(PathGroups::pathDelayGroupName()); + names.emplace_back(PathGroups::gatedClkGroupName()); + names.emplace_back(PathGroups::unconstrainedGroupName()); return names; } ExceptionFrom * Sta::makeExceptionFrom(PinSet *from_pins, - ClockSet *from_clks, - InstanceSet *from_insts, - const RiseFallBoth *from_rf) + ClockSet *from_clks, + InstanceSet *from_insts, + const RiseFallBoth *from_rf, + const Sdc *sdc) { - return sdc_->makeExceptionFrom(from_pins, from_clks, from_insts, - from_rf); + return sdc->makeExceptionFrom(from_pins, from_clks, from_insts, from_rf); } void Sta::checkExceptionFromPins(ExceptionFrom *from, - const char *file, - int line) const + std::string_view filename, + int line, + const Sdc *sdc) const { if (from) { - PinSet::ConstIterator pin_iter(from->pins()); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - if (!sdc_->isExceptionStartpoint(pin)) { - if (line) - report_->fileWarn(1554, file, line, "'%s' is not a valid start point.", - cmd_network_->pathName(pin)); - else - report_->warn(1550, "'%s' is not a valid start point.", - cmd_network_->pathName(pin)); + PinSet *pins = from->pins(); + if (pins) { + for (const Pin *pin : *pins) { + if (!sdc->isExceptionStartpoint(pin)) { + if (line) + report_->fileWarn(1554, filename, line, "'{}' is not a valid start point.", + cmd_network_->pathName(pin)); + else + report_->warn(1550, "'{}' is not a valid start point.", + cmd_network_->pathName(pin)); + } } } } @@ -2102,11 +2166,12 @@ Sta::deleteExceptionFrom(ExceptionFrom *from) ExceptionThru * Sta::makeExceptionThru(PinSet *pins, - NetSet *nets, - InstanceSet *insts, - const RiseFallBoth *rf) + NetSet *nets, + InstanceSet *insts, + const RiseFallBoth *rf, + const Sdc *sdc) { - return sdc_->makeExceptionThru(pins, nets, insts, rf); + return sdc->makeExceptionThru(pins, nets, insts, rf); } void @@ -2117,12 +2182,13 @@ Sta::deleteExceptionThru(ExceptionThru *thru) ExceptionTo * Sta::makeExceptionTo(PinSet *to_pins, - ClockSet *to_clks, - InstanceSet *to_insts, - const RiseFallBoth *rf, - const RiseFallBoth *end_rf) + ClockSet *to_clks, + InstanceSet *to_insts, + const RiseFallBoth *rf, + const RiseFallBoth *end_rf, + const Sdc *sdc) { - return sdc_->makeExceptionTo(to_pins, to_clks, to_insts, rf, end_rf); + return sdc->makeExceptionTo(to_pins, to_clks, to_insts, rf, end_rf); } void @@ -2133,82 +2199,67 @@ Sta::deleteExceptionTo(ExceptionTo *to) void Sta::checkExceptionToPins(ExceptionTo *to, - const char *file, - int line) const + const char *file, + int line, + const Sdc *sdc) const { if (to) { - PinSet::Iterator pin_iter(to->pins()); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - if (!sdc_->isExceptionEndpoint(pin)) { - if (line) - report_->fileWarn(1551, file, line, "'%s' is not a valid endpoint.", - cmd_network_->pathName(pin)); - else - report_->warn(1552, "'%s' is not a valid endpoint.", - cmd_network_->pathName(pin)); + PinSet *pins = to->pins(); + if (pins) { + for (const Pin *pin : *pins) { + if (!sdc->isExceptionEndpoint(pin)) { + if (line) + report_->fileWarn(1551, file, line, "'{}' is not a valid endpoint.", + cmd_network_->pathName(pin)); + else + report_->warn(1552, "'{}' is not a valid endpoint.", + cmd_network_->pathName(pin)); + } } } } } void -Sta::removeConstraints() -{ - levelize_->invalid(); - graph_delay_calc_->clear(); - search_->clear(); - sim_->constantsInvalid(); - if (graph_) - sdc_->removeGraphAnnotations(); - sdc_->clear(); - clk_network_->clear(); -} - -void -Sta::constraintsChanged() -{ - levelize_->invalid(); - delaysInvalid(); - sim_->constantsInvalid(); -} - -void -Sta::writeSdc(const char *filename, - bool leaf, - bool native, - int digits, +Sta::writeSdc(const Sdc *sdc, + std::string_view filename, + bool leaf, + bool native, + int digits, bool gzip, - bool no_timestamp) + bool no_timestamp) { ensureLibLinked(); - sta::writeSdc(network_->topInstance(), filename, "write_sdc", - leaf, native, digits, gzip, no_timestamp, sdc_); + sta::writeSdc(sdc, network_->topInstance(), filename, "write_sdc", leaf, native, + digits, gzip, no_timestamp); } //////////////////////////////////////////////////////////////// CheckErrorSeq & -Sta::checkTiming(bool no_input_delay, - bool no_output_delay, - bool reg_multiple_clks, - bool reg_no_clks, - bool unconstrained_endpoints, - bool loops, - bool generated_clks) -{ - searchPreamble(); - if (unconstrained_endpoints) - // Only need non-clock arrivals for unconstrained_endpoints. +Sta::checkTiming(const Mode *mode, + bool no_input_delay, + bool no_output_delay, + bool reg_multiple_clks, + bool reg_no_clks, + bool unconstrained_endpoints, + bool loops, + bool generated_clks) +{ + if (unconstrained_endpoints) { + // Only need non-clock arrivals to find unconstrained_endpoints. + searchPreamble(); search_->findAllArrivals(); - else - search_->findClkArrivals(); - if (check_timing_ == nullptr) - makeCheckTiming(); - return check_timing_->check(no_input_delay, no_output_delay, - reg_multiple_clks, reg_no_clks, - unconstrained_endpoints, - loops, generated_clks); + } + else { + ensureGraph(); + ensureLevelized(); + mode->sim()->ensureConstantsPropagated(); + mode->clkNetwork()->ensureClkNetwork(); + } + return check_timing_->check(mode, no_input_delay, no_output_delay, + reg_multiple_clks, reg_no_clks, + unconstrained_endpoints, loops, generated_clks); } //////////////////////////////////////////////////////////////// @@ -2222,9 +2273,7 @@ Sta::crprEnabled() const void Sta::setCrprEnabled(bool enabled) { - // Pessimism is only relevant for on_chip_variation analysis. - if (sdc_->analysisType() == AnalysisType::ocv - && enabled != variables_->crprEnabled()) + if (enabled != variables_->crprEnabled()) search_->arrivalsInvalid(); variables_->setCrprEnabled(enabled); } @@ -2239,32 +2288,81 @@ void Sta::setCrprMode(CrprMode mode) { // Pessimism is only relevant for on_chip_variation analysis. - if (sdc_->analysisType() == AnalysisType::ocv - && variables_->crprEnabled() - && variables_->crprMode() != mode) + if (variables_->crprEnabled() && variables_->crprMode() != mode) search_->arrivalsInvalid(); variables_->setCrprMode(mode); } -bool -Sta::pocvEnabled() const +PocvMode +Sta::pocvMode() const { - return variables_->pocvEnabled(); + return variables_->pocvMode(); } void -Sta::setPocvEnabled(bool enabled) +Sta::setPocvMode(PocvMode mode) { - if (enabled != variables_->pocvEnabled()) + if (mode != variables_->pocvMode()) { + variables_->setPocvMode(mode); + + delete delay_ops_; + switch (mode) { + case PocvMode::scalar: + default: + delay_ops_ = new DelayOpsScalar(); + break; + case PocvMode::normal: + checkLibrarayPocv(); + delay_ops_ = new DelayOpsNormal(); + break; + case PocvMode::skew_normal: + checkLibrarayPocv(); + delay_ops_ = new DelayOpsSkewNormal(); + break; + } + updateComponentsState(); delaysInvalid(); - variables_->setPocvEnabled(enabled); + } } void -Sta::setSigmaFactor(float factor) +Sta::checkLibrarayPocv() +{ + LibertyLibraryIterator *lib_iter = network_->libertyLibraryIterator(); + while (lib_iter->hasNext()) { + LibertyLibrary *lib = lib_iter->next(); + LibertyCellIterator cell_iter(lib); + while (cell_iter.hasNext()) { + LibertyCell *cell = cell_iter.next(); + for (const TimingArcSet *arc_set : cell->timingArcSets()) { + for (const TimingArc *arc : arc_set->arcs()) { + GateTableModel *gate_model = arc->gateTableModel(); + if (gate_model) { + const TableModels *models = gate_model->delayModels(); + if (models->sigma(EarlyLate::early()) != nullptr) { + delete lib_iter; + return; + } + } + } + } + } + } + delete lib_iter; + report_->warn(1578, "No liberty POCV/LVF models found."); +} + +float +Sta::pocvQuantile() { - if (!fuzzyEqual(factor, sigma_factor_)) { - sigma_factor_ = factor; + return variables_->pocvQuantile(); +} + +void +Sta::setPocvQuantile(float quantile) +{ + if (!fuzzyEqual(quantile, variables_->pocvQuantile())) { + variables_->setPocvQuantile(quantile); search_->arrivalsInvalid(); updateComponentsState(); } @@ -2331,26 +2429,11 @@ Sta::setBidirectInstPathsEnabled(bool enabled) } } -bool -Sta::bidirectNetPathsEnabled() const -{ - return variables_->bidirectNetPathsEnabled(); -} - -void -Sta::setBidirectNetPathsEnabled(bool enabled) -{ - if (variables_->bidirectNetPathsEnabled() != enabled) { - delaysInvalid(); - variables_->setBidirectNetPathsEnabled(enabled); - } -} - bool Sta::recoveryRemovalChecksEnabled() const { return variables_->recoveryRemovalChecksEnabled(); -} +} void Sta::setRecoveryRemovalChecksEnabled(bool enabled) @@ -2386,11 +2469,12 @@ void Sta::setDynamicLoopBreaking(bool enable) { if (variables_->dynamicLoopBreaking() != enable) { - if (levelize_->levelized()) { + for (Mode *mode : modes_) { + Sdc *sdc = mode->sdc(); if (enable) - sdc_->makeLoopExceptions(); + sdc->makeLoopExceptions(); else - sdc_->deleteLoopExceptions(); + sdc->deleteLoopExceptions(); } search_->arrivalsInvalid(); variables_->setDynamicLoopBreaking(enable); @@ -2525,54 +2609,218 @@ Sta::setEnableCollections(bool enable) //////////////////////////////////////////////////////////////// -Corner * -Sta::findCorner(const char *corner_name) +// Init one scene named "default". +void +Sta::makeDefaultScene() { - return corners_->findCorner(corner_name); + std::string name("default"); + StringSeq scene_names; + scene_names.emplace_back(name); + Parasitics *parasitics = makeConcreteParasitics(name, ""); + + Mode *mode = new Mode(name, 0, this); + modes_.push_back(mode); + mode_name_map_[name] = mode; + mode->sim()->setMode(mode); + mode->sim()->setObserver(new StaSimObserver(this)); + + deleteScenes(); + makeScene(name, mode, parasitics); + + cmd_scene_ = scenes_[0]; } -bool -Sta::multiCorner() +// define_corners (before read_liberty). +void +Sta::makeScenes(const StringSeq &scene_names) +{ + if (scene_names.size() > scene_count_max) + report_->error(1553, "maximum scene count exceeded"); + Parasitics *parasitics = findParasitics("default"); + Mode *mode = modes_[0]; + mode->sdc()->makeSceneBefore(); + mode->clear(); + + deleteScenes(); + for (const std::string &name : scene_names) + makeScene(name, mode, parasitics); + + cmd_scene_ = scenes_[0]; + updateComponentsState(); + if (graph_) + graph_->makeSceneAfter(); +} + +void +Sta::makeScene(const std::string &name, + const std::string &mode_name, + const StringSeq &liberty_min_files, + const StringSeq &liberty_max_files, + const std::string &spef_min_file, + const std::string &spef_max_file) +{ + Mode *mode = findMode(mode_name); + if (mode) { + Parasitics *parasitics_default = findParasitics("default"); + Parasitics *parasitics_min = parasitics_default; + Parasitics *parasitics_max = parasitics_default; + if (!spef_min_file.empty() && !spef_max_file.empty()) { + parasitics_min = findParasitics(spef_min_file); + parasitics_max = findParasitics(spef_max_file); + if (parasitics_min == nullptr) + report_->error(1558, "Spef file {} not found.", spef_min_file); + if (parasitics_max == nullptr && spef_max_file != spef_min_file) + report_->error(1559, "Spef file {} not found.", spef_max_file); + } + + mode->sdc()->makeSceneBefore(); + Scene *scene = makeScene(name, mode, parasitics_min, parasitics_max); + updateComponentsState(); + if (graph_) + graph_->makeSceneAfter(); + updateSceneLiberty(scene, liberty_min_files, liberty_max_files); + cmd_scene_ = scene; + } + else + report_->error(1572, "mode {} not found.", mode_name); +} + +Scene * +Sta::makeScene(const std::string &name, + Mode *mode, + Parasitics *parasitics) { - return corners_->multiCorner(); + Scene *scene = new Scene(name, scenes_.size(), mode, parasitics); + scene_name_map_[name] = scene; + scenes_.push_back(scene); + mode->addScene(scene); + return scene; } -// Init one corner named "default". void -Sta::makeCorners() +Sta::deleteScenes() { - corners_ = new Corners(this); - StringSet corner_names; - corner_names.insert("default"); - corners_->makeCorners(&corner_names); - cmd_corner_ = corners_->findCorner(0); - sdc_->makeCornersAfter(corners_); + for (Scene *scene : scenes_) { + scene->mode()->removeScene(scene); + delete scene; + } + scenes_.clear(); + scene_name_map_.clear(); +} + +Scene * +Sta::makeScene(const std::string &name, + Mode *mode, + Parasitics *parasitics_min, + Parasitics *parasitics_max) +{ + if (scenes_.size() == 1 && findScene("default")) + deleteScenes(); + + Scene *scene = + new Scene(name, scenes_.size(), mode, parasitics_min, parasitics_max); + scene_name_map_[name] = scene; + scenes_.push_back(scene); + mode->addScene(scene); + return scene; +} + +Scene * +Sta::findScene(const std::string &name) const +{ + return findKey(scene_name_map_, name); +} + +SceneSeq +Sta::findScenes(const std::string &name) const +{ + SceneSeq matches; + PatternMatch pattern(name); + for (Scene *scene : scenes_) { + if (pattern.match(scene->name())) + matches.push_back(scene); + } + return matches; +} + +SceneSeq +Sta::findScenes(const std::string &name, + ModeSeq &modes) const +{ + SceneSeq matches; + PatternMatch pattern(name); + for (Mode *mode : modes) { + for (Scene *scene : mode->scenes()) { + if (pattern.match(scene->name())) + matches.push_back(scene); + } + } + return matches; } void -Sta::makeCorners(StringSet *corner_names) +Sta::updateSceneLiberty(Scene *scene, + const StringSeq &liberty_min_files, + const StringSeq &liberty_max_files) { - if (corner_names->size() > corner_count_max) - report_->error(1553, "maximum corner count exceeded"); - sdc_->makeCornersBefore(); - parasitics_->deleteParasitics(); - corners_->makeCorners(corner_names); - makeParasiticAnalysisPts(); - cmd_corner_ = corners_->findCorner(0); - updateComponentsState(); - sdc_->makeCornersAfter(corners_); + if (liberty_min_files == liberty_max_files) + updateSceneLiberty(scene, liberty_max_files, MinMaxAll::minMax()); + else { + updateSceneLiberty(scene, liberty_min_files, MinMaxAll::min()); + updateSceneLiberty(scene, liberty_max_files, MinMaxAll::max()); + } +} + +void +Sta::updateSceneLiberty(Scene *scene, + const StringSeq &liberty_files, + const MinMaxAll *min_max) +{ + for (const std::string &lib_file : liberty_files) { + LibertyLibrary *lib = network_->findLiberty(lib_file); + if (lib == nullptr) + lib = network_->findLibertyFilename(lib_file); + if (lib) + LibertyLibrary::makeSceneMap(lib, scene, min_max, network_, report_); + else + report_->warn(1555, "liberty name/filename {} not found.", lib_file); + } +} + +void +Sta::updateLibertyScenes() +{ + for (Scene *scene : scenes_) { + LibertyLibraryIterator *iter = network_->libertyLibraryIterator(); + while (iter->hasNext()) { + LibertyLibrary *lib = iter->next(); + LibertyLibrary::makeSceneMap(lib, scene, MinMaxAll::minMax(), + network_, report_); + } + } } -Corner * -Sta::cmdCorner() const +Scene * +Sta::cmdScene() const { - return cmd_corner_; + return cmd_scene_; } void -Sta::setCmdCorner(Corner *corner) +Sta::setCmdScene(Scene *scene) { - cmd_corner_ = corner; + cmd_scene_ = scene; +} + +SceneSeq +Sta::makeSceneSeq(Scene *scene) const +{ + SceneSeq scenes; + if (scene) + scenes.push_back(scene); + else + scenes = scenes_; + return scenes; } //////////////////////////////////////////////////////////////// @@ -2582,55 +2830,54 @@ Sta::setCmdCorner(Corner *corner) // PathEnds are owned by Search PathGroups and deleted on next call. PathEndSeq Sta::findPathEnds(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool unconstrained, - const Corner *corner, - const MinMaxAll *min_max, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - bool sort_by_slack, - PathGroupNameSet *group_names, - bool setup, - bool hold, - bool recovery, - bool removal, - bool clk_gating_setup, - bool clk_gating_hold) + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool unconstrained, + const SceneSeq &scenes, + const MinMaxAll *min_max, + int group_path_count, + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + bool sort_by_slack, + StringSeq &group_names, + bool setup, + bool hold, + bool recovery, + bool removal, + bool clk_gating_setup, + bool clk_gating_hold) { searchPreamble(); clk_skews_->clear(); - return search_->findPathEnds(from, thrus, to, unconstrained, - corner, min_max, group_path_count, - endpoint_path_count, - unique_pins, unique_edges, - slack_min, slack_max, - sort_by_slack, group_names, - setup, hold, - recovery, removal, - clk_gating_setup, clk_gating_hold); + return search_->findPathEnds(from, thrus, to, unconstrained, scenes, min_max, + group_path_count, endpoint_path_count, unique_pins, + unique_edges, slack_min, slack_max, sort_by_slack, + group_names, setup, hold, recovery, removal, + clk_gating_setup, clk_gating_hold); } //////////////////////////////////////////////////////////////// // Overall flow: // make graph -// propagate constants // levelize // delay calculation // update generated clocks +// propagate constants // find arrivals void Sta::searchPreamble() { findDelays(); + for (Mode *mode : modes_) { + mode->sim()->ensureConstantsPropagated(); + mode->sdc()->searchPreamble(); + } updateGeneratedClks(); - sdc_->searchPreamble(); // Delete results from last findPathEnds because they point to filtered arrivals. search_->deletePathGroups(); search_->deleteFilteredArrivals(); @@ -2643,7 +2890,7 @@ Sta::setReportPathFormat(ReportPathFormat format) } void -Sta::setReportPathFieldOrder(StringSeq *field_names) +Sta::setReportPathFieldOrder(const StringSeq &field_names) { report_path_->setReportFieldOrder(field_names); } @@ -2651,19 +2898,20 @@ Sta::setReportPathFieldOrder(StringSeq *field_names) void Sta::setReportPathFields(bool report_input_pin, bool report_hier_pins, - bool report_net, - bool report_cap, - bool report_slew, - bool report_fanout, - bool report_src_attr) + bool report_net, + bool report_cap, + bool report_slew, + bool report_fanout, + bool report_variation, + bool report_src_attr) { report_path_->setReportFields(report_input_pin, report_hier_pins, report_net, report_cap, report_slew, report_fanout, - report_src_attr); + report_variation, report_src_attr); } ReportField * -Sta::findReportPathField(const char *name) +Sta::findReportPathField(std::string_view name) { return report_path_->findField(name); } @@ -2680,12 +2928,6 @@ Sta::setReportPathNoSplit(bool no_split) report_path_->setNoSplit(no_split); } -void -Sta::setReportPathSigmas(bool report_sigmas) -{ - report_path_->setReportSigmas(report_sigmas); -} - void Sta::setReportDedupByWord(bool dedup_by_word) { @@ -2724,9 +2966,10 @@ Sta::reportPathEnd(PathEnd *end) void Sta::reportPathEnd(PathEnd *end, - PathEnd *prev_end) + PathEnd *prev_end, + bool last) { - report_path_->reportPathEnd(end, prev_end); + report_path_->reportPathEnd(end, prev_end, last); } void @@ -2754,24 +2997,22 @@ Sta::updateTiming(bool full) void Sta::reportClkSkew(ConstClockSeq &clks, - const Corner *corner, - const SetupHold *setup_hold, + const SceneSeq &scenes, + const SetupHold *setup_hold, bool include_internal_latency, - int digits) + int digits) { clkSkewPreamble(); - clk_skews_->reportClkSkew(clks, corner, setup_hold, - include_internal_latency, digits); + clk_skews_->reportClkSkew(clks, scenes, setup_hold, include_internal_latency, + digits); } -float +Delay Sta::findWorstClkSkew(const SetupHold *setup_hold, bool include_internal_latency) { - clkSkewPreamble(); - return clk_skews_->findWorstClkSkew(nullptr, setup_hold, - include_internal_latency); + return clk_skews_->findWorstClkSkew(scenes_, setup_hold, include_internal_latency); } void @@ -2790,22 +3031,23 @@ Sta::makeClkSkews() void Sta::reportClkLatency(ConstClockSeq &clks, - const Corner *corner, + const SceneSeq &scenes, bool include_internal_latency, int digits) { ensureClkArrivals(); ClkLatency clk_latency(this); - clk_latency.reportClkLatency(clks, corner, include_internal_latency, digits); + clk_latency.reportClkLatency(clks, scenes, include_internal_latency, digits); } ClkDelays Sta::findClkDelays(const Clock *clk, + const Scene *scene, bool include_internal_latency) { ensureClkArrivals(); ClkLatency clk_latency(this); - return clk_latency.findClkDelays(clk, nullptr, include_internal_latency); + return clk_latency.findClkDelays(clk, scene, include_internal_latency); } //////////////////////////////////////////////////////////////// @@ -2832,17 +3074,7 @@ Sta::ensureClkArrivals() //////////////////////////////////////////////////////////////// -PinSet -Sta::startpointPins() -{ - ensureGraph(); - PinSet pins(network_); - VertexPinCollector visitor(pins); - search_->visitStartpoints(&visitor); - return pins; -} - -VertexSet * +VertexSet & Sta::endpoints() { ensureGraph(); @@ -2854,7 +3086,7 @@ Sta::endpointPins() { ensureGraph(); PinSet pins(network_); - for (Vertex *vertex : *search_->endpoints()) + for (Vertex *vertex : search_->endpoints()) pins.insert(vertex->pin()); return pins; } @@ -2863,8 +3095,8 @@ int Sta::endpointViolationCount(const MinMax *min_max) { int violations = 0; - for (Vertex *end : *search_->endpoints()) { - if (delayLess(vertexSlack(end, min_max), 0.0, this)) + for (Vertex *end : search_->endpoints()) { + if (delayLess(slack(end, min_max), 0.0, this)) violations++; } return violations; @@ -2882,33 +3114,17 @@ Sta::findRequireds() //////////////////////////////////////////////////////////////// -VertexPathIterator * -Sta::vertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap) -{ - return new VertexPathIterator(vertex, rf, path_ap, this); -} - -VertexPathIterator * -Sta::vertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max) -{ - return new VertexPathIterator(vertex, rf, min_max, this); -} - Path * Sta::vertexWorstArrivalPath(Vertex *vertex, - const MinMax *min_max) + const MinMax *min_max) { return vertexWorstArrivalPath(vertex, nullptr, min_max); } Path * Sta::vertexWorstArrivalPath(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max) + const RiseFall *rf, + const MinMax *min_max) { Path *worst_path = nullptr; Arrival worst_arrival = min_max->initValue(); @@ -2917,7 +3133,7 @@ Sta::vertexWorstArrivalPath(Vertex *vertex, Path *path = path_iter.next(); Arrival arrival = path->arrival(); if (!path->tag(this)->isGenClkSrcPath() - && delayGreater(arrival, worst_arrival, min_max, this)) { + && delayGreater(arrival, worst_arrival, min_max, this)) { worst_arrival = arrival; worst_path = path; } @@ -2945,7 +3161,7 @@ Sta::vertexWorstRequiredPath(Vertex *vertex, Path *path = path_iter.next(); const Required path_req = path->required(); if (!path->tag(this)->isGenClkSrcPath() - && delayGreater(path_req, worst_req, req_min_max, this)) { + && delayGreater(path_req, worst_req, req_min_max, this)) { worst_req = path_req; worst_path = path; } @@ -2955,8 +3171,8 @@ Sta::vertexWorstRequiredPath(Vertex *vertex, Path * Sta::vertexWorstSlackPath(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max) + const RiseFall *rf, + const MinMax *min_max) { Path *worst_path = nullptr; Slack min_slack = MinMax::min()->initValue(); @@ -2964,8 +3180,7 @@ Sta::vertexWorstSlackPath(Vertex *vertex, while (path_iter.hasNext()) { Path *path = path_iter.next(); Slack slack = path->slack(this); - if (!path->tag(this)->isGenClkSrcPath() - && delayLess(slack, min_slack, this)) { + if (!path->tag(this)->isGenClkSrcPath() && delayLess(slack, min_slack, this)) { min_slack = slack; worst_path = path; } @@ -2975,207 +3190,201 @@ Sta::vertexWorstSlackPath(Vertex *vertex, Path * Sta::vertexWorstSlackPath(Vertex *vertex, - const MinMax *min_max) + const MinMax *min_max) { return vertexWorstSlackPath(vertex, nullptr, min_max); } Arrival -Sta::pinArrival(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max) +Sta::arrival(const Pin *pin, + const RiseFallBoth *rf, + const MinMax *min_max) { Vertex *vertex, *bidirect_vertex; graph_->pinVertices(pin, vertex, bidirect_vertex); - Arrival arrival; + Arrival worst_arrival = min_max->initValue(); if (vertex) - arrival = vertexArrival(vertex, rf, clk_edge_wildcard, nullptr, min_max); + worst_arrival = arrival(vertex, rf, scenes_, min_max); if (bidirect_vertex) { - Arrival arrival1 = vertexArrival(bidirect_vertex, rf, clk_edge_wildcard, - nullptr, min_max); - if (delayLess(arrival1, arrival, this)) - arrival = arrival1; + Arrival arrival2 = arrival(bidirect_vertex, rf, scenes_, min_max); + if (delayGreater(arrival2, worst_arrival, min_max, this)) + worst_arrival = arrival2; } - return arrival; -} - -Arrival -Sta::vertexArrival(Vertex *vertex, - const MinMax *min_max) -{ - return vertexArrival(vertex, nullptr, clk_edge_wildcard, nullptr, min_max); + return worst_arrival; } Arrival -Sta::vertexArrival(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap) -{ - return vertexArrival(vertex, rf, clk_edge_wildcard, path_ap, nullptr); -} - -Arrival -Sta::vertexArrival(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap, - const MinMax *min_max) +Sta::arrival(Vertex *vertex, + const RiseFallBoth *rf, + const SceneSeq &scenes, + const MinMax *min_max) { searchPreamble(); search_->findArrivals(vertex->level()); - if (min_max == nullptr) - min_max = path_ap->pathMinMax(); + const SceneSet scenes_set = Scene::sceneSet(scenes); Arrival arrival = min_max->initValue(); - VertexPathIterator path_iter(vertex, rf, path_ap, this); + VertexPathIterator path_iter(vertex, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); const Arrival &path_arrival = path->arrival(); const ClkInfo *clk_info = path->clkInfo(search_); - if ((clk_edge == clk_edge_wildcard - || clk_info->clkEdge() == clk_edge) - && !clk_info->isGenClkSrcPath() - && delayGreater(path->arrival(), arrival, min_max, this)) + if (!clk_info->isGenClkSrcPath() + && (rf == RiseFallBoth::riseFall() + || path->transition(this)->asRiseFallBoth() == rf) + && path->minMax(this) == min_max && scenes_set.contains(path->scene(this)) + && delayGreater(path->arrival(), arrival, min_max, this)) arrival = path_arrival; } return arrival; } Required -Sta::vertexRequired(Vertex *vertex, - const MinMax *min_max) -{ - return vertexRequired(vertex, nullptr, clk_edge_wildcard, nullptr, min_max); -} - -Required -Sta::vertexRequired(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max) -{ - return vertexRequired(vertex, rf, clk_edge_wildcard, nullptr, min_max); -} - -Required -Sta::vertexRequired(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap) -{ - return vertexRequired(vertex, rf, clk_edge_wildcard, path_ap, nullptr); -} - -Required -Sta::vertexRequired(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap) -{ - return vertexRequired(vertex, rf, clk_edge, path_ap, nullptr); -} - -Required -Sta::vertexRequired(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap, - const MinMax *min_max) +Sta::required(Vertex *vertex, + const RiseFallBoth *rf, + const SceneSeq &scenes, + const MinMax *min_max) { findRequired(vertex); - const MinMax *req_min_max = min_max - ? min_max->opposite() - : path_ap->pathMinMax()->opposite(); + const SceneSet scenes_set = Scene::sceneSet(scenes); + const MinMax *req_min_max = min_max->opposite(); Required required = req_min_max->initValue(); - VertexPathIterator path_iter(vertex, rf, path_ap, min_max, this); + VertexPathIterator path_iter(vertex, this); while (path_iter.hasNext()) { const Path *path = path_iter.next(); const Required path_required = path->required(); - if ((clk_edge == clk_edge_wildcard - || path->clkEdge(search_) == clk_edge) - && delayGreater(path_required, required, req_min_max, this)) + if ((rf == RiseFallBoth::riseFall() + || path->transition(this)->asRiseFallBoth() == rf) + && path->minMax(this) == min_max && scenes_set.contains(path->scene(this)) + && delayGreater(path_required, required, req_min_max, this)) required = path_required; } return required; } +//////////////////////////////////////////////////////////////// + Slack -Sta::netSlack(const Net *net, - const MinMax *min_max) +Sta::slack(const Net *net, + const MinMax *min_max) { ensureGraph(); - Slack slack = MinMax::min()->initValue(); + Slack min_slack = MinMax::min()->initValue(); NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); if (network_->isLoad(pin)) { Vertex *vertex = graph_->pinLoadVertex(pin); - Slack pin_slack = vertexSlack(vertex, min_max); - if (delayLess(pin_slack, slack, this)) - slack = pin_slack; + Slack pin_slack = slack(vertex, min_max); + if (delayLess(pin_slack, min_slack, this)) + min_slack = pin_slack; } } delete pin_iter; - return slack; + return min_slack; } Slack -Sta::pinSlack(const Pin *pin, - const MinMax *min_max) +Sta::slack(const Pin *pin, + const RiseFallBoth *rf, + const SceneSeq &scenes, + const MinMax *min_max) { ensureGraph(); Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - Slack slack = MinMax::min()->initValue(); + Slack min_slack = MinMax::min()->initValue(); if (vertex) - slack = vertexSlack(vertex, min_max); + min_slack = slack(vertex, rf, scenes, min_max); if (bidirect_drvr_vertex) { - Slack slack1 = vertexSlack(bidirect_drvr_vertex, min_max); - if (delayLess(slack1, slack, this)) - slack = slack1; + Slack slack1 = slack(bidirect_drvr_vertex, rf, scenes, min_max); + if (delayLess(slack1, min_slack, this)) + min_slack = slack1; } - return slack; + return min_slack; } Slack -Sta::pinSlack(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max) +Sta::slack(Vertex *vertex, + const MinMax *min_max) { - ensureGraph(); - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - Slack slack = MinMax::min()->initValue(); - if (vertex) - slack = vertexSlack(vertex, rf, min_max); - if (bidirect_drvr_vertex) { - Slack slack1 = vertexSlack(bidirect_drvr_vertex, rf, min_max); - if (delayLess(slack1, slack, this)) - slack = slack1; + return slack(vertex, RiseFallBoth::riseFall(), scenes_, min_max); +} + +Slack +Sta::slack(Vertex *vertex, + const RiseFall *rf, + const MinMax *min_max) +{ + return slack(vertex, rf->asRiseFallBoth(), scenes_, min_max); +} + +Slack +Sta::slack(Vertex *vertex, + const RiseFallBoth *rf, + const SceneSeq &scenes, + const MinMax *min_max) +{ + findRequired(vertex); + const SceneSet scenes_set = Scene::sceneSet(scenes); + const MinMax *min = MinMax::min(); + Slack slack = min->initValue(); + VertexPathIterator path_iter(vertex, this); + while (path_iter.hasNext()) { + Path *path = path_iter.next(); + Slack path_slack = path->slack(this); + if ((rf == RiseFallBoth::riseFall() + || path->transition(this)->asRiseFallBoth() == rf) + && path->minMax(this) == min_max && scenes_set.contains(path->scene(this)) + && delayLess(path_slack, slack, this)) + slack = path_slack; } return slack; } +void +Sta::slacks(Vertex *vertex, + Slack (&slacks)[RiseFall::index_count][MinMax::index_count]) +{ + findRequired(vertex); + for (int rf_index : RiseFall::rangeIndex()) { + for (const MinMax *min_max : MinMax::range()) { + slacks[rf_index][min_max->index()] = MinMax::min()->initValue(); + } + } + VertexPathIterator path_iter(vertex, this); + while (path_iter.hasNext()) { + Path *path = path_iter.next(); + Slack path_slack = path->slack(this); + int rf_index = path->rfIndex(this); + int mm_index = path->minMax(this)->index(); + if (delayLess(path_slack, slacks[rf_index][mm_index], this)) + slacks[rf_index][mm_index] = path_slack; + } +} + //////////////////////////////////////////////////////////////// class EndpointPathEndVisitor : public PathEndVisitor { public: - EndpointPathEndVisitor(const std::string &path_group_name, - const MinMax *min_max, - const StaState *sta); - PathEndVisitor *copy() const; - void visit(PathEnd *path_end); + EndpointPathEndVisitor(std::string_view path_group_name, + const MinMax *min_max, + const StaState *sta); + PathEndVisitor *copy() const override; + void visit(PathEnd *path_end) override; Slack slack() const { return slack_; } private: - const std::string &path_group_name_; + std::string_view path_group_name_; const MinMax *min_max_; Slack slack_; const StaState *sta_; }; -EndpointPathEndVisitor::EndpointPathEndVisitor(const std::string &path_group_name, - const MinMax *min_max, - const StaState *sta) : +EndpointPathEndVisitor::EndpointPathEndVisitor(std::string_view path_group_name, + const MinMax *min_max, + const StaState *sta) : path_group_name_(path_group_name), min_max_(min_max), slack_(MinMax::min()->initValue()), @@ -3193,12 +3402,12 @@ void EndpointPathEndVisitor::visit(PathEnd *path_end) { if (path_end->minMax(sta_) == min_max_) { - StdStringSeq group_names = PathGroups::pathGroupNames(path_end, sta_); + 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; + Slack end_slack = path_end->slack(sta_); + if (delayLess(end_slack, slack_, sta_)) + slack_ = end_slack; } } } @@ -3206,8 +3415,8 @@ EndpointPathEndVisitor::visit(PathEnd *path_end) Slack Sta::endpointSlack(const Pin *pin, - const std::string &path_group_name, - const MinMax *min_max) + std::string_view path_group_name, + const MinMax *min_max) { ensureGraph(); Vertex *vertex = graph_->pinLoadVertex(pin); @@ -3224,101 +3433,126 @@ Sta::endpointSlack(const Pin *pin, //////////////////////////////////////////////////////////////// -Slack -Sta::vertexSlack(Vertex *vertex, - const MinMax *min_max) +void +Sta::reportArrivalWrtClks(const Pin *pin, + const Scene *scene, + bool report_variance, + int digits) { - findRequired(vertex); - Slack slack = MinMax::min()->initValue(); - VertexPathIterator path_iter(vertex, this); - while (path_iter.hasNext()) { - Path *path = path_iter.next(); - if (path->minMax(this) == min_max) { - Slack path_slack = path->slack(this); - if (delayLess(path_slack, slack, this)) - slack = path_slack; - } - } - return slack; + searchPreamble(); + reportDelaysWrtClks(pin, scene, report_variance, digits, false, + [] (const Path *path) { + return path->arrival(); + }); } -Slack -Sta::vertexSlack(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max) +void +Sta::reportRequiredWrtClks(const Pin *pin, + const Scene *scene, + bool report_variance, + int digits) { - findRequired(vertex); - Slack slack = MinMax::min()->initValue(); - VertexPathIterator path_iter(vertex, rf, min_max, this); - while (path_iter.hasNext()) { - Path *path = path_iter.next(); - Slack path_slack = path->slack(this); - if (delayLess(path_slack, slack, this)) - slack = path_slack; - } - return slack; + reportDelaysWrtClks(pin, scene, report_variance, digits, true, + [] (const Path *path) { + return path->required(); + }); } -Slack -Sta::vertexSlack(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap) +void +Sta::reportSlackWrtClks(const Pin *pin, + const Scene *scene, + bool report_variance, + int digits) { - findRequired(vertex); - return vertexSlack1(vertex, rf, clk_edge_wildcard, path_ap); + reportDelaysWrtClks(pin, scene, report_variance, digits, true, + [this] (const Path *path) { + return path->slack(this); + }); } -Slack -Sta::vertexSlack(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap) +void +Sta::reportDelaysWrtClks(const Pin *pin, + const Scene *scene, + bool report_variance, + int digits, + bool find_required, + const PathDelayFunc &get_path_delay) { - findRequired(vertex); - return vertexSlack1(vertex, rf, clk_edge, path_ap); + ensureGraph(); + Vertex *vertex, *bidir_vertex; + graph_->pinVertices(pin, vertex, bidir_vertex); + if (vertex) + reportDelaysWrtClks(vertex, scene, report_variance, digits, + find_required, get_path_delay); + if (bidir_vertex) + reportDelaysWrtClks(vertex, scene, report_variance, digits, + find_required, get_path_delay); } -Slack -Sta::vertexSlack1(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap) +void +Sta::reportDelaysWrtClks(Vertex *vertex, + const Scene *scene, + bool report_variance, + int digits, + bool find_required, + const PathDelayFunc &get_path_delay) { - const MinMax *min = MinMax::min(); - Slack slack = min->initValue(); - VertexPathIterator path_iter(vertex, rf, path_ap, this); - while (path_iter.hasNext()) { - Path *path = path_iter.next(); - Slack path_slack = path->slack(this); - if ((clk_edge == clk_edge_wildcard - || path->clkEdge(search_) == clk_edge) - && delayLess(path_slack, slack, this)) - slack = path_slack; + if (find_required) + findRequired(vertex); + else + search_->findArrivals(vertex->level()); + const Sdc *sdc = scene->sdc(); + DelaysWrtClks clk_delays = search_->delaysWrtClks(vertex, scene, get_path_delay); + reportDelaysWrtClks(nullptr, report_variance, digits, clk_delays); + const ClockEdge *default_clk_edge = sdc->defaultArrivalClock()->edge(RiseFall::rise()); + reportDelaysWrtClks(default_clk_edge, report_variance, digits, clk_delays); + for (const Clock *clk : sdc->sortedClocks()) { + for (const RiseFall *rf : RiseFall::range()) { + const ClockEdge *clk_edge = clk->edge(rf); + reportDelaysWrtClks(clk_edge, report_variance, digits, clk_delays); + } } - return slack; } void -Sta::vertexSlacks(Vertex *vertex, - Slack (&slacks)[RiseFall::index_count][MinMax::index_count]) -{ - findRequired(vertex); - for (int rf_index : RiseFall::rangeIndex()) { - for (const MinMax *min_max : MinMax::range()) { - slacks[rf_index][min_max->index()] = MinMax::min()->initValue(); - } - } - VertexPathIterator path_iter(vertex, this); - while (path_iter.hasNext()) { - Path *path = path_iter.next(); - Slack path_slack = path->slack(this); - int rf_index = path->rfIndex(this); - int mm_index = path->minMax(this)->index(); - if (delayLess(path_slack, slacks[rf_index][mm_index], this)) - slacks[rf_index][mm_index] = path_slack; +Sta::reportDelaysWrtClks(const ClockEdge *clk_edge, + bool report_variance, + int digits, + DelaysWrtClks &clk_delays) +{ + const RiseFallMinMaxDelay &delays = clk_delays[clk_edge]; + if (!delays.empty()) { + std::string clk_name; + if (clk_edge) + clk_name = sta::format("({})", clk_edge->name()); + report_->report("{} r {}:{} f {}:{}", clk_name, + formatDelay(RiseFall::rise(), MinMax::min(), + delays, report_variance, digits), + formatDelay(RiseFall::rise(), MinMax::max(), + delays, report_variance, digits), + formatDelay(RiseFall::fall(), MinMax::min(), + delays, report_variance, digits), + formatDelay(RiseFall::fall(), MinMax::max(), + delays, report_variance, digits)); } } +std::string +Sta::formatDelay(const RiseFall *rf, + const MinMax *min_max, + const RiseFallMinMaxDelay &delays, + bool report_variance, + int digits) +{ + Delay delay; + bool exists; + delays.value(rf, min_max, delay, exists); + if (exists) + return delayAsString(delay, min_max, report_variance, digits, this); + else + return "---"; +} + //////////////////////////////////////////////////////////////// class MinPeriodEndVisitor : public PathEndVisitor @@ -3328,8 +3562,8 @@ class MinPeriodEndVisitor : public PathEndVisitor bool include_port_paths, StaState *sta); MinPeriodEndVisitor(const MinPeriodEndVisitor &) = default; - virtual PathEndVisitor *copy() const; - virtual void visit(PathEnd *path_end); + PathEndVisitor *copy() const override; + void visit(PathEnd *path_end) override; float minPeriod() const { return min_period_; } private: @@ -3338,7 +3572,7 @@ class MinPeriodEndVisitor : public PathEndVisitor const Clock *clk_; bool include_port_paths_; StaState *sta_; - float min_period_; + float min_period_{0.0}; }; MinPeriodEndVisitor::MinPeriodEndVisitor(const Clock *clk, @@ -3346,8 +3580,7 @@ MinPeriodEndVisitor::MinPeriodEndVisitor(const Clock *clk, StaState *sta) : clk_(clk), include_port_paths_(include_port_paths), - sta_(sta), - min_period_(0) + sta_(sta) { } @@ -3365,10 +3598,8 @@ MinPeriodEndVisitor::visit(PathEnd *path_end) const ClockEdge *src_edge = path_end->sourceClkEdge(sta_); const ClockEdge *tgt_edge = path_end->targetClkEdge(sta_); PathEnd::Type end_type = path_end->type(); - if ((end_type == PathEnd::Type::check - || end_type == PathEnd::Type::output_delay) - && path->minMax(sta_) == MinMax::max() - && src_edge->clock() == clk_ + if ((end_type == PathEnd::Type::check || end_type == PathEnd::Type::output_delay) + && path->minMax(sta_) == MinMax::max() && src_edge->clock() == clk_ && tgt_edge->clock() == clk_ // Only consider rise/rise and fall/fall paths. && src_edge->transition() == tgt_edge->transition() @@ -3377,8 +3608,8 @@ MinPeriodEndVisitor::visit(PathEnd *path_end) || !(network->isTopLevelPort(path->pin(sta_)) || pathIsFromInputPort(path_end)))) { Slack slack = path_end->slack(sta_); - float period = clk_->period() - delayAsFloat(slack); - min_period_ = max(min_period_, period); + float period = clk_->period() - delayAsFloat(slack, MinMax::min(), sta_); + min_period_ = std::max(min_period_, period); } } @@ -3401,7 +3632,7 @@ Sta::findClkMinPeriod(const Clock *clk, search_->findArrivals(); VisitPathEnds visit_ends(this); MinPeriodEndVisitor min_period_visitor(clk, include_port_paths, this); - for (Vertex *vertex : *search_->endpoints()) { + for (Vertex *vertex : search_->endpoints()) { findRequired(vertex); visit_ends.visitPathEnds(vertex, &min_period_visitor); } @@ -3417,7 +3648,7 @@ Sta::findRequired(Vertex *vertex) search_->findAllArrivals(); if (search_->isEndpoint(vertex) // Need to include downstream required times if there is fanout. - && !hasFanout(vertex, search_->searchAdj(), graph_)) + && !hasFanout(vertex, search_->searchAdj(), graph_, cmdMode())) search_->seedRequired(vertex); else search_->findRequireds(vertex->level()); @@ -3431,11 +3662,11 @@ Sta::totalNegativeSlack(const MinMax *min_max) } Slack -Sta::totalNegativeSlack(const Corner *corner, - const MinMax *min_max) +Sta::totalNegativeSlack(const Scene *scene, + const MinMax *min_max) { searchPreamble(); - return search_->totalNegativeSlack(corner, min_max); + return search_->totalNegativeSlack(scene, min_max); } Slack @@ -3450,43 +3681,43 @@ Sta::worstSlack(const MinMax *min_max) void Sta::worstSlack(const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex) + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { searchPreamble(); search_->worstSlack(min_max, worst_slack, worst_vertex); } void -Sta::worstSlack(const Corner *corner, - const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex) +Sta::worstSlack(const Scene *scene, + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { searchPreamble(); - return search_->worstSlack(corner, min_max, worst_slack, worst_vertex); + search_->worstSlack(scene, min_max, worst_slack, worst_vertex); } //////////////////////////////////////////////////////////////// -string +std::string Sta::reportDelayCalc(Edge *edge, - TimingArc *arc, - const Corner *corner, - const MinMax *min_max, - int digits) + TimingArc *arc, + const Scene *scene, + const MinMax *min_max, + int digits) { findDelays(); - return graph_delay_calc_->reportDelayCalc(edge, arc, corner, min_max, digits); + return graph_delay_calc_->reportDelayCalc(edge, arc, scene, min_max, digits); } void -Sta::setArcDelayCalc(const char *delay_calc_name) +Sta::setArcDelayCalc(std::string_view delay_calc_name) { delete arc_delay_calc_; - arc_delay_calc_ = makeDelayCalc(delay_calc_name, sta_); + arc_delay_calc_ = makeDelayCalc(std::string(delay_calc_name), sta_); // Update pointers to arc_delay_calc. updateComponentsState(); delaysInvalid(); @@ -3516,8 +3747,9 @@ Sta::findDelays(Level level) void Sta::delayCalcPreamble() { - ensureLibLinked(); - ensureClkNetwork(); + ensureLevelized(); + for (Mode *mode : modes_) + mode->clkNetwork()->ensureClkNetwork(); } void @@ -3528,79 +3760,51 @@ Sta::setIncrementalDelayTolerance(float tol) ArcDelay Sta::arcDelay(Edge *edge, - TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap) + TimingArc *arc, + DcalcAPIndex ap_index) { findDelays(edge->to(graph_)); - return graph_->arcDelay(edge, arc, dcalc_ap->index()); + return graph_->arcDelay(edge, arc, ap_index); } bool Sta::arcDelayAnnotated(Edge *edge, - TimingArc *arc, - DcalcAnalysisPt *dcalc_ap) + TimingArc *arc, + const Scene *scene, + const MinMax *min_max) { - return graph_->arcDelayAnnotated(edge, arc, dcalc_ap->index()); + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + return graph_->arcDelayAnnotated(edge, arc, ap_index); } void -Sta::setArcDelayAnnotated(Edge *edge, - TimingArc *arc, - DcalcAnalysisPt *dcalc_ap, - bool annotated) -{ - graph_->setArcDelayAnnotated(edge, arc, dcalc_ap->index(), annotated); - Vertex *to = edge->to(graph_); - search_->arrivalInvalid(to); - search_->requiredInvalid(edge->from(graph_)); - if (!annotated) - graph_delay_calc_->delayInvalid(to); -} - -Slew -Sta::vertexSlew(Vertex *vertex, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max) -{ - findDelays(vertex); - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - return graph_->slew(vertex, rf, dcalc_ap->index()); -} - -Slew -Sta::vertexSlew(Vertex *vertex, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) -{ - findDelays(vertex); - return graph_->slew(vertex, rf, dcalc_ap->index()); -} - -Slew -Sta::vertexSlew(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max) -{ - findDelays(vertex); - Slew mm_slew = min_max->initValue(); - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - Slew slew = graph_->slew(vertex, rf, dcalc_ap->index()); - if (delayGreater(slew, mm_slew, min_max, this)) - mm_slew = slew; - } - return mm_slew; +Sta::setArcDelayAnnotated(Edge *edge, + TimingArc *arc, + const Scene *scene, + const MinMax *min_max, + bool annotated) +{ + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + graph_->setArcDelayAnnotated(edge, arc, ap_index, annotated); + Vertex *to = edge->to(graph_); + search_->arrivalInvalid(to); + search_->requiredInvalid(edge->from(graph_)); + if (!annotated) + graph_delay_calc_->delayInvalid(to); } Slew -Sta::vertexSlew(Vertex *vertex, - const MinMax *min_max) +Sta::slew(Vertex *vertex, + const RiseFallBoth *rf, + const SceneSeq &scenes, + const MinMax *min_max) { findDelays(vertex); Slew mm_slew = min_max->initValue(); - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - for (const RiseFall *rf : RiseFall::range()) { - Slew slew = graph_->slew(vertex, rf, dcalc_ap->index()); + for (const Scene *scene : scenes) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + for (const RiseFall *rf : rf->range()) { + Slew slew = graph_->slew(vertex, rf, ap_index); if (delayGreater(slew, mm_slew, min_max, this)) mm_slew = slew; } @@ -3649,7 +3853,7 @@ Sta::ensureGraph() void Sta::makeGraph() { - graph_ = new Graph(this, 2, corners_->dcalcAnalysisPtCount()); + graph_ = new Graph(this, dcalcAnalysisPtCount()); graph_->makeGraph(); } @@ -3657,10 +3861,6 @@ void Sta::ensureLevelized() { ensureGraph(); - ensureGraphSdcAnnotated(); - // Need constant propagation before levelization to know edges that - // are disabled by constants. - sim_->ensureConstantsPropagated(); levelize_->ensureLevelized(); } @@ -3669,18 +3869,22 @@ Sta::updateGeneratedClks() { if (update_genclks_) { ensureLevelized(); - bool gen_clk_changed = true; - while (gen_clk_changed) { - gen_clk_changed = false; - for (Clock *clk : sdc_->clks()) { - if (clk->isGenerated() && !clk->waveformValid()) { - search_->genclks()->ensureMaster(clk); - Clock *master_clk = clk->masterClk(); - if (master_clk && master_clk->waveformValid()) { - clk->generate(master_clk); - gen_clk_changed = true; - } - } + for (Mode *mode : modes_) { + Genclks *genclks = mode->genclks(); + Sdc *sdc = mode->sdc(); + bool gen_clk_changed = true; + while (gen_clk_changed) { + gen_clk_changed = false; + for (Clock *clk : sdc->clocks()) { + if (clk->isGenerated() && !clk->waveformValid()) { + genclks->ensureMaster(clk, sdc); + Clock *master_clk = clk->masterClk(); + if (master_clk && master_clk->waveformValid()) { + clk->generate(master_clk); + gen_clk_changed = true; + } + } + } } } } @@ -3707,18 +3911,6 @@ Sta::graphLoops() return levelize_->loops(); } -PathAnalysisPt * -Sta::pathAnalysisPt(Path *path) -{ - return path->pathAnalysisPt(this); -} - -DcalcAnalysisPt * -Sta::pathDcalcAnalysisPt(Path *path) -{ - return pathAnalysisPt(path)->dcalcAnalysisPt(); -} - Vertex * Sta::maxPathCountVertex() const { @@ -3737,7 +3929,7 @@ Sta::maxPathCountVertex() const } int -Sta::vertexPathCount(Vertex *vertex) const +Sta::vertexPathCount(Vertex *vertex) const { TagGroup *tag_group = search_->tagGroup(vertex); if (tag_group) @@ -3778,15 +3970,14 @@ Sta::clkInfoCount() const void Sta::setArcDelay(Edge *edge, - TimingArc *arc, - const Corner *corner, - const MinMaxAll *min_max, - ArcDelay delay) + TimingArc *arc, + const Scene *scene, + const MinMaxAll *min_max, + ArcDelay delay) { ensureGraph(); for (const MinMax *mm : min_max->range()) { - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm); - DcalcAPIndex ap_index = dcalc_ap->index(); + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(mm); graph_->setArcDelay(edge, arc, ap_index, delay); // Don't let delay calculation clobber the value. graph_->setArcDelayAnnotated(edge, arc, ap_index, true); @@ -3801,15 +3992,14 @@ Sta::setArcDelay(Edge *edge, void Sta::setAnnotatedSlew(Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - const RiseFallBoth *rf, - float slew) + const Scene *scene, + const MinMaxAll *min_max, + const RiseFallBoth *rf, + float slew) { ensureGraph(); for (const MinMax *mm : min_max->range()) { - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm); - DcalcAPIndex ap_index = dcalc_ap->index(); + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(mm); for (const RiseFall *rf1 : rf->range()) { graph_->setSlew(vertex, rf1, ap_index, slew); // Don't let delay calculation clobber the value. @@ -3820,18 +4010,18 @@ Sta::setAnnotatedSlew(Vertex *vertex, } void -Sta::writeSdf(const char *filename, - const Corner *corner, - char divider, - bool include_typ, +Sta::writeSdf(std::string_view filename, + const Scene *scene, + char divider, + bool include_typ, int digits, - bool gzip, - bool no_timestamp, - bool no_version) + bool gzip, + bool no_timestamp, + bool no_version) { findDelays(); - sta::writeSdf(filename, corner, divider, include_typ, digits, gzip, - no_timestamp, no_version, this); + sta::writeSdf(filename, scene, divider, include_typ, digits, gzip, no_timestamp, + no_version, this); } void @@ -3844,50 +4034,55 @@ Sta::removeDelaySlewAnnotations() } LogicValue -Sta::simLogicValue(const Pin *pin) +Sta::simLogicValue(const Pin *pin, + const Mode *mode) { ensureGraph(); - sim_->ensureConstantsPropagated(); - return sim_->logicValue(pin); + Sim *sim = mode->sim(); + sim->ensureConstantsPropagated(); + return sim->simValue(pin); } +//////////////////////////////////////////////////////////////// + +// These constants are mode/sdc independent. + void Sta::findLogicConstants() { ensureGraph(); - sim_->findLogicConstants(); + // Sdc independent constants so any mode should return the same values. + Sim *sim = cmdMode()->sim(); + sim->findLogicConstants(); } void Sta::clearLogicConstants() { - sim_->clear(); + Sim *sim = cmdMode()->sim(); + sim->clear(); } +//////////////////////////////////////////////////////////////// + void Sta::setPortExtPinCap(const Port *port, - const RiseFallBoth *rf, - const Corner *corner, - const MinMaxAll *min_max, - float cap) + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float cap, + Sdc *sdc) { for (const RiseFall *rf1 : rf->range()) { - for (const MinMax *mm : min_max->range()) { - if (corner == nullptr) { - for (const Corner *corner : *corners_) - sdc_->setPortExtPinCap(port, rf1, corner, mm, cap); - } - else - sdc_->setPortExtPinCap(port, rf1, corner, mm, cap); - } + for (const MinMax *mm : min_max->range()) + sdc->setPortExtPinCap(port, rf1, mm, cap); } delaysInvalidFromFanin(port); } void Sta::portExtCaps(const Port *port, - const Corner *corner, const MinMax *min_max, + const Sdc *sdc, float &pin_cap, float &wire_cap, int &fanout) @@ -3902,10 +4097,8 @@ Sta::portExtCaps(const Port *port, float pin_cap1, wire_cap1; int fanout1; bool pin_exists1, wire_exists1, fanout_exists1; - sdc_->portExtCap(port, rf, corner, min_max, - pin_cap1, pin_exists1, - wire_cap1, wire_exists1, - fanout1, fanout_exists1); + sdc->portExtCap(port, rf, min_max, pin_cap1, pin_exists1, wire_cap1, + wire_exists1, fanout1, fanout_exists1); if (pin_exists1) { pin_cap = min_max->minMax(pin_cap, pin_cap1); pin_exists = true; @@ -3929,94 +4122,74 @@ Sta::portExtCaps(const Port *port, void Sta::setPortExtWireCap(const Port *port, - bool subtract_pin_cap, - const RiseFallBoth *rf, - const Corner *corner, - const MinMaxAll *min_max, - float cap) + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float cap, + Sdc *sdc) { for (const RiseFall *rf1 : rf->range()) { - for (const MinMax *mm : min_max->range()) { - if (corner == nullptr) { - for (const Corner *corner : *corners_) - sdc_->setPortExtWireCap(port, subtract_pin_cap, rf1, corner, mm, cap); - } - else - sdc_->setPortExtWireCap(port, subtract_pin_cap, rf1, corner, mm, cap); - } + for (const MinMax *mm : min_max->range()) + sdc->setPortExtWireCap(port, rf1, mm, cap); } delaysInvalidFromFanin(port); } void -Sta::removeNetLoadCaps() const +Sta::removeNetLoadCaps(Sdc *sdc) const { - sdc_->removeNetLoadCaps(); + sdc->removeNetLoadCaps(); delaysInvalid(); } void Sta::setPortExtFanout(const Port *port, - int fanout, - const Corner *corner, - const MinMaxAll *min_max) + int fanout, + const MinMaxAll *min_max, + Sdc *sdc) { - for (const MinMax *mm : min_max->range()) { - if (corner == nullptr) { - for (const Corner *corner : *corners_) - sdc_->setPortExtFanout(port, corner, mm, fanout); - } - else - sdc_->setPortExtFanout(port, corner, mm, fanout); - } + for (const MinMax *mm : min_max->range()) + sdc->setPortExtFanout(port, mm, fanout); delaysInvalidFromFanin(port); } void Sta::setNetWireCap(const Net *net, - bool subtract_pin_cap, - const Corner *corner, - const MinMaxAll *min_max, - float cap) + bool subtract_pin_cap, + const MinMaxAll *min_max, + float cap, + Sdc *sdc) { - for (const MinMax *mm : min_max->range()) { - if (corner == nullptr) { - for (const Corner *corner : *corners_) - sdc_->setNetWireCap(net, subtract_pin_cap, corner, mm, cap); - } - else - sdc_->setNetWireCap(net, subtract_pin_cap, corner, mm, cap); - } + for (const MinMax *mm : min_max->range()) + sdc->setNetWireCap(net, subtract_pin_cap, mm, cap); delaysInvalidFromFanin(net); } void Sta::connectedCap(const Pin *drvr_pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float &pin_cap, - float &wire_cap) const + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + float &pin_cap, + float &wire_cap) const { - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - graph_delay_calc_->loadCap(drvr_pin, rf, dcalc_ap, pin_cap, wire_cap); + graph_delay_calc_->loadCap(drvr_pin, rf, scene, min_max, pin_cap, wire_cap); } void Sta::connectedCap(const Net *net, - Corner *corner, - const MinMax *min_max, - float &pin_cap, - float &wire_cap) const + Scene *scene, + const MinMax *min_max, + float &pin_cap, + float &wire_cap) const { const Pin *drvr_pin = findNetParasiticDrvrPin(net); if (drvr_pin) { pin_cap = min_max->initValue(); wire_cap = min_max->initValue(); - for (const Corner *corner : makeCornerSeq(corner)) { + for (const Scene *scene : makeSceneSeq(scene)) { for (const RiseFall *rf : RiseFall::range()) { float pin_cap1, wire_cap1; - connectedCap(drvr_pin, rf, corner, min_max, pin_cap1, wire_cap1); + connectedCap(drvr_pin, rf, scene, min_max, pin_cap1, wire_cap1); pin_cap = min_max->minMax(pin_cap, pin_cap1); wire_cap = min_max->minMax(wire_cap, wire_cap1); } @@ -4028,28 +4201,19 @@ Sta::connectedCap(const Net *net, } } -CornerSeq -Sta::makeCornerSeq(Corner *corner) const -{ - CornerSeq corners; - if (corner) - corners.push_back(corner); - else - corners = corners_->corners(); - return corners; -} - float Sta::capacitance(const LibertyPort *port, - Corner *corner, + Scene *scene, const MinMax *min_max) { - OperatingConditions *op_cond = operatingConditions(min_max); float cap = min_max->initValue(); - for (const Corner *corner : makeCornerSeq(corner)) { - const LibertyPort *corner_port = port->cornerPort(corner, min_max); + for (const Scene *scene : makeSceneSeq(scene)) { + const Sdc *sdc = scene->sdc(); + OperatingConditions *op_cond = operatingConditions(min_max, sdc); + const LibertyPort *scene_port = port->scenePort(scene, min_max); for (const RiseFall *rf : RiseFall::range()) - cap = min_max->minMax(cap, corner_port->capacitance(rf, min_max, op_cond, op_cond)); + cap = min_max->minMax(cap, + scene_port->capacitance(rf, min_max, op_cond, op_cond)); } return cap; } @@ -4076,78 +4240,100 @@ Sta::findNetParasiticDrvrPin(const Net *net) const void Sta::setResistance(const Net *net, - const MinMaxAll *min_max, - float res) + const MinMaxAll *min_max, + float res, + Sdc *sdc) { - sdc_->setResistance(net, min_max, res); + sdc->setResistance(net, min_max, res); } //////////////////////////////////////////////////////////////// bool -Sta::readSpef(const char *filename, - Instance *instance, - const Corner *corner, - const MinMaxAll *min_max, - bool pin_cap_included, - bool keep_coupling_caps, - float coupling_cap_factor, - bool reduce) +Sta::readSpef(std::string_view name, + std::string_view filename, + Instance *instance, + Scene *scene, // -scene deprecated 11/20/2025 + const MinMaxAll *min_max, + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + bool reduce) { ensureLibLinked(); - setParasiticAnalysisPts(corner != nullptr); - const MinMax *ap_min_max = (min_max == MinMaxAll::all()) - ? MinMax::max() - : min_max->asMinMax(); - const Corner *ap_corner = corner ? corner : corners_->corners()[0]; - ParasiticAnalysisPt *ap = ap_corner->findParasiticAnalysisPt(ap_min_max); - bool success = readSpefFile(filename, instance, ap, - pin_cap_included, keep_coupling_caps, - coupling_cap_factor, reduce, - corner, min_max, this); + Parasitics *parasitics = nullptr; + // Use -name to distinguish rel 2.7 args for compatibility. + if (name.empty()) { + std::string spef_name = "default"; + if (scene || min_max != MinMaxAll::minMax()) { + if (scene) + spef_name = scene->name(); + if (min_max != MinMaxAll::minMax()) { + spef_name += "_"; + spef_name += min_max->to_string(); + } + parasitics = makeConcreteParasitics(spef_name, std::string(filename)); + } + else + parasitics = findParasitics(spef_name); + if (scene) + scene->setParasitics(parasitics, min_max); + else { + parasitics = findParasitics(spef_name); + for (Scene *scene : scenes_) + scene->setParasitics(parasitics, min_max); + } + } + else { + parasitics = findParasitics(std::string(name)); + if (parasitics == nullptr) + parasitics = makeConcreteParasitics(std::string(name), std::string(filename)); + } + + bool success = readSpefFile(filename, instance, pin_cap_included, + keep_coupling_caps, coupling_cap_factor, reduce, + scene, min_max, parasitics, this); delaysInvalid(); return success; } -void -Sta::setParasiticAnalysisPts(bool per_corner) -{ - if (per_corner != parasitics_per_corner_) { - deleteParasitics(); - parasitics_per_corner_ = per_corner; - makeParasiticAnalysisPts(); - } -} - -void -Sta::makeParasiticAnalysisPts() +Parasitics * +Sta::findParasitics(const std::string &name) { - corners_->makeParasiticAnalysisPts(parasitics_per_corner_); + return findKey(parasitics_name_map_, name); } void -Sta::reportParasiticAnnotation(bool report_unannotated, - const Corner *corner) +Sta::reportParasiticAnnotation(const std::string &spef_name, + bool report_unannotated) { ensureLibLinked(); ensureGraph(); - sta::reportParasiticAnnotation(report_unannotated, corner, this); + Parasitics *parasitics = nullptr; + if (!spef_name.empty()) { + parasitics = findParasitics(spef_name); + if (parasitics == nullptr) + report_->error(1560, "spef {} not found.", spef_name); + } + else + parasitics = cmd_scene_->parasitics(MinMax::max()); + sta::reportParasiticAnnotation(parasitics, report_unannotated, cmd_scene_, this); } void Sta::findPiElmore(Pin *drvr_pin, - const RiseFall *rf, - const MinMax *min_max, - float &c2, - float &rpi, - float &c1, - bool &exists) const -{ - Corner *corner = cmd_corner_; - const ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(min_max); - Parasitic *pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap); + const RiseFall *rf, + const MinMax *min_max, + float &c2, + float &rpi, + float &c1, + bool &exists) const +{ + Scene *scene = cmd_scene_; + const Parasitics *parasitics = scene->parasitics(min_max); + Parasitic *pi_elmore = parasitics->findPiElmore(drvr_pin, rf, min_max); if (pi_elmore) { - parasitics_->piModel(pi_elmore, c2, rpi, c1); + parasitics->piModel(pi_elmore, c2, rpi, c1); exists = true; } else @@ -4156,50 +4342,50 @@ Sta::findPiElmore(Pin *drvr_pin, void Sta::makePiElmore(Pin *drvr_pin, - const RiseFall *rf, - const MinMaxAll *min_max, - float c2, - float rpi, - float c1) + const RiseFall *rf, + const MinMaxAll *min_max, + float c2, + float rpi, + float c1) { - const Corner *corner = cmd_corner_; + const Scene *scene = cmd_scene_; for (const MinMax *mm : min_max->range()) { - ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(mm); - parasitics_->makePiElmore(drvr_pin, rf, ap, c2, rpi, c1); + Parasitics *parasitics = scene->parasitics(mm); + parasitics->makePiElmore(drvr_pin, rf, mm, c2, rpi, c1); } delaysInvalidFrom(drvr_pin); } void Sta::findElmore(Pin *drvr_pin, - Pin *load_pin, - const RiseFall *rf, - const MinMax *min_max, - float &elmore, - bool &exists) const -{ - Corner *corner = cmd_corner_; - const ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(min_max); - Parasitic *pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap); + Pin *load_pin, + const RiseFall *rf, + const MinMax *min_max, + float &elmore, + bool &exists) const +{ + Scene *scene = cmd_scene_; + const Parasitics *parasitics = scene->parasitics(min_max); + Parasitic *pi_elmore = parasitics->findPiElmore(drvr_pin, rf, min_max); if (pi_elmore) - parasitics_->findElmore(pi_elmore, load_pin, elmore, exists); + parasitics->findElmore(pi_elmore, load_pin, elmore, exists); else exists = false; } void Sta::setElmore(Pin *drvr_pin, - Pin *load_pin, - const RiseFall *rf, - const MinMaxAll *min_max, - float elmore) + Pin *load_pin, + const RiseFall *rf, + const MinMaxAll *min_max, + float elmore) { - const Corner *corner = cmd_corner_; + const Scene *scene = cmd_scene_; for (const MinMax *mm : min_max->range()) { - const ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(mm); - Parasitic *pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap); + Parasitics *parasitics = scene->parasitics(mm); + Parasitic *pi_elmore = parasitics->findPiElmore(drvr_pin, rf, mm); if (pi_elmore) - parasitics_->setElmore(pi_elmore, load_pin, elmore); + parasitics->setElmore(pi_elmore, load_pin, elmore); } delaysInvalidFrom(drvr_pin); } @@ -4207,17 +4393,40 @@ Sta::setElmore(Pin *drvr_pin, void Sta::deleteParasitics() { - parasitics_->deleteParasitics(); + Parasitics *parasitics_default = findParasitics("default"); + for (auto &[name, parasitics] : parasitics_name_map_) { + if (parasitics != parasitics_default) + delete parasitics; + } + parasitics_name_map_.clear(); + + parasitics_name_map_[parasitics_default->name()] = parasitics_default; + parasitics_default->clear(); + + for (Scene *scene : scenes_) + scene->setParasitics(parasitics_default, MinMaxAll::minMax()); + delaysInvalid(); } +Parasitics * +Sta::makeConcreteParasitics(std::string_view name, + std::string_view filename) +{ + Parasitics *parasitics = new ConcreteParasitics(name, filename, this); + parasitics_name_map_[std::string(name)] = parasitics; + return parasitics; +} + Parasitic * Sta::makeParasiticNetwork(const Net *net, bool includes_pin_caps, - const ParasiticAnalysisPt *ap) + const Scene *scene, + const MinMax *min_max) { - Parasitic *parasitic = parasitics_->makeParasiticNetwork(net, includes_pin_caps, ap); - delaysInvalidFromFanin(const_cast(net)); + Parasitics *parasitics = scene->parasitics(min_max); + Parasitic *parasitic = parasitics->makeParasiticNetwork(net, includes_pin_caps); + delaysInvalidFromFanin(net); return parasitic; } @@ -4236,13 +4445,13 @@ Sta::makeParasiticNetwork(const Net *net, NetworkEdit * Sta::networkCmdEdit() { - return dynamic_cast(cmd_network_); + return dynamic_cast(cmd_network_); } Instance * Sta::makeInstance(const char *name, - LibertyCell *cell, - Instance *parent) + LibertyCell *cell, + Instance *parent) { NetworkEdit *network = networkCmdEdit(); Instance *inst = network->makeInstance(cell, name, parent); @@ -4261,7 +4470,7 @@ Sta::deleteInstance(Instance *inst) void Sta::replaceCell(Instance *inst, - LibertyCell *to_lib_cell) + LibertyCell *to_lib_cell) { Cell *to_cell = network_->cell(to_lib_cell); replaceCell(inst, to_cell, to_lib_cell); @@ -4269,7 +4478,7 @@ Sta::replaceCell(Instance *inst, void Sta::replaceCell(Instance *inst, - Cell *to_cell) + Cell *to_cell) { LibertyCell *to_lib_cell = network_->libertyCell(to_cell); replaceCell(inst, to_cell, to_lib_cell); @@ -4277,8 +4486,8 @@ Sta::replaceCell(Instance *inst, void Sta::replaceCell(Instance *inst, - Cell *to_cell, - LibertyCell *to_lib_cell) + Cell *to_cell, + LibertyCell *to_lib_cell) { NetworkEdit *network = networkCmdEdit(); LibertyCell *from_lib_cell = network->libertyCell(inst); @@ -4298,7 +4507,7 @@ Sta::replaceCell(Instance *inst, Net * Sta::makeNet(const char *name, - Instance *parent) + Instance *parent) { NetworkEdit *network = networkCmdEdit(); Net *net = network->makeNet(name, parent); @@ -4316,8 +4525,8 @@ Sta::deleteNet(Net *net) void Sta::connectPin(Instance *inst, - Port *port, - Net *net) + Port *port, + Net *net) { NetworkEdit *network = networkCmdEdit(); Pin *pin = network->connect(inst, port, net); @@ -4326,8 +4535,8 @@ Sta::connectPin(Instance *inst, void Sta::connectPin(Instance *inst, - LibertyPort *port, - Net *net) + LibertyPort *port, + Net *net) { NetworkEdit *network = networkCmdEdit(); Pin *pin = network->connect(inst, port, net); @@ -4347,7 +4556,7 @@ Sta::makePortPin(const char *port_name, PortDirection *dir) { ensureLinked(); - NetworkReader *network = dynamic_cast(network_); + NetworkReader *network = dynamic_cast(network_); Instance *top_inst = network->topInstance(); Cell *top_cell = network->cell(top_inst); Port *port = network->makePort(top_cell, port_name); @@ -4355,7 +4564,7 @@ Sta::makePortPin(const char *port_name, Pin *pin = network->makePin(top_inst, port, nullptr); makePortPinAfter(pin); } - + //////////////////////////////////////////////////////////////// // // Network edit before/after methods. @@ -4365,7 +4574,7 @@ Sta::makePortPin(const char *port_name, void Sta::makeInstanceAfter(const Instance *inst) { - debugPrint(debug_, "network_edit", 1, "make instance %s", + debugPrint(debug_, "network_edit", 1, "make instance {}", sdc_network_->pathName(inst)); if (graph_) { LibertyCell *lib_cell = network_->libertyCell(inst); @@ -4378,7 +4587,6 @@ Sta::makeInstanceAfter(const Instance *inst) if (pin) { Vertex *vertex, *bidir_drvr_vertex; graph_->makePinVertices(pin, vertex, bidir_drvr_vertex); - } } graph_->makeInstanceEdges(inst); @@ -4398,7 +4606,7 @@ Sta::makePortPinAfter(Pin *pin) void Sta::replaceEquivCellBefore(const Instance *inst, - const LibertyCell *to_cell) + const LibertyCell *to_cell) { if (graph_) { InstancePinIterator *pin_iter = network_->pinIterator(inst); @@ -4422,15 +4630,16 @@ Sta::replaceEquivCellBefore(const Instance *inst, if (to_set) edge->setTimingArcSet(to_set); else - report_->critical(1555, "corresponding timing arc set not found in equiv cells"); + report_->critical( + 1556, "corresponding timing arc set not found in equiv cells"); } } } else { // Force delay calculation on output pins. Vertex *vertex = graph_->pinDrvrVertex(pin); - if (vertex) - graph_delay_calc_->delayInvalid(vertex); + if (vertex) + graph_delay_calc_->delayInvalid(vertex); } } } @@ -4445,8 +4654,10 @@ Sta::replaceEquivCellAfter(const Instance *inst) InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - if (network_->direction(pin)->isAnyInput()) - parasitics_->loadPinCapacitanceChanged(pin); + if (network_->direction(pin)->isAnyInput()) { + for (auto [name, parasitics] : parasitics_name_map_) + parasitics->loadPinCapacitanceChanged(pin); + } } delete pin_iter; clk_skews_->clear(); @@ -4456,16 +4667,15 @@ Sta::replaceEquivCellAfter(const Instance *inst) void Sta::replaceCellPinInvalidate(const LibertyPort *from_port, - Vertex *vertex, - const LibertyCell *to_cell) + Vertex *vertex, + const LibertyCell *to_cell) { LibertyPort *to_port = to_cell->findLibertyPort(from_port->name()); if (to_port == nullptr || (!libertyPortCapsEqual(to_port, from_port) // If this is an ideal clock pin, do not invalidate // arrivals and delay calc on the clock pin driver. - && !(to_port->isClock() - && idealClockMode()))) + && !(to_port->isClock() && idealClockMode()))) // Input port capacitance changed, so invalidate delay // calculation from input driver. delaysInvalidFromFanin(vertex); @@ -4476,30 +4686,33 @@ Sta::replaceCellPinInvalidate(const LibertyPort *from_port, bool Sta::idealClockMode() { - for (Clock *clk : sdc_->clks()) { - if (clk->isPropagated()) - return false; + for (Mode *mode : modes_) { + Sdc *sdc = mode->sdc(); + for (Clock *clk : sdc->clocks()) { + if (clk->isPropagated()) + return false; + } } return true; } static bool libertyPortCapsEqual(const LibertyPort *port1, - const LibertyPort *port2) + const LibertyPort *port2) { return port1->capacitance(RiseFall::rise(), MinMax::min()) - == port2->capacitance(RiseFall::rise(), MinMax::min()) - && port1->capacitance(RiseFall::rise(), MinMax::max()) - == port2->capacitance(RiseFall::rise(), MinMax::max()) - && port1->capacitance(RiseFall::fall(), MinMax::min()) - == port2->capacitance(RiseFall::fall(), MinMax::min()) - && port1->capacitance(RiseFall::fall(), MinMax::max()) - == port2->capacitance(RiseFall::fall(), MinMax::max()); + == port2->capacitance(RiseFall::rise(), MinMax::min()) + && port1->capacitance(RiseFall::rise(), MinMax::max()) + == port2->capacitance(RiseFall::rise(), MinMax::max()) + && port1->capacitance(RiseFall::fall(), MinMax::min()) + == port2->capacitance(RiseFall::fall(), MinMax::min()) + && port1->capacitance(RiseFall::fall(), MinMax::max()) + == port2->capacitance(RiseFall::fall(), MinMax::max()); } void Sta::replaceCellBefore(const Instance *inst, - const LibertyCell *to_cell) + const LibertyCell *to_cell) { if (graph_) { // Delete all graph edges between instance pins. @@ -4508,16 +4721,16 @@ Sta::replaceCellBefore(const Instance *inst, Pin *pin = pin_iter->next(); LibertyPort *port = network_->libertyPort(pin); if (port->direction()->isAnyInput()) { - Vertex *vertex = graph_->pinLoadVertex(pin); - replaceCellPinInvalidate(port, vertex, to_cell); - - 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) - deleteEdge(edge); - } + Vertex *vertex = graph_->pinLoadVertex(pin); + replaceCellPinInvalidate(port, vertex, to_cell); + + 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) + deleteEdge(edge); + } } } delete pin_iter; @@ -4532,9 +4745,12 @@ Sta::replaceCellAfter(const Instance *inst) InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - sim_->pinSetFuncAfter(pin); - if (network_->direction(pin)->isAnyInput()) - parasitics_->loadPinCapacitanceChanged(pin); + for (const Mode *mode : modes_) + mode->sim()->pinSetFuncAfter(pin); + if (network_->direction(pin)->isAnyInput()) { + for (auto [name, parasitics] : parasitics_name_map_) + parasitics->loadPinCapacitanceChanged(pin); + } } delete pin_iter; } @@ -4543,7 +4759,7 @@ Sta::replaceCellAfter(const Instance *inst) void Sta::connectPinAfter(const Pin *pin) { - debugPrint(debug_, "network_edit", 1, "connect %s to %s", + debugPrint(debug_, "network_edit", 1, "connect {} to {}", sdc_network_->pathName(pin), sdc_network_->pathName(network_->net(pin))); if (graph_) { @@ -4551,11 +4767,11 @@ Sta::connectPinAfter(const Pin *pin) graph_->makeWireEdgesThruPin(pin); EdgesThruHierPinIterator edge_iter(pin, network_, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->role()->isWire()) { - connectDrvrPinAfter(edge->from(graph_)); - connectLoadPinAfter(edge->to(graph_)); - } + Edge *edge = edge_iter.next(); + if (edge->role()->isWire()) { + connectDrvrPinAfter(edge->from(graph_)); + connectLoadPinAfter(edge->to(graph_)); + } } } else { @@ -4583,8 +4799,10 @@ Sta::connectPinAfter(const Pin *pin) } } } - sdc_->connectPinAfter(pin); - sim_->connectPinAfter(pin); + for (Mode *mode : modes_) { + mode->sdc()->connectPinAfter(pin); + mode->sim()->connectPinAfter(pin); + } clk_skews_->clear(); power_->powerInvalid(); } @@ -4599,15 +4817,18 @@ Sta::connectDrvrPinAfter(Vertex *vertex) Vertex *to_vertex = edge->to(graph_); search_->arrivalInvalid(to_vertex); search_->endpointInvalid(to_vertex); - sdc_->clkHpinDisablesChanged(to_vertex->pin()); + for (Mode *mode : modes_) + mode->sdc()->clkHpinDisablesChanged(to_vertex->pin()); } Pin *pin = vertex->pin(); - sdc_->clkHpinDisablesChanged(pin); + for (Mode *mode : modes_) { + mode->sdc()->clkHpinDisablesChanged(pin); + mode->clkNetwork()->connectPinAfter(pin); + } graph_delay_calc_->delayInvalid(vertex); search_->requiredInvalid(vertex); search_->endpointInvalid(vertex); levelize_->relevelizeFrom(vertex); - clk_network_->connectPinAfter(pin); } void @@ -4617,64 +4838,77 @@ Sta::connectLoadPinAfter(Vertex *vertex) VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); - Vertex *from_vertex = edge->from(graph_); - graph_delay_calc_->delayInvalid(from_vertex); - search_->requiredInvalid(from_vertex); - sdc_->clkHpinDisablesChanged(from_vertex->pin()); - levelize_->relevelizeFrom(from_vertex); + if (!edge->role()->isTimingCheck()) { + Vertex *from_vertex = edge->from(graph_); + graph_delay_calc_->delayInvalid(from_vertex); + search_->requiredInvalid(from_vertex); + levelize_->relevelizeFrom(from_vertex); + for (Mode *mode : modes_) + mode->sdc()->clkHpinDisablesChanged(from_vertex->pin()); + } } Pin *pin = vertex->pin(); - sdc_->clkHpinDisablesChanged(pin); + for (Mode *mode : modes_) { + mode->sdc()->clkHpinDisablesChanged(pin); + mode->clkNetwork()->connectPinAfter(pin); + } graph_delay_calc_->delayInvalid(vertex); search_->arrivalInvalid(vertex); search_->endpointInvalid(vertex); - clk_network_->connectPinAfter(pin); } void Sta::disconnectPinBefore(const Pin *pin) { - debugPrint(debug_, "network_edit", 1, "disconnect %s from %s", + debugPrint(debug_, "network_edit", 1, "disconnect {} from {}", sdc_network_->pathName(pin), sdc_network_->pathName(network_->net(pin))); - parasitics_->disconnectPinBefore(pin, network_); - sim_->disconnectPinBefore(pin); + + for (auto [name, parasitics] : parasitics_name_map_) + parasitics->disconnectPinBefore(pin); + bool is_hierarchical = network_->isHierarchical(pin); + for (Mode *mode : modes_) { + mode->sim()->disconnectPinBefore(pin); + if (!is_hierarchical) + mode->clkNetwork()->disconnectPinBefore(pin); + } + if (graph_) { if (network_->isDriver(pin)) { Vertex *vertex = graph_->pinDrvrVertex(pin); // Delete wire edges from pin. if (vertex) { - VertexOutEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->role()->isWire()) - deleteEdge(edge); - } - clk_network_->disconnectPinBefore(pin); + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role()->isWire()) + deleteEdge(edge); + } } } if (network_->isLoad(pin)) { // Delete wire edges to pin. Vertex *vertex = graph_->pinLoadVertex(pin); if (vertex) { - VertexInEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->role()->isWire()) - deleteEdge(edge); - } - clk_network_->disconnectPinBefore(pin); + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role()->isWire()) + deleteEdge(edge); + } } } - if (network_->isHierarchical(pin)) { + if (is_hierarchical) { // Delete wire edges thru pin. EdgesThruHierPinIterator edge_iter(pin, network_, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->role()->isWire()) { - deleteEdge(edge); - clk_network_->disconnectPinBefore(edge->from(graph_)->pin()); - } + Edge *edge = edge_iter.next(); + if (edge->role()->isWire()) { + deleteEdge(edge); + const Pin *from_pin = edge->from(graph_)->pin(); + for (Mode *mode : modes_) + mode->clkNetwork()->disconnectPinBefore(from_pin); + } } } clk_skews_->clear(); @@ -4685,44 +4919,48 @@ Sta::disconnectPinBefore(const Pin *pin) void Sta::deleteEdge(Edge *edge) { - debugPrint(debug_, "network_edit", 2, "delete edge %s -> %s", + debugPrint(debug_, "network_edit", 2, "delete edge {} -> {}", edge->from(graph_)->name(sdc_network_), edge->to(graph_)->name(sdc_network_)); Vertex *to = edge->to(graph_); - search_->deleteEdgeBefore(edge); - graph_delay_calc_->delayInvalid(to); - levelize_->relevelizeFrom(to); - levelize_->deleteEdgeBefore(edge); - sdc_->clkHpinDisablesChanged(edge->from(graph_)->pin()); + if (!edge->role()->isTimingCheck()) { + search_->deleteEdgeBefore(edge); + graph_delay_calc_->delayInvalid(to); + levelize_->relevelizeFrom(to); + levelize_->deleteEdgeBefore(edge); + for (Mode *mode : modes_) + mode->sdc()->clkHpinDisablesChanged(edge->from(graph_)->pin()); + } graph_->deleteEdge(edge); } void Sta::deleteNetBefore(const Net *net) { - debugPrint(debug_, "network_edit", 1, "delete net %s", + debugPrint(debug_, "network_edit", 1, "delete net {}", sdc_network_->pathName(net)); if (graph_) { NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); if (!network_->isHierarchical(pin)) { - disconnectPinBefore(pin); - // Delete wire edges on net pins. - Vertex *vertex = graph_->pinDrvrVertex(pin); - if (vertex) { - VertexOutEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->role()->isWire()) - deleteEdge(edge); - } - } + disconnectPinBefore(pin); + // Delete wire edges on net pins. + Vertex *vertex = graph_->pinDrvrVertex(pin); + if (vertex) { + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role()->isWire()) + deleteEdge(edge); + } + } } } delete pin_iter; } - sdc_->deleteNetBefore(net); + for (Mode *mode : modes_) + mode->sdc()->deleteNetBefore(net); clk_skews_->clear(); power_->powerInvalid(); } @@ -4730,7 +4968,7 @@ Sta::deleteNetBefore(const Net *net) void Sta::deleteInstanceBefore(const Instance *inst) { - debugPrint(debug_, "network_edit", 1, "delete instance %s", + debugPrint(debug_, "network_edit", 1, "delete instance {}", sdc_network_->pathName(inst)); if (network_->isLeaf(inst)) { deleteInstancePinsBefore(inst); @@ -4750,8 +4988,10 @@ Sta::deleteInstanceBefore(const Instance *inst) void Sta::deleteLeafInstanceBefore(const Instance *inst) { - sim_->deleteInstanceBefore(inst); - sdc_->deleteInstanceBefore(inst); + for (Mode *mode : modes_) { + mode->sim()->deleteInstanceBefore(inst); + mode->sdc()->deleteInstanceBefore(inst); + } clk_skews_->clear(); power_->powerInvalid(); } @@ -4771,8 +5011,8 @@ void Sta::deletePinBefore(const Pin *pin) { if (graph_) { - debugPrint(debug_, "network_edit", 1, "delete pin %s", - sdc_network_->pathName(pin)); + debugPrint(debug_, "network_edit", 1, "delete pin {}", + sdc_network_->pathName(pin)); if (network_->isLoad(pin)) { Vertex *vertex = graph_->pinLoadVertex(pin); if (vertex) { @@ -4790,7 +5030,7 @@ Sta::deletePinBefore(const Pin *pin) } levelize_->deleteEdgeBefore(edge); } - // Deletes edges to/from vertex also. + // Deletes edges to/from vertex also. graph_->deleteVertex(vertex); } } @@ -4813,7 +5053,7 @@ Sta::deletePinBefore(const Pin *pin) } levelize_->deleteEdgeBefore(edge); } - // Deletes edges to/from vertex also. + // Deletes edges to/from vertex also. graph_->deleteVertex(vertex); } } @@ -4828,9 +5068,12 @@ Sta::deletePinBefore(const Pin *pin) } } } - sdc_->deletePinBefore(pin); - sim_->deletePinBefore(pin); - clk_network_->deletePinBefore(pin); + + for (const Mode *mode : modes_) { + mode->sdc()->deletePinBefore(pin); + mode->sim()->deletePinBefore(pin); + mode->clkNetwork()->deletePinBefore(pin); + } } void @@ -4911,12 +5154,12 @@ Sta::delaysInvalidFromFanin(const Net *net) while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); if (!network_->isHierarchical(pin)) { - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - if (vertex) - delaysInvalidFrom(vertex); - if (bidirect_drvr_vertex) - delaysInvalidFrom(bidirect_drvr_vertex); + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + if (vertex) + delaysInvalidFrom(vertex); + if (bidirect_drvr_vertex) + delaysInvalidFrom(bidirect_drvr_vertex); } } delete pin_iter; @@ -4938,79 +5181,85 @@ Sta::delaysInvalidFromFanin(Vertex *vertex) //////////////////////////////////////////////////////////////// ClockSet -Sta::clocks(const Pin *pin) +Sta::clocks(const Pin *pin, + const Mode *mode) { ensureClkArrivals(); - return search_->clocks(pin); + return search_->clocks(pin, mode); } ClockSet -Sta::clockDomains(const Pin *pin) +Sta::clockDomains(const Pin *pin, + const Mode *mode) { searchPreamble(); search_->findAllArrivals(); - return search_->clockDomains(pin); + return search_->clockDomains(pin, mode); } //////////////////////////////////////////////////////////////// InstanceSet Sta::findRegisterInstances(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches) + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode) { - findRegisterPreamble(); - return findRegInstances(clks, clk_rf, edge_triggered, latches, this); + findRegisterPreamble(mode); + return findRegInstances(clks, clk_rf, registers, latches, mode, this); } PinSet Sta::findRegisterDataPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches) + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode) { - findRegisterPreamble(); - return findRegDataPins(clks, clk_rf, edge_triggered, latches, this); + findRegisterPreamble(mode); + return findRegDataPins(clks, clk_rf, registers, latches, mode, this); } PinSet Sta::findRegisterClkPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches) + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode) { - findRegisterPreamble(); - return findRegClkPins(clks, clk_rf, edge_triggered, latches, this); + findRegisterPreamble(mode); + return findRegClkPins(clks, clk_rf, registers, latches, mode, this); } PinSet Sta::findRegisterAsyncPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches) + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode) { - findRegisterPreamble(); - return findRegAsyncPins(clks, clk_rf, edge_triggered, latches, this); + findRegisterPreamble(mode); + return findRegAsyncPins(clks, clk_rf, registers, latches, mode, this); } PinSet Sta::findRegisterOutputPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches) + const RiseFallBoth *clk_rf, + bool registers, + bool latches, + const Mode *mode) { - findRegisterPreamble(); - return findRegOutputPins(clks, clk_rf, edge_triggered, latches, this); + findRegisterPreamble(mode); + return findRegOutputPins(clks, clk_rf, registers, latches, mode, this); } void -Sta::findRegisterPreamble() +Sta::findRegisterPreamble(const Mode *mode) { ensureLibLinked(); ensureGraph(); - ensureGraphSdcAnnotated(); - sim_->ensureConstantsPropagated(); + mode->sim()->ensureConstantsPropagated(); } //////////////////////////////////////////////////////////////// @@ -5019,15 +5268,18 @@ class FanInOutSrchPred : public SearchPred { public: FanInOutSrchPred(bool thru_disabled, - bool thru_constants, - const StaState *sta); - virtual bool searchFrom(const Vertex *from_vertex); - virtual bool searchThru(Edge *edge); - virtual bool searchTo(const Vertex *to_vertex); + bool thru_constants, + const StaState *sta); + bool searchFrom(const Vertex *from_vertex, + const Mode *mode) const override; + bool searchThru(Edge *edge, + const Mode *mode) const override; + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; protected: bool crossesHierarchy(Edge *edge); - virtual bool searchThruRole(Edge *edge); + virtual bool searchThruRole(Edge *edge) const; bool thru_disabled_; bool thru_constants_; @@ -5035,9 +5287,9 @@ class FanInOutSrchPred : public SearchPred }; FanInOutSrchPred::FanInOutSrchPred(bool thru_disabled, - bool thru_constants, - const StaState *sta) : - SearchPred(), + bool thru_constants, + const StaState *sta) : + SearchPred(sta), thru_disabled_(thru_disabled), thru_constants_(thru_constants), sta_(sta) @@ -5045,34 +5297,35 @@ FanInOutSrchPred::FanInOutSrchPred(bool thru_disabled, } bool -FanInOutSrchPred::searchFrom(const Vertex *from_vertex) +FanInOutSrchPred::searchFrom(const Vertex *from_vertex, + const Mode *mode) const { - return (thru_disabled_ - || !from_vertex->isDisabledConstraint()) - && (thru_constants_ - || !from_vertex->isConstant()); + const Pin *from_pin = from_vertex->pin(); + const Sdc *sdc = mode->sdc(); + return (thru_disabled_ || !sdc->isDisabledConstraint(from_pin)) + && (thru_constants_ || !mode->sim()->isConstant(from_vertex)); } bool -FanInOutSrchPred::searchThru(Edge *edge) +FanInOutSrchPred::searchThru(Edge *edge, + const Mode *mode) const { + const Sdc *sdc = mode->sdc(); + const Sim *sim = mode->sim(); return searchThruRole(edge) - && (thru_disabled_ - || !(edge->isDisabledConstraint() - || edge->isDisabledCond() - || sta_->isDisabledCondDefault(edge))) - && (thru_constants_ - || edge->simTimingSense() != TimingSense::none); + && (thru_disabled_ + || !(sdc->isDisabledConstraint(edge) || sim->isDisabledCond(edge) + || sta_->isDisabledCondDefault(edge))) + && (thru_constants_ || sim->simTimingSense(edge) != TimingSense::none); } bool -FanInOutSrchPred::searchThruRole(Edge *edge) +FanInOutSrchPred::searchThruRole(Edge *edge) const { const TimingRole *role = edge->role(); - return role == TimingRole::wire() - || role == TimingRole::combinational() - || role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable(); + return role == TimingRole::wire() || role == TimingRole::combinational() + || role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable(); } bool @@ -5088,70 +5341,73 @@ FanInOutSrchPred::crossesHierarchy(Edge *edge) } bool -FanInOutSrchPred::searchTo(const Vertex *to_vertex) +FanInOutSrchPred::searchTo(const Vertex *to_vertex, + const Mode *mode) const { - return (thru_disabled_ - || !to_vertex->isDisabledConstraint()) - && (thru_constants_ - || !to_vertex->isConstant()); + const Pin *to_pin = to_vertex->pin(); + const Sdc *sdc = mode->sdc(); + return (thru_disabled_ || !sdc->isDisabledConstraint(to_pin)) + && (thru_constants_ || !mode->sim()->isConstant(to_vertex)); } class FaninSrchPred : public FanInOutSrchPred { public: FaninSrchPred(bool thru_disabled, - bool thru_constants, - const StaState *sta); + bool thru_constants, + const StaState *sta); protected: - virtual bool searchThruRole(Edge *edge); + bool searchThruRole(Edge *edge) const override; }; FaninSrchPred::FaninSrchPred(bool thru_disabled, - bool thru_constants, - const StaState *sta) : - FanInOutSrchPred(thru_disabled, thru_constants, sta) + bool thru_constants, + const StaState *sta) : + FanInOutSrchPred(thru_disabled, + thru_constants, + sta) { } bool -FaninSrchPred::searchThruRole(Edge *edge) +FaninSrchPred::searchThruRole(Edge *edge) const { const TimingRole *role = edge->role(); - return role == TimingRole::wire() - || role == TimingRole::combinational() - || role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable() - || role == TimingRole::regClkToQ() - || role == TimingRole::latchEnToQ(); + return role == TimingRole::wire() || role == TimingRole::combinational() + || role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable() || role == TimingRole::regClkToQ() + || role == TimingRole::latchEnToQ(); } PinSet Sta::findFaninPins(PinSeq *to, - bool flat, - bool startpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants) + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants, + const Mode *mode) { ensureGraph(); ensureLevelized(); + mode->sim()->ensureConstantsPropagated(); PinSet fanin(network_); FaninSrchPred pred(thru_disabled, thru_constants, this); for (const Pin *pin : *to) { if (network_->isHierarchical(pin)) { EdgesThruHierPinIterator edge_iter(pin, network_, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - findFaninPins(edge->from(graph_), flat, startpoints_only, - inst_levels, pin_levels, fanin, pred); + Edge *edge = edge_iter.next(); + findFaninPins(edge->from(graph_), flat, startpoints_only, inst_levels, + pin_levels, fanin, pred, mode); } } else { Vertex *vertex = graph_->pinLoadVertex(pin); - findFaninPins(vertex, flat, startpoints_only, - inst_levels, pin_levels, fanin, pred); + findFaninPins(vertex, flat, startpoints_only, inst_levels, pin_levels, fanin, + pred, mode); } } return fanin; @@ -5159,62 +5415,52 @@ Sta::findFaninPins(PinSeq *to, void Sta::findFaninPins(Vertex *vertex, - bool flat, - bool startpoints_only, - int inst_levels, - int pin_levels, - PinSet &fanin, - SearchPred &pred) -{ - VertexSet visited(graph_); - findFaninPins(vertex, flat, inst_levels, - pin_levels, visited, &pred, 0, 0); - VertexSet::Iterator visited_iter(visited); - while (visited_iter.hasNext()) { - Vertex *visited_vertex = visited_iter.next(); + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + PinSet &fanin, + SearchPred &pred, + const Mode *mode) +{ + VertexSet visited = makeVertexSet(this); + findFaninPins(vertex, flat, inst_levels, pin_levels, visited, &pred, 0, 0, mode); + for (Vertex *visited_vertex : visited) { Pin *visited_pin = visited_vertex->pin(); - if (!startpoints_only - || network_->isRegClkPin(visited_pin) - || !hasFanin(visited_vertex, &pred, graph_)) + if (!startpoints_only || network_->isRegClkPin(visited_pin) + || !hasFanin(visited_vertex, &pred, graph_, mode)) fanin.insert(visited_pin); } } void Sta::findFaninPins(Vertex *to, - bool flat, - int inst_levels, - int pin_levels, - VertexSet &visited, - SearchPred *pred, - int inst_level, - int pin_level) -{ - debugPrint(debug_, "fanin", 1, "%s", - to->to_string(this).c_str()); - if (!visited.hasKey(to)) { + bool flat, + int inst_levels, + int pin_levels, + VertexSet &visited, + SearchPred *pred, + int inst_level, + int pin_level, + const Mode *mode) +{ + debugPrint(debug_, "fanin", 1, "{}", to->to_string(this)); + if (!visited.contains(to)) { visited.insert(to); Pin *to_pin = to->pin(); bool is_reg_clk_pin = network_->isRegClkPin(to_pin); - if (!is_reg_clk_pin - && (inst_levels <= 0 - || inst_level < inst_levels) - && (pin_levels <= 0 - || pin_level < pin_levels) - && pred->searchTo(to)) { + if (!is_reg_clk_pin && (inst_levels <= 0 || inst_level < inst_levels) + && (pin_levels <= 0 || pin_level < pin_levels) && pred->searchTo(to, mode)) { VertexInEdgeIterator edge_iter(to, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - Vertex *from_vertex = edge->from(graph_); - if (pred->searchThru(edge) - && (flat - || !crossesHierarchy(edge)) - && pred->searchFrom(from_vertex)) { - findFaninPins(from_vertex, flat, inst_levels, - pin_levels, visited, pred, - edge->role()->isWire() ? inst_level : inst_level+1, - pin_level+1); - } + Edge *edge = edge_iter.next(); + Vertex *from_vertex = edge->from(graph_); + if (pred->searchThru(edge, mode) && (flat || !crossesHierarchy(edge)) + && pred->searchFrom(from_vertex, mode)) { + findFaninPins(from_vertex, flat, inst_levels, pin_levels, visited, pred, + edge->role()->isWire() ? inst_level : inst_level + 1, + pin_level + 1, mode); + } } } } @@ -5222,46 +5468,47 @@ Sta::findFaninPins(Vertex *to, InstanceSet Sta::findFaninInstances(PinSeq *to, - bool flat, - bool startpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants) -{ - PinSet pins = findFaninPins(to, flat, startpoints_only, inst_levels, - pin_levels, thru_disabled, thru_constants); + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants, + const Mode *mode) +{ + PinSet pins = findFaninPins(to, flat, startpoints_only, inst_levels, pin_levels, + thru_disabled, thru_constants, mode); return pinInstances(pins, network_); } PinSet Sta::findFanoutPins(PinSeq *from, - bool flat, - bool endpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants) + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants, + const Mode *mode) { ensureGraph(); ensureLevelized(); + mode->sim()->ensureConstantsPropagated(); PinSet fanout(network_); FanInOutSrchPred pred(thru_disabled, thru_constants, this); - PinSeq::Iterator from_iter(from); - while (from_iter.hasNext()) { - const Pin *pin = from_iter.next(); + for (const Pin *pin : *from) { if (network_->isHierarchical(pin)) { EdgesThruHierPinIterator edge_iter(pin, network_, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - findFanoutPins(edge->to(graph_), flat, endpoints_only, - inst_levels, pin_levels, fanout, pred); + Edge *edge = edge_iter.next(); + findFanoutPins(edge->to(graph_), flat, endpoints_only, inst_levels, + pin_levels, fanout, pred, mode); } } else { Vertex *vertex = graph_->pinDrvrVertex(pin); - findFanoutPins(vertex, flat, endpoints_only, - inst_levels, pin_levels, fanout, pred); + findFanoutPins(vertex, flat, endpoints_only, inst_levels, pin_levels, fanout, + pred, mode); } } return fanout; @@ -5269,22 +5516,19 @@ Sta::findFanoutPins(PinSeq *from, void Sta::findFanoutPins(Vertex *vertex, - bool flat, - bool endpoints_only, - int inst_levels, - int pin_levels, - PinSet &fanout, - SearchPred &pred) -{ - VertexSet visited(graph_); - findFanoutPins(vertex, flat, inst_levels, - pin_levels, visited, &pred, 0, 0); - VertexSet::Iterator visited_iter(visited); - while (visited_iter.hasNext()) { - Vertex *visited_vertex = visited_iter.next(); + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + PinSet &fanout, + SearchPred &pred, + const Mode *mode) +{ + VertexSet visited = makeVertexSet(this); + findFanoutPins(vertex, flat, inst_levels, pin_levels, visited, &pred, 0, 0, mode); + for (Vertex *visited_vertex : visited) { Pin *visited_pin = visited_vertex->pin(); - if (!endpoints_only - || search_->isEndpoint(visited_vertex, &pred)) + if (!endpoints_only || search_->isEndpoint(visited_vertex, &pred, mode)) fanout.insert(visited_pin); } } @@ -5292,37 +5536,32 @@ Sta::findFanoutPins(Vertex *vertex, // DFS to support level limits. void Sta::findFanoutPins(Vertex *from, - bool flat, - int inst_levels, - int pin_levels, - VertexSet &visited, - SearchPred *pred, - int inst_level, - int pin_level) -{ - debugPrint(debug_, "fanout", 1, "%s", - from->to_string(this).c_str()); - if (!visited.hasKey(from)) { + bool flat, + int inst_levels, + int pin_levels, + VertexSet &visited, + SearchPred *pred, + int inst_level, + int pin_level, + const Mode *mode) +{ + debugPrint(debug_, "fanout", 1, "{}", from->to_string(this)); + if (!visited.contains(from)) { visited.insert(from); - if (!search_->isEndpoint(from, pred) - && (inst_levels <= 0 - || inst_level < inst_levels) - && (pin_levels <= 0 - || pin_level < pin_levels) - && pred->searchFrom(from)) { + if (!search_->isEndpoint(from, pred, mode) + && (inst_levels <= 0 || inst_level < inst_levels) + && (pin_levels <= 0 || pin_level < pin_levels) + && pred->searchFrom(from, mode)) { VertexOutEdgeIterator edge_iter(from, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - Vertex *to_vertex = edge->to(graph_); - if (pred->searchThru(edge) - && (flat - || !crossesHierarchy(edge)) - && pred->searchTo(to_vertex)) { - findFanoutPins(to_vertex, flat, inst_levels, - pin_levels, visited, pred, - edge->role()->isWire() ? inst_level : inst_level+1, - pin_level+1); - } + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (pred->searchThru(edge, mode) && (flat || !crossesHierarchy(edge)) + && pred->searchTo(to_vertex, mode)) { + findFanoutPins(to_vertex, flat, inst_levels, pin_levels, visited, pred, + edge->role()->isWire() ? inst_level : inst_level + 1, + pin_level + 1, mode); + } } } } @@ -5330,21 +5569,22 @@ Sta::findFanoutPins(Vertex *from, InstanceSet Sta::findFanoutInstances(PinSeq *from, - bool flat, - bool endpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants) -{ - PinSet pins = findFanoutPins(from, flat, endpoints_only, inst_levels, - pin_levels, thru_disabled, thru_constants); + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants, + const Mode *mode) +{ + PinSet pins = findFanoutPins(from, flat, endpoints_only, inst_levels, pin_levels, + thru_disabled, thru_constants, mode); return pinInstances(pins, network_); } static InstanceSet pinInstances(PinSet &pins, - const Network *network) + const Network *network) { InstanceSet insts(network); for (const Pin *pin : pins) @@ -5352,9 +5592,7 @@ pinInstances(PinSet &pins, return insts; } -InstanceSeq -Sta::clockGatedRegisters() -{ +InstanceSeq Sta::clockGatedRegisters() { InstanceSeq result; // Find all leaf registers @@ -5407,11 +5645,11 @@ Sta::crossesHierarchy(Edge *edge) const // Treat input/output port pins as "inside". if (network_->isTopInstance(from_inst)) from_parent = from_inst; - else + else from_parent = network_->parent(from_inst); if (network_->isTopInstance(to_inst)) to_parent = to_inst; - else + else to_parent = network_->parent(to_inst); return from_parent != to_parent; } @@ -5430,7 +5668,8 @@ instMaxSlew(const Instance *inst, Pin *pin = pin_iter->next(); if (network->isDriver(pin)) { Vertex *vertex = graph->pinDrvrVertex(pin); - Slew slew = sta->vertexSlew(vertex, MinMax::max()); + Slew slew = + sta->slew(vertex, RiseFallBoth::riseFall(), sta->scenes(), MinMax::max()); if (delayGreater(slew, max_slew, sta)) max_slew = slew; } @@ -5444,11 +5683,8 @@ Sta::slowDrivers(int count) { findDelays(); InstanceSeq insts = network_->leafInstances(); - sort(insts, [this] (const Instance *inst1, - const Instance *inst2) { - return delayGreater(instMaxSlew(inst1, this), - instMaxSlew(inst2, this), - this); + sort(insts, [this](const Instance *inst1, const Instance *inst2) { + return delayGreater(instMaxSlew(inst1, this), instMaxSlew(inst2, this), this); }); insts.resize(count); return insts; @@ -5457,404 +5693,358 @@ Sta::slowDrivers(int count) //////////////////////////////////////////////////////////////// void -Sta::checkSlewLimitPreamble() +Sta::reportSlewChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes, + const MinMax *min_max) +{ + checkSlewsPreamble(); + SlewCheckSeq &checks = + check_slews_->check(net, max_count, violators, scenes, min_max); + if (!checks.empty()) { + report_->report("{} slew", min_max->to_string()); + report_->report(""); + + if (!verbose) + report_path_->reportLimitShortHeader(report_path_->fieldSlew()); + for (SlewCheck &check : checks) { + if (verbose) + report_path_->reportLimitVerbose(report_path_->fieldSlew(), + check.pin(), check.edge(), + delayAsFloat(check.slew(), min_max, this), + check.limit(), check.slack(), + check.scene(), min_max); + else + report_path_->reportLimitShort(report_path_->fieldSlew(), check.pin(), + delayAsFloat(check.slew(), min_max, this), + check.limit(), check.slack()); + } + report_->report(""); + } +} + +void +Sta::checkSlewsPreamble() { - if (sdc_->haveClkSlewLimits()) + ensureLevelized(); + bool have_clk_slew_limits = false; + for (Mode *mode : modes_) { + if (mode->sdc()->haveClkSlewLimits()) + have_clk_slew_limits = true; + mode->clkNetwork()->ensureClkNetwork(); + } + if (have_clk_slew_limits) // Arrivals are needed to know pin clock domains. updateTiming(false); else findDelays(); - if (check_slew_limits_ == nullptr) - makeCheckSlewLimits(); - ensureClkNetwork(); -} - -PinSeq -Sta::checkSlewLimits(Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max) -{ - checkSlewLimitPreamble(); - return check_slew_limits_->checkSlewLimits(net, violators, corner, min_max); -} - -void -Sta::reportSlewLimitShortHeader() -{ - report_path_->reportLimitShortHeader(report_path_->fieldSlew()); -} - -void -Sta::reportSlewLimitShort(Pin *pin, - const Corner *corner, - const MinMax *min_max) -{ - const Corner *corner1; - const RiseFall *rf1; - Slew slew1; - float limit1, slack1; - check_slew_limits_->checkSlew(pin, corner, min_max, true, - corner1, rf1, slew1, limit1, slack1); - report_path_->reportLimitShort(report_path_->fieldSlew(), pin, - delayAsFloat(slew1), limit1, slack1); -} - -void -Sta::reportSlewLimitVerbose(Pin *pin, - const Corner *corner, - const MinMax *min_max) -{ - const Corner *corner1; - const RiseFall *rf1; - Slew slew1; - float limit1, slack1; - check_slew_limits_->checkSlew(pin, corner, min_max, true, - corner1, rf1, slew1, limit1, slack1); - report_path_->reportLimitVerbose(report_path_->fieldSlew(), pin, rf1, - delayAsFloat(slew1), - limit1, slack1, corner1, min_max); + if (check_slews_ == nullptr) + makeCheckSlews(); } void Sta::checkSlew(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - bool check_clks, - // Return values. - const Corner *&corner1, - const RiseFall *&rf, - Slew &slew, - float &limit, - float &slack) -{ - check_slew_limits_->checkSlew(pin, corner, min_max, check_clks, - corner1, rf, slew, limit, slack); -} - -void -Sta::maxSlewCheck(// Return values. - const Pin *&pin, - Slew &slew, - float &slack, - float &limit) -{ - checkSlewLimitPreamble(); - PinSeq pins = check_slew_limits_->checkSlewLimits(nullptr, false, nullptr, - MinMax::max()); - pin = nullptr; - slew = 0.0; - slack = INF; - limit = INF; - if (!pins.empty()) { - pin = pins[0]; - const Corner *corner; - const RiseFall *rf; - check_slew_limits_->checkSlew(pin, nullptr, MinMax::max(), true, - corner, rf, slew, limit, slack); + const SceneSeq &scenes, + const MinMax *min_max, + bool check_clks, + // Return values. + Slew &slew, + float &limit, + float &slack, + const RiseFall *&rf, + const Scene *&scene) +{ + check_slews_->check(pin, scenes, min_max, check_clks, slew, limit, slack, rf, + scene); +} + +size_t +Sta::maxSlewViolationCount() +{ + checkSlewsPreamble(); + return check_slews_->check(nullptr, 1, true, scenes_, MinMax::max()).size(); +} + +void +Sta::maxSlewCheck( // Return values. + const Pin *&pin, + Slew &slew, + float &slack, + float &limit) +{ + checkSlewsPreamble(); + SlewCheckSeq &checks = + check_slews_->check(nullptr, 1, false, scenes_, MinMax::max()); + if (!checks.empty()) { + SlewCheck &check = checks[0]; + pin = check.pin(); + slew = check.slew(); + slack = check.slack(); + limit = check.limit(); + } + else { + pin = nullptr; + slew = 0.0; + slack = INF; } } void Sta::findSlewLimit(const LibertyPort *port, - const Corner *corner, + const Scene *scene, const MinMax *min_max, // Return values. float &limit, bool &exists) { - if (check_slew_limits_ == nullptr) - makeCheckSlewLimits(); - check_slew_limits_->findLimit(port, corner, min_max, - limit, exists); + if (check_slews_ == nullptr) + makeCheckSlews(); + check_slews_->findLimit(port, scene, min_max, limit, exists); } ////////////////////////////////////////////////////////////////' void -Sta::checkFanoutLimitPreamble() -{ - if (check_fanout_limits_ == nullptr) - makeCheckFanoutLimits(); - ensureClkNetwork(); -} - -PinSeq -Sta::checkFanoutLimits(Net *net, - bool violators, - const MinMax *min_max) -{ - checkFanoutLimitPreamble(); - return check_fanout_limits_->checkFanoutLimits(net, violators, min_max); -} - -void -Sta::reportFanoutLimitShortHeader() -{ - report_path_->reportLimitShortHeader(report_path_->fieldFanout()); +Sta::reportFanoutChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes, + const MinMax *min_max) +{ + checkFanoutPreamble(); + const ModeSeq modes = Scene::modesSorted(scenes); + FanoutCheckSeq &checks = + check_fanouts_->check(net, max_count, violators, modes, min_max); + if (!checks.empty()) { + report_->report("{} fanout", min_max->to_string()); + report_->report(""); + + if (!verbose) + report_path_->reportLimitShortHeader(report_path_->fieldFanout()); + + for (const FanoutCheck &check : checks) { + if (verbose) + report_path_->reportLimitVerbose(report_path_->fieldFanout(), check.pin(), + nullptr, check.fanout(), check.limit(), + check.slack(), nullptr, min_max); + else + report_path_->reportLimitShort(report_path_->fieldFanout(), check.pin(), + check.fanout(), check.limit(), check.slack()); + } + report_->report(""); + } } void -Sta::reportFanoutLimitShort(Pin *pin, - const MinMax *min_max) +Sta::checkFanoutPreamble() { - float fanout, limit, slack; - check_fanout_limits_->checkFanout(pin, min_max, - fanout, limit, slack); - report_path_->reportLimitShort(report_path_->fieldFanout(), - pin, fanout, limit, slack); + if (check_fanouts_ == nullptr) + makeCheckFanouts(); + ensureLevelized(); + for (Mode *mode : modes_) { + mode->sim()->ensureConstantsPropagated(); + mode->clkNetwork()->ensureClkNetwork(); + } } -void -Sta::reportFanoutLimitVerbose(Pin *pin, - const MinMax *min_max) +size_t +Sta::fanoutViolationCount(const MinMax *min_max, + const ModeSeq &modes) { - float fanout, limit, slack; - check_fanout_limits_->checkFanout(pin, min_max, - fanout, limit, slack); - report_path_->reportLimitVerbose(report_path_->fieldFanout(), - pin, nullptr, fanout, - limit, slack, nullptr, min_max); + FanoutCheckSeq &checks = check_fanouts_->check(nullptr, 0, true, modes, min_max); + return checks.size(); } void Sta::checkFanout(const Pin *pin, - const MinMax *min_max, - // Return values. - float &fanout, - float &limit, - float &slack) -{ - check_fanout_limits_->checkFanout(pin, min_max, - fanout, limit, slack); -} - -void -Sta::maxFanoutCheck(// Return values. - const Pin *&pin, - float &fanout, - float &slack, - float &limit) -{ - checkFanoutLimitPreamble(); - PinSeq pins = check_fanout_limits_->checkFanoutLimits(nullptr, false, MinMax::max()); - pin = nullptr; - fanout = 0; - slack = INF; - limit = INF; - if (!pins.empty()) { - pin = pins[0]; - check_fanout_limits_->checkFanout(pin, MinMax::max(), - fanout, limit, slack); + const Mode *mode, + const MinMax *min_max, + // Return values. + float &fanout, + float &limit, + float &slack) +{ + FanoutCheck check = check_fanouts_->check(pin, mode, min_max); + fanout = check.fanout(); + limit = check.limit(); + slack = check.slack(); +} + +void +Sta::maxFanoutMinSlackPin(const ModeSeq &modes, + // Return values. + const Pin *&pin, + float &fanout, + float &limit, + float &slack, + const Mode *&mode) +{ + checkFanoutPreamble(); + FanoutCheckSeq &checks = + check_fanouts_->check(nullptr, 1, false, modes, MinMax::max()); + if (!checks.empty()) { + FanoutCheck &check = checks[0]; + pin = check.pin(); + fanout = check.fanout(); + limit = check.limit(); + slack = check.slack(); + mode = check.mode(); + } + else { + pin = nullptr; + fanout = 0; + limit = INF; + slack = INF; + mode = nullptr; } } ////////////////////////////////////////////////////////////////' void -Sta::checkCapacitanceLimitPreamble() -{ - if (check_capacitance_limits_ == nullptr) - makeCheckCapacitanceLimits(); - ensureClkNetwork(); -} - -PinSeq -Sta::checkCapacitanceLimits(Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max) +Sta::reportCapacitanceChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes, + const MinMax *min_max) { - checkCapacitanceLimitPreamble(); - return check_capacitance_limits_->checkCapacitanceLimits(net, violators, - corner, min_max); + checkCapacitancesPreamble(scenes); + CapacitanceCheckSeq &checks = + check_capacitances_->check(net, max_count, violators, scenes, min_max); + if (!checks.empty()) { + report_->report("{} capacitance", min_max->to_string()); + report_->report(""); + + if (!verbose) + report_path_->reportLimitShortHeader(report_path_->fieldCapacitance()); + for (CapacitanceCheck &check : checks) { + if (verbose) + report_path_->reportLimitVerbose(report_path_->fieldCapacitance(), + check.pin(), check.rf(), + check.capacitance(), check.limit(), + check.slack(), check.scene(), min_max); + else + report_path_->reportLimitShort(report_path_->fieldCapacitance(), check.pin(), + check.capacitance(), check.limit(), + check.slack()); + report_->report(""); + } + } } void -Sta::reportCapacitanceLimitShortHeader() +Sta::checkCapacitancesPreamble(const SceneSeq &scenes) { - report_path_->reportLimitShortHeader(report_path_->fieldCapacitance()); + ensureLevelized(); + if (check_capacitances_ == nullptr) + makeCheckCapacitances(); + for (Mode *mode : Scene::modes(scenes)) { + mode->sim()->ensureConstantsPropagated(); + mode->clkNetwork()->ensureClkNetwork(); + } } void -Sta::reportCapacitanceLimitShort(Pin *pin, - const Corner *corner, - const MinMax *min_max) +Sta::checkCapacitance(const Pin *pin, + const SceneSeq &scenes, + const MinMax *min_max, + // Return values. + float &capacitance, + float &limit, + float &slack, + const RiseFall *&rf, + const Scene *&scene) { - const Corner *corner1; - const RiseFall *rf; - float capacitance, limit, slack; - check_capacitance_limits_->checkCapacitance(pin, corner, min_max, - corner1, rf, capacitance, - limit, slack); - report_path_->reportLimitShort(report_path_->fieldCapacitance(), - pin, capacitance, limit, slack); + CapacitanceCheck check = check_capacitances_->check(pin, scenes, min_max); + capacitance = check.capacitance(); + limit = check.limit(); + slack = check.slack(); + rf = check.rf(); + scene = check.scene(); } -void -Sta::reportCapacitanceLimitVerbose(Pin *pin, - const Corner *corner, - const MinMax *min_max) +size_t +Sta::maxCapacitanceViolationCount() { - const Corner *corner1; - const RiseFall *rf1; - float capacitance1, limit1, slack1; - check_capacitance_limits_->checkCapacitance(pin, corner, min_max, - corner1, rf1, capacitance1, - limit1, slack1); - report_path_->reportLimitVerbose(report_path_->fieldCapacitance(), - pin, rf1, capacitance1, - limit1, slack1, corner1, min_max); + checkCapacitancesPreamble(scenes_); + return check_capacitances_->check(nullptr, 1, true, scenes_, MinMax::max()).size(); } void -Sta::checkCapacitance(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - const Corner *&corner1, - const RiseFall *&rf, - float &capacitance, - float &limit, - float &slack) -{ - check_capacitance_limits_->checkCapacitance(pin, corner, min_max, - corner1, rf, capacitance, - limit, slack); -} - -void -Sta::maxCapacitanceCheck(// Return values. - const Pin *&pin, - float &capacitance, - float &slack, - float &limit) -{ - checkCapacitanceLimitPreamble(); - PinSeq pins = check_capacitance_limits_->checkCapacitanceLimits(nullptr, false, - nullptr, - MinMax::max()); +Sta::maxCapacitanceCheck( // Return values. + const Pin *&pin, + float &capacitance, + float &slack, + float &limit) +{ + checkCapacitancesPreamble(scenes_); + CapacitanceCheckSeq &checks = + check_capacitances_->check(nullptr, 1, false, scenes_, MinMax::max()); pin = nullptr; capacitance = 0.0; slack = INF; limit = INF; - if (!pins.empty()) { - pin = pins[0]; - const Corner *corner; - const RiseFall *rf; - check_capacitance_limits_->checkCapacitance(pin, nullptr, MinMax::max(), - corner, rf, capacitance, limit, slack); + if (!checks.empty()) { + const CapacitanceCheck &check = checks[0]; + pin = check.pin(); + capacitance = check.capacitance(); + limit = check.limit(); + slack = check.slack(); } } //////////////////////////////////////////////////////////////// void -Sta::minPulseWidthPreamble() +Sta::reportMinPulseWidthChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes) { ensureClkArrivals(); if (check_min_pulse_widths_ == nullptr) makeCheckMinPulseWidths(); -} - -MinPulseWidthCheckSeq & -Sta::minPulseWidthChecks(PinSeq *pins, - const Corner *corner) -{ - minPulseWidthPreamble(); - return check_min_pulse_widths_->check(pins, corner); -} - -MinPulseWidthCheckSeq & -Sta::minPulseWidthChecks(const Corner *corner) -{ - minPulseWidthPreamble(); - return check_min_pulse_widths_->check(corner); -} - -MinPulseWidthCheckSeq & -Sta::minPulseWidthViolations(const Corner *corner) -{ - minPulseWidthPreamble(); - return check_min_pulse_widths_->violations(corner); -} - -MinPulseWidthCheck * -Sta::minPulseWidthSlack(const Corner *corner) -{ - minPulseWidthPreamble(); - return check_min_pulse_widths_->minSlackCheck(corner); -} - -void -Sta::reportMpwChecks(MinPulseWidthCheckSeq *checks, - bool verbose) -{ + MinPulseWidthCheckSeq &checks = + check_min_pulse_widths_->check(net, max_count, violators, scenes); report_path_->reportMpwChecks(checks, verbose); } -void -Sta::reportMpwCheck(MinPulseWidthCheck *check, - bool verbose) -{ - report_path_->reportMpwCheck(check, verbose); -} - //////////////////////////////////////////////////////////////// -MinPeriodCheckSeq & -Sta::minPeriodViolations() -{ - minPeriodPreamble(); - const Corner *corner = cmdCorner(); - return check_min_periods_->violations(corner); -} - -MinPeriodCheck * -Sta::minPeriodSlack() -{ - minPeriodPreamble(); - const Corner *corner = cmdCorner(); - return check_min_periods_->minSlackCheck(corner); -} - void -Sta::minPeriodPreamble() +Sta::reportMinPeriodChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes) { // Need clk arrivals to know what clks arrive at the clk tree endpoints. ensureClkArrivals(); if (check_min_periods_ == nullptr) makeCheckMinPeriods(); -} - -void -Sta::reportChecks(MinPeriodCheckSeq *checks, - bool verbose) -{ + MinPeriodCheckSeq &checks = + check_min_periods_->check(net, max_count, violators, scenes); report_path_->reportChecks(checks, verbose); } -void -Sta::reportCheck(MinPeriodCheck *check, - bool verbose) -{ - report_path_->reportCheck(check, verbose); -} - //////////////////////////////////////////////////////////////// -MaxSkewCheckSeq & -Sta::maxSkewViolations() -{ - maxSkewPreamble(); - return check_max_skews_->violations(); -} - -MaxSkewCheck * -Sta::maxSkewSlack() +void +Sta::reportMaxSkewChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes) { maxSkewPreamble(); - return check_max_skews_->minSlackCheck(); + MaxSkewCheckSeq &checks = + check_max_skews_->check(net, max_count, violators, scenes); + report_path_->reportChecks(checks, verbose); } void @@ -5865,25 +6055,11 @@ Sta::maxSkewPreamble() makeCheckMaxSkews(); } -void -Sta::reportChecks(MaxSkewCheckSeq *checks, - bool verbose) -{ - report_path_->reportChecks(checks, verbose); -} - -void -Sta::reportCheck(MaxSkewCheck *check, - bool verbose) -{ - report_path_->reportCheck(check, verbose); -} - //////////////////////////////////////////////////////////////// void Sta::makeEquivCells(LibertyLibrarySeq *equiv_libs, - LibertyLibrarySeq *map_libs) + LibertyLibrarySeq *map_libs) { delete equiv_cells_; equiv_cells_ = new EquivCells(equiv_libs, map_libs); @@ -5901,121 +6077,179 @@ Sta::equivCells(LibertyCell *cell) //////////////////////////////////////////////////////////////// void -Sta::writeTimingModel(const char *lib_name, - const char *cell_name, - const char *filename, - const Corner *corner, +Sta::writeTimingModel(std::string_view lib_name, + std::string_view cell_name, + std::string_view filename, + const Scene *scene, const bool scalar) { ensureLibLinked(); ensureGraph(); - LibertyLibrary *library = makeTimingModel(lib_name, cell_name, filename, - corner, scalar, this); - writeLiberty(library, filename, this); + LibertyLibrary *library = makeTimingModel(lib_name, cell_name, + filename, scene, scalar, this); + writeLiberty(library, std::string(filename).c_str(), this); } //////////////////////////////////////////////////////////////// void -Sta::powerPreamble() +Sta::reportPowerDesign(const Scene *scene, + int digits) +{ + powerPreamble(scene); + power_->reportDesign(scene, digits); +} + +void +Sta::reportPowerInsts(const InstanceSeq &insts, + const Scene *scene, + int digits) +{ + powerPreamble(scene); + power_->reportInsts(insts, scene, digits); +} + +void +Sta::reportPowerHighestInsts(size_t count, + const Scene *scene, + int digits) +{ + powerPreamble(scene); + power_->reportHighestInsts(count, scene, digits); +} + +void +Sta::reportPowerDesignJson(const Scene *scene, + int digits) +{ + powerPreamble(scene); + power_->reportDesignJson(scene, digits); +} + +void +Sta::reportPowerInstsJson(const InstanceSeq &insts, + const Scene *scene, + int digits) +{ + powerPreamble(scene); + power_->reportInstsJson(insts, scene, digits); +} + +void +Sta::powerPreamble(const Scene *scene) { ensureLibLinked(); // Use arrivals to find clocking info. searchPreamble(); search_->findAllArrivals(); - ensureClkNetwork(); + if (scene) + scene->mode()->clkNetwork()->ensureClkNetwork(); + else { + for (Mode *mode : modes_) + mode->clkNetwork()->ensureClkNetwork(); + } } void -Sta::power(const Corner *corner, - // Return values. - PowerResult &total, - PowerResult &sequential, - PowerResult &combinational, - PowerResult &clock, - PowerResult ¯o, - PowerResult &pad) +Sta::power(const Scene *scene, + // Return values. + PowerResult &total, + PowerResult &sequential, + PowerResult &combinational, + PowerResult &clock, + PowerResult ¯o, + PowerResult &pad) { - powerPreamble(); - power_->power(corner, total, sequential, combinational, clock, macro, pad); + powerPreamble(scene); + power_->power(scene, total, sequential, combinational, clock, macro, pad); } PowerResult Sta::power(const Instance *inst, - const Corner *corner) + const Scene *scene) { - powerPreamble(); - return power_->power(inst, corner); + powerPreamble(scene); + return power_->power(inst, scene); } PwrActivity -Sta::activity(const Pin *pin) +Sta::activity(const Pin *pin, + const Scene *scene) { - powerPreamble(); - return power_->pinActivity(pin); + powerPreamble(scene); + return power_->pinActivity(pin, scene); } //////////////////////////////////////////////////////////////// void -Sta::writePathSpice(Path *path, - const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, +Sta::writePathSpice(const Path *path, + std::string_view spice_filename, + std::string_view subckt_filename, + std::string_view lib_subckt_filename, + std::string_view model_filename, + std::string_view power_name, + std::string_view gnd_name, CircuitSim ckt_sim) { ensureLibLinked(); - sta::writePathSpice(path, spice_filename, subckt_filename, - lib_subckt_filename, model_filename, - power_name, gnd_name, ckt_sim, this); + sta::writePathSpice(path, spice_filename, subckt_filename, lib_subckt_filename, + model_filename, power_name, gnd_name, ckt_sim, this); } //////////////////////////////////////////////////////////////// void -Sta::ensureClkNetwork() +Sta::ensureClkNetwork(const Mode *mode) { ensureLevelized(); - clk_network_->ensureClkNetwork(); + mode->clkNetwork()->ensureClkNetwork(); } bool -Sta::isClock(const Pin *pin) const +Sta::isClock(const Pin *pin, + const Mode *mode) { - return clk_network_->isClock(pin); + ensureClkNetwork(mode); + return mode->clkNetwork()->isClock(pin); } bool -Sta::isClock(const Net *net) const +Sta::isClock(const Net *net, + const Mode *mode) { - return clk_network_->isClock(net); + ensureClkNetwork(mode); + return mode->clkNetwork()->isClock(net); } bool -Sta::isIdealClock(const Pin *pin) const +Sta::isIdealClock(const Pin *pin, + const Mode *mode) { - return clk_network_->isIdealClock(pin); + ensureClkNetwork(mode); + return mode->clkNetwork()->isIdealClock(pin); } bool -Sta::isPropagatedClock(const Pin *pin) const +Sta::isPropagatedClock(const Pin *pin, + const Mode *mode) { - return clk_network_->isPropagatedClock(pin); + return mode->clkNetwork()->isPropagatedClock(pin); } const PinSet * -Sta::pins(const Clock *clk) +Sta::pins(const Clock *clk, + const Mode *mode) { - return clk_network_->pins(clk); + ensureClkNetwork(mode); + return mode->clkNetwork()->pins(clk); } void -Sta::clkPinsInvalid() +Sta::clkPinsInvalid(const Mode *mode) { - clk_network_->clkPinsInvalid(); + ensureClkNetwork(mode); + mode->clkNetwork()->clkPinsInvalid(); } -} // namespace +} // namespace sta diff --git a/search/StaState.cc b/search/StaState.cc index c9eb7438b..ea673b382 100644 --- a/search/StaState.cc +++ b/search/StaState.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,13 +26,16 @@ #include +#include "ContainerHelpers.hh" #include "DispatchQueue.hh" -#include "Units.hh" +#include "Graph.hh" +#include "Mode.hh" #include "Network.hh" -#include "Variables.hh" +#include "Scene.hh" #include "Sdc.hh" -#include "Graph.hh" #include "TimingArc.hh" +#include "Units.hh" +#include "Variables.hh" namespace sta { @@ -41,21 +44,16 @@ StaState::StaState() : debug_(nullptr), units_(nullptr), network_(nullptr), - sdc_(nullptr), - corners_(nullptr), graph_(nullptr), levelize_(nullptr), - parasitics_(nullptr), arc_delay_calc_(nullptr), graph_delay_calc_(nullptr), - sim_(nullptr), search_(nullptr), + delay_ops_(nullptr), latches_(nullptr), - clk_network_(nullptr), variables_(nullptr), thread_count_(1), - dispatch_queue_(nullptr), - sigma_factor_(1.0) + dispatch_queue_(nullptr) { } @@ -113,17 +111,60 @@ StaState::setDebug(Debug *debug) } bool -StaState::crprActive() const +StaState::crprActive(const Mode *mode) const { - return sdc_->analysisType() == AnalysisType::ocv + return mode->sdc()->analysisType() == AnalysisType::ocv && variables_->crprEnabled(); } bool -StaState::isDisabledCondDefault(Edge *edge) const +StaState::isDisabledCondDefault(const Edge *edge) const { return !variables_->condDefaultArcsEnabled() && edge->timingArcSet()->isCondDefault(); } -} // namespace +//////////////////////////////////////////////////////////////// + +size_t +StaState::scenePathCount() const +{ + return scenes_.size() * MinMax::index_count; +} + +// The clock insertion delay (source latency) required for setup and +// hold checks is: +// +// hold check +// report_timing -delay_type min +// path insertion pll_delay +// src clk min early max +// tgt clk max late min +// +// setup check +// report_timing -delay_type max +// path insertion pll_delay +// src clk max late min +// tgt clk min early max +// +// For analysis type single or bc_wc only one path is required, but as +// shown above both early and late insertion delays are required. +// To find propagated generated clock insertion delays both early and +// late clock network paths are required. Thus, analysis type single +// makes min and max analysis points. +// Only one of them is enabled to "report paths". + +DcalcAPIndex +StaState::dcalcAnalysisPtCount() const +{ + return MinMax::index_count * scenes_.size(); +} + +SceneSet +StaState::scenesSet() +{ + return Scene::sceneSet(scenes_); +} + + +} // namespace sta diff --git a/search/Tag.cc b/search/Tag.cc index 27badadcc..8dffce4a9 100644 --- a/search/Tag.cc +++ b/search/Tag.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,51 +24,49 @@ #include "Tag.hh" -#include "Report.hh" -#include "Network.hh" +#include "ClkInfo.hh" #include "Clock.hh" -#include "PortDelay.hh" #include "ExceptionPath.hh" -#include "Sdc.hh" #include "Graph.hh" -#include "Corner.hh" +#include "Network.hh" +#include "PortDelay.hh" +#include "Report.hh" +#include "Scene.hh" +#include "Sdc.hh" #include "Search.hh" -#include "PathAnalysisPt.hh" -#include "ClkInfo.hh" namespace sta { -Tag::Tag(TagIndex index, - int rf_index, - PathAPIndex path_ap_index, - const ClkInfo *clk_info, - bool is_clk, - InputDelay *input_delay, - bool is_segment_start, - ExceptionStateSet *states, - bool own_states, - const StaState *sta) : +Tag::Tag(Scene *scene, + TagIndex index, + const RiseFall *rf, + const MinMax *min_max, + const ClkInfo *clk_info, + bool is_clk, + InputDelay *input_delay, + bool is_segment_start, + ExceptionStateSet *states, + bool own_states) : + scene_(scene), clk_info_(clk_info), input_delay_(input_delay), states_(states), index_(index), is_clk_(is_clk), - is_filter_(false), - is_loop_(false), is_segment_start_(is_segment_start), own_states_(own_states), - rf_index_(rf_index), - path_ap_index_(path_ap_index) + rf_index_(rf->index()), + min_max_index_(min_max->index()) { findHash(); if (states_) { - FilterPath *filter = sta->search()->filter(); + FilterPath *filter = scene_->sdc()->filter(); for (ExceptionState *state : *states_) { ExceptionPath *exception = state->exception(); if (exception->isLoop()) - is_loop_ = true; + is_loop_ = true; if (exception == filter) - is_filter_ = true; + is_filter_ = true; } } } @@ -91,24 +89,24 @@ Tag::to_string(bool report_index, const StaState *sta) const { const Network *network = sta->network(); - const Corners *corners = sta->corners(); std::string result; - if (report_index) + if (report_index) { result += std::to_string(index_); + result += " "; + } + + result += scene_->name(); + result += " "; if (report_rf_min_max) { const RiseFall *rf = transition(); - PathAnalysisPt *path_ap = corners->findPathAnalysisPt(path_ap_index_); + const MinMax *min_max = minMax(); + result += rf->shortName(); + result += min_max->to_string(); result += " "; - result += rf->to_string().c_str(); - result += " "; - result += path_ap->pathMinMax()->to_string(); - result += "/"; - result += std::to_string(path_ap_index_); } - result += " "; const ClockEdge *clk_edge = clkEdge(); if (clk_edge) result += clk_edge->name(); @@ -121,11 +119,11 @@ Tag::to_string(bool report_index, if (is_clk_) { result += "clock"; if (clk_info_->isPropagated()) - result += " prop"; + result += " prop"; else - result += " ideal"; + result += " ideal"; if (is_genclk_src) - result += " "; + result += " "; } if (clk_info_->isGenClkSrcPath()) result += "genclk"; @@ -160,15 +158,15 @@ Tag::to_string(bool report_index, for (ExceptionState *state : *states_) { ExceptionPath *exception = state->exception(); result += " "; - result += exception->asString(network); + result += exception->to_string(network); if (state->nextThru()) { - result += " (next thru "; - result += state->nextThru()->asString(network); - result += ")"; + result += " (next thru "; + result += state->nextThru()->to_string(network); + result += ")"; } else { - if (exception->thrus() != nullptr) - result += " (thrus complete)"; + if (exception->thrus() != nullptr) + result += " (thrus complete)"; } } } @@ -182,16 +180,9 @@ Tag::transition() const } const MinMax * -Tag::minMax(const StaState *sta) const +Tag::minMax() const { - return pathAnalysisPt(sta)->pathMinMax(); -} - -PathAnalysisPt * -Tag::pathAnalysisPt(const StaState *sta) const -{ - const Corners *corners = sta->corners(); - return corners->findPathAnalysisPt(path_ap_index_); + return MinMax::find(min_max_index_); } void @@ -225,24 +216,24 @@ Tag::isGenClkSrcPath() const } const Clock * -Tag::genClkSrcPathClk(const StaState *sta) const +Tag::genClkSrcPathClk() const { if (clk_info_->isGenClkSrcPath() && states_) { - FilterPath *filter = sta->search()->filter(); + FilterPath *filter = scene_->sdc()->filter(); for (ExceptionState *state : *states_) { ExceptionPath *except = state->exception(); if (except->isFilter() - && except != filter) { - ExceptionTo *to = except->to(); - if (to) { - ClockSet *clks = to->clks(); - if (clks && clks->size() == 1) { - ClockSet::Iterator clk_iter(clks); - const Clock *clk = clk_iter.next(); - return clk; - } - } + && except != filter) { + ExceptionTo *to = except->to(); + if (to) { + ClockSet *clks = to->clks(); + if (clks && clks->size() == 1) { + ClockSet::iterator clk_iter = clks->begin(); + const Clock *clk = *clk_iter; + return clk; + } + } } } } @@ -254,8 +245,9 @@ Tag::findHash() { // Common to hash_ and match_hash_. hash_ = hash_init_value; + hashIncr(hash_, scene_->index()); hashIncr(hash_, rf_index_); - hashIncr(hash_, path_ap_index_); + hashIncr(hash_, min_max_index_); hashIncr(hash_, is_clk_); hashIncr(hash_, is_segment_start_); if (states_) { @@ -275,7 +267,7 @@ Tag::findHash() size_t Tag::hash(bool match_crpr_clk_pin, - const StaState *sta) const + const StaState *sta) const { if (match_crpr_clk_pin) return hashSum(hash_, clk_info_->crprClkVertexId(sta)); @@ -302,32 +294,32 @@ TagLess::TagLess(const StaState *sta) : bool TagLess::operator()(const Tag *tag1, - const Tag *tag2) const + const Tag *tag2) const { return Tag::cmp(tag1, tag2, sta_) < 0; } int Tag::cmp(const Tag *tag1, - const Tag *tag2, - const StaState *sta) + const Tag *tag2, + const StaState *sta) { if (tag1 == tag2) return 0; + size_t scene_index1 = tag1->scene()->index(); + size_t scene_index2 = tag2->scene()->index(); + if (scene_index1 < scene_index2) + return -1; + else if (scene_index1 > scene_index2) + return 1; + const ClkInfo *clk_info1 = tag1->clkInfo(); const ClkInfo *clk_info2 = tag2->clkInfo(); int clk_cmp = ClkInfo::cmp(clk_info1, clk_info2, sta); if (clk_cmp != 0) return clk_cmp; - PathAPIndex path_ap_index1 = tag1->pathAPIndex(); - PathAPIndex path_ap_index2 = tag2->pathAPIndex(); - if (path_ap_index1 < path_ap_index2) - return -1; - if (path_ap_index1 > path_ap_index2) - return 1; - int rf_index1 = tag1->rfIndex(); int rf_index2 = tag2->rfIndex(); if (rf_index1 < rf_index2) @@ -335,6 +327,13 @@ Tag::cmp(const Tag *tag1, if (rf_index1 > rf_index2) return 1; + int mm_index1 = tag1->minMaxIndex(); + int mm_index2 = tag2->minMaxIndex(); + if (mm_index1 < mm_index2) + return -1; + if (mm_index1 > mm_index2) + return 1; + bool is_clk1 = tag1->isClock(); bool is_clk2 = tag2->isClock(); if (!is_clk1 && is_clk2) @@ -363,8 +362,8 @@ Tag::cmp(const Tag *tag1, bool Tag::equal(const Tag *tag1, - const Tag *tag2, - const StaState *sta) + const Tag *tag2, + const StaState *sta) { return cmp(tag1, tag2, sta) == 0; } @@ -373,7 +372,7 @@ Tag::equal(const Tag *tag1, bool TagIndexLess::operator()(const Tag *tag1, - const Tag *tag2) const + const Tag *tag2) const { return tag1->index() < tag2->index(); } @@ -381,7 +380,7 @@ TagIndexLess::operator()(const Tag *tag1, //////////////////////////////////////////////////////////////// TagMatchLess::TagMatchLess(bool match_crpr_clk_pin, - const StaState *sta) : + const StaState *sta) : match_crpr_clk_pin_(match_crpr_clk_pin), sta_(sta) { @@ -389,7 +388,7 @@ TagMatchLess::TagMatchLess(bool match_crpr_clk_pin, bool TagMatchLess::operator()(const Tag *tag1, - const Tag *tag2) const + const Tag *tag2) const { return Tag::matchCmp(tag1, tag2, match_crpr_clk_pin_, sta_) < 0; } @@ -398,30 +397,37 @@ TagMatchLess::operator()(const Tag *tag1, bool Tag::match(const Tag *tag1, - const Tag *tag2, - const StaState *sta) + const Tag *tag2, + const StaState *sta) { return Tag::matchCmp(tag1, tag2, true, sta) == 0; } bool Tag::match(const Tag *tag1, - const Tag *tag2, - bool match_crpr_clk_pin, - const StaState *sta) + const Tag *tag2, + bool match_crpr_clk_pin, + const StaState *sta) { return Tag::matchCmp(tag1, tag2, match_crpr_clk_pin, sta) == 0; } int Tag::matchCmp(const Tag *tag1, - const Tag *tag2, - bool match_crpr_clk_pin, - const StaState *sta) + const Tag *tag2, + bool match_crpr_clk_pin, + const StaState *sta) { if (tag1 == tag2) return 0; + size_t scene_index1 = tag1->scene()->index(); + size_t scene_index2 = tag2->scene()->index(); + if (scene_index1 < scene_index2) + return -1; + else if (scene_index1 > scene_index2) + return 1; + int rf_index1 = tag1->rfIndex(); int rf_index2 = tag2->rfIndex(); if (rf_index1 < rf_index2) @@ -429,11 +435,11 @@ Tag::matchCmp(const Tag *tag1, if (rf_index1 > rf_index2) return 1; - PathAPIndex path_ap_index1 = tag1->pathAPIndex(); - PathAPIndex path_ap_index2 = tag2->pathAPIndex(); - if (path_ap_index1 < path_ap_index2) + int mm_index1 = tag1->minMaxIndex(); + int mm_index2 = tag2->minMaxIndex(); + if (mm_index1 < mm_index2) return -1; - if (path_ap_index1 > path_ap_index2) + if (mm_index1 > mm_index2) return 1; const ClkInfo *clk_info1 = tag1->clkInfo(); @@ -468,8 +474,7 @@ Tag::matchCmp(const Tag *tag1, if (is_segment_start1 && !is_segment_start2) return 1; - if (match_crpr_clk_pin - && sta->crprActive()) { + if (match_crpr_clk_pin) { VertexId crpr_vertex1 = clk_info1->crprClkVertexId(sta); VertexId crpr_vertex2 = clk_info2->crprClkVertexId(sta); if (crpr_vertex1 < crpr_vertex2) @@ -483,54 +488,59 @@ Tag::matchCmp(const Tag *tag1, bool Tag::matchNoCrpr(const Tag *tag1, - const Tag *tag2) + const Tag *tag2) { const ClkInfo *clk_info1 = tag1->clkInfo(); const ClkInfo *clk_info2 = tag2->clkInfo(); return tag1 == tag2 - || (clk_info1->clkEdge() == clk_info2->clkEdge() - && tag1->rfIndex() == tag2->rfIndex() - && tag1->pathAPIndex() == tag2->pathAPIndex() - && tag1->isClock() == tag2->isClock() - && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() - && Tag::stateEqual(tag1, tag2)); + || (tag1->scene() == tag2->scene() + && clk_info1->clkEdge() == clk_info2->clkEdge() + && tag1->rfIndex() == tag2->rfIndex() + && tag1->minMaxIndex() == tag2->minMaxIndex() + && tag1->isClock() == tag2->isClock() + && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() + && Tag::stateEqual(tag1, tag2)); } bool Tag::matchNoPathAp(const Tag *tag1, - const Tag *tag2) + const Tag *tag2) { const ClkInfo *clk_info1 = tag1->clkInfo(); const ClkInfo *clk_info2 = tag2->clkInfo(); return tag1 == tag2 - || (clk_info1->clkEdge() == clk_info2->clkEdge() - && tag1->rfIndex() == tag2->rfIndex() - && tag1->isClock() == tag2->isClock() - && tag1->isSegmentStart() == tag2->isSegmentStart() - && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() - && Tag::stateEqual(tag1, tag2)); + || (tag1->scene() == tag2->scene() + && clk_info1->clkEdge() == clk_info2->clkEdge() + && tag1->rfIndex() == tag2->rfIndex() + // min_max not matched + && tag1->isClock() == tag2->isClock() + && tag1->isSegmentStart() == tag2->isSegmentStart() + && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() + && Tag::stateEqual(tag1, tag2)); } bool Tag::matchCrpr(const Tag *tag1, - const Tag *tag2) + const Tag *tag2) { const ClkInfo *clk_info1 = tag1->clkInfo(); const ClkInfo *clk_info2 = tag2->clkInfo(); return tag1 == tag2 - || (clk_info1->clkEdge() == clk_info2->clkEdge() - && tag1->rfIndex() == tag2->rfIndex() - && tag1->isClock() == tag2->isClock() - && tag1->isSegmentStart() == tag2->isSegmentStart() - && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() - && stateEqualCrpr(tag1, tag2)); + || (tag1->scene() == tag2->scene() + && clk_info1->clkEdge() == clk_info2->clkEdge() + && tag1->rfIndex() == tag2->rfIndex() + // min_max not matched + && tag1->isClock() == tag2->isClock() + && tag1->isSegmentStart() == tag2->isSegmentStart() + && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() + && stateEqualCrpr(tag1, tag2)); } //////////////////////////////////////////////////////////////// int Tag::stateCmp(const Tag *tag1, - const Tag *tag2) + const Tag *tag2) { ExceptionStateSet *states1 = tag1->states(); ExceptionStateSet *states2 = tag2->states(); @@ -553,23 +563,24 @@ Tag::stateCmp(const Tag *tag1, if (state_size1 > state_size2) return 1; - ExceptionStateSet::Iterator state_iter1(states1); - ExceptionStateSet::Iterator state_iter2(states2); - while (state_iter1.hasNext() - && state_iter2.hasNext()) { - ExceptionState *state1 = state_iter1.next(); - ExceptionState *state2 = state_iter2.next(); - if (exceptionStateLess(state1, state2)) - return -1; - if (exceptionStateLess(state2, state1)) - return 1; + auto iter1 = states1->begin(); + auto iter2 = states2->begin(); + while (iter1 != states1->end() + && iter2 != states2->end()) { + ExceptionState *state1 = *iter1; + ExceptionState *state2 = *iter2; + int state_cmp = exceptionStateCmp(state1, state2); + if (state_cmp != 0) + return state_cmp; + ++iter1; + ++iter2; } return 0; } bool Tag::stateEqual(const Tag *tag1, - const Tag *tag2) + const Tag *tag2) { return stateCmp(tag1, tag2) == 0; } @@ -577,37 +588,43 @@ Tag::stateEqual(const Tag *tag1, // Match loop exception states only for crpr min/max paths. bool Tag::stateEqualCrpr(const Tag *tag1, - const Tag *tag2) + const Tag *tag2) { ExceptionStateSet *states1 = tag1->states(); ExceptionStateSet *states2 = tag2->states(); - ExceptionStateSet::Iterator state_iter1(states1); - ExceptionStateSet::Iterator state_iter2(states2); - ExceptionState *state1, *state2; - do { - state1 = nullptr; - while (state_iter1.hasNext()) { - state1 = state_iter1.next(); - ExceptionPath *exception1 = state1->exception(); - if (exception1->isLoop()) - break; - else - state1 = nullptr; - } - state2 = nullptr; - while (state_iter2.hasNext()) { - state2 = state_iter2.next(); - ExceptionPath *exception2 = state2->exception(); - if (exception2->isLoop()) - break; - else - state2 = nullptr; - } - if (state1 != state2) - return false; - } while (state1 && state2); - return state1 == nullptr - && state2 == nullptr; + if (states1 && states2) { + auto iter1 = states1->begin(); + auto iter2 = states2->begin(); + ExceptionState *state1, *state2; + do { + state1 = nullptr; + while (iter1 != states1->end()) { + state1 = *iter1; + ++iter1; + ExceptionPath *exception1 = state1->exception(); + if (exception1->isLoop()) + break; + else + state1 = nullptr; + } + state2 = nullptr; + while (iter2 != states2->end()) { + state2 = *iter2; + ++iter2; + ExceptionPath *exception2 = state2->exception(); + if (exception2->isLoop()) + break; + else + state2 = nullptr; + } + if (state1 != state2) + return false; + } while (state1 && state2); + return state1 == nullptr + && state2 == nullptr; + } + else + return true; } //////////////////////////////////////////////////////////////// @@ -620,8 +637,7 @@ TagHash::TagHash(const StaState *sta) : size_t TagHash::operator()(const Tag *tag) const { - bool crpr_on = sta_->crprActive(); - return tag->matchHash(crpr_on, sta_); + return tag->matchHash(true, sta_); } TagEqual::TagEqual(const StaState *sta) : @@ -631,13 +647,13 @@ TagEqual::TagEqual(const StaState *sta) : bool TagEqual::operator()(const Tag *tag1, - const Tag *tag2) const + const Tag *tag2) const { return Tag::equal(tag1, tag2, sta_); } TagMatchHash::TagMatchHash(bool match_crpr_clk_pin, - const StaState *sta) : + const StaState *sta) : match_crpr_clk_pin_(match_crpr_clk_pin), sta_(sta) { @@ -650,7 +666,7 @@ TagMatchHash::operator()(const Tag *tag) const } TagMatchEqual::TagMatchEqual(bool match_crpr_clk_pin, - const StaState *sta) : + const StaState *sta) : match_crpr_clk_pin_(match_crpr_clk_pin), sta_(sta) { @@ -658,9 +674,9 @@ TagMatchEqual::TagMatchEqual(bool match_crpr_clk_pin, bool TagMatchEqual::operator()(const Tag *tag1, - const Tag *tag2) const + const Tag *tag2) const { return Tag::match(tag1, tag2, match_crpr_clk_pin_, sta_); } -} // namespace +} // namespace sta diff --git a/search/Tag.hh b/search/Tag.hh index e084bd29f..006e88656 100644 --- a/search/Tag.hh +++ b/search/Tag.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,12 +24,15 @@ #pragma once -#include "Vector.hh" -#include "Set.hh" -#include "Transition.hh" +#include +#include + +#include "NetworkClass.hh" +#include "Scene.hh" #include "SdcClass.hh" #include "SearchClass.hh" -#include "Path.hh" +#include "StaState.hh" +#include "Transition.hh" namespace sta { @@ -51,82 +54,83 @@ namespace sta { class Tag { public: - Tag(TagIndex index, - int rf_index, - PathAPIndex path_ap_index, + Tag(Scene *scene, + TagIndex index, + const RiseFall *rf, + const MinMax *min_max, const ClkInfo *clk_info, bool is_clk, InputDelay *input_delay, bool is_segment_start, ExceptionStateSet *states, - bool own_states, - const StaState *sta); + bool own_states); ~Tag(); std::string to_string(const StaState *sta) const; std::string to_string(bool report_index, bool report_rf_min_max, const StaState *sta) const; + Scene *scene() const { return scene_; } const ClkInfo *clkInfo() const { return clk_info_; } bool isClock() const { return is_clk_; } const ClockEdge *clkEdge() const; const Clock *clock() const; const Pin *clkSrc() const; - int rfIndex() const { return rf_index_; } + size_t rfIndex() const { return rf_index_; } const RiseFall *transition() const; - const MinMax *minMax(const StaState *sta) const; - PathAnalysisPt *pathAnalysisPt(const StaState *sta) const; - PathAPIndex pathAPIndex() const { return path_ap_index_; } + const MinMax *minMax() const; + int minMaxIndex() const { return min_max_index_; } TagIndex index() const { return index_; } ExceptionStateSet *states() const { return states_; } void setStates(ExceptionStateSet *states); bool isGenClkSrcPath() const; - const Clock *genClkSrcPathClk(const StaState *sta) const; + const Clock *genClkSrcPathClk() const; // Input delay at search startpoint (not propagated). InputDelay *inputDelay() const { return input_delay_; } bool isLoop() const { return is_loop_; } bool isFilter() const { return is_filter_; } bool isSegmentStart() const { return is_segment_start_; } size_t hash(bool match_crpr_clk_pin, - const StaState *sta) const; + const StaState *sta) const; size_t matchHash(bool match_crpr_clk_pin, const StaState *sta) const; static int cmp(const Tag *tag1, - const Tag *tag2, - const StaState *sta); + const Tag *tag2, + const StaState *sta); static int matchCmp(const Tag *tag1, - const Tag *tag2, - bool match_clk_clk_pin, - const StaState *sta); + const Tag *tag2, + bool match_crpr_clk_pin, + const StaState *sta); static bool match(const Tag *tag1, - const Tag *tag2, - bool match_crpr_clk_pin, - const StaState *sta); + const Tag *tag2, + bool match_crpr_clk_pin, + const StaState *sta); static bool equal(const Tag *tag1, - const Tag *tag2, - const StaState *sta); + const Tag *tag2, + const StaState *sta); static bool matchNoPathAp(const Tag *tag1, - const Tag *tag2); + const Tag *tag2); static bool matchCrpr(const Tag *tag1, - const Tag *tag2); + const Tag *tag2); static bool matchNoCrpr(const Tag *tag1, - const Tag *tag2); + const Tag *tag2); protected: void findHash(); // Match tag clock edge, clock driver and exception states but not clk info. static bool match(const Tag *tag1, - const Tag *tag2, - const StaState *sta); + const Tag *tag2, + const StaState *sta); static bool stateEqual(const Tag *tag1, - const Tag *tag2); + const Tag *tag2); static int stateCmp(const Tag *tag1, - const Tag *tag2); + const Tag *tag2); static bool stateEqualCrpr(const Tag *tag1, - const Tag *tag2); + const Tag *tag2); private: + Scene *scene_; const ClkInfo *clk_info_; InputDelay *input_delay_; ExceptionStateSet *states_; @@ -134,13 +138,13 @@ private: size_t match_hash_; TagIndex index_; bool is_clk_:1; - bool is_filter_:1; - bool is_loop_:1; - bool is_segment_start_:1; + bool is_filter_:1 {false}; + bool is_loop_:1 {false}; + bool is_segment_start_:1 {false}; // Indicates that states_ is owned by the tag. - bool own_states_:1; + bool own_states_:1 {false}; unsigned int rf_index_:RiseFall::index_bit_count; - unsigned int path_ap_index_:path_ap_index_bit_count; + unsigned int min_max_index_:MinMax::index_bit_count; }; class TagLess @@ -148,7 +152,7 @@ class TagLess public: TagLess(const StaState *sta); bool operator()(const Tag *tag1, - const Tag *tag2) const; + const Tag *tag2) const; private: const StaState *sta_; @@ -158,7 +162,7 @@ class TagIndexLess { public: bool operator()(const Tag *tag1, - const Tag *tag2) const; + const Tag *tag2) const; }; class TagHash @@ -176,10 +180,10 @@ class TagEqual public: TagEqual(const StaState *sta); bool operator()(const Tag *tag1, - const Tag *tag2) const; + const Tag *tag2) const; private: const StaState *sta_; }; -} // namespace +} // namespace sta diff --git a/search/TagGroup.cc b/search/TagGroup.cc index 2496ef11a..6fbe4d933 100644 --- a/search/TagGroup.cc +++ b/search/TagGroup.cc @@ -1,50 +1,50 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "TagGroup.hh" -#include "Report.hh" +#include "ClkInfo.hh" #include "Debug.hh" #include "Graph.hh" -#include "PathAnalysisPt.hh" -#include "ClkInfo.hh" -#include "Tag.hh" -#include "Corner.hh" -#include "Search.hh" #include "Path.hh" +#include "Report.hh" +#include "Scene.hh" +#include "Search.hh" +#include "Tag.hh" namespace sta { TagGroup::TagGroup(TagGroupIndex index, - PathIndexMap *path_index_map, - bool has_clk_tag, - bool has_genclk_src_tag, - bool has_filter_tag, - bool has_loop_tag, - const StaState *sta) : + PathIndexMap *path_index_map, + bool has_clk_tag, + bool has_genclk_src_tag, + bool has_filter_tag, + bool has_loop_tag, + const StaState *sta) : path_index_map_(path_index_map), - hash_(hash(path_index_map, sta)), + hash_(hash(path_index_map, + sta)), ref_count_(0), index_(index), has_clk_tag_(has_clk_tag), @@ -56,9 +56,10 @@ TagGroup::TagGroup(TagGroupIndex index, } TagGroup::TagGroup(TagGroupBldr *tag_bldr, - const StaState *sta) : + const StaState *sta) : path_index_map_(&tag_bldr->pathIndexMap()), - hash_(hash(path_index_map_, sta)), + hash_(hash(path_index_map_, + sta)), ref_count_(0), own_path_map_(false) { @@ -84,28 +85,30 @@ TagGroup::decrRefCount() size_t TagGroup::hash(PathIndexMap *path_index_map, - const StaState *sta) + const StaState *sta) { - bool crpr_on = sta->crprActive(); size_t hash = 0; for (auto const [tag, path_index] : *path_index_map) - hash += tag->hash(crpr_on, sta); + hash += tag->hash(true, sta); return hash; } bool TagGroup::hasTag(Tag *tag) const { - return path_index_map_->hasKey(tag); + return path_index_map_->contains(tag); } size_t TagGroup::pathIndex(Tag *tag) const { - size_t path_index = 0; + size_t path_index; bool exists; - pathIndex(tag, path_index, exists); - return path_index; + findKeyValue(path_index_map_, tag, path_index, exists); + if (exists) + return path_index; + else + return 0; } void @@ -113,14 +116,14 @@ TagGroup::pathIndex(Tag *tag, size_t &path_index, bool &exists) const { - path_index_map_->findKey(tag, path_index, exists); + findKeyValue(path_index_map_, tag, path_index, exists); } void TagGroup::report(const StaState *sta) const { Report *report = sta->report(); - report->reportLine("Group %u hash = %zu", index_, hash_); + report->report("Group {} hash = {}", index_, hash_); pathIndexMapReport(path_index_map_, sta); } @@ -136,26 +139,19 @@ pathIndexMapReport(const PathIndexMap *path_index_map, { Report *report = sta->report(); for (auto const [tag, path_index] : *path_index_map) - report->reportLine(" %2zu %s", - path_index, - tag->to_string(sta).c_str()); + report->report(" {:2} {}", path_index, tag->to_string(sta)); report->reportBlankLine(); } //////////////////////////////////////////////////////////////// TagGroupBldr::TagGroupBldr(bool match_crpr_clk_pin, - const StaState *sta) : - default_path_count_(sta->corners()->count() - * RiseFall::index_count + const StaState *sta) : + default_path_count_(sta->scenes().size() * RiseFall::index_count * MinMax::index_count), - path_index_map_(TagMatchLess(match_crpr_clk_pin, sta)), + path_index_map_(TagMatchLess(match_crpr_clk_pin, + sta)), paths_(default_path_count_), - has_clk_tag_(false), - has_genclk_src_tag_(false), - has_filter_tag_(false), - has_loop_tag_(false), - has_propagated_clk_(false), sta_(sta) { } @@ -203,8 +199,7 @@ TagGroupBldr::tagMatchPath(Tag *tag, // Match is not necessarily equal to original tag because it // must only satisfy tagMatch. bool exists; - Tag *tag_match; - path_index_map_.findKey(tag, tag_match, path_index, exists); + findKeyValue(path_index_map_, tag, path_index, exists); if (exists) match = &paths_[path_index]; else { @@ -213,7 +208,7 @@ TagGroupBldr::tagMatchPath(Tag *tag, } } -Arrival +Arrival TagGroupBldr::arrival(size_t path_index) const { return paths_[path_index].arrival(); @@ -221,7 +216,7 @@ TagGroupBldr::arrival(size_t path_index) const void TagGroupBldr::setArrival(Tag *tag, - const Arrival &arrival) + const Arrival &arrival) { // Find matching group tag (not necessarily equal to original tag). Path *match; @@ -245,10 +240,10 @@ TagGroupBldr::setMatchPath(Path *match, if (tag_match != tag) { // Replace tag in arrival map. path_index_map_.erase(tag_match); - path_index_map_.insert(tag, path_index); + path_index_map_[tag] = path_index; } - paths_[path_index].init(vertex_, tag, arrival, prev_path, - prev_edge, prev_arc, sta_); + paths_[path_index].init(vertex_, tag, arrival, prev_path, prev_edge, prev_arc, + sta_); } else insertPath(tag, arrival, prev_path, prev_edge, prev_arc); @@ -263,16 +258,14 @@ TagGroupBldr::insertPath(Tag *tag, { size_t path_index = paths_.size(); - path_index_map_.insert(tag, path_index); - paths_.emplace_back(vertex_, tag, arrival, prev_path, - prev_edge, prev_arc, sta_); + path_index_map_[tag] = path_index; + paths_.emplace_back(vertex_, tag, arrival, prev_path, prev_edge, prev_arc, sta_); if (tag->isClock()) has_clk_tag_ = true; if (tag->isGenClkSrcPath()) has_genclk_src_tag_ = true; - if (tag->isFilter() - || tag->clkInfo()->crprPathRefsFilter()) + if (tag->isFilter() || tag->clkInfo()->crprPathRefsFilter()) has_filter_tag_ = true; if (tag->isLoop()) has_loop_tag_ = true; @@ -283,28 +276,25 @@ TagGroupBldr::insertPath(Tag *tag, void TagGroupBldr::insertPath(const Path &path) { - insertPath(path.tag(sta_), path.arrival(), path.prevPath(), - path.prevEdge(sta_), path.prevArc(sta_)); + insertPath(path.tag(sta_), path.arrival(), path.prevPath(), path.prevEdge(sta_), + path.prevArc(sta_)); } TagGroup * TagGroupBldr::makeTagGroup(TagGroupIndex index, - const StaState *sta) + const StaState *sta) { - return new TagGroup(index, makePathIndexMap(sta), - has_clk_tag_, has_genclk_src_tag_, has_filter_tag_, - has_loop_tag_, sta); - + return new TagGroup(index, makePathIndexMap(sta), has_clk_tag_, + has_genclk_src_tag_, has_filter_tag_, has_loop_tag_, sta); } PathIndexMap * TagGroupBldr::makePathIndexMap(const StaState *sta) { PathIndexMap *path_index_map = new PathIndexMap(TagMatchLess(true, sta)); - size_t path_index = 0; for (auto const [tag, path_index1] : path_index_map_) { - path_index_map->insert(tag, path_index); + (*path_index_map)[tag] = path_index; path_index++; } return path_index_map; @@ -321,7 +311,7 @@ TagGroupBldr::copyPaths(TagGroup *tag_group, if (exists2) paths[path_index2] = paths_[path_index1]; else - sta_->report()->critical(1351, "tag group missing tag"); + sta_->report()->critical(1360, "tag group missing tag"); } } @@ -339,12 +329,8 @@ pathIndexMapEqual(const PathIndexMap *path_index_map1, { if (path_index_map1->size() == path_index_map2->size()) { for (auto const [tag1, path_index1] : *path_index_map1) { - Tag *tag2; - size_t path_index2; - bool exists2; - path_index_map2->findKey(tag1, tag2, path_index2, exists2); - if (!exists2) - return false; + if (!path_index_map2->contains(tag1)) + return false; } return true; } @@ -354,12 +340,12 @@ pathIndexMapEqual(const PathIndexMap *path_index_map1, bool TagGroupEqual::operator()(const TagGroup *tag_group1, - const TagGroup *tag_group2) const + const TagGroup *tag_group2) const { return tag_group1 == tag_group2 - || (tag_group1->hash() == tag_group2->hash() - && pathIndexMapEqual(tag_group1->pathIndexMap(), - tag_group2->pathIndexMap())); + || (tag_group1->hash() == tag_group2->hash() + && pathIndexMapEqual(tag_group1->pathIndexMap(), + tag_group2->pathIndexMap())); } -} // namespace +} // namespace sta diff --git a/search/TagGroup.hh b/search/TagGroup.hh index 88722d5f7..2865186a3 100644 --- a/search/TagGroup.hh +++ b/search/TagGroup.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,14 +25,13 @@ #pragma once #include +#include -#include "Vector.hh" -#include "Map.hh" -#include "Iterator.hh" -#include "MinMax.hh" -#include "Transition.hh" +#include "Delay.hh" #include "GraphClass.hh" +#include "LibertyClass.hh" #include "SearchClass.hh" +#include "StaState.hh" #include "Tag.hh" namespace sta { @@ -43,15 +42,15 @@ class TagGroup { public: TagGroup(TagGroupIndex index, - PathIndexMap *path_index_map, - bool has_clk_tag, - bool has_genclk_src_tag, - bool has_filter_tag, - bool has_loop_tag, - const StaState *sta); + PathIndexMap *path_index_map, + bool has_clk_tag, + bool has_genclk_src_tag, + bool has_filter_tag, + bool has_loop_tag, + const StaState *sta); // For Search::findTagGroup to probe. TagGroup(TagGroupBldr *tag_bldr, - const StaState *sta); + const StaState *sta); ~TagGroup(); TagGroupIndex index() const { return index_; } size_t hash() const { return hash_; } @@ -75,7 +74,7 @@ public: protected: static size_t hash(PathIndexMap *path_index_map, - const StaState *sta); + const StaState *sta); // tag -> path index PathIndexMap *path_index_map_; @@ -92,14 +91,14 @@ protected: class TagGroupHash { public: - size_t operator()(const TagGroup *tag) const; + size_t operator()(const TagGroup *group) const; }; class TagGroupEqual { public: bool operator()(const TagGroup *group1, - const TagGroup *group2) const; + const TagGroup *group2) const; }; // Incremental tag group used to build tag group and associated @@ -108,12 +107,12 @@ class TagGroupBldr { public: TagGroupBldr(bool match_crpr_clk_pin, - const StaState *sta); + const StaState *sta); void init(Vertex *vertex); bool empty(); void reportArrivalEntries() const; TagGroup *makeTagGroup(TagGroupIndex index, - const StaState *sta); + const StaState *sta); size_t pathCount() const { return path_index_map_.size();; } bool hasClkTag() const { return has_clk_tag_; } bool hasGenClkSrcTag() const { return has_genclk_src_tag_; } @@ -128,7 +127,7 @@ public: Arrival arrival(size_t path_index) const; // prev_path == hull void setArrival(Tag *tag, - const Arrival &arrival); + const Arrival &arrival); void setMatchPath(Path *match, size_t path_index, Tag *tag, @@ -154,11 +153,11 @@ protected: int default_path_count_; PathIndexMap path_index_map_; std::vector paths_; - bool has_clk_tag_; - bool has_genclk_src_tag_; - bool has_filter_tag_; - bool has_loop_tag_; - bool has_propagated_clk_; + bool has_clk_tag_{false}; + bool has_genclk_src_tag_{false}; + bool has_filter_tag_{false}; + bool has_loop_tag_{false}; + bool has_propagated_clk_{false}; const StaState *sta_; }; @@ -166,4 +165,4 @@ void pathIndexMapReport(const PathIndexMap *path_index_map, const StaState *sta); -} // namespace +} // namespace sta diff --git a/search/VertexVisitor.cc b/search/VertexVisitor.cc index 900c78237..c622c286a 100644 --- a/search/VertexVisitor.cc +++ b/search/VertexVisitor.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -47,4 +47,4 @@ VertexPinCollector::visit(Vertex *vertex) pins_.insert(vertex->pin()); } -} // namespace +} // namespace sta diff --git a/search/VisitPathEnds.cc b/search/VisitPathEnds.cc index b5a762d54..3a420aada 100644 --- a/search/VisitPathEnds.cc +++ b/search/VisitPathEnds.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,21 +24,23 @@ #include "VisitPathEnds.hh" +#include "ClkInfo.hh" #include "Debug.hh" -#include "Liberty.hh" -#include "Network.hh" -#include "TimingArc.hh" #include "ExceptionPath.hh" -#include "PortDelay.hh" -#include "Sdc.hh" +#include "GatedClk.hh" #include "Graph.hh" -#include "ClkInfo.hh" -#include "Tag.hh" +#include "Liberty.hh" +#include "Mode.hh" +#include "Network.hh" #include "Path.hh" -#include "PathAnalysisPt.hh" #include "PathEnd.hh" +#include "PortDelay.hh" +#include "Scene.hh" +#include "Sdc.hh" #include "Search.hh" -#include "GatedClk.hh" +#include "Sim.hh" +#include "Tag.hh" +#include "TimingArc.hh" #include "Variables.hh" namespace sta { @@ -50,134 +52,134 @@ VisitPathEnds::VisitPathEnds(const StaState *sta) : void VisitPathEnds::visitPathEnds(Vertex *vertex, - PathEndVisitor *visitor) + PathEndVisitor *visitor) { - visitPathEnds(vertex, nullptr, MinMaxAll::all(), false, visitor); + visitPathEnds(vertex, Scene::sceneSet(scenes_), MinMaxAll::all(), false, visitor); } void VisitPathEnds::visitPathEnds(Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - bool filtered, - PathEndVisitor *visitor) + const SceneSet &scenes, + const MinMaxAll *min_max, + bool filtered, + PathEndVisitor *visitor) { // Ignore slack on bidirect driver vertex. The load vertex gets the slack. if (!vertex->isBidirectDriver()) { const Pin *pin = vertex->pin(); - debugPrint(debug_, "search", 2, "find end slack %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 2, "find end slack {}", + vertex->to_string(this)); visitor->vertexBegin(vertex); bool is_constrained = false; - visitClkedPathEnds(pin, vertex, corner, min_max, filtered, visitor, - is_constrained); + visitClkedPathEnds(pin, vertex, scenes, min_max, filtered, visitor, + is_constrained); if (search_->unconstrainedPaths() - && !is_constrained - && !vertex->isDisabledConstraint()) - visitUnconstrainedPathEnds(pin, vertex, corner, min_max, filtered, - visitor); + && !is_constrained) + visitUnconstrainedPathEnds(pin, vertex, scenes, min_max, filtered, + visitor); visitor->vertexEnd(vertex); } } void VisitPathEnds::visitClkedPathEnds(const Pin *pin, - Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained) + Vertex *vertex, + const SceneSet &scenes, + const MinMaxAll *min_max, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) { - bool is_segment_start = search_->isSegmentStart(pin); VertexPathIterator path_iter(vertex, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); - PathAnalysisPt *path_ap = path->pathAnalysisPt(this); - const MinMax *path_min_max = path_ap->pathMinMax(); + const MinMax *path_min_max = path->minMax(this); const RiseFall *end_rf = path->transition(this); Tag *tag = path->tag(this); - if ((corner == nullptr - || path_ap->corner() == corner) - && min_max->matches(path_min_max) - // Ignore generated clock source paths. - && !path->clkInfo(this)->isGenClkSrcPath() - && !falsePathTo(path, pin, end_rf, path_min_max) - // Ignore segment startpoint paths. - && (!is_segment_start - || !tag->isSegmentStart())) { + const Mode *mode = path->mode(this); + const Sdc *sdc = mode->sdc(); + Scene *scene = path->scene(this); + if (scenes.contains(scene) + && min_max->matches(path_min_max) + // Ignore generated clock source paths. + && !path->clkInfo(this)->isGenClkSrcPath() + && !falsePathTo(path, pin, end_rf, path_min_max) + // Ignore segment startpoint paths. + && !tag->isSegmentStart()) { // set_output_delay to timing check has precedence. - if (sdc_->hasOutputDelay(pin)) - visitOutputDelayEnd(pin, path, end_rf, path_ap, filtered, visitor, - is_constrained); + if (sdc->hasOutputDelay(pin)) + visitOutputDelayEnd(pin, path, end_rf, filtered, visitor, + is_constrained); else if (vertex->hasChecks()) - visitCheckEnd(pin, vertex, path, end_rf, path_ap, filtered, visitor, - is_constrained); + visitCheckEnd(pin, vertex, path, end_rf, filtered, visitor, + is_constrained); else if (!filtered || search_->matchesFilter(path, nullptr)) { - PathDelay *path_delay = pathDelayTo(path, pin, end_rf, path_min_max); - if (path_delay) { - PathEndPathDelay path_end(path_delay, path, this); - visitor->visit(&path_end); - is_constrained = true; - } + PathDelay *path_delay = pathDelayTo(path, pin, end_rf, path_min_max); + if (path_delay) { + PathEndPathDelay path_end(path_delay, path, this); + visitor->visit(&path_end); + is_constrained = true; + } } if (variables_->gatedClkChecksEnabled()) - visitGatedClkEnd(pin, vertex, path, end_rf, path_ap, filtered, visitor, - is_constrained); - visitDataCheckEnd(pin, path, end_rf, path_ap, filtered, visitor, - is_constrained); + visitGatedClkEnd(pin, vertex, path, end_rf, filtered, visitor, + is_constrained); + visitDataCheckEnd(pin, path, end_rf, filtered, visitor, + is_constrained); } } } void VisitPathEnds::visitCheckEnd(const Pin *pin, - Vertex *vertex, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained) + Vertex *vertex, + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) { const ClockEdge *src_clk_edge = path->clkEdge(this); const Clock *src_clk = path->clock(this); - const MinMax *min_max = path_ap->pathMinMax(); - const PathAnalysisPt *tgt_clk_path_ap = path_ap->tgtClkAnalysisPt(); + const MinMax *min_max = path->minMax(this); + const MinMax *tgt_min_max = path->tgtClkMinMax(this); bool check_clked = false; + const Scene *scene = path->scene(this); + const Mode *mode = scene->mode(); + const Sdc *sdc = scene->sdc(); VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *tgt_clk_vertex = edge->from(graph_); const TimingRole *check_role = edge->role(); - if (checkEdgeEnabled(edge) - && check_role->pathMinMax() == min_max) { + if (checkEdgeEnabled(edge, mode) + && check_role->pathMinMax() == min_max) { TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *check_arc : arc_set->arcs()) { - const RiseFall *clk_rf = check_arc->fromEdge()->asRiseFall(); - if (check_arc->toEdge()->asRiseFall() == end_rf - && clk_rf) { - VertexPathIterator tgt_clk_path_iter(tgt_clk_vertex, clk_rf, - tgt_clk_path_ap, this); - while (tgt_clk_path_iter.hasNext()) { - Path *tgt_clk_path = tgt_clk_path_iter.next(); - const ClkInfo *tgt_clk_info = tgt_clk_path->clkInfo(this); - const ClockEdge *tgt_clk_edge = tgt_clk_path->clkEdge(this); - const Clock *tgt_clk = tgt_clk_path->clock(this); - const Pin *tgt_pin = tgt_clk_vertex->pin(); - ExceptionPath *exception = exceptionTo(path, pin, end_rf, - tgt_clk_edge, min_max); - // Ignore generated clock source paths. - if (!tgt_clk_info->isGenClkSrcPath() + const RiseFall *clk_rf = check_arc->fromEdge()->asRiseFall(); + if (check_arc->toEdge()->asRiseFall() == end_rf + && clk_rf) { + VertexPathIterator tgt_clk_path_iter(tgt_clk_vertex, scene, + tgt_min_max, clk_rf, this); + while (tgt_clk_path_iter.hasNext()) { + Path *tgt_clk_path = tgt_clk_path_iter.next(); + const ClkInfo *tgt_clk_info = tgt_clk_path->clkInfo(this); + const ClockEdge *tgt_clk_edge = tgt_clk_path->clkEdge(this); + const Clock *tgt_clk = tgt_clk_path->clock(this); + const Pin *tgt_pin = tgt_clk_vertex->pin(); + ExceptionPath *exception = exceptionTo(path, pin, end_rf, + tgt_clk_edge, min_max); + // Ignore generated clock source paths. + if (!tgt_clk_info->isGenClkSrcPath() && tgt_clk_path->isClock(this)) { check_clked = true; if (!filtered || search_->matchesFilter(path, tgt_clk_edge)) { if (src_clk_edge - && tgt_clk != sdc_->defaultArrivalClock() - && sdc_->sameClockGroup(src_clk, tgt_clk) - && !sdc_->clkStopPropagation(tgt_pin, tgt_clk) + && tgt_clk != sdc->defaultArrivalClock() + && sdc->sameClockGroup(src_clk, tgt_clk) + && !sdc->clkStopPropagation(tgt_pin, tgt_clk) // False paths and path delays override // paths. && (exception == nullptr @@ -203,8 +205,7 @@ VisitPathEnds::visitCheckEnd(const Pin *pin, else if (exception && exception->isPathDelay() && (src_clk == nullptr - || sdc_->sameClockGroup(src_clk, - tgt_clk))) { + || sdc->sameClockGroup(src_clk, tgt_clk))) { PathDelay *path_delay = dynamic_cast(exception); if (network_->isLatchData(pin) && check_role == TimingRole::setup()) { @@ -221,113 +222,117 @@ VisitPathEnds::visitCheckEnd(const Pin *pin, } } } - } - } - } + } + } + } } } } if (!check_clked) - visitCheckEndUnclked(pin, vertex, path, end_rf, path_ap, filtered, - visitor, is_constrained); + visitCheckEndUnclked(pin, vertex, path, end_rf, filtered, + visitor, is_constrained); } void VisitPathEnds::visitCheckEndUnclked(const Pin *pin, - Vertex *vertex, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained) + Vertex *vertex, + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) { - const MinMax *min_max = path_ap->pathMinMax(); + const Mode *mode = path->mode(this); + const MinMax *min_max = path->minMax(this); VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); const TimingRole *check_role = edge->role(); - if (checkEdgeEnabled(edge) - && check_role->pathMinMax() == min_max) { + if (checkEdgeEnabled(edge, mode) + && check_role->pathMinMax() == min_max) { TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *check_arc : arc_set->arcs()) { - const RiseFall *clk_rf = check_arc->fromEdge()->asRiseFall(); - if (check_arc->toEdge()->asRiseFall() == end_rf - && clk_rf - && (!filtered - || search_->matchesFilter(path, nullptr))) { - ExceptionPath *exception = exceptionTo(path, pin, end_rf, - nullptr, min_max); - // False paths and path delays override multicycle paths. - if (exception - && exception->isPathDelay()) { - PathDelay *path_delay = dynamic_cast(exception); - PathEndPathDelay path_end(path_delay, path, nullptr, - check_arc, edge, this); - visitor->visit(&path_end); - is_constrained = true; - } - } + const RiseFall *clk_rf = check_arc->fromEdge()->asRiseFall(); + if (check_arc->toEdge()->asRiseFall() == end_rf + && clk_rf + && (!filtered + || search_->matchesFilter(path, nullptr))) { + ExceptionPath *exception = exceptionTo(path, pin, end_rf, + nullptr, min_max); + // False paths and path delays override multicycle paths. + if (exception + && exception->isPathDelay()) { + PathDelay *path_delay = dynamic_cast(exception); + PathEndPathDelay path_end(path_delay, path, nullptr, + check_arc, edge, this); + visitor->visit(&path_end); + is_constrained = true; + } + } } } } } bool -VisitPathEnds::checkEdgeEnabled(Edge *edge) const +VisitPathEnds::checkEdgeEnabled(const Edge *edge, + const Mode *mode) const { const TimingRole *check_role = edge->role(); + const Sdc *sdc = mode->sdc(); return check_role->isTimingCheck() - && search_->evalPred()->searchFrom(edge->from(graph_)) - && !edge->isDisabledConstraint() - && !edge->isDisabledCond() - && !sdc_->isDisabledCondDefault(edge) + && search_->evalPred()->searchFrom(edge->from(graph_), mode) + && !sdc->isDisabledConstraint(edge) + && !mode->sim()->isDisabledCond(edge) + && !isDisabledCondDefault(edge) && !((check_role == TimingRole::recovery() - || check_role == TimingRole::removal()) - && !variables_->recoveryRemovalChecksEnabled()); + || check_role == TimingRole::removal()) + && !variables_->recoveryRemovalChecksEnabled()); } void VisitPathEnds::visitOutputDelayEnd(const Pin *pin, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained) + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) { - const MinMax *min_max = path_ap->pathMinMax(); - OutputDelaySet *output_delays = sdc_->outputDelaysLeafPin(pin); + const Scene *scene = path->scene(this); + const Sdc *sdc = scene->sdc(); + const MinMax *min_max = path->minMax(this); + OutputDelaySet *output_delays = sdc->outputDelaysLeafPin(pin); if (output_delays) { for (OutputDelay *output_delay : *output_delays) { float margin; bool exists; output_delay->delays()->value(end_rf, min_max, margin, exists); if (exists) { - const Pin *ref_pin = output_delay->refPin(); - const ClockEdge *tgt_clk_edge = output_delay->clkEdge(); - if (!filtered - || search_->matchesFilter(path, tgt_clk_edge)) { - if (ref_pin) { - Clock *tgt_clk = output_delay->clock(); - Vertex *ref_vertex = graph_->pinLoadVertex(ref_pin); - const RiseFall *ref_rf = output_delay->refTransition(); - VertexPathIterator ref_path_iter(ref_vertex,ref_rf,path_ap,this); - while (ref_path_iter.hasNext()) { - Path *ref_path = ref_path_iter.next(); - if (ref_path->isClock(this) - && (tgt_clk == nullptr - || ref_path->clock(this) == tgt_clk)) - visitOutputDelayEnd1(output_delay, pin, path, end_rf, - ref_path->clkEdge(this), ref_path, min_max, - visitor, is_constrained); - } - } - else - visitOutputDelayEnd1(output_delay, pin, path, end_rf, - tgt_clk_edge, nullptr, min_max, - visitor, is_constrained); - } + const Pin *ref_pin = output_delay->refPin(); + const ClockEdge *tgt_clk_edge = output_delay->clkEdge(); + if (!filtered + || search_->matchesFilter(path, tgt_clk_edge)) { + if (ref_pin) { + Clock *tgt_clk = output_delay->clock(); + Vertex *ref_vertex = graph_->pinLoadVertex(ref_pin); + const RiseFall *ref_rf = output_delay->refTransition(); + VertexPathIterator ref_path_iter(ref_vertex, scene, min_max, + ref_rf, this); + while (ref_path_iter.hasNext()) { + Path *ref_path = ref_path_iter.next(); + if (ref_path->isClock(this) + && (tgt_clk == nullptr + || ref_path->clock(this) == tgt_clk)) + visitOutputDelayEnd1(output_delay, pin, path, end_rf, + ref_path->clkEdge(this), ref_path, min_max, + visitor, is_constrained); + } + } + else + visitOutputDelayEnd1(output_delay, pin, path, end_rf, + tgt_clk_edge, nullptr, min_max, + visitor, is_constrained); + } } } } @@ -335,20 +340,21 @@ VisitPathEnds::visitOutputDelayEnd(const Pin *pin, void VisitPathEnds::visitOutputDelayEnd1(OutputDelay *output_delay, - const Pin *pin, - Path *path, - const RiseFall *end_rf, - const ClockEdge *tgt_clk_edge, - Path *ref_path, - const MinMax *min_max, - PathEndVisitor *visitor, - bool &is_constrained) + const Pin *pin, + Path *path, + const RiseFall *end_rf, + const ClockEdge *tgt_clk_edge, + Path *ref_path, + const MinMax *min_max, + PathEndVisitor *visitor, + bool &is_constrained) { // Target clk is not required for path delay, // but the exception may be -to clk. ExceptionPath *exception = exceptionTo(path, pin, end_rf, tgt_clk_edge, - min_max); + min_max); const ClockEdge *src_clk_edge = path->clkEdge(this); + const Sdc *sdc = path->sdc(this); if (exception && exception->isPathDelay()) { PathDelay *path_delay = dynamic_cast(exception); @@ -358,12 +364,12 @@ VisitPathEnds::visitOutputDelayEnd1(OutputDelay *output_delay, } else if (src_clk_edge && tgt_clk_edge - && sdc_->sameClockGroup(path->clock(this), tgt_clk_edge->clock()) - // False paths and path delays override. - && (exception == nullptr - || exception->isFilter() - || exception->isGroupPath() - || exception->isMultiCycle())) { + && sdc->sameClockGroup(path->clock(this), tgt_clk_edge->clock()) + // False paths and path delays override. + && (exception == nullptr + || exception->isFilter() + || exception->isGroupPath() + || exception->isMultiCycle())) { MultiCyclePath *mcp = dynamic_cast(exception); PathEndOutputDelay path_end(output_delay, path, ref_path, mcp, this); visitor->visit(&path_end); @@ -376,71 +382,77 @@ VisitPathEnds::visitOutputDelayEnd1(OutputDelay *output_delay, // Look for clock gating functions where path is the clock enable. void VisitPathEnds::visitGatedClkEnd(const Pin *pin, - Vertex *vertex, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained) + Vertex *vertex, + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) { + const Scene *scene = path->scene(this); + const Sdc *sdc = scene->sdc(); const ClockEdge *src_clk_edge = path->clkEdge(this); - if (src_clk_edge) { + if (src_clk_edge + && !path->isClock(this) + && !sdc->isDisableClockGatingCheck(pin) + && !sdc->isDisableClockGatingCheck(network_->instance(pin))) { + const Mode *mode = scene->mode(); GatedClk *gated_clk = search_->gatedClk(); Clock *src_clk = src_clk_edge->clock(); bool is_gated_clk_enable; const Pin *clk_pin; LogicValue logic_active_value; - gated_clk->isGatedClkEnable(vertex, - is_gated_clk_enable, clk_pin, logic_active_value); + gated_clk->isGatedClkEnable(vertex, mode, + is_gated_clk_enable, clk_pin, logic_active_value); if (is_gated_clk_enable) { - const PathAnalysisPt *clk_path_ap = path_ap->tgtClkAnalysisPt(); - const MinMax *min_max = path_ap->pathMinMax(); + const MinMax *min_max = path->minMax(this); + const MinMax *tgt_min_max = path->tgtClkMinMax(this); Vertex *clk_vertex = graph_->pinLoadVertex(clk_pin); LogicValue active_value = - sdc_->clockGatingActiveValue(clk_pin, pin); + sdc->clockGatingActiveValue(clk_pin, pin); const RiseFall *clk_rf = - // Clock active value specified by set_clock_gating_check - // overrides the library cell function active value. - gated_clk->gatedClkActiveTrans((active_value == LogicValue::unknown) ? - logic_active_value : active_value, - min_max); - VertexPathIterator clk_path_iter(clk_vertex, clk_rf, clk_path_ap, this); + // Clock active value specified by set_clock_gating_check + // overrides the library cell function active value. + gated_clk->gatedClkActiveTrans((active_value == LogicValue::unknown) ? + logic_active_value : active_value, + min_max); + VertexPathIterator clk_path_iter(clk_vertex, scene, tgt_min_max, + clk_rf, this); while (clk_path_iter.hasNext()) { - Path *clk_path = clk_path_iter.next(); - const ClockEdge *clk_edge = clk_path->clkEdge(this); - const Clock *clk = clk_edge ? clk_edge->clock() : nullptr; - if (clk_path->isClock(this) - // Ignore unclocked paths (from path delay constraints). - && clk_edge - && clk_edge != sdc_->defaultArrivalClockEdge() - // Ignore generated clock source paths. - && !path->clkInfo(this)->isGenClkSrcPath() - && !sdc_->clkStopPropagation(pin, clk) - && clk_vertex->hasDownstreamClkPin()) { - const TimingRole *check_role = (min_max == MinMax::max()) - ? TimingRole::gatedClockSetup() - : TimingRole::gatedClockHold(); - float margin = clockGatingMargin(clk, clk_pin, - pin, end_rf, min_max); - ExceptionPath *exception = exceptionTo(path, pin, end_rf, - clk_edge, min_max); - if (sdc_->sameClockGroup(src_clk, clk) - // False paths and path delays override. - && (exception == nullptr - || exception->isFilter() - || exception->isGroupPath() - || exception->isMultiCycle()) - && (!filtered - || search_->matchesFilter(path, clk_edge))) { - MultiCyclePath *mcp = - dynamic_cast(exception); - PathEndGatedClock path_end(path, clk_path, check_role, - mcp, margin, this); - visitor->visit(&path_end); - is_constrained = true; - } - } + Path *clk_path = clk_path_iter.next(); + const ClockEdge *clk_edge = clk_path->clkEdge(this); + const Clock *clk = clk_edge ? clk_edge->clock() : nullptr; + if (clk_path->isClock(this) + // Ignore unclocked paths (from path delay constraints). + && clk_edge + && clk_edge != sdc->defaultArrivalClockEdge() + // Ignore generated clock source paths. + && !path->clkInfo(this)->isGenClkSrcPath() + && !sdc->clkStopPropagation(pin, clk) + && clk_vertex->hasDownstreamClkPin()) { + const TimingRole *check_role = (min_max == MinMax::max()) + ? TimingRole::gatedClockSetup() + : TimingRole::gatedClockHold(); + float margin = clockGatingMargin(clk, clk_pin, pin, + end_rf, min_max, sdc); + ExceptionPath *exception = exceptionTo(path, pin, end_rf, + clk_edge, min_max); + if (sdc->sameClockGroup(src_clk, clk) + // False paths and path delays override. + && (exception == nullptr + || exception->isFilter() + || exception->isGroupPath() + || exception->isMultiCycle()) + && (!filtered + || search_->matchesFilter(path, clk_edge))) { + MultiCyclePath *mcp = + dynamic_cast(exception); + PathEndGatedClock path_end(path, clk_path, check_role, + mcp, margin, this); + visitor->visit(&path_end); + is_constrained = true; + } + } } } } @@ -450,32 +462,33 @@ VisitPathEnds::visitGatedClkEnd(const Pin *pin, // Look for margin from highest precedence level to lowest. float VisitPathEnds::clockGatingMargin(const Clock *clk, - const Pin *clk_pin, - const Pin *enable_pin, - const RiseFall *enable_rf, - const SetupHold *setup_hold) + const Pin *clk_pin, + const Pin *enable_pin, + const RiseFall *enable_rf, + const SetupHold *setup_hold, + const Sdc *sdc) { bool exists; float margin; - sdc_->clockGatingMarginEnablePin(enable_pin, enable_rf, - setup_hold, exists, margin); + sdc->clockGatingMarginEnablePin(enable_pin, enable_rf, + setup_hold, exists, margin); if (exists) return margin; Instance *inst = network_->instance(enable_pin); - sdc_->clockGatingMarginInstance(inst, enable_rf, setup_hold, - exists, margin); + sdc->clockGatingMarginInstance(inst, enable_rf, setup_hold, + exists, margin); if (exists) return margin; - sdc_->clockGatingMarginClkPin(clk_pin, enable_rf, setup_hold, - exists, margin); + sdc->clockGatingMarginClkPin(clk_pin, enable_rf, setup_hold, + exists, margin); if (exists) return margin; - sdc_->clockGatingMarginClk(clk, enable_rf, setup_hold, - exists, margin); + sdc->clockGatingMarginClk(clk, enable_rf, setup_hold, + exists, margin); if (exists) return margin; - sdc_->clockGatingMargin(enable_rf, setup_hold, - exists, margin); + sdc->clockGatingMargin(enable_rf, setup_hold, + exists, margin); if (exists) return margin; else @@ -486,34 +499,31 @@ VisitPathEnds::clockGatingMargin(const Clock *clk, void VisitPathEnds::visitDataCheckEnd(const Pin *pin, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained) + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) { const ClockEdge *src_clk_edge = path->clkEdge(this); if (src_clk_edge) { - DataCheckSet *checks = sdc_->dataChecksTo(pin); + const Sdc *sdc = path->sdc(this); + DataCheckSet *checks = sdc->dataChecksTo(pin); if (checks) { const Clock *src_clk = src_clk_edge->clock(); - const MinMax *min_max = path_ap->pathMinMax(); - const PathAnalysisPt *clk_ap = path_ap->tgtClkAnalysisPt(); - DataCheckSet::Iterator check_iter(checks); - while (check_iter.hasNext()) { - DataCheck *check = check_iter.next(); - const Pin *from_pin = check->from(); - Vertex *from_vertex = graph_->pinLoadVertex(from_pin); - for (auto from_rf : RiseFall::range()) { - float margin; - bool margin_exists; - check->margin(from_rf, end_rf, min_max, margin, margin_exists); - if (margin_exists) - visitDataCheckEnd1(check, pin, path, src_clk, end_rf, - min_max, clk_ap, from_pin, from_vertex, - from_rf, filtered, visitor, is_constrained); - } + const MinMax *min_max = path->minMax(this); + for (DataCheck *check : *checks) { + const Pin *from_pin = check->from(); + Vertex *from_vertex = graph_->pinLoadVertex(from_pin); + for (auto from_rf : RiseFall::range()) { + float margin; + bool margin_exists; + check->margin(from_rf, end_rf, min_max, margin, margin_exists); + if (margin_exists) + visitDataCheckEnd1(check, pin, path, src_clk, end_rf, + min_max, from_pin, from_vertex, + from_rf, filtered, visitor, is_constrained); + } } } } @@ -521,21 +531,24 @@ VisitPathEnds::visitDataCheckEnd(const Pin *pin, bool VisitPathEnds::visitDataCheckEnd1(DataCheck *check, - const Pin *pin, - Path *path, - const Clock *src_clk, - const RiseFall *end_rf, - const MinMax *min_max, - const PathAnalysisPt *clk_ap, - const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained) + const Pin *pin, + Path *path, + const Clock *src_clk, + const RiseFall *end_rf, + const MinMax *min_max, + const Pin *from_pin, + Vertex *from_vertex, + const RiseFall *from_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) { bool found_from_path = false; - VertexPathIterator tgt_clk_path_iter(from_vertex,from_rf,clk_ap,this); + const Scene *scene = path->scene(this); + const Sdc *sdc = scene->sdc(); + const MinMax *tgt_min_max = path->tgtClkMinMax(this); + VertexPathIterator tgt_clk_path_iter(from_vertex, scene, tgt_min_max, + from_rf,this); while (tgt_clk_path_iter.hasNext()) { Path *tgt_clk_path = tgt_clk_path_iter.next(); const ClockEdge *tgt_clk_edge = tgt_clk_path->clkEdge(this); @@ -546,19 +559,19 @@ VisitPathEnds::visitDataCheckEnd1(DataCheck *check, const Clock *tgt_clk = tgt_clk_edge->clock(); ExceptionPath *exception = exceptionTo(path, pin, end_rf, tgt_clk_edge, min_max); - if (sdc_->sameClockGroup(src_clk, tgt_clk) - && !sdc_->clkStopPropagation(from_pin, tgt_clk) - // False paths and path delays override. - && (exception == 0 - || exception->isFilter() - || exception->isGroupPath() - || exception->isMultiCycle()) - && (!filtered - || search_->matchesFilter(path, tgt_clk_edge))) { - MultiCyclePath *mcp=dynamic_cast(exception); - PathEndDataCheck path_end(check, path, tgt_clk_path, mcp, this); - visitor->visit(&path_end); - is_constrained = true; + if (sdc->sameClockGroup(src_clk, tgt_clk) + && !sdc->clkStopPropagation(from_pin, tgt_clk) + // False paths and path delays override. + && (exception == nullptr + || exception->isFilter() + || exception->isGroupPath() + || exception->isMultiCycle()) + && (!filtered + || search_->matchesFilter(path, tgt_clk_edge))) { + MultiCyclePath *mcp=dynamic_cast(exception); + PathEndDataCheck path_end(check, path, tgt_clk_path, mcp, this); + visitor->visit(&path_end); + is_constrained = true; } } } @@ -569,26 +582,27 @@ VisitPathEnds::visitDataCheckEnd1(DataCheck *check, void VisitPathEnds::visitUnconstrainedPathEnds(const Pin *pin, - Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - bool filtered, - PathEndVisitor *visitor) + Vertex *vertex, + const SceneSet &scenes, + const MinMaxAll *min_max, + bool filtered, + PathEndVisitor *visitor) { VertexPathIterator path_iter(vertex, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); - PathAnalysisPt *path_ap = path->pathAnalysisPt(this); - const MinMax *path_min_max = path_ap->pathMinMax(); - if ((corner == nullptr - || path_ap->corner() == corner) - && min_max->matches(path_min_max) - // Ignore generated clock source paths. - && !path->clkInfo(this)->isGenClkSrcPath() - && (!filtered - || search_->matchesFilter(path, nullptr)) - && !falsePathTo(path, pin, path->transition(this), - path->minMax(this))) { + const MinMax *path_min_max = path->minMax(this); + Scene *scene = path->scene(this); + const Sdc *sdc = scene->sdc(); + if (scenes.contains(scene) + && min_max->matches(path_min_max) + && !sdc->isDisabledConstraint(pin) + // Ignore generated clock source paths. + && !path->clkInfo(this)->isGenClkSrcPath() + && (!filtered + || search_->matchesFilter(path, nullptr)) + && !falsePathTo(path, pin, path->transition(this), + path_min_max)) { PathEndUnconstrained path_end(path); visitor->visit(&path_end); } @@ -599,40 +613,41 @@ VisitPathEnds::visitUnconstrainedPathEnds(const Pin *pin, bool VisitPathEnds::falsePathTo(Path *path, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max) + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max) { ExceptionPath *exception = search_->exceptionTo(ExceptionPathType::false_path, path, - pin, rf, nullptr, min_max, - false, false); + pin, rf, nullptr, min_max, + false, false, path->sdc(this)); return exception != nullptr; } PathDelay * VisitPathEnds::pathDelayTo(Path *path, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max) + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max) { ExceptionPath *exception = search_->exceptionTo(ExceptionPathType::path_delay, - path, pin, rf, nullptr, - min_max, false, - // Register clk pins only - // match with -to pin. - network_->isRegClkPin(pin)); + path, pin, rf, nullptr, + min_max, false, + // Register clk pins only + // match with -to pin. + network_->isRegClkPin(pin), + path->sdc(this)); return dynamic_cast(exception); } ExceptionPath * VisitPathEnds::exceptionTo(const Path *path, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max) const + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max) const { return search_->exceptionTo(ExceptionPathType::any, path, pin, rf, clk_edge, - min_max, false, false); + min_max, false, false, path->sdc(this)); } -} // namespace +} // namespace sta diff --git a/search/WorstSlack.cc b/search/WorstSlack.cc index e61fcccaa..769319b9e 100644 --- a/search/WorstSlack.cc +++ b/search/WorstSlack.cc @@ -1,61 +1,62 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "WorstSlack.hh" +#include + +#include "ContainerHelpers.hh" #include "Debug.hh" -#include "Report.hh" -#include "Mutex.hh" #include "Graph.hh" -#include "Corner.hh" +#include "Mutex.hh" +#include "Report.hh" +#include "Scene.hh" #include "Search.hh" -#include "PathAnalysisPt.hh" namespace sta { -using std::min; - WorstSlacks::WorstSlacks(StaState *sta) : - worst_slacks_(sta->corners()->pathAnalysisPtCount(), sta), + worst_slacks_(sta->scenePathCount(), + sta), sta_(sta) { } void WorstSlacks::worstSlack(const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex) + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { worst_slack = MinMax::min()->initValue(); worst_vertex = nullptr; - for (auto corner : *sta_->corners()) { - PathAPIndex path_ap_index = corner->findPathAnalysisPt(min_max)->index(); + for (Scene *scene : sta_->scenes()) { + PathAPIndex path_ap_index = scene->pathIndex(min_max); Slack worst_slack1; Vertex *worst_vertex1; - worst_slacks_[path_ap_index].worstSlack(path_ap_index, - worst_slack1, worst_vertex1); + worst_slacks_[path_ap_index].worstSlack(path_ap_index, worst_slack1, + worst_vertex1); if (delayLess(worst_slack1, worst_slack, sta_)) { worst_slack = worst_slack1; worst_vertex = worst_vertex1; @@ -64,22 +65,21 @@ WorstSlacks::worstSlack(const MinMax *min_max, } void -WorstSlacks::worstSlack(const Corner *corner, - const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex) +WorstSlacks::worstSlack(const Scene *scene, + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { - PathAPIndex path_ap_index = corner->findPathAnalysisPt(min_max)->index(); - worst_slacks_[path_ap_index].worstSlack(path_ap_index, - worst_slack, worst_vertex); + PathAPIndex path_ap_index = scene->pathIndex(min_max); + worst_slacks_[path_ap_index].worstSlack(path_ap_index, worst_slack, worst_vertex); } void WorstSlacks::updateWorstSlacks(Vertex *vertex, - SlackSeq &slacks) + SlackSeq &slacks) { - PathAPIndex path_ap_count = sta_->corners()->pathAnalysisPtCount(); + PathAPIndex path_ap_count = sta_->scenePathCount(); for (PathAPIndex i = 0; i < path_ap_count; i++) worst_slacks_[i].updateWorstSlack(vertex, slacks, i); } @@ -87,11 +87,8 @@ WorstSlacks::updateWorstSlacks(Vertex *vertex, void WorstSlacks::worstSlackNotifyBefore(Vertex *vertex) { - WorstSlackSeq::Iterator worst_iter(worst_slacks_); - while (worst_iter.hasNext()) { - WorstSlack &worst_slack = worst_iter.next(); + for (WorstSlack &worst_slack : worst_slacks_) worst_slack.deleteVertexBefore(vertex); - } } //////////////////////////////////////////////////////////////// @@ -99,29 +96,20 @@ WorstSlacks::worstSlackNotifyBefore(Vertex *vertex) WorstSlack::WorstSlack(StaState *sta) : StaState(sta), slack_init_(MinMax::min()->initValue()), - worst_vertex_(nullptr), worst_slack_(slack_init_), slack_threshold_(slack_init_), - queue_(new VertexSet(graph_)), - min_queue_size_(10), - max_queue_size_(20) + queue_(new VertexSet(VertexIdLess(graph_))) { } -WorstSlack::~WorstSlack() -{ - delete queue_; -} +WorstSlack::~WorstSlack() { delete queue_; } WorstSlack::WorstSlack(const WorstSlack &worst_slack) : StaState(worst_slack), slack_init_(MinMax::min()->initValue()), - worst_vertex_(nullptr), worst_slack_(slack_init_), slack_threshold_(slack_init_), - queue_(new VertexSet(graph_)), - min_queue_size_(10), - max_queue_size_(20) + queue_(new VertexSet(VertexIdLess(graph_))) { } @@ -138,9 +126,9 @@ WorstSlack::deleteVertexBefore(Vertex *vertex) void WorstSlack::worstSlack(PathAPIndex path_ap_index, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex) + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { findWorstSlack(path_ap_index); worst_slack = worst_slack_; @@ -167,27 +155,27 @@ WorstSlack::initQueue(PathAPIndex path_ap_index) worst_vertex_ = nullptr; worst_slack_ = slack_init_; slack_threshold_ = slack_init_; - for(Vertex *vertex : *search_->endpoints()) { + for (Vertex *vertex : search_->endpoints()) { Slack slack = search_->wnsSlack(vertex, path_ap_index); - if (!delayEqual(slack, slack_init_)) { + if (!delayEqual(slack, slack_init_, this)) { if (delayLess(slack, worst_slack_, this)) - setWorstSlack(vertex, slack); + setWorstSlack(vertex, slack); if (delayLessEqual(slack, slack_threshold_, this)) - queue_->insert(vertex); - int queue_size = queue_->size(); + queue_->insert(vertex); + size_t queue_size = queue_->size(); if (queue_size >= max_queue_size_) - sortQueue(path_ap_index); + sortQueue(path_ap_index); } } - debugPrint(debug_, "wns", 3, "threshold %s", - delayAsString(slack_threshold_, this)); -// checkQueue(); + debugPrint(debug_, "wns", 3, "threshold {}", + delayAsString(slack_threshold_, MinMax::max(), this)); + //checkQueue(); } void WorstSlack::sortQueue(PathAPIndex path_ap_index) { - if (queue_->size() > 0) { + if (!queue_->empty()) { debugPrint(debug_, "wns", 3, "sort queue"); VertexSeq vertices; @@ -197,21 +185,19 @@ WorstSlack::sortQueue(PathAPIndex path_ap_index) WnsSlackLess slack_less(path_ap_index, this); sort(vertices, slack_less); - int vertex_count = vertices.size(); - int threshold_index = min(min_queue_size_, vertex_count - 1); + size_t vertex_count = vertices.size(); + size_t threshold_index = std::min(min_queue_size_, vertex_count - 1); Vertex *threshold_vertex = vertices[threshold_index]; slack_threshold_ = search_->wnsSlack(threshold_vertex, path_ap_index); - debugPrint(debug_, "wns", 3, "threshold %s", - delayAsString(slack_threshold_, this)); + debugPrint(debug_, "wns", 3, "threshold {}", + delayAsString(slack_threshold_, MinMax::max(), this)); // Reinsert vertices with slack < threshold. queue_->clear(); - VertexSeq::Iterator queue_iter2(vertices); - while (queue_iter2.hasNext()) { - Vertex *vertex = queue_iter2.next(); + for (Vertex *vertex : vertices) { Slack slack = search_->wnsSlack(vertex, path_ap_index); if (delayGreater(slack, slack_threshold_, this)) - break; + break; queue_->insert(vertex); } max_queue_size_ = queue_->size() * 2; @@ -239,39 +225,37 @@ void WorstSlack::checkQueue(PathAPIndex path_ap_index) { VertexSeq ends; - for(Vertex *end : *search_->endpoints()) { - if (delayLessEqual(search_->wnsSlack(end, path_ap_index), - slack_threshold_, this)) + for (Vertex *end : search_->endpoints()) { + if (delayLessEqual(search_->wnsSlack(end, path_ap_index), slack_threshold_, + this)) ends.push_back(end); } WnsSlackLess slack_less(path_ap_index, this); sort(ends, slack_less); - VertexSet end_set(graph_); + VertexSet end_set = makeVertexSet(this); for (Vertex *end : ends) { end_set.insert(end); - if (!queue_->hasKey(end) - && delayLessEqual(search_->wnsSlack(end, path_ap_index), - slack_threshold_, this)) - report_->reportLine("WorstSlack queue missing %s %s < %s", - end->to_string(this).c_str(), - delayAsString(search_->wnsSlack(end, path_ap_index), this), - delayAsString(slack_threshold_, this)); + if (!queue_->contains(end) + && delayLessEqual(search_->wnsSlack(end, path_ap_index), slack_threshold_, + this)) + report_->report("WorstSlack queue missing {} {} < {}", end->to_string(this), + delayAsString(search_->wnsSlack(end, path_ap_index), this), + delayAsString(slack_threshold_, this)); } for (Vertex *end : *queue_) { - if (!end_set.hasKey(end)) - report_->reportLine("WorstSlack queue extra %s %s > %s", - end->to_string(this).c_str(), - delayAsString(search_->wnsSlack(end, path_ap_index), this), - delayAsString(slack_threshold_, this)); + if (!end_set.contains(end)) + report_->report("WorstSlack queue extra {} {} > {}", end->to_string(this), + delayAsString(search_->wnsSlack(end, path_ap_index), this), + delayAsString(slack_threshold_, this)); } } void WorstSlack::updateWorstSlack(Vertex *vertex, - SlackSeq &slacks, - PathAPIndex path_ap_index) + SlackSeq &slacks, + PathAPIndex path_ap_index) { // Do not touch the state unless queue has been initialized if (!queue_->empty()) { @@ -279,36 +263,32 @@ WorstSlack::updateWorstSlack(Vertex *vertex, // Locking is required because ArrivalVisitor is called by multiple // threads. LockGuard lock(lock_); - if (worst_vertex_ - && delayLess(slack, worst_slack_, this)) + if (worst_vertex_ && delayLess(slack, worst_slack_, this)) setWorstSlack(vertex, slack); else if (vertex == worst_vertex_) // Mark worst slack as unknown (updated by findWorstSlack(). worst_vertex_ = nullptr; - if (!delayEqual(slack, slack_init_) + if (!delayEqual(slack, slack_init_, this) && delayLessEqual(slack, slack_threshold_, this)) { - debugPrint(debug_, "wns", 3, "insert %s %s", - vertex->to_string(this).c_str(), + debugPrint(debug_, "wns", 3, "insert {} {}", vertex->to_string(this), delayAsString(slack, this)); queue_->insert(vertex); } else { - debugPrint(debug_, "wns", 3, "delete %s %s", - vertex->to_string(this).c_str(), + debugPrint(debug_, "wns", 3, "delete {} {}", vertex->to_string(this), delayAsString(slack, this)); queue_->erase(vertex); } - //checkQueue(path_ap_index); + // checkQueue(path_ap_index); } } void WorstSlack::setWorstSlack(Vertex *vertex, - Slack slack) + Slack slack) { - debugPrint(debug_, "wns", 3, "%s %s", - vertex->to_string(this).c_str(), + debugPrint(debug_, "wns", 3, "{} {}", vertex->to_string(this), delayAsString(slack, this)); worst_vertex_ = vertex; worst_slack_ = slack; @@ -317,7 +297,7 @@ WorstSlack::setWorstSlack(Vertex *vertex, //////////////////////////////////////////////////////////////// WnsSlackLess::WnsSlackLess(PathAPIndex path_ap_index, - const StaState *sta) : + const StaState *sta) : path_ap_index_(path_ap_index), search_(sta->search()) { @@ -325,11 +305,10 @@ WnsSlackLess::WnsSlackLess(PathAPIndex path_ap_index, bool WnsSlackLess::operator()(Vertex *vertex1, - Vertex *vertex2) + Vertex *vertex2) { return delayLess(search_->wnsSlack(vertex1, path_ap_index_), - search_->wnsSlack(vertex2, path_ap_index_), - search_); + search_->wnsSlack(vertex2, path_ap_index_), search_); } -} // namespace +} // namespace sta diff --git a/search/WorstSlack.hh b/search/WorstSlack.hh index c304fd6b7..89141a98c 100644 --- a/search/WorstSlack.hh +++ b/search/WorstSlack.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,10 +25,11 @@ #pragma once #include +#include -#include "MinMax.hh" -#include "Vector.hh" +#include "Delay.hh" #include "GraphClass.hh" +#include "MinMax.hh" #include "SearchClass.hh" #include "StaState.hh" @@ -38,23 +39,23 @@ class StaState; class WorstSlack; class WnsSlackLess; -typedef Vector WorstSlackSeq; +using WorstSlackSeq = std::vector; class WorstSlacks { public: WorstSlacks(StaState *sta); void worstSlack(const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex); - void worstSlack(const Corner *corner, - const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex); + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); + void worstSlack(const Scene *scene, + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); void updateWorstSlacks(Vertex *vertex, - SlackSeq &slacks); + SlackSeq &slacks); void worstSlackNotifyBefore(Vertex *vertex); protected: @@ -66,9 +67,9 @@ class WnsSlackLess { public: WnsSlackLess(PathAPIndex path_ap_index, - const StaState *sta); + const StaState *sta); bool operator()(Vertex *vertex1, - Vertex *vertex2); + Vertex *vertex2); private: PathAPIndex path_ap_index_; @@ -79,15 +80,15 @@ class WorstSlack : public StaState { public: WorstSlack(StaState *sta); - ~WorstSlack(); + ~WorstSlack() override; WorstSlack(const WorstSlack &); void worstSlack(PathAPIndex path_ap_index, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex); + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); void updateWorstSlack(Vertex *vertex, - SlackSeq &slacks, - PathAPIndex path_ap_index); + SlackSeq &slacks, + PathAPIndex path_ap_index); void deleteVertexBefore(Vertex *vertex); protected: @@ -95,23 +96,23 @@ protected: void initQueue(PathAPIndex path_ap_index); void findWorstInQueue(PathAPIndex path_ap_index); void setWorstSlack(Vertex *vertex, - Slack slack); + Slack slack); void sortQueue(PathAPIndex path_ap_index); void checkQueue(PathAPIndex path_ap_index); Slack slack_init_; // Vertex with the worst slack. // When nullptr the worst slack is unknown but in the queue. - Vertex *worst_vertex_; + Vertex *worst_vertex_{nullptr}; Slack worst_slack_; Slack slack_threshold_; // Vertices with slack < threshold_ VertexSet *queue_; // Queue is sorted and pruned to min_queue_size_ vertices when it // reaches max_queue_size_. - int min_queue_size_; - int max_queue_size_; + size_t min_queue_size_{10}; + size_t max_queue_size_{20}; std::mutex lock_; }; -} // namespace +} // namespace sta diff --git a/spice/WritePathSpice.cc b/spice/WritePathSpice.cc index ae8bab8f7..1b90af87b 100644 --- a/spice/WritePathSpice.cc +++ b/spice/WritePathSpice.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,55 +24,51 @@ #include "WritePathSpice.hh" -#include +#include #include +#include +#include #include "Debug.hh" #include "Error.hh" -#include "Report.hh" -#include "StringUtil.hh" +#include "Format.hh" #include "FuncExpr.hh" -#include "Units.hh" -#include "Sequential.hh" +#include "Graph.hh" #include "Liberty.hh" -#include "TimingArc.hh" -#include "TableModel.hh" -#include "PortDirection.hh" #include "Network.hh" -#include "Graph.hh" -#include "Sdc.hh" -#include "DcalcAnalysisPt.hh" #include "Parasitics.hh" -#include "PathAnalysisPt.hh" #include "Path.hh" #include "PathExpanded.hh" +#include "PortDirection.hh" +#include "Report.hh" +#include "Sdc.hh" +#include "Sequential.hh" #include "StaState.hh" -#include "search/Sim.hh" +#include "StringUtil.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "Units.hh" #include "WriteSpice.hh" +#include "search/Sim.hh" namespace sta { -using std::string; -using std::ofstream; -using std::ifstream; -using std::max; - -typedef int Stage; +using Stage = int; //////////////////////////////////////////////////////////////// class WritePathSpice : public WriteSpice { public: - WritePathSpice(Path *path, - const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, + WritePathSpice(const Path *path, + std::string_view spice_filename, + std::string_view subckt_filename, + std::string_view lib_subckt_filename, + std::string_view model_filename, + std::string_view power_name, + std::string_view gnd_name, CircuitSim ckt_sim, - const StaState *sta); + const StaState *sta); void writeSpice(); private: @@ -87,15 +83,15 @@ class WritePathSpice : public WriteSpice void writeGateStage(Stage stage); void writeStageParasitics(Stage stage); void writeSubckts(); - StdStringSet findPathCellNames(); - void findPathCellSubckts(StdStringSet &path_cell_names); + StringSet findPathCellNames(); + void findPathCellSubckts(StringSet &path_cell_names); float maxTime(); float pathMaxTime(); void writeMeasureDelayStmt(Stage stage, - const Path *from_path, - const Path *to_path); + const Path *from_path, + const Path *to_path); void writeMeasureSlewStmt(Stage stage, - const Path *path); + const Path *path); void writeInputWaveform(); void writeClkWaveform(); @@ -114,7 +110,7 @@ class WritePathSpice : public WriteSpice // Stage stageFirst(); Stage stageLast(); - string stageName(Stage stage); + std::string stageName(Stage stage); int stageGateInputPathIndex(Stage stage); int stageDrvrPathIndex(Stage stage); int stageLoadPathIndex(Stage stage); @@ -123,27 +119,27 @@ class WritePathSpice : public WriteSpice const Path *stageLoadPath(Stage stage); const TimingArc *stageGateArc(Stage stage); const TimingArc *stageWireArc(Stage stage); - Edge *stageGateEdge(Stage stage); - Edge *stageWireEdge(Stage stage); - Pin *stageGateInputPin(Stage stage); - Pin *stageDrvrPin(Stage stage); - LibertyPort *stageGateInputPort(Stage stage); - LibertyPort *stageDrvrPort(Stage stage); - Pin *stageLoadPin(Stage stage); - const char *stageGateInputPinName(Stage stage); - const char *stageDrvrPinName(Stage stage); - const char *stageLoadPinName(Stage stage); - LibertyCell *stageLibertyCell(Stage stage); - Instance *stageInstance(Stage stage); + const Edge *stageGateEdge(Stage stage); + const Edge *stageWireEdge(Stage stage); + const Pin *stageGateInputPin(Stage stage); + const Pin *stageDrvrPin(Stage stage); + const LibertyPort *stageGateInputPort(Stage stage); + const LibertyPort *stageDrvrPort(Stage stage); + const Pin *stageLoadPin(Stage stage); + std::string stageGateInputPinName(Stage stage); + std::string stageDrvrPinName(Stage stage); + std::string stageLoadPinName(Stage stage); + const LibertyCell *stageLibertyCell(Stage stage); + const Instance *stageInstance(Stage stage); float findSlew(const Path *path); float findSlew(const Path *path, - const RiseFall *rf, - const TimingArc *next_arc); - Path *path_; + const RiseFall *rf, + const TimingArc *next_arc); + const Path *path_; PathExpanded path_expanded_; // Input clock waveform cycles. - int clk_cycle_count_; + int clk_cycle_count_{3}; InstanceSet written_insts_; @@ -159,15 +155,15 @@ class WritePathSpice : public WriteSpice //////////////////////////////////////////////////////////////// void -writePathSpice(Path *path, - const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, +writePathSpice(const Path *path, + std::string_view spice_filename, + std::string_view subckt_filename, + std::string_view lib_subckt_filename, + std::string_view model_filename, + std::string_view power_name, + std::string_view gnd_name, CircuitSim ckt_sim, - StaState *sta) + StaState *sta) { WritePathSpice writer(path, spice_filename, subckt_filename, lib_subckt_filename, model_filename, @@ -175,21 +171,20 @@ writePathSpice(Path *path, writer.writeSpice(); } -WritePathSpice::WritePathSpice(Path *path, - const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, +WritePathSpice::WritePathSpice(const Path *path, + std::string_view spice_filename, + std::string_view subckt_filename, + std::string_view lib_subckt_filename, + std::string_view model_filename, + std::string_view power_name, + std::string_view gnd_name, CircuitSim ckt_sim, - const StaState *sta) : + const StaState *sta) : WriteSpice(spice_filename, subckt_filename, lib_subckt_filename, model_filename, power_name, gnd_name, ckt_sim, - path->dcalcAnalysisPt(sta), sta), + path->scene(sta), path->minMax(sta), sta), path_(path), path_expanded_(sta), - clk_cycle_count_(3), written_insts_(network_) { initPowerGnd(); @@ -210,7 +205,7 @@ WritePathSpice::writeSpice() writeInputSource(); writeStageInstances(); writeStageSubckts(); - streamPrint(spice_stream_, ".end\n"); + sta::print(spice_stream_, ".end\n"); spice_stream_.close(); } else @@ -221,11 +216,11 @@ void WritePathSpice::writeHeader() { const Path *start_path = path_expanded_.startPath(); - string title = stdstrPrint("Path from %s %s to %s %s", - network_->pathName(start_path->pin(this)), - start_path->transition(this)->to_string().c_str(), - network_->pathName(path_->pin(this)), - path_->transition(this)->to_string().c_str()); + std::string title = sta::format("Path from {} {} to {} {}", + network_->pathName(start_path->pin(this)), + start_path->transition(this)->shortName(), + network_->pathName(path_->pin(this)), + path_->transition(this)->shortName()); float max_time = maxTime(); float time_step = 1e-13; writeHeader(title, max_time, time_step); @@ -234,7 +229,7 @@ WritePathSpice::writeHeader() void WritePathSpice::writePrintStmt() { - StdStringSeq node_names; + StringSeq node_names; for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { node_names.push_back(stageDrvrPinName(stage)); node_names.push_back(stageLoadPinName(stage)); @@ -274,13 +269,11 @@ WritePathSpice::pathMaxTime() Edge *edge = edge_iter.next(); Vertex *load = edge->to(graph_); float load_slew = railToRailSlew(findSlew(load, rf, nullptr), rf); - if (load_slew > path_max_slew) - path_max_slew = load_slew; + path_max_slew = std::max(load_slew, path_max_slew); } } float path_max_time = delayAsFloat(path->arrival()) + path_max_slew * 2.0; - if (path_max_time > max_time) - max_time = path_max_time; + max_time = std::max(path_max_time, max_time); } return max_time; } @@ -288,37 +281,36 @@ WritePathSpice::pathMaxTime() void WritePathSpice::writeStageInstances() { - streamPrint(spice_stream_, "*****************\n"); - streamPrint(spice_stream_, "* Stage instances\n"); - streamPrint(spice_stream_, "*****************\n\n"); + sta::print(spice_stream_, "*****************\n"); + sta::print(spice_stream_, "* Stage instances\n"); + sta::print(spice_stream_, "*****************\n\n"); for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { - string stage_name = stageName(stage); - const char *stage_cname = stage_name.c_str(); + std::string stage_name = stageName(stage); if (stage == stageFirst()) - streamPrint(spice_stream_, "x%s %s %s %s\n", - stage_cname, - stageDrvrPinName(stage), - stageLoadPinName(stage), - stage_cname); + sta::print(spice_stream_, "x{} {} {} {}\n", + stage_name, + stageDrvrPinName(stage), + stageLoadPinName(stage), + stage_name); else { - streamPrint(spice_stream_, "x%s %s %s %s %s\n", - stage_cname, - stageGateInputPinName(stage), - stageDrvrPinName(stage), - stageLoadPinName(stage), - stage_cname); + sta::print(spice_stream_, "x{} {} {} {} {}\n", + stage_name, + stageGateInputPinName(stage), + stageDrvrPinName(stage), + stageLoadPinName(stage), + stage_name); } } - streamPrint(spice_stream_, "\n"); + sta::print(spice_stream_, "\n"); } void WritePathSpice::writeInputSource() { - streamPrint(spice_stream_, "**************\n"); - streamPrint(spice_stream_, "* Input source\n"); - streamPrint(spice_stream_, "**************\n\n"); + sta::print(spice_stream_, "**************\n"); + sta::print(spice_stream_, "* Input source\n"); + sta::print(spice_stream_, "**************\n\n"); Stage input_stage = stageFirst(); const Path *input_path = stageDrvrPath(input_stage); @@ -326,7 +318,7 @@ WritePathSpice::writeInputSource() writeClkWaveform(); else writeInputWaveform(); - streamPrint(spice_stream_, "\n"); + sta::print(spice_stream_, "\n"); } void @@ -379,17 +371,17 @@ WritePathSpice::writeClkWaveform() } float slew0 = findSlew(input_path, rf0, next_arc); float slew1 = findSlew(input_path, rf1, next_arc); - streamPrint(spice_stream_, "v1 %s 0 pwl(\n", - stageDrvrPinName(input_stage)); - streamPrint(spice_stream_, "+%.3e %.3e\n", 0.0, volt0); + sta::print(spice_stream_, "v1 {} 0 pwl(\n", + stageDrvrPinName(input_stage)); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", 0.0, volt0); for (int cycle = 0; cycle < clk_cycle_count_; cycle++) { float time0 = time_offset + cycle * period; float time1 = time0 + period / 2.0; writeWaveformEdge(rf0, time0, slew0); writeWaveformEdge(rf1, time1, slew1); } - streamPrint(spice_stream_, "+%.3e %.3e\n", max_time_, volt0); - streamPrint(spice_stream_, "+)\n"); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", max_time_, volt0); + sta::print(spice_stream_, "+)\n"); } float @@ -414,9 +406,9 @@ WritePathSpice::findSlew(const Path *path, void WritePathSpice::writeMeasureStmts() { - streamPrint(spice_stream_, "********************\n"); - streamPrint(spice_stream_, "* Measure statements\n"); - streamPrint(spice_stream_, "********************\n\n"); + sta::print(spice_stream_, "********************\n"); + sta::print(spice_stream_, "* Measure statements\n"); + sta::print(spice_stream_, "********************\n\n"); for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { const Path *gate_input_path = stageGateInputPath(stage); @@ -433,13 +425,13 @@ WritePathSpice::writeMeasureStmts() if (stage == stageLast()) writeMeasureSlewStmt(stage, load_path); } - streamPrint(spice_stream_, "\n"); + sta::print(spice_stream_, "\n"); } void WritePathSpice::writeMeasureDelayStmt(Stage stage, - const Path *from_path, - const Path *to_path) + const Path *from_path, + const Path *to_path) { writeMeasureDelayStmt(from_path->pin(this), from_path->transition(this), to_path->pin(this), to_path->transition(this), @@ -448,20 +440,20 @@ WritePathSpice::writeMeasureDelayStmt(Stage stage, void WritePathSpice::writeMeasureSlewStmt(Stage stage, - const Path *path) + const Path *path) { const Pin *pin = path->pin(this); const RiseFall *rf = path->transition(this); - string prefix = stageName(stage); + std::string prefix = stageName(stage); writeMeasureSlewStmt(pin, rf, prefix); } void WritePathSpice::writeStageSubckts() { - streamPrint(spice_stream_, "***************\n"); - streamPrint(spice_stream_, "* Stage subckts\n"); - streamPrint(spice_stream_, "***************\n\n"); + sta::print(spice_stream_, "***************\n"); + sta::print(spice_stream_, "* Stage subckts\n"); + sta::print(spice_stream_, "***************\n\n"); for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { cap_index_ = 1; @@ -480,15 +472,15 @@ WritePathSpice::writeInputStage(Stage stage) { // Input arc. // External driver not handled. - const char *drvr_pin_name = stageDrvrPinName(stage); - const char *load_pin_name = stageLoadPinName(stage); - string prefix = stageName(stage); - streamPrint(spice_stream_, ".subckt %s %s %s\n", - prefix.c_str(), - drvr_pin_name, - load_pin_name); + std::string drvr_pin_name = stageDrvrPinName(stage); + std::string load_pin_name = stageLoadPinName(stage); + std::string prefix = stageName(stage); + sta::print(spice_stream_, ".subckt {} {} {}\n", + prefix, + drvr_pin_name, + load_pin_name); writeStageParasitics(stage); - streamPrint(spice_stream_, ".ends\n\n"); + sta::print(spice_stream_, ".ends\n\n"); } // Gate and load parasitics. @@ -496,33 +488,33 @@ void WritePathSpice::writeGateStage(Stage stage) { const Pin *input_pin = stageGateInputPin(stage); - const char *input_pin_name = stageGateInputPinName(stage); + std::string input_pin_name = stageGateInputPinName(stage); const Pin *drvr_pin = stageDrvrPin(stage); - const char *drvr_pin_name = stageDrvrPinName(stage); + std::string drvr_pin_name = stageDrvrPinName(stage); const Pin *load_pin = stageLoadPin(stage); - const char *load_pin_name = stageLoadPinName(stage); - string subckt_name = "stage" + std::to_string(stage); + std::string load_pin_name = stageLoadPinName(stage); + std::string subckt_name = "stage" + std::to_string(stage); const Instance *inst = stageInstance(stage); - LibertyPort *input_port = stageGateInputPort(stage); - LibertyPort *drvr_port = stageDrvrPort(stage); + const LibertyPort *input_port = stageGateInputPort(stage); + const LibertyPort *drvr_port = stageDrvrPort(stage); - streamPrint(spice_stream_, ".subckt %s %s %s %s\n", - subckt_name.c_str(), - input_pin_name, - drvr_pin_name, - load_pin_name); + sta::print(spice_stream_, ".subckt {} {} {} {}\n", + subckt_name, + input_pin_name, + drvr_pin_name, + load_pin_name); // Driver subckt call. - streamPrint(spice_stream_, "* Gate %s %s -> %s\n", - network_->pathName(inst), - input_port->name(), - drvr_port->name()); + sta::print(spice_stream_, "* Gate {} {} -> {}\n", + network_->pathName(inst), + input_port->name(), + drvr_port->name()); writeSubcktInst(inst); const Path *drvr_path = stageDrvrPath(stage); const RiseFall *drvr_rf = drvr_path->transition(this); - Edge *gate_edge = stageGateEdge(stage); + const Edge *gate_edge = stageGateEdge(stage); LibertyPortLogicValues port_values; bool is_clked; @@ -532,7 +524,7 @@ WritePathSpice::writeGateStage(Stage stage) PinSet inputs(network_); inputs.insert(input_pin); writeSubcktInstVoltSrcs(inst, port_values, inputs); - streamPrint(spice_stream_, "\n"); + sta::print(spice_stream_, "\n"); PinSet drvr_loads(network_); PinConnectedPinIterator *pin_iter = network_->connectedPinIterator(drvr_pin); @@ -544,20 +536,19 @@ WritePathSpice::writeGateStage(Stage stage) writeSubcktInstLoads(drvr_pin, load_pin, drvr_loads, written_insts_); writeStageParasitics(stage); - streamPrint(spice_stream_, ".ends\n\n"); + sta::print(spice_stream_, ".ends\n\n"); } void WritePathSpice::writeStageParasitics(Stage stage) { const Path *drvr_path = stageDrvrPath(stage); - DcalcAnalysisPt *dcalc_ap = drvr_path->dcalcAnalysisPt(this); - ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); + const MinMax *min_max = drvr_path->minMax(this); const Pin *drvr_pin = stageDrvrPin(stage); - const Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + const Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin); if (parasitic == nullptr) { const RiseFall *drvr_rf = drvr_path->transition(this); - parasitic = parasitics_->findPiElmore(drvr_pin, drvr_rf, parasitic_ap); + parasitic = parasitics_->findPiElmore(drvr_pin, drvr_rf, min_max); } NetSet coupling_nets; writeDrvrParasitics(drvr_pin, parasitic, coupling_nets); @@ -570,32 +561,32 @@ WritePathSpice::writeStageParasitics(Stage stage) void WritePathSpice::writeSubckts() { - StdStringSet cell_names = findPathCellNames(); + StringSet cell_names = findPathCellNames(); writeSubckts(cell_names); } -StdStringSet +StringSet WritePathSpice::findPathCellNames() { - StdStringSet path_cell_names; + StringSet path_cell_names; for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { const TimingArc *arc = stageGateArc(stage); if (arc) { LibertyCell *cell = arc->set()->libertyCell(); if (cell) { - debugPrint(debug_, "write_spice", 2, "cell %s", cell->name()); - path_cell_names.insert(cell->name()); + debugPrint(debug_, "write_spice", 2, "cell {}", cell->name()); + path_cell_names.insert(cell->name()); } // Include side receivers. - Pin *drvr_pin = stageDrvrPin(stage); + const Pin *drvr_pin = stageDrvrPin(stage); auto pin_iter = network_->connectedPinIterator(drvr_pin); while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - LibertyPort *port = network_->libertyPort(pin); - if (port) { - LibertyCell *cell = port->libertyCell(); - path_cell_names.insert(cell->name()); - } + const Pin *pin = pin_iter->next(); + LibertyPort *port = network_->libertyPort(pin); + if (port) { + LibertyCell *cell = port->libertyCell(); + path_cell_names.insert(cell->name()); + } } delete pin_iter; } @@ -617,12 +608,10 @@ WritePathSpice::stageLast() return (path_expanded_.size() + 1) / 2; } -string +std::string WritePathSpice::stageName(Stage stage) { - string name; - stringPrint(name, "stage%d", stage); - return name; + return sta::format("stage{}", stage); } int @@ -681,88 +670,88 @@ WritePathSpice::stageWireArc(Stage stage) return path_expanded_.path(path_index)->prevArc(this); } -Edge * +const Edge * WritePathSpice::stageGateEdge(Stage stage) { const Path *path = stageDrvrPath(stage); return path->prevEdge(this); } -Edge * +const Edge * WritePathSpice::stageWireEdge(Stage stage) { const Path *path = stageLoadPath(stage); return path->prevEdge(this); } -Pin * +const Pin * WritePathSpice::stageGateInputPin(Stage stage) { const Path *path = stageGateInputPath(stage); return path->pin(this); } -LibertyPort * +const LibertyPort * WritePathSpice::stageGateInputPort(Stage stage) { - Pin *pin = stageGateInputPin(stage); + const Pin *pin = stageGateInputPin(stage); return network_->libertyPort(pin); } -Pin * +const Pin * WritePathSpice::stageDrvrPin(Stage stage) { const Path *path = stageDrvrPath(stage); return path->pin(this); } -LibertyPort * +const LibertyPort * WritePathSpice::stageDrvrPort(Stage stage) { - Pin *pin = stageDrvrPin(stage); + const Pin *pin = stageDrvrPin(stage); return network_->libertyPort(pin); } -Pin * +const Pin * WritePathSpice::stageLoadPin(Stage stage) { const Path *path = stageLoadPath(stage); return path->pin(this); } -const char * +std::string WritePathSpice::stageGateInputPinName(Stage stage) { - Pin *pin = stageGateInputPin(stage); + const Pin *pin = stageGateInputPin(stage); return network_->pathName(pin); } -const char * +std::string WritePathSpice::stageDrvrPinName(Stage stage) { - Pin *pin = stageDrvrPin(stage); + const Pin *pin = stageDrvrPin(stage); return network_->pathName(pin); } -const char * +std::string WritePathSpice::stageLoadPinName(Stage stage) { - Pin *pin = stageLoadPin(stage); + const Pin *pin = stageLoadPin(stage); return network_->pathName(pin); } -Instance * +const Instance * WritePathSpice::stageInstance(Stage stage) { - Pin *pin = stageDrvrPin(stage); + const Pin *pin = stageDrvrPin(stage); return network_->instance(pin); } -LibertyCell * +const LibertyCell * WritePathSpice::stageLibertyCell(Stage stage) { - Pin *pin = stageDrvrPin(stage); + const Pin *pin = stageDrvrPin(stage); return network_->libertyPort(pin)->libertyCell(); } -} // namespace +} // namespace sta diff --git a/spice/WritePathSpice.hh b/spice/WritePathSpice.hh index bf4ab5061..fd574278c 100644 --- a/spice/WritePathSpice.hh +++ b/spice/WritePathSpice.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,7 +24,8 @@ #pragma once -#include "StringSet.hh" +#include + #include "CircuitSim.hh" namespace sta { @@ -35,18 +36,18 @@ class StaState; // Write a spice deck for path. // Throws FileNotReadable, FileNotWritable, SubcktEndsMissing void -writePathSpice(Path *path, - // Spice file written for path. - const char *spice_filename, - // Subckts used by path included in spice file. - const char *subckt_filename, - // File of all cell spice subckt definitions. - const char *lib_subckt_filename, - // Device model file included in spice file. - const char *model_filename, - const char *power_name, - const char *gnd_name, +writePathSpice(const Path *path, + // Spice file written for path. + std::string_view spice_filename, + // Subckts used by path included in spice file. + std::string_view subckt_filename, + // File of all cell spice subckt definitions. + std::string_view lib_subckt_filename, + // Device model file included in spice file. + std::string_view model_filename, + std::string_view power_name, + std::string_view gnd_name, CircuitSim ckt_sim, - StaState *sta); + StaState *sta); -} // namespace +} // namespace sta diff --git a/spice/WriteSpice.cc b/spice/WriteSpice.cc index f336bc8fb..6d3f9395f 100644 --- a/spice/WriteSpice.cc +++ b/spice/WriteSpice.cc @@ -1,99 +1,70 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "spice/WriteSpice.hh" -#include // swap +#include // swap #include +#include +#include +#include +#include "Bdd.hh" +#include "Clock.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" -#include "Units.hh" -#include "TableModel.hh" -#include "TimingRole.hh" #include "FuncExpr.hh" -#include "Sequential.hh" -#include "PortDirection.hh" -#include "TimingArc.hh" +#include "Graph.hh" #include "Liberty.hh" +#include "Mode.hh" #include "Network.hh" -#include "Graph.hh" -#include "search/Sim.hh" -#include "Clock.hh" #include "Path.hh" -#include "DcalcAnalysisPt.hh" -#include "Bdd.hh" +#include "PortDirection.hh" +#include "Sdc.hh" +#include "Sequential.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Units.hh" #include "cudd.h" +#include "search/Sim.hh" namespace sta { -using std::string; -using std::ifstream; -using std::ofstream; -using std::swap; -using std::set; - Net * pinNet(const Pin *pin, const Network *network); -class SubcktEndsMissing : public Exception -{ -public: - SubcktEndsMissing(const char *cell_name, - const char *subckt_filename); - const char *what() const noexcept; - -protected: - string what_; -}; - -SubcktEndsMissing::SubcktEndsMissing(const char *cell_name, - const char *subckt_filename) : - Exception() -{ - what_ = "spice subckt for cell "; - what_ += cell_name; - what_ += " missing .ends in "; - what_ += subckt_filename; -} - -const char * -SubcktEndsMissing::what() const noexcept -{ - return what_.c_str(); -} - -//////////////////////////////////////////////////////////////// - -WriteSpice::WriteSpice(const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, +WriteSpice::WriteSpice(std::string_view spice_filename, + std::string_view subckt_filename, + std::string_view lib_subckt_filename, + std::string_view model_filename, + std::string_view power_name, + std::string_view gnd_name, CircuitSim ckt_sim, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const StaState *sta) : StaState(sta), spice_filename_(spice_filename), @@ -103,13 +74,11 @@ WriteSpice::WriteSpice(const char *spice_filename, power_name_(power_name), gnd_name_(gnd_name), ckt_sim_(ckt_sim), - dcalc_ap_(dcalc_ap), + scene_(scene), + min_max_(min_max), default_library_(network_->defaultLibertyLibrary()), - short_ckt_resistance_(.0001), - cap_index_(1), - res_index_(1), - volt_index_(1), - bdd_(sta) + bdd_(sta), + parasitics_(scene->parasitics(min_max)) { } @@ -119,7 +88,8 @@ WriteSpice::initPowerGnd() bool exists = false; default_library_->supplyVoltage(power_name_, power_voltage_, exists); if (!exists) { - const OperatingConditions *op_cond = dcalc_ap_->operatingConditions(); + const OperatingConditions *op_cond = + scene_->sdc()->operatingConditions(min_max_); if (op_cond == nullptr) op_cond = network_->defaultLibertyLibrary()->defaultOperatingConditions(); power_voltage_ = op_cond->voltage(); @@ -130,117 +100,117 @@ WriteSpice::initPowerGnd() } void -WriteSpice::writeHeader(string &title, +WriteSpice::writeHeader(std::string &title, float max_time, float time_step) { - streamPrint(spice_stream_, "* %s\n", title.c_str()); - streamPrint(spice_stream_, ".include \"%s\"\n", model_filename_); - std::filesystem::path subckt_filename - = std::filesystem::path(subckt_filename_).filename(); - streamPrint(spice_stream_, ".include \"%s\"\n", subckt_filename.c_str()); - streamPrint(spice_stream_, ".tran %.3g %.3g\n", time_step, max_time); + sta::print(spice_stream_, "* {}\n", title); + sta::print(spice_stream_, ".include \"{}\"\n", model_filename_); + std::filesystem::path subckt_filename = + std::filesystem::path(subckt_filename_).filename(); + sta::print(spice_stream_, ".include \"{}\"\n", subckt_filename.string()); + sta::print(spice_stream_, ".tran {:.3g} {:.3g}\n", time_step, max_time); // Suppress printing model parameters. if (ckt_sim_ == CircuitSim::hspice) - streamPrint(spice_stream_, ".options nomod\n"); - streamPrint(spice_stream_, "\n"); + sta::print(spice_stream_, ".options nomod\n"); + sta::print(spice_stream_, "\n"); max_time_ = max_time; } void -WriteSpice::writePrintStmt(StdStringSeq &node_names) +WriteSpice::writePrintStmt(StringSeq &node_names) { - streamPrint(spice_stream_, ".print tran"); + sta::print(spice_stream_, ".print tran"); if (ckt_sim_ == CircuitSim::xyce) { - string csv_filename = replaceFileExt(spice_filename_, "csv"); - streamPrint(spice_stream_, " format=csv file=%s", csv_filename.c_str()); + std::string csv_filename = replaceFileExt(spice_filename_, "csv"); + sta::print(spice_stream_, " format=csv file={}", csv_filename); writeGnuplotFile(node_names); } - for (string &name : node_names) - streamPrint(spice_stream_, " v(%s)", name.c_str()); - streamPrint(spice_stream_, "\n\n"); + for (std::string &name : node_names) + sta::print(spice_stream_, " v({})", name); + sta::print(spice_stream_, "\n\n"); } -string -WriteSpice::replaceFileExt(string filename, - const char *ext) +std::string +WriteSpice::replaceFileExt(std::string_view filename, + std::string_view ext) { size_t dot = filename.rfind('.'); - string ext_filename = filename.substr(0, dot + 1); + std::string ext_filename(filename.substr(0, dot + 1)); ext_filename += ext; return ext_filename; } // Write gnuplot command file for use with xyce csv file. void -WriteSpice::writeGnuplotFile(StdStringSeq &node_nanes) +WriteSpice::writeGnuplotFile(StringSeq &node_nanes) { std::string gnuplot_filename = replaceFileExt(spice_filename_, "gnuplot"); std::string csv_filename = replaceFileExt(spice_filename_, "csv"); - ofstream gnuplot_stream; + std::ofstream gnuplot_stream; gnuplot_stream.open(gnuplot_filename); if (gnuplot_stream.is_open()) { - streamPrint(gnuplot_stream, "set datafile separator ','\n"); - streamPrint(gnuplot_stream, "set key autotitle columnhead\n"); - streamPrint(gnuplot_stream, "plot\\\n"); - streamPrint(gnuplot_stream, "\"%s\" using 1:2 with lines", - csv_filename.c_str()); + sta::print(gnuplot_stream, "set datafile separator ','\n"); + sta::print(gnuplot_stream, "set key autotitle columnhead\n"); + sta::print(gnuplot_stream, "plot\\\n"); + sta::print(gnuplot_stream, "\"{}\" using 1:2 with lines", csv_filename); for (size_t i = 3; i <= node_nanes.size() + 1; i++) { - streamPrint(gnuplot_stream, ",\\\n"); - streamPrint(gnuplot_stream, "'' using 1:%zu with lines", i); + sta::print(gnuplot_stream, ",\\\n"); + sta::print(gnuplot_stream, "'' using 1:{} with lines", i); } - streamPrint(gnuplot_stream, "\n"); - streamPrint(gnuplot_stream, "pause mouse close\n"); + sta::print(gnuplot_stream, "\n"); + sta::print(gnuplot_stream, "pause mouse close\n"); gnuplot_stream.close(); } } void -WriteSpice::writeSubckts(StdStringSet &cell_names) +WriteSpice::writeSubckts(StringSet &cell_names) { findCellSubckts(cell_names); - ifstream lib_subckts_stream(lib_subckt_filename_); + std::ifstream lib_subckts_stream{std::string(lib_subckt_filename_)}; if (lib_subckts_stream.is_open()) { - ofstream subckts_stream(subckt_filename_); + std::ofstream subckts_stream{std::string(subckt_filename_)}; if (subckts_stream.is_open()) { - string line; - while (getline(lib_subckts_stream, line)) { - // .subckt [args..] - StringVector tokens; - split(line, " \t", tokens); - if (tokens.size() >= 2 - && stringEqual(tokens[0].c_str(), ".subckt")) { - const char *cell_name = tokens[1].c_str(); - if (cell_names.find(cell_name) != cell_names.end()) { - subckts_stream << line << "\n"; - bool found_ends = false; - while (getline(lib_subckts_stream, line)) { - subckts_stream << line << "\n"; - if (stringBeginEqual(line.c_str(), ".ends")) { - subckts_stream << "\n"; - found_ends = true; - break; - } - } - if (!found_ends) - throw SubcktEndsMissing(cell_name, lib_subckt_filename_); - cell_names.erase(cell_name); - } - recordSpicePortNames(cell_name, tokens); - } + std::string line; + int line_num = 0; + while (std::getline(lib_subckts_stream, line)) { + line_num++; + // .subckt [args..] + StringSeq tokens = parseTokens(line); + if (tokens.size() >= 2 && stringEqual(tokens[0], ".subckt")) { + const std::string &cell_name = tokens[1]; + if (cell_names.contains(cell_name)) { + subckts_stream << line << "\n"; + bool found_ends = false; + while (std::getline(lib_subckts_stream, line)) { + subckts_stream << line << "\n"; + if (stringBeginEqual(line, ".ends")) { + subckts_stream << "\n"; + found_ends = true; + break; + } + } + if (!found_ends) + report_->fileError(1606, lib_subckt_filename_, line_num, + "spice subckt for cell {} missing .ends.", + cell_name); + cell_names.erase(cell_name); + } + recordSpicePortNames(cell_name, tokens); + } } subckts_stream.close(); lib_subckts_stream.close(); if (!cell_names.empty()) { - string missing_cells; - for (const string &cell_name : cell_names) { - missing_cells += "\n"; - missing_cells += cell_name; + std::string missing_cells; + for (const std::string &cell_name : cell_names) { + missing_cells += "\n"; + missing_cells += cell_name; } - report_->error(1605, "The subkct file %s is missing definitions for %s", - lib_subckt_filename_, - missing_cells.c_str()); + report_->error(1605, "The subkct file {} is missing definitions for {}", + lib_subckt_filename_, missing_cells); } } else { @@ -253,22 +223,22 @@ WriteSpice::writeSubckts(StdStringSet &cell_names) } void -WriteSpice::recordSpicePortNames(const char *cell_name, - StringVector &tokens) +WriteSpice::recordSpicePortNames(std::string_view cell_name, + StringSeq &tokens) { LibertyCell *cell = network_->findLibertyCell(cell_name); if (cell) { - StringVector &spice_port_names = cell_spice_port_names_[cell_name]; + StringSeq &spice_port_names = cell_spice_port_names_[std::string(cell_name)]; for (size_t i = 2; i < tokens.size(); i++) { - const char *port_name = tokens[i].c_str(); + const std::string &port_name = tokens[i]; LibertyPort *port = cell->findLibertyPort(port_name); - LibertyPort *pg_port = cell->findLibertyPort(port_name); if (port == nullptr - && pg_port == nullptr - && !stringEqual(port_name, power_name_) - && !stringEqual(port_name, gnd_name_)) - report_->error(1606, "subckt %s port %s has no corresponding liberty port, pg_port and is not power or ground.", - cell_name, port_name); + && port_name != power_name_ + && port_name != gnd_name_) + report_->error(1606, + "subckt {} port {} has no corresponding liberty port, " + "pg_port and is not power or ground.", + cell_name, port_name); spice_port_names.push_back(port_name); } } @@ -276,34 +246,32 @@ WriteSpice::recordSpicePortNames(const char *cell_name, // Subckts can call subckts (asap7). void -WriteSpice::findCellSubckts(StdStringSet &cell_names) +WriteSpice::findCellSubckts(StringSet &cell_names) { - ifstream lib_subckts_stream(lib_subckt_filename_); + std::ifstream lib_subckts_stream{std::string(lib_subckt_filename_)}; if (lib_subckts_stream.is_open()) { - string line; - while (getline(lib_subckts_stream, line)) { + std::string line; + while (std::getline(lib_subckts_stream, line)) { // .subckt [args..] - StringVector tokens; - split(line, " \t", tokens); - if (tokens.size() >= 2 - && stringEqual(tokens[0].c_str(), ".subckt")) { - const char *cell_name = tokens[1].c_str(); - if (cell_names.find(cell_name) != cell_names.end()) { + StringSeq tokens = parseTokens(line); + if (tokens.size() >= 2 && stringEqual(tokens[0], ".subckt")) { + const std::string &cell_name = tokens[1]; + if (cell_names.contains(cell_name)) { // Scan the subckt definition for subckt calls. - string stmt; - while (getline(lib_subckts_stream, line)) { + std::string stmt; + while (std::getline(lib_subckts_stream, line)) { if (line[0] == '+') stmt += line.substr(1); else { // Process previous statement. if (tolower(stmt[0]) == 'x') { - split(stmt, " \t", tokens); - string &subckt_cell = tokens[tokens.size() - 1]; + StringSeq tokens = parseTokens(line); + std::string &subckt_cell = tokens[tokens.size() - 1]; cell_names.insert(subckt_cell); } stmt = line; } - if (stringBeginEqual(line.c_str(), ".ends")) + if (stringBeginEqual(line, ".ends")) break; } } @@ -319,27 +287,23 @@ WriteSpice::findCellSubckts(StdStringSet &cell_names) void WriteSpice::writeSubcktInst(const Instance *inst) { - const char *inst_name = network_->pathName(inst); + std::string inst_name = network_->pathName(inst); LibertyCell *cell = network_->libertyCell(inst); - const char *cell_name = cell->name(); - StringVector &spice_port_names = cell_spice_port_names_[cell_name]; - streamPrint(spice_stream_, "x%s", inst_name); - for (string subckt_port_name : spice_port_names) { - const char *subckt_port_cname = subckt_port_name.c_str(); - Pin *pin = network_->findPin(inst, subckt_port_cname); - LibertyPort *pg_port = cell->findLibertyPort(subckt_port_cname); - const char *pin_name; - if (pin) { - pin_name = network_->pathName(pin); - streamPrint(spice_stream_, " %s", pin_name); - } + const std::string &cell_name = cell->name(); + StringSeq &spice_port_names = cell_spice_port_names_[cell_name]; + sta::print(spice_stream_, "x{}", inst_name); + for (std::string &subckt_port_name : spice_port_names) { + Pin *pin = network_->findPin(inst, subckt_port_name); + LibertyPort *pg_port = cell->findLibertyPort(subckt_port_name); + if (pin) + sta::print(spice_stream_, " {}", network_->pathName(pin)); else if (pg_port) - streamPrint(spice_stream_, " %s/%s", inst_name, subckt_port_cname); - else if (stringEq(subckt_port_cname, power_name_) - || stringEq(subckt_port_cname, gnd_name_)) - streamPrint(spice_stream_, " %s/%s", inst_name, subckt_port_cname); + sta::print(spice_stream_, " {}/{}", inst_name, subckt_port_name); + else if (subckt_port_name == power_name_ + || subckt_port_name == gnd_name_) + sta::print(spice_stream_, " {}/{}", inst_name, subckt_port_name); } - streamPrint(spice_stream_, " %s\n", cell_name); + sta::print(spice_stream_, " {}\n", cell_name); } // Power/ground and input voltage sources. @@ -349,115 +313,116 @@ WriteSpice::writeSubcktInstVoltSrcs(const Instance *inst, const PinSet &excluded_input_pins) { LibertyCell *cell = network_->libertyCell(inst); - const char *cell_name = cell->name(); - StringVector &spice_port_names = cell_spice_port_names_[cell_name]; - const char *inst_name = network_->pathName(inst); + const std::string &cell_name = cell->name(); + StringSeq &spice_port_names = cell_spice_port_names_[cell_name]; + std::string inst_name = network_->pathName(inst); - debugPrint(debug_, "write_spice", 2, "subckt %s", cell->name()); - for (string subckt_port_sname : spice_port_names) { - const char *subckt_port_name = subckt_port_sname.c_str(); + debugPrint(debug_, "write_spice", 2, "subckt {}", cell->name()); + for (std::string &subckt_port_name : spice_port_names) { LibertyPort *port = cell->findLibertyPort(subckt_port_name); const Pin *pin = port ? network_->findPin(inst, port) : nullptr; bool is_pg_port = port && port->isPwrGnd(); - debugPrint(debug_, "write_spice", 2, " port %s%s", + debugPrint(debug_, "write_spice", 2, " port {}{}", subckt_port_name, is_pg_port ? " pwr/gnd" : ""); if (is_pg_port) - writeVoltageSource(inst_name, subckt_port_name, - pgPortVoltage(port)); - else if (stringEq(subckt_port_name, power_name_)) + writeVoltageSource(inst_name, subckt_port_name, pgPortVoltage(port)); + else if (subckt_port_name == power_name_) writeVoltageSource(inst_name, subckt_port_name, power_voltage_); - else if (stringEq(subckt_port_name, gnd_name_)) + else if (subckt_port_name == gnd_name_) writeVoltageSource(inst_name, subckt_port_name, gnd_voltage_); - else if (port - && excluded_input_pins.find(pin) == excluded_input_pins.end() + else if (port && !excluded_input_pins.contains(pin) && port->direction()->isAnyInput()) { // Input voltage to sensitize path from gate input to output. // Look for tie high/low or propagated constant values. - LogicValue port_value = sim_->logicValue(pin); + LogicValue port_value = scene_->mode()->sim()->simValue(pin); if (port_value == LogicValue::unknown) { bool has_value; LogicValue value; - port_values.findKey(port, value, has_value); + findKeyValue(port_values, port, value, has_value); if (has_value) port_value = value; } switch (port_value) { - case LogicValue::zero: - case LogicValue::unknown: - writeVoltageSource(cell, inst_name, subckt_port_name, - port->relatedGroundPin(), - gnd_voltage_); - break; - case LogicValue::one: - writeVoltageSource(cell, inst_name, subckt_port_name, - port->relatedPowerPin(), - power_voltage_); - break; - case LogicValue::rise: - case LogicValue::fall: - break; + case LogicValue::zero: + case LogicValue::unknown: + writeVoltageSource(inst_name, subckt_port_name, + port->relatedGroundPort(), gnd_voltage_); + break; + case LogicValue::one: + writeVoltageSource(inst_name, subckt_port_name, + port->relatedPowerPort(), power_voltage_); + break; + case LogicValue::rise: + case LogicValue::fall: + break; } } } } void -WriteSpice::writeVoltageSource(const char *inst_name, - const char *port_name, +WriteSpice::writeVoltageSource(std::string_view inst_name, + std::string_view port_name, float voltage) { - string node_name = inst_name; + std::string node_name(inst_name); node_name += '/'; node_name += port_name; - writeVoltageSource(node_name.c_str(), voltage); + writeVoltageSource(node_name, voltage); +} + +void +WriteSpice::writeVoltageSource(std::string_view inst_name, + std::string_view subckt_port_name, + const LibertyPort *pg_port, + float voltage) +{ + if (pg_port) + voltage = pgPortVoltage(pg_port); + writeVoltageSource(inst_name, subckt_port_name, voltage); } void WriteSpice::writeVoltageSource(LibertyCell *cell, - const char *inst_name, - const char *subckt_port_name, - const char *pg_port_name, + std::string_view inst_name, + std::string_view subckt_port_name, + const std::string &pg_port_name, float voltage) { - if (pg_port_name) { + if (!pg_port_name.empty()) { LibertyPort *pg_port = cell->findLibertyPort(pg_port_name); if (pg_port) voltage = pgPortVoltage(pg_port); else - report_->error(1603, "%s pg_port %s not found,", - cell->name(), - pg_port_name); - + report_->error(1603, "{} pg_port {} not found,", cell->name(), pg_port_name); } writeVoltageSource(inst_name, subckt_port_name, voltage); } float -WriteSpice::pgPortVoltage(LibertyPort *pg_port) +WriteSpice::pgPortVoltage(const LibertyPort *pg_port) { LibertyLibrary *liberty = pg_port->libertyCell()->libertyLibrary(); float voltage = 0.0; bool exists; - const char *voltage_name = pg_port->voltageName(); - if (voltage_name) { + const std::string &voltage_name = pg_port->voltageName(); + if (!voltage_name.empty()) { liberty->supplyVoltage(voltage_name, voltage, exists); if (!exists) { - if (stringEqual(voltage_name, power_name_)) - voltage = power_voltage_; - else if (stringEqual(voltage_name, gnd_name_)) - voltage = gnd_voltage_; + if (voltage_name == power_name_) + voltage = power_voltage_; + else if (voltage_name == gnd_name_) + voltage = gnd_voltage_; else - report_->error(1601 , "pg_pin %s/%s voltage %s not found,", - pg_port->libertyCell()->name(), - pg_port->name(), - voltage_name); + report_->error(1601, "pg_pin {}/{} voltage {} not found,", + pg_port->libertyCell()->name(), pg_port->name(), + voltage_name); } } else - report_->error(1602, "Liberty pg_port %s/%s missing voltage_name attribute,", - pg_port->libertyCell()->name(), - pg_port->name()); + report_->error(1602, "Liberty pg_port {}/{} missing voltage_name attribute,", + pg_port->libertyCell()->name(), pg_port->name()); return voltage; } @@ -468,7 +433,8 @@ WriteSpice::findSlew(Vertex *vertex, const RiseFall *rf, const TimingArc *next_arc) { - float slew = delayAsFloat(graph_->slew(vertex, rf, dcalc_ap_->index())); + DcalcAPIndex ap_index = scene_->dcalcAnalysisPtIndex(min_max_); + float slew = delayAsFloat(graph_->slew(vertex, rf, ap_index)); if (slew == 0.0 && next_arc) slew = slewAxisMinValue(next_arc); if (slew == 0.0) @@ -480,25 +446,25 @@ WriteSpice::findSlew(Vertex *vertex, float WriteSpice::slewAxisMinValue(const TimingArc *arc) { - GateTableModel *gate_model = arc->gateTableModel(dcalc_ap_); + GateTableModel *gate_model = arc->gateTableModel(scene_, min_max_); if (gate_model) { - const TableModel *model = gate_model->delayModel(); + const TableModel *model = gate_model->delayModels()->model(); const TableAxis *axis1 = model->axis1(); TableAxisVariable var1 = axis1->variable(); if (var1 == TableAxisVariable::input_transition_time - || var1 == TableAxisVariable::input_net_transition) + || var1 == TableAxisVariable::input_net_transition) return axis1->axisValue(0); const TableAxis *axis2 = model->axis2(); TableAxisVariable var2 = axis2->variable(); if (var2 == TableAxisVariable::input_transition_time - || var2 == TableAxisVariable::input_net_transition) + || var2 == TableAxisVariable::input_net_transition) return axis2->axisValue(0); const TableAxis *axis3 = model->axis3(); TableAxisVariable var3 = axis3->variable(); if (var3 == TableAxisVariable::input_transition_time - || var3 == TableAxisVariable::input_net_transition) + || var3 == TableAxisVariable::input_net_transition) return axis3->axisValue(0); } return 0.0; @@ -512,15 +478,17 @@ WriteSpice::writeDrvrParasitics(const Pin *drvr_pin, const NetSet &coupling_nets) { Net *net = network_->net(drvr_pin); - const char *net_name = net ? network_->pathName(net) : network_->pathName(drvr_pin); - streamPrint(spice_stream_, "* Net %s\n", net_name); + std::string net_name = net + ? std::string(network_->pathName(net)) + : std::string(network_->pathName(drvr_pin)); + sta::print(spice_stream_, "* Net {}\n", net_name); if (parasitics_->isParasiticNetwork(parasitic)) writeParasiticNetwork(drvr_pin, parasitic, coupling_nets); else if (parasitics_->isPiElmore(parasitic)) writePiElmore(drvr_pin, parasitic); else { - streamPrint(spice_stream_, "* Net has no parasitics.\n"); + sta::print(spice_stream_, "* Net has no parasitics.\n"); writeNullParasitic(drvr_pin); } } @@ -530,23 +498,18 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, const Parasitic *parasitic, const NetSet &coupling_nets) { - set reachable_pins; + std::set reachable_pins; // Sort resistors for consistent regression results. ParasiticResistorSeq resistors = parasitics_->resistors(parasitic); - sort(resistors.begin(), resistors.end(), - [this] (const ParasiticResistor *r1, - const ParasiticResistor *r2) { - return parasitics_->id(r1) < parasitics_->id(r2); - }); + sort(resistors, [this](const ParasiticResistor *r1, const ParasiticResistor *r2) { + return parasitics_->id(r1) < parasitics_->id(r2); + }); for (ParasiticResistor *resistor : resistors) { float resistance = parasitics_->value(resistor); ParasiticNode *node1 = parasitics_->node1(resistor); ParasiticNode *node2 = parasitics_->node2(resistor); - streamPrint(spice_stream_, "R%d %s %s %.3e\n", - res_index_++, - parasitics_->name(node1), - parasitics_->name(node2), - resistance); + sta::print(spice_stream_, "R{} {} {} {:.3e}\n", res_index_++, + parasitics_->name(node1), parasitics_->name(node2), resistance); // Necessary but not sufficient. Need a DFS. const Pin *pin1 = parasitics_->pin(node1); @@ -561,15 +524,11 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, auto pin_iter = network_->connectedPinIterator(drvr_pin); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); - if (pin != drvr_pin - && network_->isLoad(pin) - && !network_->isHierarchical(pin) - && reachable_pins.find(pin) == reachable_pins.end()) { - streamPrint(spice_stream_, "R%d %s %s %.3e\n", - res_index_++, - network_->pathName(drvr_pin), - network_->pathName(pin), - short_ckt_resistance_); + if (pin != drvr_pin && network_->isLoad(pin) && !network_->isHierarchical(pin) + && !reachable_pins.contains(pin)) { + sta::print(spice_stream_, "R{} {} {} {:.3e}\n", res_index_++, + network_->pathName(drvr_pin), network_->pathName(pin), + short_ckt_resistance_); } } delete pin_iter; @@ -577,30 +536,25 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, // Grounded node capacitors. // Sort nodes for consistent regression results. ParasiticNodeSeq nodes = parasitics_->nodes(parasitic); - sort(nodes.begin(), nodes.end(), - [this] (const ParasiticNode *node1, - const ParasiticNode *node2) { - const char *name1 = parasitics_->name(node1); - const char *name2 = parasitics_->name(node2); - return stringLess(name1, name2); - }); + sort(nodes, [this](const ParasiticNode *node1, const ParasiticNode *node2) { + std::string name1 = parasitics_->name(node1); + std::string name2 = parasitics_->name(node2); + return name1 < name2; + }); for (ParasiticNode *node : nodes) { float cap = parasitics_->nodeGndCap(node); // Spice has a cow over zero value caps. if (cap > 0.0) { - streamPrint(spice_stream_, "C%d %s 0 %.3e\n", - cap_index_++, - parasitics_->name(node), - cap); + sta::print(spice_stream_, "C{} {} 0 {:.3e}\n", cap_index_++, + parasitics_->name(node), cap); } } // Sort coupling capacitors for consistent regression results. ParasiticCapacitorSeq capacitors = parasitics_->capacitors(parasitic); - sort(capacitors.begin(), capacitors.end(), - [this] (const ParasiticCapacitor *c1, - const ParasiticCapacitor *c2) { + sort(capacitors, + [this](const ParasiticCapacitor *c1, const ParasiticCapacitor *c2) { return parasitics_->id(c1) < parasitics_->id(c2); }); const Net *net = pinNet(drvr_pin, network_); @@ -611,21 +565,16 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, const Net *net1 = node1 ? parasitics_->net(node1, network_) : nullptr; const Net *net2 = node2 ? parasitics_->net(node2, network_) : nullptr; if (net2 == net) { - swap(net1, net2); - swap(node1, node2); + std::swap(net1, net2); + std::swap(node1, node2); } - if (net2 && coupling_nets.hasKey(net2)) + if (net2 && coupling_nets.contains(net2)) // Write half the capacitance because the coupled net will do the same. - streamPrint(spice_stream_, "C%d %s %s %.3e\n", - cap_index_++, - parasitics_->name(node1), - parasitics_->name(node2), - cap * .5); + sta::print(spice_stream_, "C{} {} {} {:.3e}\n", cap_index_++, + parasitics_->name(node1), parasitics_->name(node2), cap * .5); else - streamPrint(spice_stream_, "C%d %s 0 %.3e\n", - cap_index_++, - parasitics_->name(node1), - cap); + sta::print(spice_stream_, "C{} {} 0 {:.3e}\n", cap_index_++, + parasitics_->name(node1), cap); } } @@ -651,50 +600,35 @@ WriteSpice::writePiElmore(const Pin *drvr_pin, float c2, rpi, c1; parasitics_->piModel(parasitic, c2, rpi, c1); const char *c1_node = "n1"; - streamPrint(spice_stream_, "RPI %s %s %.3e\n", - network_->pathName(drvr_pin), - c1_node, - rpi); + sta::print(spice_stream_, "RPI {} {} {:.3e}\n", network_->pathName(drvr_pin), + c1_node, rpi); if (c2 > 0.0) - streamPrint(spice_stream_, "C2 %s 0 %.3e\n", - network_->pathName(drvr_pin), - c2); + sta::print(spice_stream_, "C2 {} 0 {:.3e}\n", network_->pathName(drvr_pin), c2); if (c1 > 0.0) - streamPrint(spice_stream_, "C1 %s 0 %.3e\n", - c1_node, - c1); - + sta::print(spice_stream_, "C1 {} 0 {:.3e}\n", c1_node, c1); + int load_index = 3; auto pin_iter = network_->connectedPinIterator(drvr_pin); while (pin_iter->hasNext()) { const Pin *load_pin = pin_iter->next(); - if (load_pin != drvr_pin - && network_->isLoad(load_pin) - && !network_->isHierarchical(load_pin)) { + if (load_pin != drvr_pin && network_->isLoad(load_pin) + && !network_->isHierarchical(load_pin)) { float elmore; bool exists; parasitics_->findElmore(parasitic, load_pin, elmore, exists); if (exists) { - streamPrint(spice_stream_, "E%d el%d 0 %s 0 1.0\n", - load_index, - load_index, - network_->pathName(drvr_pin)); - streamPrint(spice_stream_, "R%d el%d %s 1.0\n", - load_index, - load_index, - network_->pathName(load_pin)); - streamPrint(spice_stream_, "C%d %s 0 %.3e\n", - load_index, - network_->pathName(load_pin), - elmore); + sta::print(spice_stream_, "E{} el{} 0 {} 0 1.0\n", load_index, load_index, + network_->pathName(drvr_pin)); + sta::print(spice_stream_, "R{} el{} {} 1.0\n", load_index, load_index, + network_->pathName(load_pin)); + sta::print(spice_stream_, "C{} {} 0 {:.3e}\n", load_index, + network_->pathName(load_pin), elmore); } else // Add resistor from drvr to load for missing elmore. - streamPrint(spice_stream_, "R%d %s %s %.3e\n", - load_index, - network_->pathName(drvr_pin), - network_->pathName(load_pin), - short_ckt_resistance_); + sta::print(spice_stream_, "R{} {} {} {:.3e}\n", load_index, + network_->pathName(drvr_pin), network_->pathName(load_pin), + short_ckt_resistance_); load_index++; } } @@ -708,14 +642,11 @@ WriteSpice::writeNullParasitic(const Pin *drvr_pin) auto pin_iter = network_->connectedPinIterator(drvr_pin); while (pin_iter->hasNext()) { const Pin *load_pin = pin_iter->next(); - if (load_pin != drvr_pin - && network_->isLoad(load_pin) - && !network_->isHierarchical(load_pin)) { - streamPrint(spice_stream_, "R%d %s %s %.3e\n", - res_index_++, - network_->pathName(drvr_pin), - network_->pathName(load_pin), - short_ckt_resistance_); + if (load_pin != drvr_pin && network_->isLoad(load_pin) + && !network_->isHierarchical(load_pin)) { + sta::print(spice_stream_, "R{} {} {} {:.3e}\n", res_index_++, + network_->pathName(drvr_pin), network_->pathName(load_pin), + short_ckt_resistance_); } } delete pin_iter; @@ -724,13 +655,10 @@ WriteSpice::writeNullParasitic(const Pin *drvr_pin) //////////////////////////////////////////////////////////////// void -WriteSpice::writeVoltageSource(const char *node_name, +WriteSpice::writeVoltageSource(std::string_view node_name, float voltage) { - streamPrint(spice_stream_, "v%d %s 0 %.3f\n", - volt_index_++, - node_name, - voltage); + sta::print(spice_stream_, "v{} {} 0 {:.3f}\n", volt_index_++, node_name, voltage); } void @@ -751,20 +679,19 @@ WriteSpice::writeWaveformVoltSource(const Pin *pin, volt1 = gnd_voltage_; volt_factor = -power_voltage_; } - streamPrint(spice_stream_, "v%d %s 0 pwl(\n", - volt_index_++, - network_->pathName(pin)); - streamPrint(spice_stream_, "+%.3e %.3e\n", 0.0, volt0); - Table1 waveform = drvr_waveform->waveform(slew); + sta::print(spice_stream_, "v{} {} 0 pwl(\n", volt_index_++, + network_->pathName(pin)); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", 0.0, volt0); + Table waveform = drvr_waveform->waveform(slew); const TableAxis *time_axis = waveform.axis1(); - for (size_t time_index = 0; time_index < time_axis->size(); time_index++) { + for (size_t time_index = 0; time_index < time_axis->size(); time_index++) { float time = delay + time_axis->axisValue(time_index); float wave_volt = waveform.value(time_index); float volt = volt0 + wave_volt * volt_factor; - streamPrint(spice_stream_, "+%.3e %.3e\n", time, volt); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", time, volt); } - streamPrint(spice_stream_, "+%.3e %.3e\n", max_time_, volt1); - streamPrint(spice_stream_, "+)\n"); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", max_time_, volt1); + sta::print(spice_stream_, "+)\n"); } void @@ -782,13 +709,12 @@ WriteSpice::writeRampVoltSource(const Pin *pin, volt0 = power_voltage_; volt1 = gnd_voltage_; } - streamPrint(spice_stream_, "v%d %s 0 pwl(\n", - volt_index_++, - network_->pathName(pin)); - streamPrint(spice_stream_, "+%.3e %.3e\n", 0.0, volt0); + sta::print(spice_stream_, "v{} {} 0 pwl(\n", volt_index_++, + network_->pathName(pin)); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", 0.0, volt0); writeWaveformEdge(rf, time, slew); - streamPrint(spice_stream_, "+%.3e %.3e\n", max_time_, volt1); - streamPrint(spice_stream_, "+)\n"); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", max_time_, volt1); + sta::print(spice_stream_, "+)\n"); } // Write PWL rise/fall edge that crosses threshold at time. @@ -811,8 +737,8 @@ WriteSpice::writeWaveformEdge(const RiseFall *rf, float time0 = time - dt * threshold; float time1 = time0 + dt; if (time0 > 0.0) - streamPrint(spice_stream_, "+%.3e %.3e\n", time0, volt0); - streamPrint(spice_stream_, "+%.3e %.3e\n", time1, volt1); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", time0, volt0); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", time1, volt1); } float @@ -826,7 +752,7 @@ WriteSpice::railToRailSlew(float slew, //////////////////////////////////////////////////////////////// -// Find the logic values for expression inputs to enable paths from input_port. +// Find the logic values for expression inputs to sensitize the path from input_port. void WriteSpice::gatePortValues(const Pin *input_pin, const Pin *drvr_pin, @@ -842,10 +768,8 @@ WriteSpice::gatePortValues(const Pin *input_pin, const LibertyPort *drvr_port = network_->libertyPort(drvr_pin); const FuncExpr *drvr_func = drvr_port->function(); if (drvr_func) { - if (gate_edge - && gate_edge->role()->genericRole() == TimingRole::regClkToQ()) - regPortValues(input_pin, drvr_rf, drvr_port, drvr_func, - port_values, is_clked); + if (gate_edge && gate_edge->role()->genericRole() == TimingRole::regClkToQ()) + regPortValues(input_pin, drvr_rf, drvr_port, drvr_func, port_values, is_clked); else gatePortValues(inst, drvr_func, input_port, port_values); } @@ -867,24 +791,23 @@ WriteSpice::gatePortValues(const Instance *, CUDD_VALUE_TYPE value; DdGen *cube_gen = Cudd_FirstCube(cudd_mgr, diff, &cube, &value); - FuncExprPortIterator port_iter(expr); - while (port_iter.hasNext()) { - const LibertyPort *port = port_iter.next(); + LibertyPortSet ports = expr->ports(); + for (const LibertyPort *port : ports) { if (port != input_port) { DdNode *port_node = bdd_.findNode(port); int var_index = Cudd_NodeReadIndex(port_node); LogicValue value; switch (cube[var_index]) { - case 0: - value = LogicValue::zero; - break; - case 1: - value = LogicValue::one; - break; - case 2: - default: - value = LogicValue::unknown; - break; + case 0: + value = LogicValue::zero; + break; + case 1: + value = LogicValue::one; + break; + case 2: + default: + value = LogicValue::unknown; + break; } port_values[port] = value; } @@ -916,9 +839,8 @@ WriteSpice::regPortValues(const Pin *input_pin, } else { const LibertyPort *input_port = network_->libertyPort(input_pin); - report_->error(1604, "no register/latch found for path from %s to %s,", - input_port->name(), - drvr_port->name()); + report_->error(1604, "no register/latch found for path from {} to {},", + input_port->name(), drvr_port->name()); } } } @@ -936,23 +858,23 @@ WriteSpice::seqPortValues(Sequential *seq, if (port) { TimingSense sense = data->portTimingSense(port); switch (sense) { - case TimingSense::positive_unate: - if (rf == RiseFall::rise()) - port_values[port] = LogicValue::one; - else - port_values[port] = LogicValue::zero; - break; - case TimingSense::negative_unate: - if (rf == RiseFall::rise()) - port_values[port] = LogicValue::zero; - else - port_values[port] = LogicValue::one; - break; - case TimingSense::non_unate: - case TimingSense::none: - case TimingSense::unknown: - default: - break; + case TimingSense::positive_unate: + if (rf == RiseFall::rise()) + port_values[port] = LogicValue::one; + else + port_values[port] = LogicValue::zero; + break; + case TimingSense::negative_unate: + if (rf == RiseFall::rise()) + port_values[port] = LogicValue::zero; + else + port_values[port] = LogicValue::one; + break; + case TimingSense::non_unate: + case TimingSense::none: + case TimingSense::unknown: + default: + break; } } } @@ -965,21 +887,21 @@ WriteSpice::onePort(FuncExpr *expr) FuncExpr *right = expr->right(); LibertyPort *port; switch (expr->op()) { - case FuncExpr::op_port: - return expr->port(); - case FuncExpr::op_not: - return onePort(left); - case FuncExpr::op_or: - case FuncExpr::op_and: - case FuncExpr::op_xor: - port = onePort(left); - if (port == nullptr) - port = onePort(right); - return port; - case FuncExpr::op_one: - case FuncExpr::op_zero: - default: - return nullptr; + case FuncExpr::Op::port: + return expr->port(); + case FuncExpr::Op::not_: + return onePort(left); + case FuncExpr::Op::or_: + case FuncExpr::Op::and_: + case FuncExpr::Op::xor_: + port = onePort(left); + if (port == nullptr) + port = onePort(right); + return port; + case FuncExpr::Op::one: + case FuncExpr::Op::zero: + default: + return nullptr; } } @@ -1008,20 +930,18 @@ WriteSpice::writeSubcktInstLoads(const Pin *drvr_pin, const PinSet &excluded_input_pins, InstanceSet &written_insts) { - streamPrint(spice_stream_, "* Load pins\n"); + sta::print(spice_stream_, "* Load pins\n"); PinSeq drvr_loads = drvrLoads(drvr_pin); // Do not sensitize side load gates. LibertyPortLogicValues port_values; for (const Pin *load_pin : drvr_loads) { const Instance *load_inst = network_->instance(load_pin); - if (load_pin != path_load - && network_->direction(load_pin)->isAnyInput() - && !network_->isHierarchical(load_pin) - && !network_->isTopLevelPort(load_pin) - && !written_insts.hasKey(load_inst)) { + if (load_pin != path_load && network_->direction(load_pin)->isAnyInput() + && !network_->isHierarchical(load_pin) && !network_->isTopLevelPort(load_pin) + && !written_insts.contains(load_inst)) { writeSubcktInst(load_inst); writeSubcktInstVoltSrcs(load_inst, port_values, excluded_input_pins); - streamPrint(spice_stream_, "\n"); + sta::print(spice_stream_, "\n"); written_insts.insert(load_inst); } } @@ -1034,36 +954,27 @@ WriteSpice::writeMeasureDelayStmt(const Pin *from_pin, const RiseFall *from_rf, const Pin *to_pin, const RiseFall *to_rf, - string prefix) + std::string_view prefix) { - const char *from_pin_name = network_->pathName(from_pin); + std::string from_pin_name = network_->pathName(from_pin); float from_threshold = power_voltage_ * default_library_->inputThreshold(from_rf); - const char *to_pin_name = network_->pathName(to_pin); + std::string to_pin_name = network_->pathName(to_pin); float to_threshold = power_voltage_ * default_library_->inputThreshold(to_rf); - streamPrint(spice_stream_, - ".measure tran %s_%s_delay_%s\n", - prefix.c_str(), - from_pin_name, - to_pin_name); - streamPrint(spice_stream_, - "+trig v(%s) val=%.3f %s=last\n", - from_pin_name, - from_threshold, - spiceTrans(from_rf)); - streamPrint(spice_stream_, - "+targ v(%s) val=%.3f %s=last\n", - to_pin_name, - to_threshold, - spiceTrans(to_rf)); + sta::print(spice_stream_, ".measure tran {}_{}_delay_{}\n", prefix, + from_pin_name, to_pin_name); + sta::print(spice_stream_, "+trig v({}) val={:.3f} {}=last\n", from_pin_name, + from_threshold, spiceTrans(from_rf)); + sta::print(spice_stream_, "+targ v({}) val={:.3f} {}=last\n", to_pin_name, + to_threshold, spiceTrans(to_rf)); } void WriteSpice::writeMeasureSlewStmt(const Pin *pin, const RiseFall *rf, - string prefix) + std::string_view prefix) { - const char *pin_name = network_->pathName(pin); - const char *spice_rf = spiceTrans(rf); + std::string pin_name = network_->pathName(pin); + std::string_view spice_rf = spiceTrans(rf); float lower = power_voltage_ * default_library_->slewLowerThreshold(rf); float upper = power_voltage_ * default_library_->slewUpperThreshold(rf); float threshold1, threshold2; @@ -1075,26 +986,16 @@ WriteSpice::writeMeasureSlewStmt(const Pin *pin, threshold1 = upper; threshold2 = lower; } - streamPrint(spice_stream_, - ".measure tran %s_%s_slew\n", - prefix.c_str(), - pin_name); - streamPrint(spice_stream_, - "+trig v(%s) val=%.3f %s=last\n", - pin_name, - threshold1, - spice_rf); - streamPrint(spice_stream_, - "+targ v(%s) val=%.3f %s=last\n", - pin_name, - threshold2, - spice_rf); + sta::print(spice_stream_, ".measure tran {}_{}_slew\n", prefix, pin_name); + sta::print(spice_stream_, "+trig v({}) val={:.3f} {}=last\n", pin_name, threshold1, + spice_rf); + sta::print(spice_stream_, "+targ v({}) val={:.3f} {}=last\n", pin_name, threshold2, + spice_rf); } //////////////////////////////////////////////////////////////// - -const char * +std::string_view WriteSpice::spiceTrans(const RiseFall *rf) { if (rf == RiseFall::rise()) @@ -1103,23 +1004,6 @@ WriteSpice::spiceTrans(const RiseFall *rf) return "FALL"; } -// fprintf for c++ streams. -// Yes, I hate formatted output to ostream THAT much. -void -streamPrint(ofstream &stream, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - char *result = nullptr; - if (vasprintf(&result, fmt, args) == -1) - criticalError(267, "out of memory"); - stream << result; - free(result); - va_end(args); -} - //////////////////////////////////////////////////////////////// // Unused @@ -1141,4 +1025,4 @@ WriteSpice::clkWaveformTimeOffset(const Clock *clk) return clk->period() / 10; } -} // namespace +} // namespace sta diff --git a/spice/WriteSpice.hh b/spice/WriteSpice.hh index 98995a44d..a1fd64a3a 100644 --- a/spice/WriteSpice.hh +++ b/spice/WriteSpice.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,37 +25,39 @@ #pragma once #include -#include #include +#include +#include #include -#include "StaState.hh" -#include "StringSet.hh" -#include "Liberty.hh" -#include "GraphClass.hh" -#include "Parasitics.hh" #include "Bdd.hh" #include "CircuitSim.hh" +#include "Format.hh" +#include "GraphClass.hh" +#include "Liberty.hh" +#include "Parasitics.hh" +#include "StaState.hh" +#include "StringUtil.hh" namespace sta { -typedef std::map ParasiticNodeMap; -typedef Map CellSpicePortNames; -typedef Map LibertyPortLogicValues; -typedef std::vector StdStringSeq; +using ParasiticNodeMap = std::map; +using CellSpicePortNames = std::map>; +using LibertyPortLogicValues = std::map; // Utilities for writing a spice deck. class WriteSpice : public StaState { public: - WriteSpice(const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, + WriteSpice(std::string_view spice_filename, + std::string_view subckt_filename, + std::string_view lib_subckt_filename, + std::string_view model_filename, + std::string_view power_name, + std::string_view gnd_name, CircuitSim ckt_sim, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const StaState *sta); protected: @@ -63,76 +65,75 @@ protected: void writeHeader(std::string &title, float max_time, float time_step); - void writePrintStmt(StdStringSeq &node_names); - void writeGnuplotFile(StdStringSeq &node_nanes); - void writeSubckts(StdStringSet &cell_names); - void findCellSubckts(StdStringSet &cell_names); - void recordSpicePortNames(const char *cell_name, - StringVector &tokens); + void writePrintStmt(StringSeq &node_names); + void writeGnuplotFile(StringSeq &node_nanes); + void writeSubckts(StringSet &cell_names); + void findCellSubckts(StringSet &cell_names); + void recordSpicePortNames(std::string_view cell_name, + StringSeq &tokens); void writeSubcktInst(const Instance *inst); void writeSubcktInstVoltSrcs(const Instance *inst, - LibertyPortLogicValues &port_values, + LibertyPortLogicValues &port_values, const PinSet &excluded_input_pins); - float pgPortVoltage(LibertyPort *pg_port); - void writeVoltageSource(const char *inst_name, - const char *port_name, - float voltage); + float pgPortVoltage(const LibertyPort *pg_port); + void writeVoltageSource(std::string_view inst_name, + std::string_view port_name, + float voltage); + void writeVoltageSource(std::string_view inst_name, + std::string_view subckt_port_name, + const LibertyPort *pg_port, + float voltage); void writeVoltageSource(LibertyCell *cell, - const char *inst_name, - const char *subckt_port_name, - const char *pg_port_name, - float voltage); + std::string_view inst_name, + std::string_view subckt_port_name, + const std::string &pg_port_name, + float voltage); void writeClkedStepSource(const Pin *pin, - const RiseFall *rf, - const Clock *clk); - void writeDrvrParasitics(const Pin *drvr_pin, - const RiseFall *drvr_rf, - // Nets with parasitics to include coupling caps to. - const NetSet &coupling_nets, - const ParasiticAnalysisPt *parasitic_ap); + const RiseFall *rf, + const Clock *clk); void writeDrvrParasitics(const Pin *drvr_pin, const Parasitic *parasitic, const NetSet &coupling_nets); void writeParasiticNetwork(const Pin *drvr_pin, const Parasitic *parasitic, - const NetSet &aggressor_nets); + const NetSet &coupling_nets); void writePiElmore(const Pin *drvr_pin, const Parasitic *parasitic); void writeNullParasitic(const Pin *drvr_pin); - void writeVoltageSource(const char *node_name, + void writeVoltageSource(std::string_view node_name, float voltage); void writeRampVoltSource(const Pin *pin, - const RiseFall *rf, - float time, - float slew); + const RiseFall *rf, + float time, + float slew); void writeWaveformVoltSource(const Pin *pin, DriverWaveform *drvr_waveform, const RiseFall *rf, float delay, float slew); void writeWaveformEdge(const RiseFall *rf, - float time, - float slew); + float time, + float slew); float railToRailSlew(float slew, const RiseFall *rf); void seqPortValues(Sequential *seq, - const RiseFall *rf, - // Return values. - LibertyPortLogicValues &port_values); + const RiseFall *rf, + // Return values. + LibertyPortLogicValues &port_values); LibertyPort *onePort(FuncExpr *expr); void writeMeasureDelayStmt(const Pin *from_pin, const RiseFall *from_rf, const Pin *to_pin, const RiseFall *to_rf, - std::string prefix); + std::string_view prefix); void writeMeasureSlewStmt(const Pin *pin, const RiseFall *rf, - std::string prefix); - const char *spiceTrans(const RiseFall *rf); + std::string_view prefix); + std::string_view spiceTrans(const RiseFall *rf); float findSlew(Vertex *vertex, - const RiseFall *rf, - const TimingArc *next_arc); + const RiseFall *rf, + const TimingArc *next_arc); float slewAxisMinValue(const TimingArc *arc); float clkWaveformTimeOffset(const Clock *clk); @@ -140,38 +141,39 @@ protected: const Pin *drvr_pin, const RiseFall *drvr_rf, const Edge *gate_edge, - // Return values. - LibertyPortLogicValues &port_values, - bool &is_clked); + // Return values. + LibertyPortLogicValues &port_values, + bool &is_clked); void regPortValues(const Pin *input_pin, const RiseFall *drvr_rf, const LibertyPort *drvr_port, const FuncExpr *drvr_func, - // Return values. - LibertyPortLogicValues &port_values, + // Return values. + LibertyPortLogicValues &port_values, bool &is_clked); void gatePortValues(const Instance *inst, - const FuncExpr *expr, - const LibertyPort *input_port, - // Return values. - LibertyPortLogicValues &port_values); + const FuncExpr *expr, + const LibertyPort *input_port, + // Return values. + LibertyPortLogicValues &port_values); void writeSubcktInstLoads(const Pin *drvr_pin, const Pin *path_load, const PinSet &excluded_input_pins, InstanceSet &written_insts); PinSeq drvrLoads(const Pin *drvr_pin); void writeSubcktInstVoltSrcs(); - std::string replaceFileExt(std::string filename, - const char *ext); + std::string replaceFileExt(std::string_view filename, + std::string_view ext); - const char *spice_filename_; - const char *subckt_filename_; - const char *lib_subckt_filename_; - const char *model_filename_; - const char *power_name_; - const char *gnd_name_; + const std::string spice_filename_; + const std::string subckt_filename_; + const std::string lib_subckt_filename_; + const std::string model_filename_; + const std::string power_name_; + const std::string gnd_name_; CircuitSim ckt_sim_; - const DcalcAnalysisPt *dcalc_ap_; + const Scene *scene_; + const MinMax *min_max_; std::ofstream spice_stream_; LibertyLibrary *default_library_; @@ -179,19 +181,15 @@ protected: float gnd_voltage_; float max_time_; // Resistance to use to simulate a short circuit between spice nodes. - float short_ckt_resistance_; + float short_ckt_resistance_{0.0001F}; // Input clock waveform cycles. // Sequential device numbers. - int cap_index_; - int res_index_; - int volt_index_; + int cap_index_{1}; + int res_index_{1}; + int volt_index_{1}; CellSpicePortNames cell_spice_port_names_; Bdd bdd_; + Parasitics *parasitics_; }; -void -streamPrint(std::ofstream &stream, - const char *fmt, - ...) __attribute__((format (printf, 2, 3))); - -} // namespace +} // namespace sta diff --git a/spice/WriteSpice.i b/spice/WriteSpice.i index 727bf82ba..65bdda1fc 100644 --- a/spice/WriteSpice.i +++ b/spice/WriteSpice.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,8 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module write_gate_spice - %{ #include "spice/WritePathSpice.hh" @@ -33,13 +31,13 @@ %inline %{ void -write_path_spice_cmd(Path *path, - const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, +write_path_spice_cmd(const Path *path, + const char *spice_filename, + const char *subckt_filename, + const char *lib_subckt_filename, + const char *model_filename, + const char *power_name, + const char *gnd_name, CircuitSim ckt_sim) { Sta *sta = Sta::sta(); diff --git a/spice/WriteSpice.tcl b/spice/WriteSpice.tcl index af15905b4..a44526624 100644 --- a/spice/WriteSpice.tcl +++ b/spice/WriteSpice.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -25,21 +25,22 @@ namespace eval sta { define_cmd_args "write_path_spice" { -path_args path_args\ - -spice_directory spice_directory\ - -lib_subckt_file lib_subckts_file\ - -model_file model_file\ - -power power\ - -ground ground\ + -spice_file spice_file\ + -lib_subckt_file lib_subckts_file\ + -model_file model_file\ + -power power\ + -ground ground\ [-simulator hspice|ngspice|xyce]} proc write_path_spice { args } { parse_key_args "write_path_spice" args \ - keys {-spice_directory -lib_subckt_file -model_file \ - -power -ground -path_args -simulator} \ + keys {-spice_file -lib_subckt_file -model_file \ + -power -ground -path_args -simulator} \ flags {} - if { [info exists keys(-spice_directory)] } { - set spice_dir [file nativename $keys(-spice_directory)] + if { [info exists keys(-spice_file)] } { + set spice_file [file nativename $keys(-spice_file)] + set spice_dir [file dirname $spice_file] if { ![file exists $spice_dir] } { sta_error 1920 "Directory $spice_dir not found." } @@ -50,7 +51,7 @@ proc write_path_spice { args } { sta_error 1922 "Cannot write in $spice_dir." } } else { - sta_error 1923 "No -spice_directory specified." + sta_error 1923 "No -spice_file specified." } if { [info exists keys(-lib_subckt_file)] } { @@ -96,11 +97,11 @@ proc write_path_spice { args } { set path_index 1 foreach path_end $path_ends { set path [$path_end path] - set path_name "path_$path_index" - set spice_file [file join $spice_dir "$path_name.sp"] - set subckt_file [file join $spice_dir "$path_name.subckt"] - write_path_spice_cmd $path $spice_file $subckt_file \ - $lib_subckt_file $model_file $power $ground $ckt_sim + set path_file "${spice_file}_$path_index" + set spice_file1 ${path_file}.sp + set subckt_file ${path_file}.subckt + write_path_spice_cmd $path $spice_file1 $subckt_file \ + $lib_subckt_file $model_file $power $ground $ckt_sim incr path_index } } @@ -132,13 +133,13 @@ define_cmd_args "write_gate_spice" \ -power power\ -ground ground\ [-simulator hspice|ngspice|xyce]\ - [-corner corner]\ + [-scene scene]\ [-min] [-max]} proc write_gate_spice { args } { parse_key_args "write_gate_spice" args \ keys {-gates -spice_filename -lib_subckt_file -model_file \ - -power -ground -simulator -corner}\ + -power -ground -simulator -scene -corner}\ flags {-measure_stmts -min -max} if { [info exists keys(-gates)] } { @@ -188,7 +189,7 @@ proc write_gate_spice { args } { set ckt_sim [parse_ckt_sim_key keys] - set corner [parse_corner keys] + set scene [parse_scene keys] set min_max [parse_min_max_flags flags] check_argc_eq0 "write_gate_spice" $args @@ -197,7 +198,7 @@ proc write_gate_spice { args } { set subckt_file [file join $spice_dir "$spice_root.subckt"] write_gate_spice_cmd $gates $spice_file $subckt_file \ $lib_subckt_file $model_file $power $ground $ckt_sim \ - $corner $min_max + $scene $min_max } ################################################################ @@ -207,11 +208,11 @@ define_cmd_args "write_gate_gnuplot" \ { -gates {{instance input_port driver_port edge [delay]}...}\ -plot_pins plot_pins\ -plot_basename plot_basename\ - [-corner corner] [-min] [-max]} + [-scene scene] [-min] [-max]} proc write_gate_gnuplot { args } { parse_key_args "write_gate_gnuplot" args \ - keys {-gates -plot_pins -plot_basename -spice_waveforms -corner} \ + keys {-gates -plot_pins -plot_basename -spice_waveforms -corner -scene} \ flags {-min -max} if { [info exists keys(-gates)] } { @@ -262,11 +263,11 @@ proc write_gate_gnuplot { args } { set sim_wave_filename $keys(-spice_waveforms) } - set corner [parse_corner keys] + set scene [parse_scene keys] set min_max [parse_min_max_flags flags] write_gate_gnuplot_cmd $gates $plot_pins $sim_wave_filename \ - $gnuplot_filename $csv_filename $corner $min_max + $gnuplot_filename $csv_filename $scene $min_max } proc parse_gate_drvr_pin { gate_arg } { diff --git a/spice/Xyce.cc b/spice/Xyce.cc index 618b65b18..2dfbe30f0 100644 --- a/spice/Xyce.cc +++ b/spice/Xyce.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,55 +26,50 @@ #include "Xyce.hh" #include -#include #include +#include +#include #include "Error.hh" +#include "StringUtil.hh" namespace sta { -using std::string; -using std::ifstream; -using std::getline; -using std::stringstream; -using std::vector; -using std::make_shared; - void readXyceCsv(const char *csv_filename, // Return values. - StdStringSeq &titles, + StringSeq &titles, WaveformSeq &waveforms) { - ifstream file(csv_filename); + std::ifstream file(csv_filename); if (file.is_open()) { - string line; + std::string line; // Read the header line. - getline(file, line); - stringstream ss(line); - string field; + std::getline(file, line); + std::stringstream ss(line); + std::string field; size_t col = 0; - while (getline(ss, field, ',')) { + while (std::getline(ss, field, ',')) { // Skip TIME title. if (col > 0) titles.push_back(field); col++; } - vector values(titles.size() + 1); - while (getline(file, line)) { - stringstream ss(line); + std::vector values(titles.size() + 1); + while (std::getline(file, line)) { + std::stringstream ss(line); size_t col = 0; - while (getline(ss, field, ',')) { - float value = std::stof(field); + while (std::getline(ss, field, ',')) { + auto [value, valid] = stringFloat(field); values[col].push_back(value); col++; } } file.close(); - TableAxisPtr time_axis = make_shared(TableAxisVariable::time, - new FloatSeq(values[0])); + TableAxisPtr time_axis = std::make_shared(TableAxisVariable::time, + std::move(values[0])); for (size_t var = 1; var < values.size(); var++) waveforms.emplace_back(new FloatSeq(values[var]), time_axis); } @@ -82,4 +77,4 @@ readXyceCsv(const char *csv_filename, throw FileNotReadable(csv_filename); } -} // namespace +} // namespace sta diff --git a/spice/Xyce.hh b/spice/Xyce.hh index 215ddf3ff..9d3d09a3b 100644 --- a/spice/Xyce.hh +++ b/spice/Xyce.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -27,16 +27,17 @@ #include #include -#include "StringSeq.hh" +#include "StringUtil.hh" #include "TableModel.hh" namespace sta { -typedef std::vector WaveformSeq; + +using WaveformSeq = std::vector
; void readXyceCsv(const char *csv_filename, // Return values. - StdStringSeq &titles, + StringSeq &titles, WaveformSeq &waveforms); -} // namespace +} // namespace sta diff --git a/tcl/CmdArgs.tcl b/tcl/CmdArgs.tcl index bf71be4e9..c5be1f00b 100644 --- a/tcl/CmdArgs.tcl +++ b/tcl/CmdArgs.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -48,8 +48,8 @@ namespace eval sta { # net proc get_object_args { objects clks_var libcells_var libports_var \ - cells_var insts_var ports_var pins_var nets_var \ - edges_var timing_arc_sets_var } { + cells_var insts_var ports_var pins_var nets_var \ + edges_var timing_arc_sets_var } { if { $clks_var != {} } { upvar 1 $clks_var clks } @@ -87,97 +87,96 @@ proc get_object_args { objects clks_var libcells_var libports_var \ if { [sizeof_collection $obj] > 1 || [sta::is_collection $obj] } { # List arg. Recursive call without initing objects. get_object_args $obj $clks_var $libcells_var $libports_var $cells_var $insts_var \ - $ports_var $pins_var $nets_var $edges_var $timing_arc_sets_var + $ports_var $pins_var $nets_var $edges_var $timing_arc_sets_var } elseif { [is_object $obj] } { # Explicit object arg. set object_type [object_type $obj] if { $pins_var != {} && $object_type == "Pin" } { - lappend pins $obj + lappend pins $obj } elseif { $insts_var != {} && $object_type == "Instance" } { - lappend insts $obj + lappend insts $obj } elseif { $nets_var != {} && $object_type == "Net" } { - lappend nets $obj + lappend nets $obj } elseif { $ports_var != {} && $object_type == "Port" } { - lappend ports $obj + lappend ports $obj } elseif { $edges_var != {} && $object_type == "Edge" } { - lappend edges $obj + lappend edges $obj } elseif { $clks_var != {} && $object_type == "Clock" } { - lappend clks $obj + lappend clks $obj } elseif { $libcells_var != {} && $object_type == "LibertyCell" } { - lappend libcells $obj + lappend libcells $obj } elseif { $libports_var != {} && $object_type == "LibertyPort" } { - lappend libports $obj + lappend libports $obj } elseif { $cells_var != {} && $object_type == "Cell" } { - lappend cells $obj + lappend cells $obj } elseif { $timing_arc_sets_var != {} \ - && $object_type == "TimingArcSet" } { - lappend timing_arc_sets $obj + && $object_type == "TimingArcSet" } { + lappend timing_arc_sets $obj } else { - sta_error 100 "unsupported object type $object_type." + sta_error 100 "unsupported object type $object_type." } } elseif { $obj != {} } { # Check for implicit arg. # Search for most general object type first. set matches {} if { $clks_var != {} } { - set matches [get_clocks -quiet $obj] + set matches [get_clocks -quiet $obj] } if { [sizeof_collection $matches] > 0 } { - set clks [add_to_collection $clks $matches] - } else { - if { $libcells_var != {} } { - set matches [get_lib_cells -quiet $obj] - } - if { [sizeof_collection $matches] > 0 } { - set libcells [add_to_collection $libcells $matches] - } else { - - if { $libports_var != {} } { - set matches [get_lib_pins -quiet $obj] - } - if { [sizeof_collection $matches] > 0 } { - set libports [add_to_collection $libports $matches] - } else { - - if { $cells_var != {} } { - set matches [find_cells_matching $obj 0 0] - } - if { [sizeof_collection $matches] > 0 } { - set cells [add_to_collection $cells $matches] - } else { - - if { $insts_var != {} } { - set matches [get_cells -quiet $obj] - } - if { [sizeof_collection $matches] > 0 } { - set insts [add_to_collection $insts $matches] - } else { - if { $ports_var != {} } { - set matches [get_ports -quiet $obj] - } - if { [sizeof_collection $matches] > 0 } { - set ports [add_to_collection $ports $matches] - } else { - if { $pins_var != {} } { - set matches [get_pins -quiet $obj] - } - if { [sizeof_collection $matches] > 0 } { - set pins [add_to_collection $pins $matches] - } else { - if { $nets_var != {} } { - set matches [get_nets -quiet $obj] - } - if { [sizeof_collection $matches] > 0 } { - set nets [add_to_collection $nets $matches] - } else { - sta_warn 101 "object '$obj' not found." - } - } - } - } - } - } - } + set clks [add_to_collection $clks $matches] + } else { + if { $libcells_var != {} } { + set matches [get_lib_cells -quiet $obj] + } + if { [sizeof_collection $matches] > 0 } { + set libcells [add_to_collection $libcells $matches] + } else { + if { $libports_var != {} } { + set matches [get_lib_pins -quiet $obj] + } + if { [sizeof_collection $matches] > 0 } { + set libports [add_to_collection $libports $matches] + } else { + + if { $cells_var != {} } { + set matches [find_cells_matching $obj 0 0] + } + if { [sizeof_collection $matches] > 0 } { + set cells [add_to_collection $cells $matches] + } else { + + if { $insts_var != {} } { + set matches [get_cells -quiet $obj] + } + if { [sizeof_collection $matches] > 0 } { + set insts [add_to_collection $insts $matches] + } else { + if { $ports_var != {} } { + set matches [get_ports -quiet $obj] + } + if { [sizeof_collection $matches] > 0 } { + set ports [add_to_collection $ports $matches] + } else { + if { $pins_var != {} } { + set matches [get_pins -quiet $obj] + } + if { [sizeof_collection $matches] > 0 } { + set pins [add_to_collection $pins $matches] + } else { + if { $nets_var != {} } { + set matches [get_nets -quiet $obj] + } + if { [sizeof_collection $matches] > 0 } { + set nets [add_to_collection $nets $matches] + } else { + sta_warn 101 "object '$obj' not found." + } + } + } + } + } + } + } } } } @@ -194,7 +193,7 @@ proc parse_clk_cell_port_args { objects clks_var cells_var ports_var } { } proc parse_clk_cell_port_pin_args { objects clks_var cells_var ports_var \ - pins_arg } { + pins_arg } { upvar 1 $clks_var clks upvar 1 $cells_var cells upvar 1 $ports_var ports @@ -243,13 +242,13 @@ proc parse_clk_port_pin_arg { objects clks_var pins_var } { } proc parse_libcell_libport_inst_port_pin_edge_timing_arc_set_arg { objects \ - libcells_var \ - libports_var \ - insts_var \ - ports_var \ - pins_var \ - edges_var \ - timing_arc_sets_var } { + libcells_var \ + libports_var \ + insts_var \ + ports_var \ + pins_var \ + edges_var \ + timing_arc_sets_var } { upvar 1 $libcells_var libcells upvar 1 $libports_var libports upvar 1 $insts_var insts @@ -399,87 +398,156 @@ proc get_ports_or_pins { pattern } { ################################################################ -# -corner keyword is optional. -# If -corner keyword is missing: -# one corner: return default -# multiple corners: error -proc parse_corner { keys_var } { +# -scene keyword is optional. +# If -scene keyword is missing: +# one scene: return default +# multiple scenes: error +proc parse_scene { keys_var } { upvar 1 $keys_var keys + set scene_arg "" + # compabibility 05/29/2025 if { [info exists keys(-corner)] } { - set corner_arg $keys(-corner) - if { [is_object $corner_arg] } { - set object_type [object_type $corner_arg] - if { $object_type == "Corner" } { - return $corner_arg + set scene_arg $keys(-corner) + } + if { [info exists keys(-scene)] } { + set scene_arg $keys(-scene) + } + if { $scene_arg != "" } { + if { [is_object $scene_arg] } { + set object_type [object_type $scene_arg] + if { $object_type == "Scene" } { + return $scene_arg } else { - sta_error 144 "corner object type '$object_type' is not a corner." + sta_error 144 "scene object type '$object_type' is not a scene." } } else { - set corner [find_corner $corner_arg] - if { $corner == "NULL" } { - sta_error 102 "$corner_arg is not the name of process corner." + set scene [find_scene $scene_arg] + if { $scene == "NULL" } { + sta_error 102 "$scene_arg is not the name of a scene." } else { - return $corner + return $scene } } - } elseif { [multi_corner] } { - sta_error 103 "-corner keyword required with multi-corner analysis." + } elseif { [multi_scene] } { + sta_error 103 "-scene keyword required with multi-scene analysis." } else { - return [cmd_corner] + return [cmd_scene] } } -# -corner keyword is required. -proc parse_corner_required { keys_var } { +# -scene keyword is required. +proc parse_scene_required { keys_var } { upvar 1 $keys_var keys + set scene_name "" + if { [info exists keys(-scene)] } { + set scene_name $keys(-scene) + } + # compabibility 05/29/2025 if { [info exists keys(-corner)] } { - set corner_name $keys(-corner) - set corner [find_corner $corner_name] - if { $corner == "NULL" } { - sta_error 104 "$corner_name is not the name of process corner." + set scene_name $keys(-corner) + } + if { $scene_name != "" } { + set scene [find_scene $scene_name] + if { $scene == "NULL" } { + sta_error 104 "$scene_name is not the name of a scene." } else { - return $corner + return $scene } } else { - sta_error 105 "missing -corner arg." + sta_error 105 "missing -scene arg." } } -proc parse_corner_or_default { keys_var } { +proc parse_scene_or_default { keys_var } { upvar 1 $keys_var keys + set scene_name "" + if { [info exists keys(-scene)] } { + set scene_name $keys(-scene) + } + # compabibility 05/29/2025 if { [info exists keys(-corner)] } { - set corner_name $keys(-corner) - set corner [find_corner $corner_name] - if { $corner == "NULL" } { - sta_error 106 "$corner_name is not the name of process corner." + set scene_name $keys(-corner) + } + if { $scene_name != "" } { + set scene [find_scene $scene_name] + if { $scene == "NULL" } { + sta_error 106 "$scene_name is not the name of a scene." } else { - return $corner + return $scene } } else { - return [cmd_corner] + return [cmd_scene] } } -# Return NULL for all. -proc parse_corner_or_all { keys_var } { +# If -scene/-corner return scene, else return NULL. +proc parse_scene_or_null { keys_var } { upvar 1 $keys_var keys + set scene_name "" + if { [info exists keys(-scene)] } { + set scene_name $keys(-scene) + } + # compabibility 05/29/2025 if { [info exists keys(-corner)] } { - set corner_name $keys(-corner) - set corner [find_corner $corner_name] - if { $corner == "NULL" } { - sta_error 107 "$corner_name is not the name of process corner." + set scene_name $keys(-corner) + } + if { $scene_name != "" } { + set scene [find_scene $scene_name] + if { $scene == "NULL" } { + sta_error 107 "$scene_name is not the name of a scene." } else { - return $corner + return $scene } } else { return "NULL" } } +# If -scenes/-corner return scenes, else return default scene. +proc parse_scenes_or_default { keys_var } { + upvar 1 $keys_var keys + + if { [info exists keys(-scenes)] } { + return [find_scenes $keys(-scenes)] + } elseif { [info exists keys(-corner)] } { + # compabibility 05/29/2025 + return [find_scenes $keys(-corner)] + } else { + return [cmd_scene] + } +} + +# If -scenes/-corner return scenes, else return all scenes. +proc parse_scenes_or_all { keys_var } { + upvar 1 $keys_var keys + + if { [info exists keys(-scenes)] } { + return [find_scenes $keys(-scenes)] + } elseif { [info exists keys(-corner)] } { + # compabibility 05/29/2025 + return [find_scenes $keys(-corner)] + } else { + return [scenes] + } +} + +proc find_scenes { scene_names } { + set scenes {} + foreach scene_name $scene_names { + set scene [find_scene $scene_name] + if { $scene == "NULL" } { + sta_error 134 "$scene_name is not the name of a scene." + } else { + lappend scenes $scene + } + } + return $scenes +} + ################################################################ proc parse_rise_fall_flags { flags_var } { @@ -621,7 +689,7 @@ proc get_lib_cell_arg { arg_name arg error_proc } { if { $library != "NULL" } { set lib_cell [$library find_liberty_cell $cell_name] if { $lib_cell == "NULL" } { - $error_proc 117 "liberty cell '$arg' not found." + $error_proc 117 "liberty cell '$arg' not found." } } else { $error_proc 118 "library '$lib_name' not found." @@ -649,12 +717,12 @@ proc get_lib_cells_arg { arg_name arglist error_proc } { if { $object_type == "LibertyCell" || $object_type == "LibertyCellSeq" } { append_to_collection lib_cells $arg } else { - $error_proc 120 "unsupported object type $object_type." + $error_proc 120 "unsupported object type $object_type." } } elseif { $arg != {} } { set arg_lib_cells [get_lib_cells1 $arg $error_proc] if { [sizeof_collection $arg_lib_cells] > 0 } { - set lib_cells [add_to_collection $lib_cells $arg_lib_cells] + set lib_cells [add_to_collection $lib_cells $arg_lib_cells] } } } diff --git a/tcl/CmdUtil.tcl b/tcl/CmdUtil.tcl index e2bdfaf08..339f5c524 100644 --- a/tcl/CmdUtil.tcl +++ b/tcl/CmdUtil.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -65,15 +65,15 @@ proc show_cmd_args { cmd } { # Break the arglist up into max_col length lines. while {1} { if {[regexp {(^[\n ]*)([a-zA-Z0-9_\\\|\-]+|\[[^\[]+\])(.*)} \ - $arglist ignore space arg rest]} { + $arglist ignore space arg rest]} { set arg_length [string length $arg] if { $col + $arg_length < $max_col } { - set line "$line $arg" - set col [expr $col + $arg_length + 1] + set line "$line $arg" + set col [expr $col + $arg_length + 1] } else { report_line $line - set line "$indent_str $arg" - set col [expr $indent + $arg_length + 1] + set line "$indent_str $arg" + set col [expr $indent + $arg_length + 1] } set arglist $rest } else { @@ -147,7 +147,7 @@ define_cmd_args "set_units" \ proc set_units { args } { parse_key_args "set_units" args \ keys {-capacitance -resistance -time -voltage -current -power \ - -distance -digits -suffix} \ + -distance -digits -suffix} \ flags {} check_argc_eq0 "set_units" $args @@ -207,25 +207,25 @@ proc delete_objects_from_list_cmd { list objects } { # type. if {$list_is_object && ![is_object $obj]} { if {$list_type == "Clock"} { - set obj [find_clock $obj] + set obj [find_clock $obj] } elseif {$list_type == "Port"} { - set top_instance [top_instance] - set top_cell [$top_instance cell] - set obj [$top_cell find_port $obj] + set top_instance [top_instance] + set top_cell [$top_instance cell] + set obj [$top_cell find_port $obj] } elseif {$list_type == "Pin"} { - set obj [find_pin $obj] + set obj [find_pin $obj] } elseif {$list_type == "Instance"} { - set obj [find_instance $obj] + set obj [find_instance $obj] } elseif {$list_type == "Net"} { - set obj [find_net $obj] + set obj [find_net $obj] } elseif {$list_type == "LibertyLibrary"} { - set obj [find_liberty $obj] + set obj [find_liberty $obj] } elseif {$list_type == "LibertyCell"} { - set obj [find_liberty_cell $obj] + set obj [find_liberty_cell $obj] } elseif {$list_type == "LibertyPort"} { - set obj [get_lib_pins $obj] + set obj [get_lib_pins $obj] } else { - sta_error 164 "unsupported object type $list_type." + sta_error 164 "unsupported object type $list_type." } } set index [lsearch $list $obj] diff --git a/tcl/Collections.i b/tcl/Collections.i index 68446aa87..2c996efeb 100644 --- a/tcl/Collections.i +++ b/tcl/Collections.i @@ -20,7 +20,6 @@ return std::stoull(input); } } - %define COLLECTION_TYPEMAPS(CollectionType, ElementType, ElementBaseType) // Declare the class so SWIG registers a destructor, enabling own=true to actually free objects. @@ -118,10 +117,10 @@ private: void collection_sort_inplace(CollectionType *v, StringSeq *property_names, bool descending = false, bool natural = true) { auto network = Sta::sta()->network(); auto &properties = Sta::sta()->properties(); - sta::sort( - v, + std::stable_sort( + v->begin(), v->end(), [&](ElementType A, ElementType B) { - for (auto *property_name: *property_names) { + for (auto property_name: *property_names) { auto propertyA = properties.getProperty(A, property_name); auto propertyB = properties.getProperty(B, property_name); int diff = propertyA.compare(propertyB, network, natural); @@ -135,14 +134,14 @@ private: ); } - CollectionType *collection_sorted(const CollectionType *v, StringSeq *property_names, bool descending = false, bool natural = true) { + CollectionType *collection_sorted(const CollectionType *v, StringSeq property_names, bool descending = false, bool natural = true) { CollectionType *result; if (v != nullptr) { result = new CollectionType(*v); } else { result = new CollectionType(); } - collection_sort_inplace(result, property_names, descending, natural); + collection_sort_inplace(result, &property_names, descending, natural); return result; } diff --git a/tcl/Exception.i b/tcl/Exception.i index 0c4d788e6..dc40603e8 100644 --- a/tcl/Exception.i +++ b/tcl/Exception.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,12 +22,21 @@ // // This notice may not be removed or altered from any source distribution. +%{ +#include "Property.hh" +%} + %exception { try { $action } catch (std::bad_alloc &) { fprintf(stderr, "Error: out of memory.\n"); exit(1); } + catch (sta::PropertyUnknown &excp) { + Sta::sta()->report()->warn(9000, "{}", excp.what()); + Tcl_ResetResult(interp); + return TCL_OK; + } catch (ExceptionMsg &excp) { if (!excp.suppressed()) { Tcl_ResetResult(interp); diff --git a/tcl/Init.tcl b/tcl/Init.tcl index bb5c5fd7e..936321191 100644 --- a/tcl/Init.tcl +++ b/tcl/Init.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tcl/Property.tcl b/tcl/Property.tcl index 7e4229670..b31e9360c 100644 --- a/tcl/Property.tcl +++ b/tcl/Property.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tcl/Splash.tcl b/tcl/Splash.tcl index 60b49931a..3dcf1838a 100644 --- a/tcl/Splash.tcl +++ b/tcl/Splash.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -33,7 +33,7 @@ namespace eval sta { define_cmd_args show_splash {} proc show_splash {} { - report_line "OpenSTA [sta::version] [string range [sta::git_sha1] 0 9] Copyright (c) 2025, Parallax Software, Inc. + report_line "OpenSTA [version] [string range [git_sha1] 0 9] Copyright (c) 2026, Parallax Software, Inc. License GPLv3: GNU GPL version 3 This is free software, and you are free to change and redistribute it diff --git a/tcl/Sta.tcl b/tcl/Sta.tcl index e3e495645..8900b5bc5 100644 --- a/tcl/Sta.tcl +++ b/tcl/Sta.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -30,6 +30,122 @@ namespace eval sta { # ################################################################ +define_cmd_args "define_scene" {name -mode mode_name\ + -liberty liberty_files \ + | -liberty_min liberty_min_files -liberty_max liberty_max_files\ + [-spef spef_file | -spef_min spef_min_file -spef_max spef_max_file]} + +proc define_scene { args } { + parse_key_args "define_scene" args \ + keys {-mode -liberty -liberty_min -liberty_max \ + -spef -spef_min -spef_max} \ + flags {} + + check_argc_eq1 "define_scene" $args + set name [lindex $args 0] + + if { [info exists keys(-mode)] } { + set mode_name $keys(-mode) + } else { + set mode_name [cmd_mode_name] + } + + set liberty_min_files {} + set liberty_max_files {} + if { [info exists keys(-liberty)] } { + set liberty_files $keys(-liberty) + set liberty_min_files $liberty_files + set liberty_max_files $liberty_files + } else { + if { [info exists keys(-liberty_min)] && [info exists keys(-liberty_max)] } { + set liberty_min_files $keys(-liberty_min) + set liberty_max_files $keys(-liberty_max) + } else { + sta_error 483 "-liberty or -liberty_min and -liberty_max are required arguments." + } + } + + set spef_min_file "" + set spef_max_file "" + if { [info exists keys(-spef)] } { + set spef_file $keys(-spef) + set spef_min_file $spef_file + set spef_max_file $spef_file + } elseif { [info exists keys(-spef_min)] && [info exists keys(-spef_max)] } { + set spef_min_file $keys(-spef_min) + set spef_max_file $keys(-spef_max) + } elseif { [info exists keys(-spef_min)] ||[info exists keys(-spef_max)] } { + sta_error 484 "-spef_min and -spef_max are required arguments." + } + + define_scene_cmd $name $mode_name \ + $liberty_min_files $liberty_max_files \ + $spef_min_file $spef_max_file +} + +# deprecated 11/22/2025 +define_cmd_args "define_corners" { corner1 [corner2]... } + +proc define_corners { args } { + if { [get_libs -quiet *] != {} } { + sta_error 482 "define_corners must be called before read_liberty." + } + if { [llength $args] == 0 } { + sta_error 577 "define_corners must define at least one corner." + } + define_scenes_cmd $args +} + +################################################################ + +define_cmd_args "set_scene" {scene_name} + +proc set_scene { args } { + check_argc_eq1 "set_scene" $args + set_scene_cmd [lindex $args 0] +} + +################################################################ + +define_cmd_args "get_scenes" {[-modes mode_names] scene_names} + +proc get_scenes { args } { + parse_key_args "get_scenes" args keys {-modes} flags {} + check_argc_eq0or1 "get_scenes" $args + + if { [llength $args] == 0 } { + set scene_name "*" + } else { + set scene_name [lindex $args 0] + } + set mode_names {} + if { [info exists keys(-modes)] } { + set mode_names $keys(-modes) + return [find_mode_scenes_matching $scene_name $mode_names] + } else { + return [find_scenes_matching $scene_name] + } +} + +################################################################ + +define_cmd_args "get_modes" {mode_name} + +proc get_modes { args } { + return [find_modes [lindex $args 0]] +} + +################################################################ + +define_cmd_args "set_mode" {mode_name} + +proc set_mode { args } { + check_argc_eq1 "set_mode" $args + set_mode_cmd [lindex $args 0] +} + +################################################################ + define_cmd_args "get_fanin" \ {-to sink_list [-flat] [-only_cells] [-startpoints_only]\ [-levels level_count] [-pin_levels pin_count]\ @@ -85,10 +201,10 @@ proc get_fanin { args } { } if { $only_insts } { return [find_fanin_insts $pins $flat $startpoints_only \ - $inst_levels $pin_levels $thru_disabled $thru_constants] + $inst_levels $pin_levels $thru_disabled $thru_constants] } else { return [find_fanin_pins $pins $flat $startpoints_only \ - $inst_levels $pin_levels $thru_disabled $thru_constants] + $inst_levels $pin_levels $thru_disabled $thru_constants] } } @@ -143,10 +259,10 @@ proc get_fanout { args } { } if { $only_insts } { return [find_fanout_insts $pins $flat $endpoints_only \ - $inst_levels $pin_levels $thru_disabled $thru_constants] + $inst_levels $pin_levels $thru_disabled $thru_constants] } else { return [find_fanout_pins $pins $flat $endpoints_only \ - $inst_levels $pin_levels $thru_disabled $thru_constants] + $inst_levels $pin_levels $thru_disabled $thru_constants] } } @@ -167,15 +283,15 @@ proc get_timing_edges_cmd { cmd cmd_args } { set arcs {} if { [info exists keys(-of_objects)] } { if { [info exists keys(-from)] \ - || [info exists keys(-from)] } { + || [info exists keys(-from)] } { sta_error 540 "-from/-to arguments not supported with -of_objects." } set arcs [get_timing_arcs_objects $keys(-of_objects)] } elseif { [info exists keys(-from)] \ - && [info exists keys(-to)] } { + && [info exists keys(-to)] } { set arcs [get_timing_arcs_from_to \ - [get_port_pin_error "from" $keys(-from)] \ - [get_port_pin_error "to" $keys(-to)]] + [get_port_pin_error "from" $keys(-from)] \ + [get_port_pin_error "to" $keys(-to)]] } elseif { [info exists keys(-from)] } { set arcs [get_timing_arcs_from $keys(-from)] } elseif { [info exists keys(-to)] } { @@ -184,7 +300,7 @@ proc get_timing_edges_cmd { cmd cmd_args } { cmd_usage_error $cmd } if [info exists keys(-filter)] { - set arcs [filter_objs $keys(-filter) $arcs filter_timing_arcs "timing arc"] + set arcs [filter_timing_arcs $keys(-filter) $arcs] } return $arcs } @@ -214,10 +330,10 @@ proc instance_edges { inst } { foreach vertex [$pin vertices] { set edge_iter [$vertex out_edge_iterator] while {[$edge_iter has_next]} { - set edge [$edge_iter next] - if { [$edge role] != "wire" } { - lappend edges $edge - } + set edge [$edge_iter next] + if { [$edge role] != "wire" } { + lappend edges $edge + } } $edge_iter finish } @@ -234,10 +350,10 @@ proc get_timing_arcs_from_to { from_pin_arg to_pin_arg } { foreach to_vertex [$to_pin vertices] { set edge_iter [$from_vertex out_edge_iterator] while {[$edge_iter has_next]} { - set edge [$edge_iter next] - if { [$edge to] == $to_vertex } { - lappend edges $edge - } + set edge [$edge_iter next] + if { [$edge to] == $to_vertex } { + lappend edges $edge + } } $edge_iter finish } @@ -304,7 +420,7 @@ proc report_clock1 { clk } { } else { set wave "" foreach edge $waveform { - set wave "$wave[format "%10s" [format_time $edge $digits]]" + set wave "$wave[format %10s [format_time $edge $digits]]" } } if {[$clk is_generated]} { diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index 904c32fea..73eb925f7 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -23,27 +23,23 @@ // This notice may not be removed or altered from any source distribution. // Swig TCL input/output type parsers. + +#if SWIG_VERSION >= 0x040200 +%include "std_string_view.i" +#endif + %{ -#include "Machine.hh" -#include "StringUtil.hh" -#include "StringSet.hh" -#include "StringSeq.hh" -#include "PatternMatch.hh" -#include "Vector.hh" #include "Network.hh" #include "Liberty.hh" #include "FuncExpr.hh" #include "TimingArc.hh" -#include "TableModel.hh" #include "TimingRole.hh" #include "Graph.hh" -#include "NetworkClass.hh" #include "Clock.hh" -#include "Corner.hh" +#include "Scene.hh" #include "Search.hh" #include "Path.hh" -#include "search/Tag.hh" #include "PathEnd.hh" #include "SearchClass.hh" #include "CircuitSim.hh" @@ -56,7 +52,6 @@ namespace sta { -typedef MinPulseWidthCheckSeq::Iterator MinPulseWidthCheckSeqIterator; typedef MinMaxAll MinMaxAllNull; #if TCL_MAJOR_VERSION < 9 @@ -64,7 +59,7 @@ typedef MinMaxAll MinMaxAllNull; #endif template -Vector * +std::vector * tclListSeqPtr(Tcl_Obj *const source, swig_type_info *swig_type, Tcl_Interp *interp) @@ -74,7 +69,7 @@ tclListSeqPtr(Tcl_Obj *const source, if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK && argc > 0) { - Vector *seq = new Vector; + std::vector *seq = new std::vector; for (int i = 0; i < argc; i++) { void *obj; // Ignore returned TCL_ERROR because can't get swig_type_info. @@ -314,7 +309,7 @@ setPtrTclList(SET_TYPE *set, //////////////////////////////////////////////////////////////// -} // namespace +} // namespace sta using namespace sta; @@ -327,64 +322,33 @@ using namespace sta; //////////////////////////////////////////////////////////////// %include "tcl/Collections.i" -// String that is deleted after crossing over to tcland. -%typemap(out) string { - string &str = $1; +// SWIG before 4.2.0 has no std::string_view typemaps; newer SWIG defines +// them and duplicates here conflict. +#if SWIG_VERSION < 0x040200 +%typemap(out) std::string_view { + std::string str($1); // String is volatile because it is deleted. Tcl_SetResult(interp, const_cast(str.c_str()), TCL_VOLATILE); } -// String that is deleted after crossing over to tcland. -%typemap(out) TmpString* { - string *str = $1; - if (str) { - // String is volatile because it is deleted. - Tcl_SetResult(interp, const_cast(str->c_str()), TCL_VOLATILE); - delete str; - } - else - Tcl_SetResult(interp, nullptr, TCL_STATIC); -} - -%typemap(in) StringSeq* { - $1 = tclListSeqConstChar($input, interp); -} - -%typemap(freearg) StringSeq* { - delete $1; -} - -%typemap(typecheck, precedence=SWIG_TYPECHECK_POINTER) StringSeq* { - $1 = tclListSeqConstCharCheck($input, interp); +%typemap(in) std::string_view { + Tcl_Size length; + const char *str = Tcl_GetStringFromObj($input, &length); + $1 = std::string_view(str, length); } +#endif -%typemap(in) StdStringSet* { - $1 = tclListSetStdString($input, interp); +%typemap(in) StringSeq { + $1 = tclListStringSeq($input, interp); } -%typemap(out) StringSeq* { - StringSeq *strs = $1; - Tcl_Obj *list = Tcl_NewListObj(0, nullptr); - for (const char *str : *strs) { - Tcl_Obj *obj = Tcl_NewStringObj(str, strlen(str)); - Tcl_ListObjAppendElement(interp, list, obj); - } - Tcl_SetObjResult(interp, list); +%typemap(typecheck, precedence=SWIG_TYPECHECK_LIST) StringSeq { + $1 = true; } %typemap(out) StringSeq { StringSeq &strs = $1; Tcl_Obj *list = Tcl_NewListObj(0, nullptr); - for (const char *str : strs) { - Tcl_Obj *obj = Tcl_NewStringObj(str, strlen(str)); - Tcl_ListObjAppendElement(interp, list, obj); - } - Tcl_SetObjResult(interp, list); -} - -%typemap(out) StdStringSeq { - StdStringSeq &strs = $1; - Tcl_Obj *list = Tcl_NewListObj(0, nullptr); for (const std::string &str : strs) { Tcl_Obj *obj = Tcl_NewStringObj(str.c_str(), str.size()); Tcl_ListObjAppendElement(interp, list, obj); @@ -483,11 +447,11 @@ COLLECTION_HELPERS(PortSeq, const Port *, PortSeqIterator); } %typemap(in) Transition* { - int length; + Tcl_Size length; const char *arg = Tcl_GetStringFromObj($input, &length); - Transition *tr = Transition::find(arg); + Transition *tr = Transition::find(std::string_view(arg, length)); if (tr == nullptr) { - tclArgError(interp, 2150, "Unknown transition '%s'.", arg); + tclArgError(interp, 2150, "Unknown transition '{}'.", arg); return TCL_ERROR; } else @@ -496,18 +460,16 @@ COLLECTION_HELPERS(PortSeq, const Port *, PortSeqIterator); %typemap(out) Transition* { Transition *tr = $1; - const char *str = ""; - if (tr) - str = tr->to_string().c_str(); - Tcl_SetResult(interp, const_cast(str), TCL_STATIC); + const std::string &name = tr->to_string(); + Tcl_SetResult(interp, const_cast(name.c_str()), TCL_STATIC); } %typemap(in) RiseFall* { - int length; + Tcl_Size length; const char *arg = Tcl_GetStringFromObj($input, &length); - const RiseFall *rf = RiseFall::find(arg); + const RiseFall *rf = RiseFall::find(std::string_view(arg, length)); if (rf == nullptr) { - tclArgError(interp, 2151, "Unknown rise/fall edge '%s'.", arg); + tclArgError(interp, 2151, "Unknown rise/fall edge '{}'.", arg); return TCL_ERROR; } // Swig is retarded and drops const on args. @@ -516,18 +478,16 @@ COLLECTION_HELPERS(PortSeq, const Port *, PortSeqIterator); %typemap(out) RiseFall* { const RiseFall *rf = $1; - const char *str = ""; - if (rf) - str = rf->to_string().c_str(); - Tcl_SetResult(interp, const_cast(str), TCL_STATIC); + const std::string &name = rf->shortName(); + Tcl_SetResult(interp, const_cast(name.c_str()), TCL_STATIC); } %typemap(in) RiseFallBoth* { - int length; + Tcl_Size length; const char *arg = Tcl_GetStringFromObj($input, &length); - const RiseFallBoth *rf = RiseFallBoth::find(arg); + const RiseFallBoth *rf = RiseFallBoth::find(std::string_view(arg, length)); if (rf == nullptr) { - tclArgError(interp, 2152, "Unknown transition name '%s'.", arg); + tclArgError(interp, 2152, "Unknown transition name '{}'.", arg); return TCL_ERROR; } // Swig is retarded and drops const on args. @@ -535,19 +495,17 @@ COLLECTION_HELPERS(PortSeq, const Port *, PortSeqIterator); } %typemap(out) RiseFallBoth* { - RiseFallBoth *tr = $1; - const char *str = ""; - if (tr) - str = tr->asString(); - Tcl_SetResult(interp, const_cast(str), TCL_STATIC); + RiseFallBoth *rf = $1; + const std::string &name = tr->shortName(); + Tcl_SetResult(interp, const_cast(name.c_str()), TCL_STATIC); } %typemap(in) PortDirection* { - int length; + Tcl_Size length; const char *arg = Tcl_GetStringFromObj($input, &length); PortDirection *dir = PortDirection::find(arg); if (dir == nullptr) { - tclArgError(interp, 2153, "Unknown port direction '%s'.", arg); + tclArgError(interp, 2153, "Unknown port direction '{}'.", arg); return TCL_ERROR; } else @@ -555,14 +513,14 @@ COLLECTION_HELPERS(PortSeq, const Port *, PortSeqIterator); } %typemap(in) TimingRole* { - int length; + Tcl_Size length; const char *arg = Tcl_GetStringFromObj($input, &length); const TimingRole *role = TimingRole::find(arg); if (role) // Swig is retarded and drops const on args. $1 = const_cast(TimingRole::find(arg)); else { - tclArgError(interp, 2154, "Unknown timing role '%s'.", arg); + tclArgError(interp, 2154, "Unknown timing role '{}'.", arg); return TCL_ERROR; } } @@ -572,35 +530,35 @@ COLLECTION_HELPERS(PortSeq, const Port *, PortSeqIterator); } %typemap(in) LogicValue { - int length; - const char *arg = Tcl_GetStringFromObj($input, &length); - if (stringEq(arg, "0") || stringEq(arg, "zero")) + Tcl_Size length; + std::string arg = Tcl_GetStringFromObj($input, &length); + if (arg == "0" || stringEqual(arg, "zero")) $1 = LogicValue::zero; - else if (stringEq(arg, "1") || stringEq(arg, "one")) + else if (arg == "1" || stringEqual(arg, "one")) $1 = LogicValue::one; - else if (stringEq(arg, "X")) + else if (stringEqual(arg, "X")) $1 = LogicValue::unknown; - else if (stringEq(arg, "rise") || stringEq(arg, "rising")) + else if (stringEqual(arg, "rise") || stringEqual(arg, "rising")) $1 = LogicValue::rise; - else if (stringEq(arg, "fall") || stringEq(arg, "falling")) + else if (stringEqual(arg, "fall") || stringEqual(arg, "falling")) $1 = LogicValue::fall; else { - tclArgError(interp, 2155, "Unknown logic value '%s'.", arg); + tclArgError(interp, 2155, "Unknown logic value '{}'.", arg.c_str()); return TCL_ERROR; } } %typemap(in) AnalysisType { - int length; + Tcl_Size length; const char *arg = Tcl_GetStringFromObj($input, &length); if (stringEqual(arg, "single")) $1 = AnalysisType::single; else if (stringEqual(arg, "bc_wc")) $1 = AnalysisType::bc_wc; - else if (stringEq(arg, "on_chip_variation")) + else if (stringEqual(arg, "on_chip_variation")) $1 = AnalysisType::ocv; else { - tclArgError(interp, 2156, "Unknown analysis type '%s'.", arg); + tclArgError(interp, 2156, "Unknown analysis type '{}'.", arg.c_str()); return TCL_ERROR; } } @@ -777,7 +735,7 @@ COLLECTION_HELPERS(ClockSeq, Clock *, ClockSeqIterator); floats->push_back(static_cast(value)); else { delete floats; - tclArgError(interp, 2157, "%s is not a floating point number.", arg); + tclArgError(interp, 2157, "{} is not a floating point number.", arg); return TCL_ERROR; } } @@ -797,8 +755,28 @@ COLLECTION_HELPERS(ClockSeq, Clock *, ClockSeqIterator); Tcl_SetObjResult(interp, list); } +%typemap(in) FloatSeq { + Tcl_Size argc; + Tcl_Obj **argv; + FloatSeq floats; + + if (Tcl_ListObjGetElements(interp, $input, &argc, &argv) == TCL_OK) { + for (int i = 0; i < argc; i++) { + char *arg = Tcl_GetString(argv[i]); + double value; + if (Tcl_GetDouble(interp, arg, &value) == TCL_OK) + floats.push_back(static_cast(value)); + else { + tclArgError(interp, 2175, "{} is not a floating point number.", arg); + return TCL_ERROR; + } + } + } + $1 = floats; +} + %typemap(out) FloatSeq { - FloatSeq &floats = $1; + const FloatSeq &floats = $1; Tcl_Obj *list = Tcl_NewListObj(0, nullptr); for (float f : floats) { Tcl_Obj *obj = Tcl_NewDoubleObj(f); @@ -807,22 +785,19 @@ COLLECTION_HELPERS(ClockSeq, Clock *, ClockSeqIterator); Tcl_SetObjResult(interp, list); } -%typemap(in) IntSeq* { +%typemap(in) IntSeq { Tcl_Size argc; Tcl_Obj **argv; - IntSeq *ints = nullptr; + IntSeq ints; if (Tcl_ListObjGetElements(interp, $input, &argc, &argv) == TCL_OK) { - if (argc) - ints = new IntSeq; for (int i = 0; i < argc; i++) { char *arg = Tcl_GetString(argv[i]); int value; if (Tcl_GetInt(interp, arg, &value) == TCL_OK) - ints->push_back(value); + ints.push_back(value); else { - delete ints; - tclArgError(interp, 2158, "%s is not an integer.", arg); + tclArgError(interp, 2158, "{} is not an integer.", arg); return TCL_ERROR; } } @@ -830,19 +805,21 @@ COLLECTION_HELPERS(ClockSeq, Clock *, ClockSeqIterator); $1 = ints; } -%typemap(out) Table1 { - Table1 &table = $1; +%typemap(out) Table { + Table &table = $1; if (table.axis1()) { Tcl_Obj *list3 = Tcl_NewListObj(0, nullptr); Tcl_Obj *list1 = Tcl_NewListObj(0, nullptr); - for (float f : *table.axis1()->values()) { + for (float f : table.axis1()->values()) { Tcl_Obj *obj = Tcl_NewDoubleObj(f); Tcl_ListObjAppendElement(interp, list1, obj); } Tcl_Obj *list2 = Tcl_NewListObj(0, nullptr); - for (float f : *table.values()) { - Tcl_Obj *obj = Tcl_NewDoubleObj(f); - Tcl_ListObjAppendElement(interp, list2, obj); + if (table.values()) { + for (float f : *table.values()) { + Tcl_Obj *obj = Tcl_NewDoubleObj(f); + Tcl_ListObjAppendElement(interp, list2, obj); + } } Tcl_ListObjAppendElement(interp, list3, list1); Tcl_ListObjAppendElement(interp, list3, list2); @@ -850,19 +827,23 @@ COLLECTION_HELPERS(ClockSeq, Clock *, ClockSeqIterator); } } -%typemap(out) const Table1* { - const Table1 *table = $1; +%typemap(out) const Table* { + const Table *table = $1; Tcl_Obj *list3 = Tcl_NewListObj(0, nullptr); if (table) { Tcl_Obj *list1 = Tcl_NewListObj(0, nullptr); - for (float f : *table->axis1()->values()) { - Tcl_Obj *obj = Tcl_NewDoubleObj(f); - Tcl_ListObjAppendElement(interp, list1, obj); + if (table->axis1()) { + for (float f : table->axis1()->values()) { + Tcl_Obj *obj = Tcl_NewDoubleObj(f); + Tcl_ListObjAppendElement(interp, list1, obj); + } } Tcl_Obj *list2 = Tcl_NewListObj(0, nullptr); - for (float f : *table->values()) { - Tcl_Obj *obj = Tcl_NewDoubleObj(f); - Tcl_ListObjAppendElement(interp, list2, obj); + if (table->values()) { + for (float f : *table->values()) { + Tcl_Obj *obj = Tcl_NewDoubleObj(f); + Tcl_ListObjAppendElement(interp, list2, obj); + } } Tcl_ListObjAppendElement(interp, list3, list1); Tcl_ListObjAppendElement(interp, list3, list2); @@ -871,14 +852,14 @@ COLLECTION_HELPERS(ClockSeq, Clock *, ClockSeqIterator); } %typemap(in) MinMax* { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); // Swig is retarded and drops const on args. MinMax *min_max = const_cast(MinMax::find(arg)); if (min_max) $1 = min_max; else { - tclArgError(interp, 2159, "%s not min or max.", arg); + tclArgError(interp, 2159, "{} not min or max.", arg); return TCL_ERROR; } } @@ -892,20 +873,20 @@ COLLECTION_HELPERS(ClockSeq, Clock *, ClockSeqIterator); } %typemap(in) MinMaxAll* { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); // Swig is retarded and drops const on args. MinMaxAll *min_max = const_cast(MinMaxAll::find(arg)); if (min_max) $1 = min_max; else { - tclArgError(interp, 2160, "%s not min, max or min_max.", arg); + tclArgError(interp, 2160, "{} not min, max or min_max.", arg); return TCL_ERROR; } } %typemap(in) MinMaxAllNull* { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); if (stringEqual(arg, "NULL")) $1 = nullptr; @@ -915,7 +896,7 @@ COLLECTION_HELPERS(ClockSeq, Clock *, ClockSeqIterator); if (min_max) $1 = min_max; else { - tclArgError(interp, 2161, "%s not min, max or min_max.", arg); + tclArgError(interp, 2161, "{} not min, max or min_max.", arg); return TCL_ERROR; } } @@ -927,144 +908,145 @@ COLLECTION_HELPERS(ClockSeq, Clock *, ClockSeqIterator); // SetupHold is typedef'd to MinMax. %typemap(in) const SetupHold* { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); // Swig is retarded and drops const on args. if (stringEqual(arg, "hold") || stringEqual(arg, "min")) $1 = const_cast(MinMax::min()); else if (stringEqual(arg, "setup") - || stringEqual(arg, "max")) + || stringEqual(arg, "max")) $1 = const_cast(MinMax::max()); else { - tclArgError(interp, 2162, "%s not setup, hold, min or max.", arg); + tclArgError(interp, 2162, "{} not setup, hold, min or max.", arg); return TCL_ERROR; } } // SetupHoldAll is typedef'd to MinMaxAll. %typemap(in) const SetupHoldAll* { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); // Swig is retarded and drops const on args. if (stringEqual(arg, "hold") || stringEqual(arg, "min")) $1 = const_cast(SetupHoldAll::min()); else if (stringEqual(arg, "setup") - || stringEqual(arg, "max")) + || stringEqual(arg, "max")) $1 = const_cast(SetupHoldAll::max()); else if (stringEqual(arg, "setup_hold") - || stringEqual(arg, "min_max")) + || stringEqual(arg, "min_max")) $1 = const_cast(SetupHoldAll::all()); else { - tclArgError(interp, 2163, "%s not setup, hold, setup_hold, min, max or min_max.", arg); + tclArgError(interp, 2163, "{} not setup, hold, setup_hold, min, max or min_max.", + arg); return TCL_ERROR; } } // EarlyLate is typedef'd to MinMax. %typemap(in) const EarlyLate* { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); // Swig is retarded and drops const on args. EarlyLate *early_late = const_cast(EarlyLate::find(arg)); if (early_late) $1 = early_late; else { - tclArgError(interp, 2164, "%s not early/min, late/max or early_late/min_max.", arg); + tclArgError(interp, 2164, "{} not early/min, late/max or early_late/min_max.", arg); return TCL_ERROR; } } // EarlyLateAll is typedef'd to MinMaxAll. %typemap(in) const EarlyLateAll* { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); // Swig is retarded and drops const on args. EarlyLateAll *early_late = const_cast(EarlyLateAll::find(arg)); if (early_late) $1 = early_late; else { - tclArgError(interp, 2165, "%s not early/min, late/max or early_late/min_max.", arg); + tclArgError(interp, 2165, "{} not early/min, late/max or early_late/min_max.", arg); return TCL_ERROR; } } %typemap(in) TimingDerateType { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); - if (stringEq(arg, "net_delay")) + if (stringEqual(arg, "net_delay")) $1 = TimingDerateType::net_delay; - else if (stringEq(arg, "cell_delay")) + else if (stringEqual(arg, "cell_delay")) $1 = TimingDerateType::cell_delay; - else if (stringEq(arg, "cell_check")) + else if (stringEqual(arg, "cell_check")) $1 = TimingDerateType::cell_check; else { - tclArgError(interp, 2166, "%s not net_delay, cell_delay or cell_check.", arg); + tclArgError(interp, 2166, "{} not net_delay, cell_delay or cell_check.", arg); return TCL_ERROR; } } %typemap(in) TimingDerateCellType { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); - if (stringEq(arg, "cell_delay")) + if (stringEqual(arg, "cell_delay")) $1 = TimingDerateCellType::cell_delay; - else if (stringEq(arg, "cell_check")) + else if (stringEqual(arg, "cell_check")) $1 = TimingDerateCellType::cell_check; else { - tclArgError(interp, 2167, "%s not cell_delay or cell_check.", arg); + tclArgError(interp, 2167, "{} not cell_delay or cell_check.", arg); return TCL_ERROR; } } %typemap(in) PathClkOrData { - int length; - char *arg = Tcl_GetStringFromObj($input, &length); - if (stringEq(arg, "clk")) + Tcl_Size length; + std::string arg = Tcl_GetStringFromObj($input, &length); + if (stringEqual(arg, "clk")) $1 = PathClkOrData::clk; - else if (stringEq(arg, "data")) + else if (stringEqual(arg, "data")) $1 = PathClkOrData::data; else { - tclArgError(interp, 2168, "%s not clk or data.", arg); + tclArgError(interp, 2168, "{} not clk or data.", arg.c_str()); return TCL_ERROR; } } %typemap(in) ReportSortBy { - int length; - char *arg = Tcl_GetStringFromObj($input, &length); - if (stringEq(arg, "group")) + Tcl_Size length; + std::string arg = Tcl_GetStringFromObj($input, &length); + if (stringEqual(arg, "group")) $1 = sort_by_group; - else if (stringEq(arg, "slack")) + else if (stringEqual(arg, "slack")) $1 = sort_by_slack; else { - tclArgError(interp, 2169, "%s not group or slack.", arg); + tclArgError(interp, 2169, "{} not group or slack.", arg.c_str()); return TCL_ERROR; } } %typemap(in) ReportPathFormat { - int length; - char *arg = Tcl_GetStringFromObj($input, &length); - if (stringEq(arg, "full")) + Tcl_Size length; + std::string arg = Tcl_GetStringFromObj($input, &length); + if (stringEqual(arg, "full")) $1 = ReportPathFormat::full; - else if (stringEq(arg, "full_clock")) + else if (stringEqual(arg, "full_clock")) $1 = ReportPathFormat::full_clock; - else if (stringEq(arg, "full_clock_expanded")) + else if (stringEqual(arg, "full_clock_expanded")) $1 = ReportPathFormat::full_clock_expanded; - else if (stringEq(arg, "short")) + else if (stringEqual(arg, "short")) $1 = ReportPathFormat::shorter; - else if (stringEq(arg, "end")) + else if (stringEqual(arg, "end")) $1 = ReportPathFormat::endpoint; - else if (stringEq(arg, "summary")) + else if (stringEqual(arg, "summary")) $1 = ReportPathFormat::summary; - else if (stringEq(arg, "slack_only")) + else if (stringEqual(arg, "slack_only")) $1 = ReportPathFormat::slack_only; - else if (stringEq(arg, "json")) + else if (stringEqual(arg, "json")) $1 = ReportPathFormat::json; else { - tclArgError(interp, 2170, "unknown path type %s.", arg); + tclArgError(interp, 2170, "unknown path type {}.", arg.c_str()); return TCL_ERROR; } } @@ -1121,16 +1103,11 @@ COLLECTION_HELPERS(ClockSeq, Clock *, ClockSeqIterator); %typemap(out) CheckErrorSeq & { Tcl_Obj *error_list = Tcl_NewListObj(0, nullptr); CheckErrorSeq *check_errors = $1; - CheckErrorSeq::Iterator check_iter(check_errors); - while (check_iter.hasNext()) { - CheckError *error = check_iter.next(); + for (CheckError *error : *check_errors) { Tcl_Obj *string_list = Tcl_NewListObj(0, nullptr); - CheckError::Iterator string_iter(error); - while (string_iter.hasNext()) { - const char *str = string_iter.next(); - size_t str_len = strlen(str); - Tcl_Obj *obj = Tcl_NewStringObj(const_cast(str), - static_cast(str_len)); + for (const std::string &str : *error) { + Tcl_Obj *obj = Tcl_NewStringObj(str.c_str(), + static_cast(str.size())); Tcl_ListObjAppendElement(interp, string_list, obj); } Tcl_ListObjAppendElement(interp, error_list, string_list); @@ -1165,11 +1142,6 @@ COLLECTION_HELPERS(ClockSeq, Clock *, ClockSeqIterator); seqTclList($1, SWIGTYPE_p_PathEnd, interp); } -%typemap(out) MinPulseWidthCheckSeqIterator* { - Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); - Tcl_SetObjResult(interp, obj); -} - %typemap(out) PathSeq* { Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); Tcl_SetObjResult(interp, obj); @@ -1186,21 +1158,6 @@ COLLECTION_HELPERS(ClockSeq, Clock *, ClockSeqIterator); Tcl_SetObjResult(interp, list); } -%typemap(out) MinPulseWidthCheck* { - Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); - Tcl_SetObjResult(interp, obj); -} - -%typemap(out) MinPulseWidthCheckSeq & { - Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); - Tcl_SetObjResult(interp, obj); -} - -%typemap(out) MinPulseWidthCheckSeqIterator & { - Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); - Tcl_SetObjResult(interp, obj); -} - %typemap(out) VertexPathIterator* { Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); Tcl_SetObjResult(interp, obj); @@ -1231,59 +1188,110 @@ COLLECTION_HELPERS(ClockSeq, Clock *, ClockSeqIterator); Tcl_SetObjResult(interp, obj); } -%typemap(out) Delay { - Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); -} - -%typemap(out) Arrival { - Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); -} - -%typemap(out) Required { - Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); +%typemap(out) Mode* { + const Mode *mode = $1; + if (mode) + Tcl_SetResult(interp, const_cast($1->name().c_str()), TCL_VOLATILE); + else + Tcl_SetResult(interp, const_cast("NULL"), TCL_STATIC); } -%typemap(out) Slack { - Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); -} +%typemap(in) ModeSeq { + Tcl_Size argc; + Tcl_Obj **argv; -%typemap(out) ArcDelay { - Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); + Sta *sta = Sta::sta(); + std::vector seq; + if (Tcl_ListObjGetElements(interp, $input, &argc, &argv) == TCL_OK + && argc > 0) { + for (int i = 0; i < argc; i++) { + Tcl_Size length; + const char *mode_name = Tcl_GetStringFromObj(argv[i], &length); + Mode *mode = sta->findMode(mode_name); + if (mode) + seq.push_back(mode); + else { + tclArgError(interp, 2174, "mode {} not found.", mode_name); + return TCL_ERROR; + } + } + } + $1 = seq; } -%typemap(out) Slew { - Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); +%typemap(out) ModeSeq { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + ModeSeq &modes = $1; + for (Mode *mode : modes) { + const std::string &mode_name = mode->name(); + Tcl_Obj *obj = Tcl_NewStringObj(mode_name.c_str(), mode_name.size()); + Tcl_ListObjAppendElement(interp, list, obj); + } + Tcl_SetObjResult(interp, list); } -%typemap(out) Crpr { - Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); +%typemap(in) Scene* { + sta::Sta *sta = Sta::sta(); + Tcl_Size length; + std::string scene_name = Tcl_GetStringFromObj($input, &length); + // parse_scene_or_all support depreated 11/21/2025 + if (scene_name == "NULL") + $1 = nullptr; + else { + Scene *scene = sta->findScene(scene_name); + if (scene) + $1 = scene; + else { + tclArgError(interp, 2173, "scene {} not found.", scene_name.c_str()); + return TCL_ERROR; + } + } } -%typemap(in) PathGroupNameSet* { - $1 = tclListSetConstChar($input, interp); +%typemap(out) Scene* { + const Scene *scene = $1; + if (scene) + Tcl_SetResult(interp, const_cast($1->name().c_str()), TCL_VOLATILE); + else + Tcl_SetResult(interp, const_cast("NULL"), TCL_STATIC); } -%typemap(in) StringSet* { - $1 = tclListSetConstChar($input, interp); -} +%typemap(in) SceneSeq { + Tcl_Size argc; + Tcl_Obj **argv; -%typemap(out) Corner* { - Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); - Tcl_SetObjResult(interp, obj); + Sta *sta = Sta::sta(); + std::vector seq; + if (Tcl_ListObjGetElements(interp, $input, &argc, &argv) == TCL_OK + && argc > 0) { + for (int i = 0; i < argc; i++) { + Tcl_Size length; + const char *scene_name = Tcl_GetStringFromObj(argv[i], &length); + Scene *scene = sta->findScene(scene_name); + if (scene) + seq.push_back(scene); + else { + tclArgError(interp, 2172, "scene {} not found.", scene_name); + return TCL_ERROR; + } + } + } + $1 = seq; } -%typemap(out) Corners* { +%typemap(out) SceneSeq { Tcl_Obj *list = Tcl_NewListObj(0, nullptr); - Corners *corners = $1; - for (Corner *corner : *corners) { - Tcl_Obj *obj = SWIG_NewInstanceObj(corner, SWIGTYPE_p_Corner, false); + SceneSeq &scenes = $1; + for (Scene *scene : scenes) { + const std::string &scene_name = scene->name(); + Tcl_Obj *obj = Tcl_NewStringObj(scene_name.c_str(), scene_name.size()); Tcl_ListObjAppendElement(interp, list, obj); } Tcl_SetObjResult(interp, list); } %typemap(in) PropertyValue { - int length; + Tcl_Size length; const char *arg = Tcl_GetStringFromObj($input, &length); $1 = PropertyValue(arg); } @@ -1291,108 +1299,103 @@ COLLECTION_HELPERS(ClockSeq, Clock *, ClockSeqIterator); %typemap(out) PropertyValue { PropertyValue value = $1; switch (value.type()) { - case PropertyValue::Type::type_none: + case PropertyValue::Type::none: Tcl_SetResult(interp, const_cast(""), TCL_STATIC); break; - case PropertyValue::Type::type_string: - Tcl_SetResult(interp, const_cast(value.stringValue()), TCL_VOLATILE); + case PropertyValue::Type::string: + Tcl_SetResult(interp, const_cast(value.stringValue().c_str()), TCL_VOLATILE); break; - case PropertyValue::Type::type_float: { + case PropertyValue::Type::float_: { const Unit *unit = value.unit(); - const char *float_string = unit->asString(value.floatValue(), 6); - Tcl_SetResult(interp, const_cast(float_string), TCL_VOLATILE); + std::string float_string = unit->asString(value.floatValue(), 6); + Tcl_SetResult(interp, const_cast(float_string.c_str()), TCL_VOLATILE); } break; - case PropertyValue::Type::type_bool: { + case PropertyValue::Type::bool_: { const char *bool_string = value.boolValue() ? "1" : "0"; Tcl_SetResult(interp, const_cast(bool_string), TCL_STATIC); } break; - case PropertyValue::Type::type_library: { + case PropertyValue::Type::library: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.library()), - SWIGTYPE_p_Library, false); + SWIGTYPE_p_Library, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_cell: { + case PropertyValue::Type::cell: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.cell()), - SWIGTYPE_p_Cell, false); + SWIGTYPE_p_Cell, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_port: { + case PropertyValue::Type::port: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.port()), - SWIGTYPE_p_Port, false); + SWIGTYPE_p_Port, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_liberty_library: { + case PropertyValue::Type::liberty_library: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.libertyLibrary()), - SWIGTYPE_p_LibertyLibrary, false); + SWIGTYPE_p_LibertyLibrary, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_liberty_cell: { + case PropertyValue::Type::liberty_cell: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.libertyCell()), - SWIGTYPE_p_LibertyCell, false); + SWIGTYPE_p_LibertyCell, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_liberty_port: { + case PropertyValue::Type::liberty_port: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.libertyPort()), - SWIGTYPE_p_LibertyPort, false); + SWIGTYPE_p_LibertyPort, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_instance: { + case PropertyValue::Type::instance: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.instance()), - SWIGTYPE_p_Instance, false); + SWIGTYPE_p_Instance, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_pin: { + case PropertyValue::Type::pin: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.pin()), SWIGTYPE_p_Pin, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_pins: { + case PropertyValue::Type::pins: { Tcl_Obj *list = Tcl_NewListObj(0, nullptr); - PinSeq *pins = value.pins(); - PinSeq::Iterator pin_iter(pins); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); + for (const Pin *pin : *value.pins()) { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(pin), SWIGTYPE_p_Pin, false); Tcl_ListObjAppendElement(interp, list, obj); } Tcl_SetObjResult(interp, list); } break; - case PropertyValue::Type::type_net: { + case PropertyValue::Type::net: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.net()), - SWIGTYPE_p_Net, false); + SWIGTYPE_p_Net, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_clk: { + case PropertyValue::Type::clk: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.clock()), - SWIGTYPE_p_Clock, false); + SWIGTYPE_p_Clock, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_clks: { + case PropertyValue::Type::clks: { Tcl_Obj *list = Tcl_NewListObj(0, nullptr); ClockSeq *clks = value.clocks(); - ClockSeq::Iterator clk_iter(clks); - while (clk_iter.hasNext()) { - Clock *clk = clk_iter.next(); + for (Clock *clk : *clks) { Tcl_Obj *obj = SWIG_NewInstanceObj(clk, SWIGTYPE_p_Clock, false); Tcl_ListObjAppendElement(interp, list, obj); } Tcl_SetObjResult(interp, list); } break; - case PropertyValue::Type::type_paths: { + case PropertyValue::Type::paths: { Tcl_Obj *list = Tcl_NewListObj(0, nullptr); for (const Path *path : *value.paths()) { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(path), SWIGTYPE_p_Path, false); @@ -1401,22 +1404,21 @@ COLLECTION_HELPERS(ClockSeq, Clock *, ClockSeqIterator); Tcl_SetObjResult(interp, list); } break; - case PropertyValue::Type::type_pwr_activity: { + case PropertyValue::Type::pwr_activity: { PwrActivity activity = value.pwrActivity(); Tcl_Obj *list = Tcl_NewListObj(0, nullptr); Tcl_Obj *obj; - const char *str; - str = stringPrintTmp("%.5e", activity.density()); - obj = Tcl_NewStringObj(str, strlen(str)); + std::string density = sta::format("{:.5e}", activity.density()); + obj = Tcl_NewStringObj(density.c_str(), density.size()); Tcl_ListObjAppendElement(interp, list, obj); - str = stringPrintTmp("%.3f", activity.duty()); - obj = Tcl_NewStringObj(str, strlen(str)); + std::string duty = sta::format("{:.3f}", activity.duty()); + obj = Tcl_NewStringObj(duty.c_str(), duty.size()); Tcl_ListObjAppendElement(interp, list, obj); - str = activity.originName(); - obj = Tcl_NewStringObj(str, strlen(str)); + std::string name = activity.originName(); + obj = Tcl_NewStringObj(name.c_str(), name.size()); Tcl_ListObjAppendElement(interp, list, obj); Tcl_SetObjResult(interp, list); @@ -1426,16 +1428,16 @@ COLLECTION_HELPERS(ClockSeq, Clock *, ClockSeqIterator); } %typemap(in) CircuitSim { - int length; - char *arg = Tcl_GetStringFromObj($input, &length); - if (stringEq(arg, "hspice")) + Tcl_Size length; + std::string arg = Tcl_GetStringFromObj($input, &length); + if (stringEqual(arg, "hspice")) $1 = CircuitSim::hspice; - else if (stringEq(arg, "ngspice")) + else if (stringEqual(arg, "ngspice")) $1 = CircuitSim::ngspice; - else if (stringEq(arg, "xyce")) + else if (stringEqual(arg, "xyce")) $1 = CircuitSim::xyce; else { - tclArgError(interp, 2171, "unknown circuit simulator %s.", arg); + tclArgError(interp, 2171, "unknown circuit simulator {}.", arg.c_str()); return TCL_ERROR; } } diff --git a/tcl/TclTypeHelpers.cc b/tcl/TclTypeHelpers.cc index a32aced41..38324650c 100644 --- a/tcl/TclTypeHelpers.cc +++ b/tcl/TclTypeHelpers.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -30,43 +30,27 @@ namespace sta { -StringSet * -tclListSetConstChar(Tcl_Obj *const source, - Tcl_Interp *interp) +StringSeq +tclListStringSeq(Tcl_Obj *const source, + Tcl_Interp *interp) { Tcl_Size argc; Tcl_Obj **argv; + StringSeq seq; if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) { - StringSet *set = new StringSet; for (int i = 0; i < argc; i++) { - int length; - const char *str = Tcl_GetStringFromObj(argv[i], &length); - set->insert(str); + Tcl_Size length; + const char *arg = Tcl_GetStringFromObj(argv[i], &length); + seq.emplace_back(arg); } - return set; } - else - return nullptr; -} - -int -tclListSeqConstCharCheck(Tcl_Obj *const source, - Tcl_Interp *interp) -{ - Tcl_Size argc; - Tcl_Obj **argv; - - if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) { - return 1; - } - else - return 0; + return seq; } StringSeq * -tclListSeqConstChar(Tcl_Obj *const source, - Tcl_Interp *interp) +tclListStringSeqPtr(Tcl_Obj *const source, + Tcl_Interp *interp) { Tcl_Size argc; Tcl_Obj **argv; @@ -74,9 +58,9 @@ tclListSeqConstChar(Tcl_Obj *const source, if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) { StringSeq *seq = new StringSeq; for (int i = 0; i < argc; i++) { - int length; - const char *str = Tcl_GetStringFromObj(argv[i], &length); - seq->push_back(str); + Tcl_Size length; + const char *arg = Tcl_GetStringFromObj(argv[i], &length); + seq->emplace_back(arg); } return seq; } @@ -84,27 +68,6 @@ tclListSeqConstChar(Tcl_Obj *const source, return nullptr; } -StdStringSet * -tclListSetStdString(Tcl_Obj *const source, - Tcl_Interp *interp) -{ - Tcl_Size argc; - Tcl_Obj **argv; - - if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) { - StdStringSet *set = new StdStringSet; - for (int i = 0; i < argc; i++) { - int length; - const char *str = Tcl_GetStringFromObj(argv[i], &length); - set->insert(str); - } - return set; - } - else - return nullptr; -} - - void tclArgError(Tcl_Interp *interp, int id, @@ -119,42 +82,6 @@ tclArgError(Tcl_Interp *interp, } } -void -objectListNext(const char *list, - const char *type, - // Return values. - bool &type_match, - const char *&next) -{ - // Default return values (failure). - type_match = false; - next = nullptr; - // _hexaddress_p_type - const char *s = list; - char ch = *s++; - if (ch == '_') { - while (*s && isxdigit(*s)) - s++; - if ((s - list - 1) == sizeof(void*) * 2 - && *s && *s++ == '_' - && *s && *s++ == 'p' - && *s && *s++ == '_') { - const char *t = type; - while (*s && *s != ' ') { - if (*s != *t) - return; - s++; - t++; - } - type_match = true; - if (*s) - next = s + 1; - else - next = nullptr; - } - } -} - Tcl_Obj * tclArcDcalcArg(ArcDcalcArg &gate, Tcl_Interp *interp) @@ -167,28 +94,28 @@ tclArcDcalcArg(ArcDcalcArg &gate, Tcl_Obj *list = Tcl_NewListObj(0, nullptr); Tcl_Obj *obj; - const char *inst_name = network->pathName(drvr); - obj = Tcl_NewStringObj(inst_name, strlen(inst_name)); + std::string inst_name = network->pathName(drvr); + obj = Tcl_NewStringObj(inst_name.data(), inst_name.size()); Tcl_ListObjAppendElement(interp, list, obj); - const char *from_name = arc->from()->name(); - obj = Tcl_NewStringObj(from_name, strlen(from_name)); + const std::string &from_name = arc->from()->name(); + obj = Tcl_NewStringObj(from_name.c_str(), from_name.size()); Tcl_ListObjAppendElement(interp, list, obj); - const char *from_edge = arc->fromEdge()->to_string().c_str(); - obj = Tcl_NewStringObj(from_edge, strlen(from_edge)); + const std::string from_edge(arc->fromEdge()->to_string()); + obj = Tcl_NewStringObj(from_edge.c_str(), from_edge.size()); Tcl_ListObjAppendElement(interp, list, obj); - const char *to_name = arc->to()->name(); - obj = Tcl_NewStringObj(to_name, strlen(to_name)); + const std::string to_name = arc->to()->name(); + obj = Tcl_NewStringObj(to_name.c_str(), to_name.size()); Tcl_ListObjAppendElement(interp, list, obj); - const char *to_edge = arc->toEdge()->to_string().c_str(); - obj = Tcl_NewStringObj(to_edge, strlen(to_edge)); + const std::string to_edge(arc->toEdge()->to_string()); + obj = Tcl_NewStringObj(to_edge.c_str(), to_edge.size()); Tcl_ListObjAppendElement(interp, list, obj); - const char *input_delay = delayAsString(gate.inputDelay(), sta, 3); - obj = Tcl_NewStringObj(input_delay, strlen(input_delay)); + std::string input_delay = delayAsString(gate.inputDelay(), sta); + obj = Tcl_NewStringObj(input_delay.c_str(), input_delay.size()); Tcl_ListObjAppendElement(interp, list, obj); return list; @@ -200,11 +127,11 @@ arcDcalcArgTcl(Tcl_Obj *obj, { Sta *sta = Sta::sta(); sta->ensureGraph(); - int list_argc; + Tcl_Size list_argc; Tcl_Obj **list_argv; if (Tcl_ListObjGetElements(interp, obj, &list_argc, &list_argv) == TCL_OK) { const char *input_delay = "0.0"; - int length; + Tcl_Size length; if (list_argc == 6) input_delay = Tcl_GetStringFromObj(list_argv[5], &length); if (list_argc == 5 || list_argc == 6) { @@ -221,4 +148,4 @@ arcDcalcArgTcl(Tcl_Obj *obj, return ArcDcalcArg(); } -} // namespace +} // namespace sta diff --git a/tcl/Util.tcl b/tcl/Util.tcl index ad47cdce2..15443529b 100644 --- a/tcl/Util.tcl +++ b/tcl/Util.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -37,7 +37,7 @@ namespace eval sta { # $flag_var(flag) -> 1 if the flag is present # Keys and flags are removed from arg_var in the caller. proc parse_key_args { cmd arg_var key_var keys {flag_var ""} {flags {}} \ - {unknown_key_is_error 1} } { + {unknown_key_is_error 1} } { upvar 1 $arg_var args upvar 1 $key_var key_value upvar 1 $flag_var flag_present @@ -47,41 +47,41 @@ proc parse_key_args { cmd arg_var key_var keys {flag_var ""} {flags {}} \ if { [is_keyword_arg $arg] } { set key_index [lsearch -exact $keys $arg] if { $key_index >= 0 } { - set key $arg - if { [llength $args] == 1 } { - sta_error 560 "$cmd $key missing value." - } - set key_value($key) [lindex $args 1] - set args [lrange $args 1 end] + set key $arg + if { [llength $args] == 1 } { + sta_error 560 "$cmd $key missing value." + } + set key_value($key) [lindex $args 1] + set args [lrange $args 1 end] } else { - set flag_index [lsearch -exact $flags $arg] - if { $flag_index >= 0 } { - set flag_present($arg) 1 - } else { - # No exact keyword/flag match found. - # Try finding a keyword/flag that begins with - # the same substring. - set wild_arg "${arg}*" - set key_index [lsearch -glob $keys $wild_arg] - if { $key_index >= 0 } { - set key [lindex $keys $key_index] - if { [llength $args] == 1 } { - sta_error 561 "$cmd $key missing value." - } - set key_value($key) [lindex $args 1] - set args [lrange $args 1 end] - } else { - set flag_index [lsearch -glob $flags $wild_arg] - if { $flag_index >= 0 } { - set flag [lindex $flags $flag_index] - set flag_present($flag) 1 - } elseif { $unknown_key_is_error } { - sta_error 562 "$cmd $arg is not a known keyword or flag." - } else { - lappend args_rtn $arg - } - } - } + set flag_index [lsearch -exact $flags $arg] + if { $flag_index >= 0 } { + set flag_present($arg) 1 + } else { + # No exact keyword/flag match found. + # Try finding a keyword/flag that begins with + # the same substring. + set wild_arg "${arg}*" + set key_index [lsearch -glob $keys $wild_arg] + if { $key_index >= 0 } { + set key [lindex $keys $key_index] + if { [llength $args] == 1 } { + sta_error 561 "$cmd $key missing value." + } + set key_value($key) [lindex $args 1] + set args [lrange $args 1 end] + } else { + set flag_index [lsearch -glob $flags $wild_arg] + if { $flag_index >= 0 } { + set flag [lindex $flags $flag_index] + set flag_present($flag) 1 + } elseif { $unknown_key_is_error } { + sta_error 562 "$cmd $arg is not a known keyword or flag." + } else { + lappend args_rtn $arg + } + } + } } } else { lappend args_rtn $arg @@ -109,8 +109,8 @@ proc check_for_key_args { cmd arg_var } { proc is_keyword_arg { arg } { if { [string length $arg] >= 2 \ - && [string index $arg 0] == "-" \ - && [string is alpha [string index $arg 1]] } { + && [string index $arg 0] == "-" \ + && [string is alpha [string index $arg 1]] } { return 1 } else { return 0 @@ -124,11 +124,11 @@ proc is_keyword_arg { arg } { # The value of the last expression in the body is returned. proc proc_redirect { proc_name body } { set proc_body [concat "proc $proc_name { args } {" \ - "global errorCode errorInfo;" \ - "set redirect \[parse_redirect_args args\];" \ - "set code \[catch {" $body "} ret \];" \ - "if {\$redirect} { redirect_file_end };" \ - "if {\$code == 1} {return -code \$code -errorcode \$errorCode -errorinfo \$errorInfo \$ret} else {return \$ret} }" ] + "global errorCode errorInfo;" \ + "set redirect \[parse_redirect_args args\];" \ + "set code \[catch {" $body "} ret \];" \ + "if {\$redirect} { redirect_file_end };" \ + "if {\$code == 1} {return -code \$code -errorcode \$errorCode -errorinfo \$errorInfo \$ret} else {return \$ret} }" ] eval $proc_body } @@ -209,9 +209,9 @@ proc sta_warn { msg_id msg } { proc sta_error { msg_id msg } { if { ! [is_suppressed $msg_id] } { if { [sdc_filename] != "" } { - error "Error: [file tail [sdc_filename]] line [sdc_file_line], $msg" + error "Error $msg_id: [file tail [sdc_filename]] line [sdc_file_line], $msg" } else { - error "Error: $msg" + error "Error $msg_id: $msg" } } } diff --git a/tcl/Variables.tcl b/tcl/Variables.tcl index 869ef8259..e4c51e4c7 100644 --- a/tcl/Variables.tcl +++ b/tcl/Variables.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -33,21 +33,21 @@ namespace eval sta { # Default digits to print after decimal point for reporting commands. set ::sta_report_default_digits 2 -trace variable ::sta_report_default_digits "rw" \ +trace add variable ::sta_report_default_digits {read write} \ sta::trace_report_default_digits proc trace_report_default_digits { name1 name2 op } { global sta_report_default_digits - if { $op == "w" } { + if { $op == "write" } { if { !([string is integer $sta_report_default_digits] \ - && $sta_report_default_digits >= 0) } { + && $sta_report_default_digits >= 0) } { sta_error 590 "sta_report_default_digits must be a positive integer." } } } -trace variable ::sta_crpr_enabled "rw" \ +trace add variable ::sta_crpr_enabled {read write} \ sta::trace_crpr_enabled proc trace_crpr_enabled { name1 name2 op } { @@ -55,15 +55,15 @@ proc trace_crpr_enabled { name1 name2 op } { crpr_enabled set_crpr_enabled } -trace variable ::sta_crpr_mode "rw" \ +trace add variable ::sta_crpr_mode {read write} \ sta::trace_crpr_mode proc trace_crpr_mode { name1 name2 op } { global sta_crpr_mode - if { $op == "r" } { + if { $op == "read" } { set sta_crpr_mode [crpr_mode] - } elseif { $op == "w" } { + } elseif { $op == "write" } { if { $sta_crpr_mode == "same_pin" || $sta_crpr_mode == "same_transition" } { set_crpr_mode $sta_crpr_mode } else { @@ -72,7 +72,7 @@ proc trace_crpr_mode { name1 name2 op } { } } -trace variable ::sta_cond_default_arcs_enabled "rw" \ +trace add variable ::sta_cond_default_arcs_enabled {read write} \ sta::trace_cond_default_arcs_enabled proc trace_cond_default_arcs_enabled { name1 name2 op } { @@ -80,7 +80,7 @@ proc trace_cond_default_arcs_enabled { name1 name2 op } { cond_default_arcs_enabled set_cond_default_arcs_enabled } -trace variable ::sta_gated_clock_checks_enabled "rw" \ +trace add variable ::sta_gated_clock_checks_enabled {read write} \ sta::trace_gated_clk_checks_enabled proc trace_gated_clk_checks_enabled { name1 name2 op } { @@ -88,7 +88,7 @@ proc trace_gated_clk_checks_enabled { name1 name2 op } { gated_clk_checks_enabled set_gated_clk_checks_enabled } -trace variable ::sta_internal_bidirect_instance_paths_enabled "rw" \ +trace add variable ::sta_internal_bidirect_instance_paths_enabled {read write} \ sta::trace_internal_bidirect_instance_paths_enabled proc trace_internal_bidirect_instance_paths_enabled { name1 name2 op } { @@ -96,15 +96,7 @@ proc trace_internal_bidirect_instance_paths_enabled { name1 name2 op } { bidirect_inst_paths_enabled set_bidirect_inst_paths_enabled } -trace variable ::sta_bidirect_net_paths_enabled "rw" \ - sta::trace_bidirect_net_paths_enabled - -proc trace_bidirect_net_paths_enabled { name1 name2 op } { - trace_boolean_var $op ::sta_bidirect_net_paths_enabled \ - bidirect_net_paths_enabled set_bidirect_net_paths_enabled -} - -trace variable ::sta_clock_through_tristate_enabled "rw" \ +trace add variable ::sta_clock_through_tristate_enabled {read write} \ sta::trace_clock_through_tristate_enabled proc trace_clock_through_tristate_enabled { name1 name2 op } { @@ -112,7 +104,7 @@ proc trace_clock_through_tristate_enabled { name1 name2 op } { clk_thru_tristate_enabled set_clk_thru_tristate_enabled } -trace variable ::sta_preset_clear_arcs_enabled "rw" \ +trace add variable ::sta_preset_clear_arcs_enabled {read write} \ sta::trace_preset_clr_arcs_enabled proc trace_preset_clr_arcs_enabled { name1 name2 op } { @@ -120,7 +112,7 @@ proc trace_preset_clr_arcs_enabled { name1 name2 op } { preset_clr_arcs_enabled set_preset_clr_arcs_enabled } -trace variable ::sta_recovery_removal_checks_enabled "rw" \ +trace add variable ::sta_recovery_removal_checks_enabled {read write} \ sta::trace_recovery_removal_checks_enabled proc trace_recovery_removal_checks_enabled { name1 name2 op } { @@ -128,7 +120,7 @@ proc trace_recovery_removal_checks_enabled { name1 name2 op } { recovery_removal_checks_enabled set_recovery_removal_checks_enabled } -trace variable ::sta_dynamic_loop_breaking "rw" \ +trace add variable ::sta_dynamic_loop_breaking {read write} \ sta::trace_dynamic_loop_breaking proc trace_dynamic_loop_breaking { name1 name2 op } { @@ -136,7 +128,7 @@ proc trace_dynamic_loop_breaking { name1 name2 op } { dynamic_loop_breaking set_dynamic_loop_breaking } -trace variable ::sta_input_port_default_clock "rw" \ +trace add variable ::sta_input_port_default_clock {read write} \ sta::trace_input_port_default_clock proc trace_input_port_default_clock { name1 name2 op } { @@ -144,7 +136,7 @@ proc trace_input_port_default_clock { name1 name2 op } { use_default_arrival_clock set_use_default_arrival_clock } -trace variable ::sta_propagate_all_clocks "rw" \ +trace add variable ::sta_propagate_all_clocks {read write} \ sta::trace_propagate_all_clocks proc trace_propagate_all_clocks { name1 name2 op } { @@ -152,7 +144,7 @@ proc trace_propagate_all_clocks { name1 name2 op } { propagate_all_clocks set_propagate_all_clocks } -trace variable ::sta_propagate_gated_clock_enable "rw" \ +trace add variable ::sta_propagate_gated_clock_enable {read write} \ sta::trace_propagate_gated_clock_enable proc trace_propagate_gated_clock_enable { name1 name2 op } { @@ -160,15 +152,26 @@ proc trace_propagate_gated_clock_enable { name1 name2 op } { propagate_gated_clock_enable set_propagate_gated_clock_enable } -trace variable ::sta_pocv_enabled "rw" \ - sta::trace_pocv_enabled +trace add variable ::sta_pocv_mode {read write} \ + sta::trace_pocv_mode + +proc trace_pocv_mode { name1 name2 op } { + global sta_pocv_mode -proc trace_pocv_enabled { name1 name2 op } { - trace_boolean_var $op ::sta_pocv_enabled \ - pocv_enabled set_pocv_enabled + if { $op == "read" } { + set sta_pocv_mode [pocv_mode] + } elseif { $op == "write" } { + if { $sta_pocv_mode == "scalar" \ + || $sta_pocv_mode == "normal" \ + || $sta_pocv_mode == "skew_normal" } { + set_pocv_mode $sta_pocv_mode + } else { + sta_error 593 "sta_pocv_mode must be scalar, normal, or skew_normal." + } + } } -trace variable ::sta_boolean_props_as_int "rw" \ +trace add variable ::sta_boolean_props_as_int {read write} \ sta::trace_boolean_props_as_int proc trace_boolean_props_as_int { name1 name2 op } { @@ -176,7 +179,7 @@ proc trace_boolean_props_as_int { name1 name2 op } { boolean_props_as_int set_boolean_props_as_int } -trace variable ::sta_direction_props_short "rw" \ +trace add variable ::sta_direction_props_short {read write} \ sta::trace_direction_props_short proc trace_direction_props_short { name1 name2 op } { @@ -184,7 +187,7 @@ proc trace_direction_props_short { name1 name2 op } { direction_props_short set_direction_props_short } -trace variable ::sta_liberty_line_debug "rw" \ +trace add variable ::sta_liberty_line_debug {read write} \ sta::trace_liberty_line_debug proc trace_liberty_line_debug { name1 name2 op } { @@ -192,7 +195,7 @@ proc trace_liberty_line_debug { name1 name2 op } { liberty_line_debug set_liberty_line_debug } -trace variable ::sta_no_inv_delay_calc "rw" \ +trace add variable ::sta_no_inv_delay_calc {read write} \ sta::trace_no_inv_delay_calc proc trace_no_inv_delay_calc { name1 name2 op } { @@ -200,7 +203,7 @@ proc trace_no_inv_delay_calc { name1 name2 op } { no_inv_delay_calc set_no_inv_delay_calc } -trace variable ::sta_no_inv_power_calc "rw" \ +trace add variable ::sta_no_inv_power_calc {read write} \ sta::trace_no_inv_power_calc proc trace_no_inv_power_calc { name1 name2 op } { @@ -208,7 +211,7 @@ proc trace_no_inv_power_calc { name1 name2 op } { no_inv_power_calc set_no_inv_power_calc } -trace variable ::sta_strip_escaped_bus "rw" \ +trace add variable ::sta_strip_escaped_bus {read write} \ sta::trace_strip_escaped_bus proc trace_strip_escaped_bus { name1 name2 op } { @@ -216,7 +219,7 @@ proc trace_strip_escaped_bus { name1 name2 op } { strip_escaped_bus set_strip_escaped_bus } -trace variable ::sta_enable_collections "rw" \ +trace add variable ::sta_enable_collections {read write} \ sta::trace_enable_collections proc trace_enable_collections { name1 name2 op } { @@ -224,17 +227,32 @@ proc trace_enable_collections { name1 name2 op } { enable_collections set_enable_collections } -# Report path numeric field width is digits + extra. -set report_path_field_width_extra 5 +trace add variable ::sta_pocv_quantile {read write} \ + sta::trace_pocv_quantile + +proc trace_pocv_quantile { name1 name2 op } { + global sta_pocv_quantile + + if { $op == "read" } { + set sta_pocv_quantile [pocv_quantile] + } elseif { $op == "write" } { + if { [string is double $sta_pocv_quantile] \ + && $sta_pocv_quantile >= 0.0 } { + set_pocv_quantile $sta_pocv_quantile + } else { + sta_error 594 "sta_pocv_quantile must be a positive floating point number." + } + } +} ################################################################ proc trace_boolean_var { op var_name get_proc set_proc } { upvar 1 $var_name var - if { $op == "r" } { + if { $op == "read" } { set var [$get_proc] - } elseif { $op == "w" } { + } elseif { $op == "write" } { if { $var == 0 } { $set_proc 0 } elseif { $var == 1 } { diff --git a/test/asap7_small.lib.gz b/test/asap7_small.lib.gz index 3c47419fc..f0381ded3 100644 Binary files a/test/asap7_small.lib.gz and b/test/asap7_small.lib.gz differ diff --git a/test/collections.ok b/test/collections.ok index 46b60e13c..434d5a0a3 100644 --- a/test/collections.ok +++ b/test/collections.ok @@ -1,4 +1,4 @@ -Warning: ../examples/gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11. +Warning 198: ../examples/gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11. get_collection_size: 34 sizeof_collection: 34 foreach_in_collection diff --git a/test/disconnect_mcp_pin.ok b/test/disconnect_mcp_pin.ok index a0645c2ba..c05e6a3b0 100644 --- a/test/disconnect_mcp_pin.ok +++ b/test/disconnect_mcp_pin.ok @@ -1,7 +1,7 @@ -Warning: disconnect_mcp_pin.tcl line 15, 'u0/A' is not a valid endpoint. -Warning: disconnect_mcp_pin.tcl line 16, 'u1/A' is not a valid endpoint. -Warning: disconnect_mcp_pin.tcl line 17, 'u0/A' is not a valid endpoint. -Warning: disconnect_mcp_pin.tcl line 18, 'u1/A' is not a valid endpoint. +Warning 1551: disconnect_mcp_pin.tcl line 15, 'u0/A' is not a valid endpoint. +Warning 1551: disconnect_mcp_pin.tcl line 16, 'u1/A' is not a valid endpoint. +Warning 1551: disconnect_mcp_pin.tcl line 17, 'u0/A' is not a valid endpoint. +Warning 1551: disconnect_mcp_pin.tcl line 18, 'u1/A' is not a valid endpoint. Startpoint: data_in[1] (input port clocked by clk) Endpoint: u1 (falling edge-triggered data to data check clocked by clk) Path Group: clk diff --git a/test/extras.ok b/test/extras.ok index c8ce79110..53294b492 100644 --- a/test/extras.ok +++ b/test/extras.ok @@ -1,16 +1,16 @@ -Warning: ../examples/gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11. +Warning 198: ../examples/gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11. opensta Warning: all_fanin not supported, will return empty list Warning: all_fanout not supported, will return empty list -Warning: extras.tcl line 11, time scale 1e-12 does not match library scale 1e-09. -Warning: extras.tcl line 11, capacitance scale 1e-15 does not match library scale 1e-12. +Warning 345: extras.tcl line 11, time scale 1e-12 does not match library scale 1e-09. +Warning 345: extras.tcl line 11, capacitance scale 1e-15 does not match library scale 1e-12. Clock period: 10.000000 Clock period: 10.000000 dummy is empty -Warning: clock objects do not have a dummy property. +Warning 9000: clock objects do not have a dummy property. -Warning: clock '*_x4*' not found. -Warning: extras.tcl line 30, get_property is not an object. +Warning 351: clock '*_x4*' not found. +Warning 2204: extras.tcl line 30, get_property is not an object. clk_x4 clk_x41 Startpoint: req_val (input port clocked by clk) Endpoint: _413_ (rising edge-triggered flip-flop clocked by clk) @@ -104,7 +104,7 @@ Path Type: max (Path is unconstrained) -Warning: pin 'clk_x41' not found. +Warning 363: pin 'clk_x41' not found. diff --git a/test/filter_expr_to_postfix.ok b/test/filter_expr_to_postfix.ok index 4c200427d..30da2411a 100644 --- a/test/filter_expr_to_postfix.ok +++ b/test/filter_expr_to_postfix.ok @@ -1,24 +1,24 @@ -[sta::_filter_expr_to_postfix "" 1] +[sta::filter_expr_to_postfix ""] -[sta::_filter_expr_to_postfix "a" 1] +[sta::filter_expr_to_postfix "a"] {a == 1} -[sta::_filter_expr_to_postfix "!a" 1] +[sta::filter_expr_to_postfix "!a"] {a == 1} ! -[sta::_filter_expr_to_postfix "a && b" 1] +[sta::filter_expr_to_postfix "a && b"] {a == 1} {b == 1} && -[sta::_filter_expr_to_postfix "a && !b" 1] +[sta::filter_expr_to_postfix "a && !b"] {a == 1} {b == 1} ! && -[sta::_filter_expr_to_postfix "a || !(b && c)" 1] +[sta::filter_expr_to_postfix "a || !(b && c)"] {a == 1} {b == 1} {c == 1} && ! || -[sta::_filter_expr_to_postfix "!(a && b || c) && d || !(a || b && c)" 1] +[sta::filter_expr_to_postfix "!(a && b || c) && d || !(a || b && c)"] {a == 1} {b == 1} && {c == 1} || ! {d == 1} && {a == 1} {b == 1} {c == 1} && || ! || -[sta::_filter_expr_to_postfix "!(a !~ z && b == y || c != x) && d || !(a || b && c)" 1] +[sta::filter_expr_to_postfix "!(a !~ z && b == y || c != x) && d || !(a || b && c)"] {a !~ z} {b == y} && {c != x} || ! {d == 1} && {a == 1} {b == 1} {c == 1} && || ! || -[sta::_filter_expr_to_postfix "(a" 1] -Error: unmatched ( in expression -[sta::_filter_expr_to_postfix "(a))" 1] -Error: extraneous ) in expression -[sta::_filter_expr_to_postfix "a))))" 1] -Error: extraneous ) in expression -[sta::_filter_expr_to_postfix "a + b" 1] -Error: unexpected character starting at: '+ b' +[sta::filter_expr_to_postfix "(a"] +Error: 2603 -filter unmatched (. +[sta::filter_expr_to_postfix "(a))"] +Error: 2601 -filter extraneous ). +[sta::filter_expr_to_postfix "a))))"] +Error: 2601 -filter extraneous ). +[sta::filter_expr_to_postfix "a + b"] +Error: 2600 -filter parsing failed at '+ b'. diff --git a/test/filter_expr_to_postfix.tcl b/test/filter_expr_to_postfix.tcl index 8b612f26d..6e08a5466 100644 --- a/test/filter_expr_to_postfix.tcl +++ b/test/filter_expr_to_postfix.tcl @@ -1,6 +1,6 @@ proc try_expr {input} { - puts "\[sta::_filter_expr_to_postfix \"$input\" 1]" - puts [sta::_filter_expr_to_postfix $input 1] + puts "\[sta::filter_expr_to_postfix \"$input\"]" + puts [sta::filter_expr_to_postfix $input] } proc try_error {input} { diff --git a/test/get_filter.ok b/test/get_filter.ok index fc0f17219..1f0402eca 100644 --- a/test/get_filter.ok +++ b/test/get_filter.ok @@ -1,72 +1,21 @@ -[get_cells liberty_cell==BUFx2_ASAP7_75t_R *] +[get_cells -filter liberty_cell==BUFx2_ASAP7_75t_R *] u1 -[get_cells {liberty_cell == BUFx2_ASAP7_75t_R} *] -u1 -[get_cells -filter "liberty_cell==BUFx2_ASAP7_75t_R" *] -u1 -[get_cells -filter "(name!~*1&&liberty_cell=~*x2_*)" *] -u2 -[get_clocks -filter "is_virtual==0" *] -clk -[get_clocks -filter "is_virtual==1" *] -vclk -vvclk -[get_clocks -filter "is_virtual" *] -vclk -vvclk -[get_clocks -filter "is_virtual&&is_generated" *] -[get_clocks -filter "is_virtual&&is_generated||name==vvclk" *] -vvclk -[get_clocks -filter "is_virtual||name==vvclk&&is_generated" *] -vclk -vvclk -[get_clocks -filter "is_virtual||(name==vvclk&&is_generated)" *] -vclk -vvclk -[get_clocks -filter "is_virtual&&!(is_generated||name==vvclk)" *] -vclk -[get_clocks -filter "is_virtual&&is_generated==0" *] -vclk -vvclk -[get_clocks -filter "is_virtual&&!is_generated" *] +[get_clocks -filter is_virtual *] vclk -vvclk -[get_clocks -filter "is_virtual||is_generated" *] -vclk -vvclk -[get_clocks -filter "is_virtual==0||is_generated" *] -clk -[get_lib_cells -filter "is_buffer==1" *] -asap7_small/BUFx2_ASAP7_75t_R -[get_lib_cells -filter "is_inverter==0" *] -asap7_small/AND2x2_ASAP7_75t_R +[get_lib_cells -filter is_buffer *] asap7_small/BUFx2_ASAP7_75t_R -asap7_small/DFFHQx4_ASAP7_75t_R -[get_lib_cells -filter "name=~*x2_*&&!is_buffer" *] -asap7_small/AND2x2_ASAP7_75t_R -[get_lib_pins -filter "direction==input" BUFx2_ASAP7_75t_R/*] +[get_lib_cells -filter is_inverter *] +asap7_small/INVx2_ASAP7_75t_R +[get_lib_pins -filter direction==input BUFx2_ASAP7_75t_R/*] A -[get_lib_pins -filter "direction==output" BUFx2_ASAP7_75t_R/*] +[get_lib_pins -filter direction==output BUFx2_ASAP7_75t_R/*] Y -[get_libs -filter "name==asap7_small" *] +[get_libs -filter name==asap7_small *] asap7_small -[get_nets -filter "name=~*q" *] +[get_nets -filter name=~*q *] r1q r2q -[get_pins -filter "full_name=~r*/*&&(!is_register_clock||direction==output)" *] -r1/D -r1/IQ -r1/IQN -r1/Q -r2/D -r2/IQ -r2/IQN -r2/Q -r3/D -r3/IQ -r3/IQN -r3/Q -[get_pins -filter "direction==input" *] +[get_pins -filter direction==input *] r1/CLK r1/D r2/CLK @@ -76,40 +25,38 @@ r3/D u1/A u2/A u2/B -[get_pins -filter "direction==input&&!name==CLK" *] -r1/D -r2/D -r3/D -u1/A -u2/A -u2/B -[get_pins -filter "direction==output" *] +[get_pins -filter direction==output *] r1/Q r2/Q r3/Q u1/Y u2/Y -[get_ports -filter "direction==input" *] +[get_ports -filter direction==input *] clk1 clk2 clk3 in1 in2 -[get_ports -filter "direction==output" *] +[get_ports -filter direction==output *] out -[get_nets -filter "(name=~*q" *] -Error: unmatched ( in expression -[get_nets -filter "name=~*q)))" *] -Error: extraneous ) in expression -[get_nets -filter "" *] -Error: filter expression is empty -[get_nets -filter "name=~*q name=~*v" *] -Error: filter expression evaluated to multiple sets -[get_nets -filter "name=~*q+name=~*v" *] -Error: unexpected character starting at: '+name=~*v' -[get_nets -filter "&&" *] -Error: attempted to run a logical and on less than two predicates -[get_nets -filter "name=~*q ||" *] -Error: attempted to run a logical or on less than two predicates -[get_nets -filter "!&&" *] -Error: attempted to run an inversion on no predicates +[get_cells -filter {name ~= *r1*} *] +Error: 2600 -filter parsing failed at '~= *r1*'. +direction == input && name =~ clk* +clk1 +clk2 +clk3 +(direction == input) && (name =~ clk*)" +clk1 +clk2 +clk3 +[get_clocks -filter is_virtual||is_generated *] +vclk +[get_clocks -filter is_virtual==0 *] +clk +[get_clocks -filter is_virtual==false *] +clk +[get_clocks -filter is_virtual==1 *] +vclk +[get_clocks -filter is_virtual==true *] +vclk +{direction == input} {name =~ clk*} {is_clock == 1} && && diff --git a/test/get_filter.tcl b/test/get_filter.tcl index 553d95764..4aa3abc43 100644 --- a/test/get_filter.tcl +++ b/test/get_filter.tcl @@ -4,60 +4,69 @@ read_verilog reg1_asap7.v link_design top create_clock -name clk -period 500 {clk1 clk2 clk3} create_clock -name vclk -period 1000 -create_clock -name vvclk -period 1000 - -proc run_filter {method filter {target "*"}} { - puts "\[$method -filter \"$filter\" $target]" - report_object_full_names [$method -filter "$filter" $target] -} - -proc run_bad_filter {method filter {target "*"}} { - if {[catch {run_filter $method $filter $target} error]} { - puts "$error" - } else { - puts "no error raised" - } -} - -# Test filters for each SDC command -puts "\[get_cells liberty_cell==BUFx2_ASAP7_75t_R *]" + +puts {[get_cells -filter liberty_cell==BUFx2_ASAP7_75t_R *]} report_object_full_names [get_cells -filter liberty_cell==BUFx2_ASAP7_75t_R *] -puts "\[get_cells {liberty_cell == BUFx2_ASAP7_75t_R} *]" -report_object_full_names [get_cells -filter {liberty_cell == BUFx2_ASAP7_75t_R} *] -run_filter get_cells "liberty_cell==BUFx2_ASAP7_75t_R" -run_filter get_cells "(name!~*1&&liberty_cell=~*x2_*)" -run_filter get_clocks "is_virtual==0" -run_filter get_clocks "is_virtual==1" -run_filter get_clocks "is_virtual" -run_filter get_clocks "is_virtual&&is_generated" -run_filter get_clocks "is_virtual&&is_generated||name==vvclk" -run_filter get_clocks "is_virtual||name==vvclk&&is_generated" -run_filter get_clocks "is_virtual||(name==vvclk&&is_generated)" -run_filter get_clocks "is_virtual&&!(is_generated||name==vvclk)" -run_filter get_clocks "is_virtual&&is_generated==0" -run_filter get_clocks "is_virtual&&!is_generated" -run_filter get_clocks "is_virtual||is_generated" -run_filter get_clocks "is_virtual==0||is_generated" -run_filter get_lib_cells "is_buffer==1" -run_filter get_lib_cells "is_inverter==0" -run_filter get_lib_cells "name=~*x2_*&&!is_buffer" -run_filter get_lib_pins "direction==input" BUFx2_ASAP7_75t_R/* -run_filter get_lib_pins "direction==output" BUFx2_ASAP7_75t_R/* -run_filter get_libs "name==asap7_small" -run_filter get_nets "name=~*q" -run_filter get_pins "full_name=~r*/*&&(!is_register_clock||direction==output)" -run_filter get_pins "direction==input" -run_filter get_pins "direction==input&&!name==CLK" -run_filter get_pins "direction==output" -run_filter get_ports "direction==input" -run_filter get_ports "direction==output" - -# Test some bad filters -run_bad_filter get_nets "(name=~*q" -run_bad_filter get_nets "name=~*q)))" -run_bad_filter get_nets "" -run_bad_filter get_nets "name=~*q name=~*v" -run_bad_filter get_nets "name=~*q+name=~*v" -run_bad_filter get_nets "&&" -run_bad_filter get_nets "name=~*q ||" -run_bad_filter get_nets "!&&" + +puts {[get_clocks -filter is_virtual *]} +report_object_full_names [get_clocks -filter is_virtual *] + +puts {[get_lib_cells -filter is_buffer *]} +report_object_full_names [get_lib_cells -filter is_buffer *] +puts {[get_lib_cells -filter is_inverter *]} +report_object_full_names [get_lib_cells -filter is_inverter *] + +puts {[get_lib_pins -filter direction==input BUFx2_ASAP7_75t_R/*]} +report_object_full_names [get_lib_pins -filter direction==input BUFx2_ASAP7_75t_R/*] +puts {[get_lib_pins -filter direction==output BUFx2_ASAP7_75t_R/*]} +report_object_full_names [get_lib_pins -filter direction==output BUFx2_ASAP7_75t_R/*] + +puts {[get_libs -filter name==asap7_small *]} +report_object_full_names [get_libs -filter name==asap7_small *] + +puts {[get_nets -filter name=~*q *]} +report_object_full_names [get_nets -filter name=~*q *] + +puts {[get_pins -filter direction==input *]} +report_object_full_names [get_pins -filter direction==input *] +puts {[get_pins -filter direction==output *]} +report_object_full_names [get_pins -filter direction==output *] + +puts {[get_ports -filter direction==input *]} +report_object_full_names [get_ports -filter direction==input *] +puts {[get_ports -filter direction==output *]} +report_object_full_names [get_ports -filter direction==output *] + +# Test invalid operator ~= (should be =~) +puts {[get_cells -filter {name ~= *r1*} *]} +catch {get_cells -filter {name ~= *r1*} *} result +puts $result + +# AND expr +puts {direction == input && name =~ clk*} +report_object_names [get_ports -filter "direction == input && name =~ clk*" *] +# parens around sub-exprs +puts {(direction == input) && (name =~ clk*)"} +report_object_names [get_ports -filter "(direction == input) && (name =~ clk*)" *] + +# OR expr +puts {[get_clocks -filter is_virtual||is_generated *]} +report_object_full_names [get_clocks -filter is_virtual||is_generated *] + + +# unary==0 / unary==false +puts {[get_clocks -filter is_virtual==0 *]} +report_object_full_names [get_clocks -filter is_virtual==0 *] +puts {[get_clocks -filter is_virtual==false *]} +report_object_full_names [get_clocks -filter is_virtual==false *] + +# unary==1 / unary==true +puts {[get_clocks -filter is_virtual==1 *]} +report_object_full_names [get_clocks -filter is_virtual==1 *] +puts {[get_clocks -filter is_virtual==true *]} +report_object_full_names [get_clocks -filter is_virtual==true *] + +# glob pattern with . (literal dot, no match symantics) +report_object_full_names [get_cells -filter {name =~ .1} *] + +puts [sta::filter_expr_to_postfix "direction == input && name =~ clk* && is_clock"] diff --git a/test/get_is_memory.tcl b/test/get_is_memory.tcl index 23080a991..61d9d4618 100644 --- a/test/get_is_memory.tcl +++ b/test/get_is_memory.tcl @@ -1,4 +1,4 @@ -# Tests whether the is_memory attribute works for cells and libcells +# Tests whether the is_memory attribute works for instances and cells read_liberty gf180mcu_sram.lib.gz read_liberty asap7_small.lib.gz read_verilog get_is_memory.v diff --git a/test/get_lib_pins_of_objects.ok b/test/get_lib_pins_of_objects.ok index 8c8489b9e..850bfc857 100644 --- a/test/get_lib_pins_of_objects.ok +++ b/test/get_lib_pins_of_objects.ok @@ -5,6 +5,7 @@ Y [get_lib_pins -of_objects [get_lib_cells *]] A A +A B CLK D @@ -13,3 +14,4 @@ IQN Q Y Y +Y diff --git a/test/get_noargs.ok b/test/get_noargs.ok index b952fdaae..4da70ba18 100644 --- a/test/get_noargs.ok +++ b/test/get_noargs.ok @@ -11,9 +11,11 @@ vclk asap7_small/AND2x2_ASAP7_75t_R asap7_small/BUFx2_ASAP7_75t_R asap7_small/DFFHQx4_ASAP7_75t_R +asap7_small/INVx2_ASAP7_75t_R [get_lib_pins] A A +A B CLK D @@ -22,6 +24,7 @@ IQN Q Y Y +Y [get_libs] asap7_small [get_nets] diff --git a/test/get_objrefs.ok b/test/get_objrefs.ok index 5430faff8..08593630c 100644 --- a/test/get_objrefs.ok +++ b/test/get_objrefs.ok @@ -9,9 +9,11 @@ vclk asap7_small/AND2x2_ASAP7_75t_R asap7_small/BUFx2_ASAP7_75t_R asap7_small/DFFHQx4_ASAP7_75t_R +asap7_small/INVx2_ASAP7_75t_R [get_lib_pins [get_lib_pins]] A A +A B CLK D @@ -20,6 +22,7 @@ IQN Q Y Y +Y [get_libs [get_libs]] asap7_small [get_nets [get_nets]] diff --git a/test/get_property_flags.ok b/test/get_property_flags.ok index 69275cea8..69223045a 100644 --- a/test/get_property_flags.ok +++ b/test/get_property_flags.ok @@ -35,18 +35,30 @@ get_ports 2 out TEST 2 get_clocks +clk get_clocks 2 +vclk get_lib_cells +asap7_small/BUFx2_ASAP7_75t_R get_lib_cells 2 +asap7_small/AND2x2_ASAP7_75t_R +asap7_small/BUFx2_ASAP7_75t_R +asap7_small/DFFHQx4_ASAP7_75t_R get_pins get_pins 2 get_ports get_ports 2 TEST 3 get_clocks +clk get_clocks 2 +vclk get_lib_cells +asap7_small/BUFx2_ASAP7_75t_R get_lib_cells 2 +asap7_small/AND2x2_ASAP7_75t_R +asap7_small/BUFx2_ASAP7_75t_R +asap7_small/DFFHQx4_ASAP7_75t_R get_pins get_pins 2 get_ports diff --git a/test/get_property_flags.tcl b/test/get_property_flags.tcl index d57d20033..eea4ebfef 100644 --- a/test/get_property_flags.tcl +++ b/test/get_property_flags.tcl @@ -26,7 +26,9 @@ puts "get_ports 2" report_object_full_names [get_ports -filter direction==output *] # Default property settings (sta_boolean_props_as_int=1, sta_direction_props_short=0) -# Incompatible property values used: will return empty lists +# Incompatible property values used: +# - booleans: starting OpenSTA 3.0, will be translated to 1/0 automatically +# - directions: return empty lists puts "TEST 2" puts "get_clocks" report_object_full_names [get_clocks -filter is_virtual==false *] @@ -46,7 +48,9 @@ puts "get_ports 2" report_object_full_names [get_ports -filter direction==out *] # Non-default property settings (sta_boolean_props_as_int=0, sta_direction_props_short=1) -# Incompatible property values used: will return empty lists +# Incompatible property values used: +# - booleans: starting OpenSTA 3.0, will be translated to true/false automatically +# - directions: return empty lists set sta_boolean_props_as_int 0 set sta_direction_props_short 1 puts "TEST 3" diff --git a/test/gf180mcu_sram.lib.gz b/test/gf180mcu_sram.lib.gz index b4ab4b9fd..84279e469 100644 Binary files a/test/gf180mcu_sram.lib.gz and b/test/gf180mcu_sram.lib.gz differ diff --git a/test/helpers.tcl b/test/helpers.tcl new file mode 100644 index 000000000..46eb84c9b --- /dev/null +++ b/test/helpers.tcl @@ -0,0 +1,41 @@ +# Helper functions common to multiple regressions. + +set test_dir [file dirname [file normalize [info script]]] +set result_dir [file join $test_dir "results"] + +# puts [exec cat $file] without forking. +proc report_file { file } { + set stream [open $file r] + if { [file extension $file] == ".gz" } { + zlib push gunzip $stream + } + gets $stream line + while { ![eof $stream] } { + puts $line + gets $stream line + } + close $stream +} + +proc report_file_filter { file filter } { + set stream [open $file r] + gets $stream line + while { ![eof $stream] } { + set index [string first $filter $line] + if { $index != -1 } { + set line [string replace $line $index [expr $index + [string length $filter] - 1]] + } + puts $line + gets $stream line + } + close $stream +} + +proc make_result_file { filename } { + variable result_dir + return [file join $result_dir $filename] +} + +proc sort_objects { objects } { + return [sta::sort_by_full_name $objects] +} diff --git a/test/liberty_arcs_one2one_1.ok b/test/liberty_arcs_one2one_1.ok index 64c906ffd..eac1a9001 100644 --- a/test/liberty_arcs_one2one_1.ok +++ b/test/liberty_arcs_one2one_1.ok @@ -1,4 +1,5 @@ -Warning: liberty_arcs_one2one_1.lib line 48, timing port A and related port Y are different sizes. +Warning 1195: liberty_arcs_one2one_1.lib line 45, port Y function size does not match port size. +Warning 1216: liberty_arcs_one2one_1.lib line 48, timing port A and related port Y are different sizes. report_edges -from partial_wide_inv_cell/A[0] A[0] -> Y[0] combinational ^ -> v 1.00:1.00 diff --git a/test/liberty_arcs_one2one_2.ok b/test/liberty_arcs_one2one_2.ok index 879b8a747..0dbc0a3a2 100644 --- a/test/liberty_arcs_one2one_2.ok +++ b/test/liberty_arcs_one2one_2.ok @@ -1,4 +1,5 @@ -Warning: liberty_arcs_one2one_2.lib line 48, timing port A and related port Y are different sizes. +Warning 1195: liberty_arcs_one2one_2.lib line 45, port Y function size does not match port size. +Warning 1216: liberty_arcs_one2one_2.lib line 48, timing port A and related port Y are different sizes. report_edges -to partial_wide_inv_cell/Y[0] A[0] -> Y[0] combinational ^ -> v 1.00:1.00 diff --git a/test/liberty_write_escaped_names.lib b/test/liberty_write_escaped_names.lib index 8e0fe66d9..f474991e3 100644 --- a/test/liberty_write_escaped_names.lib +++ b/test/liberty_write_escaped_names.lib @@ -1,13 +1,15 @@ library (liberty_write_escaped_names) { - delay_model : "table_lookup"; - simulation : false; + comment : ""; + delay_model : table_lookup; + simulation : false; capacitive_load_unit (1,pF); - leakage_power_unit : "1pW"; - current_unit : "1A"; - pulling_resistance_unit : "1kohm"; - time_unit : "1ns"; - voltage_unit : "1v"; - library_features : "report_delay_calculation"; + leakage_power_unit : 1pW; + current_unit : "1A"; + pulling_resistance_unit : "1kohm"; + time_unit : "1ns"; + voltage_unit : "1v"; + library_features(report_delay_calculation); + input_threshold_pct_rise : 50; input_threshold_pct_fall : 50; output_threshold_pct_rise : 50; @@ -17,11 +19,13 @@ library (liberty_write_escaped_names) { slew_upper_threshold_pct_rise : 70; slew_upper_threshold_pct_fall : 70; slew_derate_from_library : 1.0; - nom_process : 1.0; - nom_temperature : 85.0; - nom_voltage : 0.75; - type ("div\/u_div\/QInv") { + + nom_process : 1.0; + nom_temperature : 85.0; + nom_voltage : 0.75; + + type ("div/u_div/QInv") { base_type : array; data_type : bit; bit_width : 2; @@ -29,37 +33,58 @@ library (liberty_write_escaped_names) { bit_to : 0; } - cell (my_buf) { - pin ("u_sub\/data[0]") { - capacitance : 1; - direction : "input"; + cell ("my_buf") { + pin("u_sub/data[0]") { + direction : input; + capacitance : 1.0000; } - bus ("div\/u_div\/QInv") { - bus_type : "div\/u_div\/QInv"; - direction : "output"; - timing () { - related_pin : "u_sub\/data[0]"; - timing_sense : "positive_unate"; - timing_type : "combinational"; - cell_rise (scalar) { - values ("1"); + bus("div/u_div/QInv") { + bus_type : div/u_div/QInv; + direction : output; + capacitance : 0.0000; + pin("div/u_div/QInv[1]") { + direction : output; + capacitance : 0.0000; + timing() { + related_pin : "u_sub/data[0]"; + timing_sense : positive_unate; + timing_type : combinational; + cell_rise(scalar) { + values("1.00000"); } - cell_fall (scalar) { - values ("1"); + rise_transition(scalar) { + values("1.00000"); } - rise_transition (scalar) { - values ("1"); + cell_fall(scalar) { + values("1.00000"); } - fall_transition (scalar) { - values ("1"); + fall_transition(scalar) { + values("1.00000"); } } - pin ("div\/u_div\/QInv[0]") { - direction : "output"; - } - pin ("div\/u_div\/QInv[1]") { - direction : "output"; + } + pin("div/u_div/QInv[0]") { + direction : output; + capacitance : 0.0000; + timing() { + related_pin : "u_sub/data[0]"; + timing_sense : positive_unate; + timing_type : combinational; + cell_rise(scalar) { + values("1.00000"); + } + rise_transition(scalar) { + values("1.00000"); + } + cell_fall(scalar) { + values("1.00000"); + } + fall_transition(scalar) { + values("1.00000"); + } } } + } } + } diff --git a/test/liberty_write_escaped_names.ok b/test/liberty_write_escaped_names.ok index fa0a06964..f474991e3 100644 --- a/test/liberty_write_escaped_names.ok +++ b/test/liberty_write_escaped_names.ok @@ -49,18 +49,18 @@ library (liberty_write_escaped_names) { related_pin : "u_sub/data[0]"; timing_sense : positive_unate; timing_type : combinational; - cell_rise(scalar) { + cell_rise(scalar) { values("1.00000"); - } - rise_transition(scalar) { + } + rise_transition(scalar) { values("1.00000"); - } - cell_fall(scalar) { + } + cell_fall(scalar) { values("1.00000"); - } - fall_transition(scalar) { + } + fall_transition(scalar) { values("1.00000"); - } + } } } pin("div/u_div/QInv[0]") { @@ -70,18 +70,18 @@ library (liberty_write_escaped_names) { related_pin : "u_sub/data[0]"; timing_sense : positive_unate; timing_type : combinational; - cell_rise(scalar) { + cell_rise(scalar) { values("1.00000"); - } - rise_transition(scalar) { + } + rise_transition(scalar) { values("1.00000"); - } - cell_fall(scalar) { + } + cell_fall(scalar) { values("1.00000"); - } - fall_transition(scalar) { + } + fall_transition(scalar) { values("1.00000"); - } + } } } } diff --git a/test/mcmm3.ok b/test/mcmm3.ok new file mode 100644 index 000000000..e220a8819 --- /dev/null +++ b/test/mcmm3.ok @@ -0,0 +1,208 @@ +Startpoint: r2 (rising edge-triggered flip-flop clocked by m1_clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by m1_clk) +Path Group: m1_clk +Path Type: max +Mode: mode1 +Corner: scene1 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock m1_clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r2/CLK (DFFHQx4_ASAP7_75t_R) + 52.65 52.65 ^ r2/Q (DFFHQx4_ASAP7_75t_R) + 49.30 101.95 ^ u1/Y (BUFx2_ASAP7_75t_R) + 61.03 162.97 ^ u2/Y (AND2x2_ASAP7_75t_R) + 15.77 178.74 ^ r3/D (DFFHQx4_ASAP7_75t_R) + 178.74 data arrival time + +1000.00 1000.00 clock m1_clk (rise edge) + 0.00 1000.00 clock network delay (ideal) + 0.00 1000.00 clock reconvergence pessimism + 1000.00 ^ r3/CLK (DFFHQx4_ASAP7_75t_R) + -13.66 986.34 library setup time + 986.34 data required time +--------------------------------------------------------- + 986.34 data required time + -178.74 data arrival time +--------------------------------------------------------- + 807.59 slack (MET) + + +Startpoint: r1 (rising edge-triggered flip-flop clocked by m2_clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by m2_clk) +Path Group: m2_clk +Path Type: max +Mode: mode2 +Corner: scene2 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock m2_clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r1/CLK (DFFHQx4_ASAP7_75t_R) + 123.56 123.56 ^ r1/Q (DFFHQx4_ASAP7_75t_R) + 112.01 235.57 ^ u2/Y (AND2x2_ASAP7_75t_R) + 22.14 257.71 ^ r3/D (DFFHQx4_ASAP7_75t_R) + 257.71 data arrival time + + 500.00 500.00 clock m2_clk (rise edge) + 0.00 500.00 clock network delay (ideal) + 0.00 500.00 clock reconvergence pessimism + 500.00 ^ r3/CLK (DFFHQx4_ASAP7_75t_R) + -40.66 459.34 library setup time + 459.34 data required time +--------------------------------------------------------- + 459.34 data required time + -257.71 data arrival time +--------------------------------------------------------- + 201.62 slack (MET) + + +Startpoint: r2 (rising edge-triggered flip-flop clocked by m1_clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by m1_clk) +Path Group: m1_clk +Path Type: max +Mode: mode1 +Corner: scene1 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock m1_clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r2/CLK (DFFHQx4_ASAP7_75t_R) + 52.65 52.65 ^ r2/Q (DFFHQx4_ASAP7_75t_R) + 49.30 101.95 ^ u1/Y (BUFx2_ASAP7_75t_R) + 61.03 162.97 ^ u2/Y (AND2x2_ASAP7_75t_R) + 15.77 178.74 ^ r3/D (DFFHQx4_ASAP7_75t_R) + 178.74 data arrival time + +1000.00 1000.00 clock m1_clk (rise edge) + 0.00 1000.00 clock network delay (ideal) + 0.00 1000.00 clock reconvergence pessimism + 1000.00 ^ r3/CLK (DFFHQx4_ASAP7_75t_R) + -13.66 986.34 library setup time + 986.34 data required time +--------------------------------------------------------- + 986.34 data required time + -178.74 data arrival time +--------------------------------------------------------- + 807.59 slack (MET) + + +Startpoint: in1 (input port clocked by m1_clk) +Endpoint: r1 (rising edge-triggered flip-flop clocked by m1_clk) +Path Group: m1_clk +Path Type: max +Mode: mode1 +Corner: scene1 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock m1_clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 100.00 100.00 ^ input external delay + 0.00 100.00 ^ in1 (in) + 12.28 112.28 ^ r1/D (DFFHQx4_ASAP7_75t_R) + 112.28 data arrival time + +1000.00 1000.00 clock m1_clk (rise edge) + 0.00 1000.00 clock network delay (ideal) + 0.00 1000.00 clock reconvergence pessimism + 1000.00 ^ r1/CLK (DFFHQx4_ASAP7_75t_R) + -12.80 987.20 library setup time + 987.20 data required time +--------------------------------------------------------- + 987.20 data required time + -112.28 data arrival time +--------------------------------------------------------- + 874.92 slack (MET) + + +Startpoint: in2 (input port clocked by m1_clk) +Endpoint: r2 (rising edge-triggered flip-flop clocked by m1_clk) +Path Group: m1_clk +Path Type: max +Mode: mode1 +Corner: scene1 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock m1_clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 100.00 100.00 ^ input external delay + 0.00 100.00 ^ in2 (in) + 12.28 112.28 ^ r2/D (DFFHQx4_ASAP7_75t_R) + 112.28 data arrival time + +1000.00 1000.00 clock m1_clk (rise edge) + 0.00 1000.00 clock network delay (ideal) + 0.00 1000.00 clock reconvergence pessimism + 1000.00 ^ r2/CLK (DFFHQx4_ASAP7_75t_R) + -12.80 987.20 library setup time + 987.20 data required time +--------------------------------------------------------- + 987.20 data required time + -112.28 data arrival time +--------------------------------------------------------- + 874.92 slack (MET) + + +Startpoint: r1 (rising edge-triggered flip-flop clocked by m2_clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by m2_clk) +Path Group: m2_clk +Path Type: max +Mode: mode2 +Corner: scene2 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock m2_clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r1/CLK (DFFHQx4_ASAP7_75t_R) + 123.56 123.56 ^ r1/Q (DFFHQx4_ASAP7_75t_R) + 112.01 235.57 ^ u2/Y (AND2x2_ASAP7_75t_R) + 22.14 257.71 ^ r3/D (DFFHQx4_ASAP7_75t_R) + 257.71 data arrival time + + 500.00 500.00 clock m2_clk (rise edge) + 0.00 500.00 clock network delay (ideal) + 0.00 500.00 clock reconvergence pessimism + 500.00 ^ r3/CLK (DFFHQx4_ASAP7_75t_R) + -40.66 459.34 library setup time + 459.34 data required time +--------------------------------------------------------- + 459.34 data required time + -257.71 data arrival time +--------------------------------------------------------- + 201.62 slack (MET) + + +Startpoint: r3 (rising edge-triggered flip-flop clocked by m2_clk) +Endpoint: out (output port clocked by m2_clk) +Path Group: m2_clk +Path Type: max +Mode: mode2 +Corner: scene2 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock m2_clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r3/CLK (DFFHQx4_ASAP7_75t_R) + 123.30 123.30 ^ r3/Q (DFFHQx4_ASAP7_75t_R) + 19.50 142.80 ^ out (out) + 142.80 data arrival time + + 500.00 500.00 clock m2_clk (rise edge) + 0.00 500.00 clock network delay (ideal) + 0.00 500.00 clock reconvergence pessimism +-100.00 400.00 output external delay + 400.00 data required time +--------------------------------------------------------- + 400.00 data required time + -142.80 data arrival time +--------------------------------------------------------- + 257.20 slack (MET) + + diff --git a/test/path_dedup_worst.ok b/test/path_dedup_worst.ok index e57c758a7..2d2f45dba 100644 --- a/test/path_dedup_worst.ok +++ b/test/path_dedup_worst.ok @@ -1,3 +1,3 @@ -Warning: ../examples/gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11. +Warning 198: ../examples/gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11. resp_msg count: 1 resp_msg[15] count: 1 diff --git a/test/path_group_names.ok b/test/path_group_names.ok index c9b21916f..39befcb01 100644 --- a/test/path_group_names.ok +++ b/test/path_group_names.ok @@ -1,2 +1,2 @@ -Initial path groups: clk asynchronous path_delay gated_clock unconstrained -Final path groups: clk In2Out In2Reg Reg2Out Reg2Reg asynchronous path_delay gated_clock unconstrained +Initial path groups: clk asynchronous {path delay} {gated clock} unconstrained +Final path groups: clk In2Out In2Reg Reg2Out Reg2Reg asynchronous {path delay} {gated clock} unconstrained diff --git a/test/power.ok b/test/power.ok index 19f4adfdd..a79a9ab6e 100644 --- a/test/power.ok +++ b/test/power.ok @@ -1,12 +1,12 @@ -Warning: gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11. +Warning 198: gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11. Group Internal Switching Leakage Total Power Power Power Power (Watts) ---------------------------------------------------------------- -Sequential 3.07e-04 4.76e-05 2.96e-10 3.54e-04 40.0% -Combinational 1.59e-04 2.05e-04 6.86e-10 3.64e-04 41.1% -Clock 4.68e-05 1.20e-04 2.30e-11 1.67e-04 18.9% +Sequential 3.06e-04 4.71e-05 2.96e-10 3.53e-04 40.1% +Combinational 1.57e-04 2.03e-04 6.86e-10 3.60e-04 40.9% +Clock 4.68e-05 1.20e-04 2.30e-11 1.67e-04 19.0% Macro 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0% Pad 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0% ---------------------------------------------------------------- -Total 5.12e-04 3.73e-04 1.00e-09 8.85e-04 100.0% - 57.8% 42.2% 0.0% +Total 5.10e-04 3.70e-04 1.00e-09 8.81e-04 100.0% + 57.9% 42.1% 0.0% diff --git a/test/power_json.tcl b/test/power_json.tcl index 7edf1d3c0..108b81b2f 100644 --- a/test/power_json.tcl +++ b/test/power_json.tcl @@ -1,4 +1,4 @@ -# report_power reg1_asap7 +# report_power json read_liberty asap7_small.lib.gz read_verilog reg1_asap7.v link_design top diff --git a/test/power_vcd.ok b/test/power_vcd.ok index 47a160029..5733375e8 100644 --- a/test/power_vcd.ok +++ b/test/power_vcd.ok @@ -1,4 +1,4 @@ -Warning: gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11. +Warning 198: gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11. Annotated 937 pin activities. vcd 937 unannotated 0 diff --git a/test/prima3.ok b/test/prima3.ok index a4ac1d9c8..cd2c8cac4 100644 --- a/test/prima3.ok +++ b/test/prima3.ok @@ -31,3 +31,55 @@ Path Type: max 228.48 slack (MET) +Library: asap7_small +Cell: BUFx2_ASAP7_75t_R +Arc sense: positive_unate +Arc type: combinational +A ^ -> Y ^ +P = 1.00 V = 0.70 T = 25.00 +------- input_net_transition = 59.28 +| total_output_net_capacitance = 13.54 +| 11.52 23.04 +v -------------------- +40.00 | 48.68 71.50 +80.00 | 56.23 79.10 +Table value = 56.33 +PVT scale factor = 1.00 +Delay = 56.33 + +------- input_net_transition = 59.28 +| total_output_net_capacitance = 13.54 +| 11.52 23.04 +v -------------------- +40.00 | 53.99 104.08 +80.00 | 54.58 104.40 +Table value = 63.04 +PVT scale factor = 1.00 +Slew = 63.04 + +............................................. + +A v -> Y v +P = 1.00 V = 0.70 T = 25.00 +------- input_net_transition = 52.93 +| total_output_net_capacitance = 12.09 +| 11.52 23.04 +v -------------------- +40.00 | 48.42 67.20 +80.00 | 57.92 76.86 +Table value = 52.43 +PVT scale factor = 1.00 +Delay = 52.43 + +------- input_net_transition = 52.93 +| total_output_net_capacitance = 12.09 +| 11.52 23.04 +v -------------------- +40.00 | 42.77 80.89 +80.00 | 43.84 81.48 +Table value = 45.00 +PVT scale factor = 1.00 +Slew = 45.00 + +............................................. + diff --git a/test/prima3.tcl b/test/prima3.tcl index b2e20eab3..0b285c728 100644 --- a/test/prima3.tcl +++ b/test/prima3.tcl @@ -9,3 +9,4 @@ set_propagated_clock {clk1 clk2 clk3} read_spef reg1_asap7.spef sta::set_delay_calculator prima report_checks -fields {input_pins slew} -format full_clock +report_dcalc -from u1/A -to u1/Y diff --git a/test/read_saif_null_instance.lib b/test/read_saif_null_instance.lib new file mode 100644 index 000000000..da8ae595c --- /dev/null +++ b/test/read_saif_null_instance.lib @@ -0,0 +1,14 @@ +library(min) { + technology(cmos); + time_unit : "1ns"; + voltage_unit : "1V"; + current_unit : "1mA"; + capacitive_load_unit(1, pf); + cell(BUF) { + pin(A) { direction : input; } + pin(Y) { direction : output; + function : "A"; + timing() { related_pin : "A"; } + } + } +} diff --git a/test/read_saif_null_instance.ok b/test/read_saif_null_instance.ok new file mode 100644 index 000000000..42f6c7a46 --- /dev/null +++ b/test/read_saif_null_instance.ok @@ -0,0 +1 @@ +Annotated 0 pin activities. diff --git a/test/read_saif_null_instance.saif b/test/read_saif_null_instance.saif new file mode 100644 index 000000000..dcaff257c --- /dev/null +++ b/test/read_saif_null_instance.saif @@ -0,0 +1,16 @@ +(SAIFILE +(SAIFVERSION "2.0") +(DIRECTION "backward") +(DIVIDER / ) +(TIMESCALE 1ns) +(DURATION 1000) + (INSTANCE TOP + (INSTANCE child + (INSTANCE grandchild + (NET + (clk (T0 500) (T1 500) (TZ 0) (TX 0) (TB 0) (TC 1000)) + ) + ) + ) + ) +) diff --git a/test/read_saif_null_instance.tcl b/test/read_saif_null_instance.tcl new file mode 100644 index 000000000..59f4d5a3d --- /dev/null +++ b/test/read_saif_null_instance.tcl @@ -0,0 +1,5 @@ +# read_saif references missing instance +read_liberty read_saif_null_instance.lib +read_verilog read_saif_null_instance.v +link_design top +read_saif -scope TOP read_saif_null_instance.saif diff --git a/test/read_saif_null_instance.v b/test/read_saif_null_instance.v new file mode 100644 index 000000000..2f1a84ace --- /dev/null +++ b/test/read_saif_null_instance.v @@ -0,0 +1,2 @@ +module top(input clk); +endmodule diff --git a/test/regression b/test/regression index a64ac7040..2d6aa6ce0 100755 --- a/test/regression +++ b/test/regression @@ -31,7 +31,7 @@ exec tclsh $0 ${1+"$@"} # Directory containing tests. set test_dir [file dirname [file normalize [info script]]] -set sta_dir [file normalize [file join $test_dir ".."]] +set sta_dir [file dirname $test_dir] source [file join $test_dir regression_vars.tcl] source [file join $test_dir regression.tcl] diff --git a/test/regression.tcl b/test/regression.tcl index dae27c63d..4c5c5879a 100755 --- a/test/regression.tcl +++ b/test/regression.tcl @@ -30,7 +30,7 @@ # # This notice may not be removed or altered from any source distribution. -# Usage: regression -help | [-threads threads] [-j jobs] [-valgrind] [-collections] [-report_stats] +# Usage: regression -help | [-j jobs] [-threads threads] [-valgrind] [-collections] [-report_stats] # test1 [test2...] proc regression_main {} { @@ -84,9 +84,9 @@ proc parse_args {} { while { $argv != {} } { set arg [lindex $argv 0] if { $arg == "help" || $arg == "-help" } { - puts {Usage: regression [-help] [-threads threads] [-j jobs] [-collections] [-valgrind] [-report_stats] tests...} - puts " -threads max|integer - number of threads to use" - puts " -j jobs - number of parallel jobs (processes) to run" + puts {Usage: regression [-help] [-threads threads] [-j jobs] [-valgrind] [-report_stats] tests...} + puts " -j jobs - number of parallel test jobs (processes) to run" + puts " -threads max|integer - number of threads the STA uses" puts " -collections - run opensta with collections support (beta)" puts " -valgrind - run valgrind (linux memory checker)" puts " -report_stats - report run time and memory" @@ -98,8 +98,8 @@ proc parse_args {} { } elseif { $arg == "-threads" } { set threads [lindex $argv 1] if { !([string is integer $threads] || $threads == "max") } { - puts "Error: -threads arg $threads is not an integer or max." - exit 0 + puts "Error: -threads arg $threads is not an integer or max." + exit 0 } lappend app_options "-threads" lappend app_options $threads @@ -183,12 +183,10 @@ proc run_tests {} { run_test $test } } - write_failure_file - write_diff_file } proc run_test { test } { - global result_dir diff_file errors diff_options + global result_dir diff_file errors diff_options failed_tests puts -nonewline $test flush stdout @@ -441,26 +439,21 @@ proc test_failed { test reason } { } lappend failed_tests $test incr errors($reason) + append_diff_file $test } -proc write_failure_file {} { - global failure_file failed_tests - - set ch [open $failure_file "w"] - foreach test $failed_tests { - puts $ch $test - } - close $ch -} +proc append_diff_file { test } { + global failure_file + global diff_file diff_options -proc write_diff_file {} { - global diff_file diff_options failed_tests + set fail_ch [open $failure_file "a"] + puts $fail_ch $test + close $fail_ch - foreach test $failed_tests { - set log_file [test_log_file $test] - set ok_file [test_ok_file $test] - catch [concat exec diff $diff_options $ok_file $log_file >> $diff_file] - } + # Append diff to results/diffs + set log_file [test_log_file $test] + set ok_file [test_ok_file $test] + catch [concat exec diff $diff_options $ok_file $log_file >> $diff_file] } # Error messages can be found in "valgrind/memcheck/mc_errcontext.c". @@ -539,6 +532,10 @@ proc show_summary {} { global app_path app puts "------------------------------------------------------" + if { $valgrind_shared_lib_failure } { + puts "WARNING: valgrind failed because the executable is not statically linked." + } + puts "See $result_dir for log files" set test_count [llength $tests] if { [found_errors] } { if { $errors(error) != 0 } { @@ -565,10 +562,6 @@ proc show_summary {} { } else { puts "Passed $test_count" } - if { $valgrind_shared_lib_failure } { - puts "WARNING: valgrind failed because the executable is not statically linked." - } - puts "See $result_dir for log files" } proc found_errors {} { @@ -599,22 +592,27 @@ proc save_ok_main {} { } } else { foreach test $argv { - save_ok $test + if { [lsearch [group_tests "all"] $test] == -1 } { + puts "Error: test $test not found." + } else { + save_ok $test + } } } } +# hook for pvt/public sync. proc save_ok { test } { - if { [lsearch [group_tests "all"] $test] == -1 } { - puts "Error: test $test not found." + save_ok_file $test +} + +proc save_ok_file { test } { + set ok_file [test_ok_file $test] + set log_file [test_log_file $test] + if { ! [file exists $log_file] } { + puts "Error: log file $log_file not found." } else { - set ok_file [test_ok_file $test] - set log_file [test_log_file $test] - if { ! [file exists $log_file] } { - puts "Error: log file $log_file not found." - } else { - file copy -force $log_file $ok_file - } + file copy -force $log_file $ok_file } } diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index 405ac17e2..67bb07365 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -56,10 +56,6 @@ if { [exec "uname"] == "Darwin" } { append valgrind_options " --dsymutil=yes" } -proc cleanse_logfile { test log_file } { - # Nothing to be done here. -} - ################################################################ # Record a test in the regression suite. @@ -136,6 +132,7 @@ proc record_example_tests { tests } { record_example_tests { delay_calc min_max_delays + mcmm3 multi_corner power power_vcd @@ -182,6 +179,7 @@ record_public_tests { power_calc_no_inv power_json prima3 + read_saif_null_instance report_checks_sorted report_checks_src_attr report_json1 @@ -191,8 +189,11 @@ record_public_tests { suppress_msg vcd_timestamp verilog_attribute + verilog_well_supplies verilog_specify write_timing_model_scalar + verilog_write_escape + verilog_unconnected_hpin } define_test_group fast [group_tests all] diff --git a/test/sdc_strip_escaped_bus.ok b/test/sdc_strip_escaped_bus.ok index 369616281..410f0dac9 100644 --- a/test/sdc_strip_escaped_bus.ok +++ b/test/sdc_strip_escaped_bus.ok @@ -1,2 +1,2 @@ -Error: sdc_strip_escaped_bus.tcl line 11, pin 'a' not found. -Error: sdc_strip_escaped_bus.tcl line 12, pin 'y' not found. +Error 132: sdc_strip_escaped_bus.tcl line 11, pin 'a' not found. +Error 132: sdc_strip_escaped_bus.tcl line 12, pin 'y' not found. diff --git a/test/suppress_msg.ok b/test/suppress_msg.ok index 1e892fe89..2673ab256 100644 --- a/test/suppress_msg.ok +++ b/test/suppress_msg.ok @@ -1,12 +1,12 @@ -Warning: suppress_msg.tcl line 18, cmd warn 1 -caught Error: suppress_msg.tcl line 18, cmd error 1 -Warning: cmd warn 2 -caught Error: cmd error 2 +Warning 1: suppress_msg.tcl line 18, cmd warn 1 +caught Error 2: suppress_msg.tcl line 18, cmd error 1 +Warning 1: cmd warn 2 +caught Error: 2 cmd error 2 after error caught caught after error -Warning: suppress_msg.tcl line 51, cmd warn 7 -caught Error: suppress_msg.tcl line 51, cmd error 7 -Warning: cmd warn 8 -caught Error: cmd error 8 +Warning 1: suppress_msg.tcl line 51, cmd warn 7 +caught Error 2: suppress_msg.tcl line 51, cmd error 7 +Warning 1: cmd warn 8 +caught Error: 2 cmd error 8 diff --git a/test/verilog_unconnected_hpin.ok b/test/verilog_unconnected_hpin.ok new file mode 100644 index 000000000..3c5cb8cfe --- /dev/null +++ b/test/verilog_unconnected_hpin.ok @@ -0,0 +1,13 @@ +Find b1/out2: b1/out2 +Find b2/out2: b2/out2 +Net b2/out2 + Pin capacitance: 0.00 + Wire capacitance: 0.00 + Total capacitance: 0.00 + Number of drivers: 1 + Number of loads: 0 + Number of pins: 1 + +Driver pins + b2/u3/Y output (BUFx2_ASAP7_75t_R) + diff --git a/test/verilog_unconnected_hpin.tcl b/test/verilog_unconnected_hpin.tcl new file mode 100644 index 000000000..351633984 --- /dev/null +++ b/test/verilog_unconnected_hpin.tcl @@ -0,0 +1,9 @@ +read_liberty asap7_small.lib.gz +read_verilog verilog_unconnected_hpin.v +link_design top +puts "Find b1/out2: [get_full_name [get_pins b1/out2]]" +puts "Find b2/out2: [get_full_name [get_pins b2/out2]]" +# Check if net is connected to "b2/u3/Y" that was the b2/out2 in parent block +set iterm [sta::find_pin "b2/u3/Y"] +set net [get_net -of_object [get_pin $iterm]] +report_net [get_full_name $net] diff --git a/test/verilog_unconnected_hpin.v b/test/verilog_unconnected_hpin.v new file mode 100644 index 000000000..eec7865d1 --- /dev/null +++ b/test/verilog_unconnected_hpin.v @@ -0,0 +1,24 @@ +module top (in, clk1, clk2, out, out2); + input in, clk1, clk2; + output out, out2; + block1 b1 (.in(in), .clk(clk1), .out(b1out), .out2(out2)); + block2 b2 (.in(b1out), .clk(clk2), .out(out)); +endmodule // top + +module block1 (in, clk, out, out2); + input in, clk; + output out, out2; + BUFx2_ASAP7_75t_R u1 (.A(in), .Y(u1out)); + DFFHQx4_ASAP7_75t_R r1 (.D(u1out), .CLK(clk), .Q(r1q)); + BUFx2_ASAP7_75t_R u2 (.A(r1q), .Y(out)); + BUFx2_ASAP7_75t_R u3 (.A(out), .Y(out2)); +endmodule // block1 + +module block2 (in, clk, out, out2); + input in, clk; + output out, out2; + BUFx2_ASAP7_75t_R u1 (.A(in), .Y(u1out)); + DFFHQx4_ASAP7_75t_R r1 (.D(u1out), .CLK(clk), .Q(r1q)); + BUFx2_ASAP7_75t_R u2 (.A(r1q), .Y(out)); + BUFx2_ASAP7_75t_R u3 (.A(out), .Y(out2)); +endmodule // block2 diff --git a/test/verilog_well_supplies.ok b/test/verilog_well_supplies.ok new file mode 100644 index 000000000..41b724820 --- /dev/null +++ b/test/verilog_well_supplies.ok @@ -0,0 +1,26 @@ +module top (y, + a); + output y; + input a; + + + sky130_fd_sc_hd__buf_1 u1 (.A(a), + .X(y)); +endmodule +module top (y, + a); + output y; + input a; + + wire VGND; + wire VNB; + wire VPB; + wire VPWR; + + sky130_fd_sc_hd__buf_1 u1 (.VGND(VGND), + .VNB(VNB), + .VPB(VPB), + .VPWR(VPWR), + .A(a), + .X(y)); +endmodule diff --git a/test/verilog_well_supplies.tcl b/test/verilog_well_supplies.tcl new file mode 100644 index 000000000..b40287118 --- /dev/null +++ b/test/verilog_well_supplies.tcl @@ -0,0 +1,12 @@ +# Check that write_verilog excludes well pins along with power/ground pins. +source helpers.tcl +read_liberty ../examples/sky130hd_tt.lib.gz +read_verilog verilog_well_supplies.v +link_design top +set verilog_file [make_result_file "verilog_well_supplies.v"] +write_verilog $verilog_file +report_file $verilog_file + +set verilog_pwr_file [make_result_file "verilog_well_supplies_pwr.v"] +write_verilog -include_pwr_gnd $verilog_pwr_file +report_file $verilog_pwr_file diff --git a/test/verilog_well_supplies.v b/test/verilog_well_supplies.v new file mode 100644 index 000000000..6ae135800 --- /dev/null +++ b/test/verilog_well_supplies.v @@ -0,0 +1,17 @@ +module top ( + output y, + input a +); + supply1 VPWR; + supply0 VGND; + supply1 VPB; + supply0 VNB; + sky130_fd_sc_hd__buf_1 u1 ( + .X(y), + .A(a), + .VPWR(VPWR), + .VGND(VGND), + .VPB(VPB), + .VNB(VNB) + ); +endmodule diff --git a/test/verilog_write_escape.ok b/test/verilog_write_escape.ok new file mode 100644 index 000000000..4906a851f --- /dev/null +++ b/test/verilog_write_escape.ok @@ -0,0 +1,18 @@ +module multi_sink (clk); + input clk; + + wire \alu_adder_result_ex[0] ; + + hier_block \h1\x (.childclk(clk), + .\Y[2:1] ({\alu_adder_result_ex[0] , + \alu_adder_result_ex[0] })); +endmodule +module hier_block (childclk, + \Y[2:1] ); + input childclk; + output [1:0] \Y[2:1] ; + + + BUFx2_ASAP7_75t_R \abuf_$100 (.A(childclk)); + BUFx2_ASAP7_75t_R \ff0/name (.A(childclk)); +endmodule diff --git a/test/verilog_write_escape.tcl b/test/verilog_write_escape.tcl new file mode 100644 index 000000000..29e785909 --- /dev/null +++ b/test/verilog_write_escape.tcl @@ -0,0 +1,10 @@ +# Check if "h1\x" and \Y[2:1] are correctly processed from input to output of Verilog +source helpers.tcl +read_liberty gf180mcu_sram.lib.gz +read_liberty asap7_small.lib.gz +read_verilog verilog_write_escape.v +link_design multi_sink +set verilog_file [make_result_file "verilog_write_escape.v"] +write_verilog $verilog_file +report_file $verilog_file +read_verilog $verilog_file diff --git a/test/verilog_write_escape.v b/test/verilog_write_escape.v new file mode 100644 index 000000000..e0c43c3e0 --- /dev/null +++ b/test/verilog_write_escape.v @@ -0,0 +1,13 @@ +module multi_sink (clk); + input clk; + wire \alu_adder_result_ex[0] ; + \hier_block \h1\x (.childclk(clk), .\Y[2:1] ({ \alu_adder_result_ex[0] , \alu_adder_result_ex[0] }) ); +endmodule // multi_sink + +module hier_block (childclk, \Y[2:1] ); + input childclk; + output [1:0] \Y[2:1] ; + wire [1:0] \Y[2:1] ; + BUFx2_ASAP7_75t_R \abuf_$100 (.A(childclk)); + BUFx2_ASAP7_75t_R \ff0/name (.A(childclk)); +endmodule // hier_block1 diff --git a/test/write_timing_model_scalar.ok b/test/write_timing_model_scalar.ok index 55cff54bd..2188caa40 100644 --- a/test/write_timing_model_scalar.ok +++ b/test/write_timing_model_scalar.ok @@ -36,22 +36,22 @@ library (counter) { timing() { timing_sense : positive_unate; timing_type : min_clock_tree_path; - cell_rise(scalar) { + cell_rise(scalar) { values("0.00000"); - } - cell_fall(scalar) { + } + cell_fall(scalar) { values("0.00000"); - } + } } timing() { timing_sense : positive_unate; timing_type : max_clock_tree_path; - cell_rise(scalar) { + cell_rise(scalar) { values("0.00000"); - } - cell_fall(scalar) { + } + cell_fall(scalar) { values("0.00000"); - } + } } } pin("reset") { @@ -60,16 +60,16 @@ library (counter) { timing() { related_pin : "clk"; timing_type : hold_rising; - rise_constraint(scalar) { + rise_constraint(scalar) { values("0.29559"); - } + } } timing() { related_pin : "clk"; timing_type : setup_rising; - rise_constraint(scalar) { + rise_constraint(scalar) { values("-0.22827"); - } + } } } pin("in") { @@ -78,22 +78,22 @@ library (counter) { timing() { related_pin : "clk"; timing_type : hold_rising; - rise_constraint(scalar) { + rise_constraint(scalar) { values("-0.03143"); - } - fall_constraint(scalar) { + } + fall_constraint(scalar) { values("-0.03771"); - } + } } timing() { related_pin : "clk"; timing_type : setup_rising; - rise_constraint(scalar) { + rise_constraint(scalar) { values("0.05608"); - } - fall_constraint(scalar) { + } + fall_constraint(scalar) { values("0.10489"); - } + } } } pin("out") { @@ -102,18 +102,18 @@ library (counter) { timing() { related_pin : "clk"; timing_type : rising_edge; - cell_rise(scalar) { + cell_rise(scalar) { values("6.24237"); - } - rise_transition(scalar) { + } + rise_transition(scalar) { values("8.46194"); - } - cell_fall(scalar) { + } + cell_fall(scalar) { values("3.79896"); - } - fall_transition(scalar) { + } + fall_transition(scalar) { values("4.53677"); - } + } } } } diff --git a/util/Debug.cc b/util/Debug.cc index 16836d7a7..6cb3f8992 100644 --- a/util/Debug.cc +++ b/util/Debug.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@ #include "Debug.hh" +#include "ContainerHelpers.hh" #include "Report.hh" namespace sta { @@ -31,99 +32,51 @@ namespace sta { bool debug_on = false; Debug::Debug(Report *report) : - report_(report), - debug_on_(false), - debug_map_(nullptr), - stats_level_(0) + report_(report) { } -Debug::~Debug() -{ - if (debug_map_) { - DebugMap::Iterator debug_iter(debug_map_); - // Delete the debug map keys. - while (debug_iter.hasNext()) { - const char *what; - int level; - debug_iter.next(what, level); - delete [] what; - } - delete debug_map_; - } -} - bool -Debug::check(const char *what, - int level) const +Debug::check(std::string_view what, + int level) const { - if (debug_on_ - && debug_map_) { - int dbg_level; - bool exists; - debug_map_->findKey(what, dbg_level, exists); - if (exists) + if (debug_on_) { + auto itr = debug_map_.find(what); + if (itr != debug_map_.end()) { + int dbg_level = itr->second; return dbg_level >= level; + } } return false; } int -Debug::level(const char *what) +Debug::level(std::string_view what) { - if (debug_map_) { - const char *key; - int dbg_level; - bool exists; - debug_map_->findKey(what, key, dbg_level, exists); - if (exists) + if (debug_on_) { + auto itr = debug_map_.find(what); + if (itr != debug_map_.end()) { + int dbg_level = itr->second; return dbg_level; + } } return 0; } void -Debug::setLevel(const char *what, - int level) +Debug::setLevel(std::string_view what, + int level) { - if (stringEq(what, "stats")) + if (what == "stats") stats_level_ = level; else if (level == 0) { - if (debug_map_) { - int dbg_level; - bool exists; - const char *key; - debug_map_->findKey(what, key, dbg_level, exists); - if (exists) { - debug_map_->erase(what); - delete [] key; - } - debug_on_ = !debug_map_->empty(); - } + debug_map_.erase(std::string(what)); + debug_on_ = !debug_map_.empty(); } else { - char *what_cpy = new char[strlen(what) + 1]; - strcpy(what_cpy, what); - if (debug_map_ == nullptr) - debug_map_ = new DebugMap; - (*debug_map_)[what_cpy] = level; + debug_map_[std::string(what)] = level; debug_on_ = true; } } -void -Debug::reportLine(const char *what, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - std::unique_lock lock(buffer_lock_); - report_->printToBuffer("%s", what); - report_->printToBufferAppend(": "); - report_->printToBufferAppend(fmt, args); - report_->printBufferLine(); - va_end(args); -} - -} // namespace +} // namespace sta diff --git a/util/DispatchQueue.cc b/util/DispatchQueue.cc index 4e828c23a..5347a07ae 100644 --- a/util/DispatchQueue.cc +++ b/util/DispatchQueue.cc @@ -30,9 +30,9 @@ DispatchQueue::terminateThreads() cv_.notify_all(); // Wait for threads to finish before we exit - for(size_t i = 0; i < threads_.size(); i++) { - if (threads_[i].joinable()) { - threads_[i].join(); + for (auto &thread : threads_) { + if (thread.joinable()) { + thread.join(); } } quit_ = false; @@ -95,10 +95,10 @@ DispatchQueue::dispatch_thread_handler(size_t i) do { // Wait until we have data or a quit signal - cv_.wait(lock, [this] { return (q_.size() || quit_); } ); + cv_.wait(lock, [this] { return (!q_.empty() || quit_); } ); //after wait, we own the lock - if(!quit_ && q_.size()) { + if (!quit_ && !q_.empty()) { auto op = std::move(q_.front()); q_.pop(); @@ -112,4 +112,4 @@ DispatchQueue::dispatch_thread_handler(size_t i) } while (!quit_); } -} // namespace +} // namespace sta diff --git a/util/Error.cc b/util/Error.cc index b198c4a42..2ca81a1de 100644 --- a/util/Error.cc +++ b/util/Error.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,21 +24,16 @@ #include "Error.hh" -#include #include +#include +#include "Format.hh" #include "StringUtil.hh" namespace sta { -Exception::Exception() : - std::exception() -{ -} - -ExceptionMsg::ExceptionMsg(const char *msg, - const bool suppressed) : - Exception(), +ExceptionMsg::ExceptionMsg(const std::string &msg, + bool suppressed) : msg_(msg), suppressed_(suppressed) { @@ -50,34 +45,33 @@ ExceptionMsg::what() const noexcept return msg_.c_str(); } -ExceptionLine::ExceptionLine(const char *filename, - int line) : - Exception(), +ExceptionLine::ExceptionLine(const std::string &filename, + int line) : filename_(filename), line_(line) { } -FileNotReadable::FileNotReadable(const char *filename) : - filename_(filename) +FileNotReadable::FileNotReadable(std::string_view filename) : + msg_(sta::format("cannot read file {}.", filename)) { } const char * FileNotReadable::what() const noexcept { - return stringPrintTmp("cannot read file %s.", filename_); + return msg_.c_str(); } -FileNotWritable::FileNotWritable(const char *filename) : - filename_(filename) +FileNotWritable::FileNotWritable(std::string_view filename) : + msg_(sta::format("cannot write file {}.", filename)) { } const char * FileNotWritable::what() const noexcept { - return stringPrintTmp("cannot write file %s.", filename_); + return msg_.c_str(); } -} // namespace +} // namespace sta diff --git a/util/FlexDisableRegister.hh b/util/FlexDisableRegister.hh index 64983ba71..52943946e 100644 --- a/util/FlexDisableRegister.hh +++ b/util/FlexDisableRegister.hh @@ -3,4 +3,3 @@ #if !(YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 6) && __cplusplus > 199711L #define register #endif - diff --git a/util/Fuzzy.cc b/util/Fuzzy.cc index b6c69e6ee..b04a35773 100644 --- a/util/Fuzzy.cc +++ b/util/Fuzzy.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -31,56 +31,53 @@ namespace sta { -using std::max; -using std::abs; - constexpr static float float_equal_tolerance = 1E-15F; bool fuzzyEqual(float v1, - float v2) + float v2) { if (v1 == v2) return true; else if (v1 == 0.0) - return abs(v2) < float_equal_tolerance; + return std::abs(v2) < float_equal_tolerance; else if (v2 == 0.0) - return abs(v1) < float_equal_tolerance; + return std::abs(v1) < float_equal_tolerance; else - return abs(v1 - v2) < 1E-6F * max(abs(v1), abs(v2)); + return std::abs(v1 - v2) < 1E-6F * std::max(std::abs(v1), std::abs(v2)); } bool fuzzyZero(float v) { return v == 0.0 - || abs(v) < float_equal_tolerance; + || std::abs(v) < float_equal_tolerance; } bool fuzzyLess(float v1, - float v2) + float v2) { return v1 < v2 && !fuzzyEqual(v1, v2); } bool fuzzyLessEqual(float v1, - float v2) + float v2) { return v1 < v2 || fuzzyEqual(v1, v2); } bool fuzzyGreater(float v1, - float v2) + float v2) { return v1 > v2 && !fuzzyEqual(v1, v2); } bool fuzzyGreaterEqual(float v1, - float v2) + float v2) { return v1 > v2 || fuzzyEqual(v1, v2); } @@ -92,4 +89,4 @@ fuzzyInf(float value) || fuzzyEqual(value, -INF); } -} // namespace +} // namespace sta diff --git a/util/Hash.cc b/util/Hash.cc index 2160ec1c2..8c86b1c63 100644 --- a/util/Hash.cc +++ b/util/Hash.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,18 +24,15 @@ #include "Hash.hh" -#include - namespace sta { size_t -hashString(const char *str) +hashString(std::string_view str) { size_t hash = hash_init_value; - size_t length = strlen(str); - for (size_t i = 0; i < length; i++) - hash = ((hash << 5) + hash) ^ str[i]; + for (char ch : str) + hash = ((hash << 5) + hash) ^ ch; return hash; } -} // namespace +} // namespace sta diff --git a/util/Machine.cc b/util/Machine.cc deleted file mode 100644 index d387e9948..000000000 --- a/util/Machine.cc +++ /dev/null @@ -1,33 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#if defined(_WIN32) - #include "MachineWin32.cc" -#elif defined(__APPLE__) - #include "MachineApple.cc" -#elif defined(__linux__) - #include "MachineLinux.cc" -#else - #include "MachineUnknown.cc" -#endif diff --git a/util/MachineApple.cc b/util/MachineApple.cc index 220df7725..e1de87f41 100644 --- a/util/MachineApple.cc +++ b/util/MachineApple.cc @@ -24,12 +24,12 @@ #include "Machine.hh" -#include -#include -#include -#include +#include +#include #include +#include #include +#include #include "StaConfig.hh" #include "StringUtil.hh" @@ -85,4 +85,4 @@ memoryUsage() return rusage.ru_maxrss; } -} // namespace +} // namespace sta diff --git a/util/MachineLinux.cc b/util/MachineLinux.cc index 231634d6d..ff7079ffb 100644 --- a/util/MachineLinux.cc +++ b/util/MachineLinux.cc @@ -24,13 +24,14 @@ #include "Machine.hh" -#include -#include -#include -#include +#include +#include #include +#include #include +#include +#include "Format.hh" #include "StaConfig.hh" #include "StringUtil.hh" @@ -81,8 +82,7 @@ systemRunTime() size_t memoryUsage() { - std::string proc_filename; - stringPrint(proc_filename, "/proc/%d/status", getpid()); + std::string proc_filename = sta::format("/proc/{}/status", getpid()); size_t memory = 0; FILE *status = fopen(proc_filename.c_str(), "r"); if (status) { @@ -91,13 +91,13 @@ memoryUsage() while (fgets(line, line_length, status) != nullptr) { char *field = strtok(line, " \t"); if (field && stringEq(field, "VmRSS:")) { - char *size = strtok(nullptr, " \t"); - if (size) { - char *ignore; - // VmRSS is in kilobytes. - memory = strtol(size, &ignore, 10) * 1000; - break; - } + char *size = strtok(nullptr, " \t"); + if (size) { + char *ignore; + // VmRSS is in kilobytes. + memory = strtol(size, &ignore, 10) * 1000; + break; + } } } fclose(status); @@ -105,4 +105,4 @@ memoryUsage() return memory; } -} // namespace +} // namespace sta diff --git a/util/MachineUnknown.cc b/util/MachineUnknown.cc index 0a28118b7..069e4a950 100644 --- a/util/MachineUnknown.cc +++ b/util/MachineUnknown.cc @@ -61,4 +61,4 @@ memoryUsage() return 0; } -} // namespace +} // namespace sta diff --git a/util/MachineWin32.cc b/util/MachineWin32.cc index 3fec862f1..2c2c36bf2 100644 --- a/util/MachineWin32.cc +++ b/util/MachineWin32.cc @@ -24,7 +24,8 @@ #include "Machine.hh" -#include +#include +#include #include // GetSystemInfo namespace sta { @@ -35,7 +36,7 @@ int vsnprint(char *str, size_t size, const char *fmt, - va_list args) + const va_list args) { // Copy args before using them because consumption is destructive. va_list args_copy1; @@ -109,4 +110,4 @@ memoryUsage() return 0; } -} // namespace +} // namespace sta diff --git a/util/MinMax.cc b/util/MinMax.cc index 074030bda..4cb0179d6 100644 --- a/util/MinMax.cc +++ b/util/MinMax.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -35,14 +35,14 @@ const float INF = 1E+30F; static bool compareMin(float value1, - float value2) + float value2) { return value1 < value2; } static bool compareMax(float value1, - float value2) + float value2) { return value1 > value2; } @@ -52,13 +52,13 @@ compareMax(float value1, const MinMax MinMax::min_("min", 0, INF, std::numeric_limits::max(), compareMin); const MinMax MinMax::max_("max", 1, -INF, std::numeric_limits::min(), compareMax); const std::array MinMax::range_{&min_, &max_}; -const std::array MinMax::range_index_{min_.index(), max_.index()}; +const std::array MinMax::range_index_{min_.index(), max_.index()}; -MinMax::MinMax(const char *name, - int index, - float init_value, +MinMax::MinMax(std::string_view name, + size_t index, + float init_value, int init_value_int, - bool (*compare)(float value1, float value2)) : + bool (*compare)(float value1, float value2)) : name_(name), index_(index), init_value_(init_value), @@ -86,20 +86,20 @@ MinMax::opposite() const } const MinMax * -MinMax::find(const char *min_max) +MinMax::find(std::string_view min_max) { - if (stringEq(min_max, "min") - || stringEq(min_max, "early")) + if (stringEqual(min_max, "min") + || stringEqual(min_max, "early")) return &min_; - else if (stringEq(min_max, "max") - || stringEq(min_max, "late")) + else if (stringEqual(min_max, "max") + || stringEqual(min_max, "late")) return &max_; else return nullptr; } const MinMax * -MinMax::find(int index) +MinMax::find(size_t index) { if (index == min_.index()) return &min_; @@ -111,14 +111,14 @@ MinMax::find(int index) bool MinMax::compare(float value1, - float value2) const + float value2) const { return compare_(value1, value2); } float MinMax::minMax(float value1, - float value2) const + float value2) const { if (compare_(value1, value2)) return value1; @@ -131,12 +131,12 @@ MinMax::minMax(float value1, const MinMaxAll MinMaxAll::min_("min", 0, {MinMax::min()}, {MinMax::min()->index()}); const MinMaxAll MinMaxAll::max_("max", 1, {MinMax::max()}, {MinMax::max()->index()}); const MinMaxAll MinMaxAll::all_("all", 2, {MinMax::min(), MinMax::max()}, - {MinMax::min()->index(), MinMax::max()->index()}); + {MinMax::min()->index(), MinMax::max()->index()}); -MinMaxAll::MinMaxAll(const char *name, - int index, - std::vector range, - std::vector range_index) : +MinMaxAll::MinMaxAll(std::string_view name, + size_t index, + const std::vector &range, + const std::vector &range_index) : name_(name), index_(index), range_(range), @@ -168,18 +168,18 @@ MinMaxAll::matches(const MinMaxAll *min_max) const const MinMaxAll * MinMaxAll::find(const char *min_max) { - if (stringEq(min_max, "min") - || stringEq(min_max, "early")) + if (stringEqual(min_max, "min") + || stringEqual(min_max, "early")) return &min_; - else if (stringEq(min_max, "max") - || stringEq(min_max, "late")) + else if (stringEqual(min_max, "max") + || stringEqual(min_max, "late")) return &max_; - else if (stringEq(min_max, "all") - || stringEq(min_max, "min_max") - || stringEq(min_max, "minmax")) + else if (stringEqual(min_max, "all") + || stringEqual(min_max, "min_max") + || stringEqual(min_max, "minmax")) return &all_; else return nullptr; } -} // namespace +} // namespace sta diff --git a/util/PatternMatch.cc b/util/PatternMatch.cc index b60d312e8..c25c48e4c 100644 --- a/util/PatternMatch.cc +++ b/util/PatternMatch.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -23,18 +23,20 @@ // This notice may not be removed or altered from any source distribution. #include "PatternMatch.hh" -#include "Sta.hh" -#include + +#include #include +#include "Sta.hh" + namespace sta { -using std::string; +static std::string stripEscapedBus(std::string_view str); -PatternMatch::PatternMatch(const char *pattern, - bool is_regexp, - bool nocase, - Tcl_Interp *interp) : +PatternMatch::PatternMatch(std::string_view pattern, + bool is_regexp, + bool nocase, + Tcl_Interp *interp) : pattern_(pattern), is_regexp_(is_regexp), nocase_(nocase), @@ -45,7 +47,7 @@ PatternMatch::PatternMatch(const char *pattern, compileRegexp(); } -PatternMatch::PatternMatch(const char *pattern) : +PatternMatch::PatternMatch(std::string_view pattern) : pattern_(pattern), is_regexp_(false), nocase_(false), @@ -54,8 +56,8 @@ PatternMatch::PatternMatch(const char *pattern) : { } -PatternMatch::PatternMatch(const char *pattern, - const PatternMatch *inherit_from) : +PatternMatch::PatternMatch(std::string_view pattern, + const PatternMatch *inherit_from) : pattern_(pattern), is_regexp_(inherit_from->is_regexp_), nocase_(inherit_from->nocase_), @@ -66,30 +68,18 @@ PatternMatch::PatternMatch(const char *pattern, compileRegexp(); } -PatternMatch::PatternMatch(const string &pattern, - const PatternMatch *inherit_from) : - pattern_(pattern.c_str()), - is_regexp_(inherit_from->is_regexp_), - nocase_(inherit_from->nocase_), - interp_(inherit_from->interp_), - regexp_(nullptr) -{ - if (is_regexp_) - compileRegexp(); -} - void PatternMatch::compileRegexp() { int flags = TCL_REG_ADVANCED; if (nocase_) flags |= TCL_REG_NOCASE; - string anchored_pattern; + std::string anchored_pattern; anchored_pattern += '^'; anchored_pattern += pattern_; anchored_pattern += '$'; Tcl_Obj *pattern_obj = Tcl_NewStringObj(anchored_pattern.c_str(), - anchored_pattern.size()); + anchored_pattern.size()); Tcl_IncrRefCount(pattern_obj); regexp_ = Tcl_GetRegExpFromObj(interp_, pattern_obj, flags); Tcl_DecrRefCount(pattern_obj); @@ -98,9 +88,9 @@ PatternMatch::compileRegexp() } static bool -regexpWildcards(const char *pattern) +regexpWildcards(std::string_view pattern) { - return strpbrk(pattern, ".+*?[]") != nullptr; + return pattern.find_first_of(".+*?[]") != std::string_view::npos; } bool @@ -113,13 +103,20 @@ PatternMatch::hasWildcards() const } bool -PatternMatch::match(const string &str) const +PatternMatch::match(std::string_view str) const { - return match(str.c_str()); + if (regexp_) { + std::string buf(str); + const char *cstr = buf.c_str(); + return Tcl_RegExpExec(nullptr, regexp_, cstr, cstr) == 1; + } + return patternMatch(pattern_, str) || + (Sta::sta()->stripEscapedBus() && + patternMatch(pattern_, stripEscapedBus(str)));; } -string -stripEscapedBus(string str) +std::string +stripEscapedBus(std::string_view str) { // strip trailing escaped bus indices from str // bus\[8\] -> bus @@ -128,59 +125,50 @@ stripEscapedBus(string str) // bus\[8\].hello -> bus\[8\].hello // bus\[hello\].world -> bus\[hello\].world // etc. + std::string result{str}; while (true) { - int len = static_cast(str.size()); + int len = static_cast(result.size()); // Minimum pattern \[0\] is 5 chars if (len < 5) break; // Must end with \] - if (str[len - 1] != ']' || str[len - 2] != '\\') + if (result[len - 1] != ']' || result[len - 2] != '\\') break; int pos = len - 3; // Need at least one digit - if (pos < 0 || !isdigit(static_cast(str[pos]))) + if (pos < 0 || !isdigit(static_cast(result[pos]))) break; // Skip digits - while (pos >= 0 && isdigit(static_cast(str[pos]))) + while (pos >= 0 && isdigit(static_cast(result[pos]))) pos--; // Must have \[ at current position - if (pos < 1 || str[pos] != '[' || str[pos - 1] != '\\') + if (pos < 1 || result[pos] != '[' || result[pos - 1] != '\\') break; // Truncate before the \[ - str.resize(pos - 1); + result.resize(pos - 1); } - return str; + return result; } bool -PatternMatch::match(const char *str) const +PatternMatch::matchNoCase(std::string_view str) const { - if (regexp_) - return Tcl_RegExpExec(nullptr, regexp_, str, str) == 1; - else - return patternMatch(pattern_, str) - || (Sta::sta()->stripEscapedBus() && - patternMatch(pattern_, stripEscapedBus(str).c_str())); -} - -bool -PatternMatch::matchNoCase(const char *str) const -{ - if (regexp_) - return Tcl_RegExpExec(0, regexp_, str, str) == 1; - else - return patternMatchNoCase(pattern_, str, nocase_) - || (Sta::sta()->stripEscapedBus() && - patternMatchNoCase(pattern_, stripEscapedBus(str).c_str(), nocase_)); + if (regexp_) { + std::string buf(str); + const char *cstr = buf.c_str(); + return Tcl_RegExpExec(nullptr, regexp_, cstr, cstr) == 1; + } + return patternMatchNoCase(pattern_, str, nocase_) || + (Sta::sta()->stripEscapedBus() && + patternMatchNoCase(pattern_, stripEscapedBus(str), nocase_)); } //////////////////////////////////////////////////////////////// -RegexpCompileError::RegexpCompileError(const char *pattern) : - Exception() +RegexpCompileError::RegexpCompileError(std::string_view pattern) { error_ = "TCL failed to compile regular expression '"; - error_ += pattern; + error_.append(pattern.data(), pattern.size()); error_ += "'."; } @@ -193,70 +181,71 @@ RegexpCompileError::what() const noexcept //////////////////////////////////////////////////////////////// bool -patternMatch(const char *pattern, - const char *str) -{ - const char *p = pattern; - const char *s = str; - - while (*p && *s && (*s == *p || *p == '?')) { - p++; - s++; +patternMatch(std::string_view pattern, + std::string_view str) +{ + size_t pi = 0; + size_t si = 0; + while (pi < pattern.size() && si < str.size() + && (str[si] == pattern[pi] || pattern[pi] == '?')) { + pi++; + si++; } - if (*p == '\0' && *s == '\0') + if (pi == pattern.size() && si == str.size()) return true; - else if (*p == '*') { - if (p[1] == '\0') + if (pi < pattern.size() && pattern[pi] == '*') { + if (pi + 1 == pattern.size()) return true; - while (*s) { - if (patternMatch(p + 1, s)) - return true; - s++; + while (si < str.size()) { + if (patternMatch(pattern.substr(pi + 1), str.substr(si))) + return true; + si++; } } return false; } -inline -bool equalCase(char s, - char p, - bool nocase) +static bool +equalCase(char s, + char p, + bool nocase) { return nocase - ? tolower(s) == tolower(p) + ? std::tolower(static_cast(s)) + == std::tolower(static_cast(p)) : s == p; } bool -patternMatchNoCase(const char *pattern, - const char *str, - bool nocase) -{ - const char *p = pattern; - const char *s = str; - - while (*p && *s && (equalCase(*s, *p, nocase) || *p == '?')) { - p++; - s++; +patternMatchNoCase(std::string_view pattern, + std::string_view str, + bool nocase) +{ + size_t pi = 0; + size_t si = 0; + while (pi < pattern.size() && si < str.size() + && (equalCase(str[si], pattern[pi], nocase) || pattern[pi] == '?')) { + pi++; + si++; } - if (*p == '\0' && *s == '\0') + if (pi == pattern.size() && si == str.size()) return true; - else if (*p == '*') { - if (p[1] == '\0') + if (pi < pattern.size() && pattern[pi] == '*') { + if (pi + 1 == pattern.size()) return true; - while (*s) { - if (patternMatchNoCase(p + 1, s, nocase)) - return true; - s++; + while (si < str.size()) { + if (patternMatchNoCase(pattern.substr(pi + 1), str.substr(si), nocase)) + return true; + si++; } } return false; } bool -patternWildcards(const char *pattern) +patternWildcards(std::string_view pattern) { - return strpbrk(pattern, "*?") != 0; + return pattern.find_first_of("*?") != std::string_view::npos; } -} // namespace +} // namespace sta diff --git a/util/Report.cc b/util/Report.cc index 63acf7018..b802738bc 100644 --- a/util/Report.cc +++ b/util/Report.cc @@ -1,59 +1,46 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Report.hh" -#include // min -#include // exit -#include // strlen -#include // signal +#include // min +#include // exit +#include // strlen -#include "Machine.hh" #include "Error.hh" +#include "Format.hh" +#include "Machine.hh" namespace sta { -using std::min; - Report *Report::default_ = nullptr; -Report::Report() : - log_stream_(nullptr), - redirect_stream_(nullptr), - redirect_to_string_(false), - buffer_size_(1000), - buffer_(new char[buffer_size_]), - buffer_length_(0) +Report::Report() { default_ = this; } -Report::~Report() -{ - delete [] buffer_; -} - size_t Report::printConsole(const char *buffer, size_t length) @@ -79,26 +66,15 @@ Report::printString(const char *buffer, redirectStringPrint(buffer, length); else { if (redirect_stream_) - ret = min(ret, fwrite(buffer, sizeof(char), length, redirect_stream_)); + ret = std::min(ret, fwrite(buffer, sizeof(char), length, redirect_stream_)); else - ret = min(ret, printConsole(buffer, length)); + ret = std::min(ret, printConsole(buffer, length)); if (log_stream_) - ret = min(ret, fwrite(buffer, sizeof(char), length, log_stream_)); + ret = std::min(ret, fwrite(buffer, sizeof(char), length, log_stream_)); } return ret; } -void -Report::reportLine(const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - std::unique_lock lock(buffer_lock_); - printToBuffer(fmt, args); - printBufferLine(); - va_end(args); -} - void Report::reportBlankLine() { @@ -106,13 +82,7 @@ Report::reportBlankLine() } void -Report::reportLineString(const char *line) -{ - printLine(line, strlen(line)); -} - -void -Report::reportLineString(const std::string &line) +Report::reportLine(const std::string &line) { printLine(line.c_str(), line.length()); } @@ -120,208 +90,10 @@ Report::reportLineString(const std::string &line) //////////////////////////////////////////////////////////////// void -Report::printToBuffer(const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - printToBuffer(fmt, args); - va_end(args); -} - -void -Report::printToBuffer(const char *fmt, - va_list args) -{ - buffer_length_ = 0; - printToBufferAppend(fmt, args); -} - -void -Report::printToBufferAppend(const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - printToBufferAppend(fmt, args); - va_end(args); -} - -void -Report::printToBufferAppend(const char *fmt, - va_list args) -{ - // Copy args in case we need to grow the buffer. - va_list args_copy; - va_copy(args_copy, args); - size_t length = vsnprint(buffer_ + buffer_length_, buffer_size_- buffer_length_, - fmt, args); - if (length >= buffer_size_ - buffer_length_) { - buffer_size_ = buffer_length_ + length * 2; - char *new_buffer = new char[buffer_size_]; - strncpy(new_buffer, buffer_, buffer_length_); - delete [] buffer_; - buffer_ = new_buffer; - length = vsnprint(buffer_ + buffer_length_, buffer_size_ - buffer_length_, - fmt, args_copy); - } - buffer_length_ += length; - va_end(args_copy); -} - -void -Report::printBufferLine() -{ - printLine(buffer_, buffer_length_); -} - -//////////////////////////////////////////////////////////////// - -void -Report::warn(int id, - const char *fmt, - ...) -{ - // Skip suppressed messages. - if (!isSuppressed(id)) { - va_list args; - va_start(args, fmt); - printToBuffer("Warning: "); - printToBufferAppend(fmt, args); - printBufferLine(); - va_end(args); - } -} - -void -Report::vwarn(int id, - const char *fmt, - va_list args) -{ - // Skip suppressed messages. - if (!isSuppressed(id)) { - printToBuffer("Warning: "); - printToBufferAppend(fmt, args); - printBufferLine(); - } -} - -void -Report::fileWarn(int id, - const char *filename, - int line, - const char *fmt, - ...) -{ - // Skip suppressed messages. - if (!isSuppressed(id)) { - va_list args; - va_start(args, fmt); - printToBuffer("Warning: %s line %d, ", filename, line); - printToBufferAppend(fmt, args); - printBufferLine(); - va_end(args); - } -} - -void -Report::vfileWarn(int id, - const char *filename, - int line, - const char *fmt, - va_list args) -{ - // Skip suppressed messages. - if (!isSuppressed(id)) { - printToBuffer("Warning: %s line %d, ", filename, line); - printToBufferAppend(fmt, args); - printBufferLine(); - } -} - -//////////////////////////////////////////////////////////////// - -void -Report::error(int id, - const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - // No prefix msg, no \n. - printToBuffer(fmt, args); - va_end(args); - throw ExceptionMsg(buffer_, isSuppressed(id)); -} - -void -Report::verror(int id, - const char *fmt, - va_list args) -{ - // No prefix msg, no \n. - printToBuffer(fmt, args); - throw ExceptionMsg(buffer_, isSuppressed(id)); -} - -void -Report::fileError(int id, - const char *filename, - int line, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - // No prefix msg, no \n. - printToBuffer("%s line %d, ", filename, line); - printToBufferAppend(fmt, args); - va_end(args); - throw ExceptionMsg(buffer_, isSuppressed(id)); -} - -void -Report::vfileError(int id, - const char *filename, - int line, - const char *fmt, - va_list args) -{ - // No prefix msg, no \n. - printToBuffer("%s line %d, ", filename, line); - printToBufferAppend(fmt, args); - throw ExceptionMsg(buffer_, isSuppressed(id)); -} - -//////////////////////////////////////////////////////////////// - -void -Report::critical(int /* id */, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - printToBuffer("Critical: "); - printToBufferAppend(fmt, args); - printBufferLine(); - va_end(args); - exit(1); -} - -void -Report::fileCritical(int /* id */, - const char *filename, - int line, - const char *fmt, - ...) +reportThrowExceptionMsg(const std::string &msg, + bool suppressed) { - va_list args; - va_start(args, fmt); - printToBuffer("Critical: %s line %d, ", filename, line); - printToBufferAppend(fmt, args); - printBufferLine(); - va_end(args); - raise(SIGABRT); + throw ExceptionMsg(msg, suppressed); } //////////////////////////////////////////////////////////////// @@ -341,15 +113,15 @@ Report::unsuppressMsgId(int id) bool Report::isSuppressed(int id) { - return suppressed_msg_ids_.find(id) != suppressed_msg_ids_.end(); + return suppressed_msg_ids_.contains(id); } //////////////////////////////////////////////////////////////// void -Report::logBegin(const char *filename) +Report::logBegin(std::string_view filename) { - log_stream_ = fopen(filename, "w"); + log_stream_ = fopen(std::string(filename).c_str(), "w"); if (log_stream_ == nullptr) throw FileNotWritable(filename); } @@ -363,17 +135,17 @@ Report::logEnd() } void -Report::redirectFileBegin(const char *filename) +Report::redirectFileBegin(std::string_view filename) { - redirect_stream_ = fopen(filename, "w"); + redirect_stream_ = fopen(std::string(filename).c_str(), "w"); if (redirect_stream_ == nullptr) throw FileNotWritable(filename); } void -Report::redirectFileAppendBegin(const char *filename) +Report::redirectFileAppendBegin(std::string_view filename) { - redirect_stream_ = fopen(filename, "a"); + redirect_stream_ = fopen(std::string(filename).c_str(), "a"); if (redirect_stream_ == nullptr) throw FileNotWritable(filename); } @@ -407,4 +179,4 @@ Report::redirectStringPrint(const char *buffer, redirect_string_.append(buffer, length); } -} // namespace +} // namespace sta diff --git a/util/ReportStd.cc b/util/ReportStd.cc index abf088d3d..d8f499e80 100644 --- a/util/ReportStd.cc +++ b/util/ReportStd.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,8 +24,8 @@ #include "ReportStd.hh" -#include #include +#include #include "Report.hh" @@ -35,10 +35,10 @@ namespace sta { class ReportStd : public Report { public: - ReportStd(); + ReportStd() = default; protected: - virtual size_t printConsole(const char *buffer, size_t length); + size_t printConsole(const char *buffer, size_t length) override; virtual size_t printErrorConsole(const char *buffer, size_t length); }; @@ -48,11 +48,6 @@ makeReportStd() return new ReportStd; } -ReportStd::ReportStd() : - Report() -{ -} - size_t ReportStd::printConsole(const char *buffer, size_t length) { @@ -65,4 +60,4 @@ ReportStd::printErrorConsole(const char *buffer, size_t length) return fwrite(buffer, sizeof(char), length, stderr); } -} // namespace +} // namespace sta diff --git a/util/ReportTcl.cc b/util/ReportTcl.cc index a8b9c159f..d5fd53839 100644 --- a/util/ReportTcl.cc +++ b/util/ReportTcl.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -72,11 +72,17 @@ encapGetHandleProc(ClientData instanceData, static int encapBlockModeProc(ClientData instanceData, int mode); +static int +encapClose2Proc(ClientData instanceData, + Tcl_Interp *interp, + int flags); + #if TCL_MAJOR_VERSION < 9 static int encapCloseProc(ClientData instanceData, Tcl_Interp *interp); static int encapSeekProc(ClientData instanceData, + // NOLINTNEXTLINE(google-runtime-int) // Tcl_DriverSeekProc offset long offset, int seekMode, int *errorCodePtr); @@ -85,43 +91,36 @@ encapSeekProc(ClientData instanceData, } // extern "C" Tcl_ChannelType tcl_encap_type_stdout = { - const_cast("file"), - TCL_CHANNEL_VERSION_5, + .typeName = "file", + .version = TCL_CHANNEL_VERSION_5, #if TCL_MAJOR_VERSION < 9 - encapCloseProc, + .closeProc = encapCloseProc, #else - nullptr, // closeProc unused + .closeProc = nullptr, // closeProc unused #endif - encapInputProc, - encapOutputProc, + .inputProc = encapInputProc, + .outputProc = encapOutputProc, #if TCL_MAJOR_VERSION < 9 - encapSeekProc, + .seekProc = encapSeekProc, #else - nullptr, // close2Proc + .seekProc = nullptr, // seekProc unused #endif - encapSetOptionProc, - encapGetOptionProc, - encapWatchProc, - encapGetHandleProc, - nullptr, // close2Proc - encapBlockModeProc, - nullptr, // flushProc - nullptr, // handlerProc - nullptr, // wideSeekProc - nullptr, // threadActionProc - nullptr // truncateProc + .setOptionProc = encapSetOptionProc, + .getOptionProc = encapGetOptionProc, + .watchProc = encapWatchProc, + .getHandleProc = encapGetHandleProc, + .close2Proc = encapClose2Proc, + .blockModeProc = encapBlockModeProc, + .flushProc = nullptr, + .handlerProc = nullptr, + .wideSeekProc = nullptr, + .threadActionProc = nullptr, + .truncateProc = nullptr, }; //////////////////////////////////////////////////////////////// -ReportTcl::ReportTcl() : - Report(), interp_(nullptr), - tcl_stdout_(nullptr), - tcl_stderr_(nullptr), - tcl_encap_stdout_(nullptr), - tcl_encap_stderr_(nullptr) -{ -} +ReportTcl::ReportTcl() = default; ReportTcl::~ReportTcl() { @@ -183,7 +182,7 @@ ReportTcl::flush() // Tcl_Main can eval multiple commands before the flushing the command // output, so the log/redirect commands must force a flush. void -ReportTcl::logBegin(const char *filename) +ReportTcl::logBegin(std::string_view filename) { flush(); Report::logBegin(filename); @@ -197,14 +196,14 @@ ReportTcl::logEnd() } void -ReportTcl::redirectFileBegin(const char *filename) +ReportTcl::redirectFileBegin(std::string_view filename) { flush(); Report::redirectFileBegin(filename); } void -ReportTcl::redirectFileAppendBegin(const char *filename) +ReportTcl::redirectFileAppendBegin(std::string_view filename) { flush(); Report::redirectFileAppendBegin(filename); @@ -290,21 +289,36 @@ encapBlockModeProc(ClientData, return 0; } -#if TCL_MAJOR_VERSION < 9 - +// Close channel implementing CloseProc() or Close2Proc() static int -encapCloseProc(ClientData instanceData, - Tcl_Interp *) +closeChannel(ReportTcl *report) { - ReportTcl *report = reinterpret_cast(instanceData); report->logEnd(); report->redirectFileEnd(); report->redirectStringEnd(); return 0; } +static int +encapClose2Proc(ClientData instanceData, + Tcl_Interp *, + int) +{ + return closeChannel(reinterpret_cast(instanceData)); +} + +#if TCL_MAJOR_VERSION < 9 + +static int +encapCloseProc(ClientData instanceData, + Tcl_Interp *) +{ + return closeChannel(reinterpret_cast(instanceData)); +} + static int encapSeekProc(ClientData, + // NOLINTNEXTLINE(google-runtime-int) // Tcl_DriverSeekProc offset long, int, int *) diff --git a/util/RiseFallMinMax.cc b/util/RiseFallMinMax.cc index abc96eb31..774fd5608 100644 --- a/util/RiseFallMinMax.cc +++ b/util/RiseFallMinMax.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -34,17 +34,16 @@ RiseFallMinMax::RiseFallMinMax() void RiseFallMinMax::clear() { - for (int rf_index = 0; rf_indexvalues_[rf_index][mm_index]; exists_[rf_index][mm_index] = rfmm->exists_[rf_index][mm_index]; } @@ -69,8 +68,8 @@ RiseFallMinMax::setValue(float value) void RiseFallMinMax::setValue(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float value) + const MinMaxAll *min_max, + float value) { for (auto rf_index : rf->rangeIndex()) { for (auto mm_index : min_max->rangeIndex()) { @@ -82,7 +81,7 @@ RiseFallMinMax::setValue(const RiseFallBoth *rf, void RiseFallMinMax::removeValue(const RiseFallBoth *rf, - const MinMax *min_max) + const MinMax *min_max) { int mm_index = min_max->index(); for (auto rf_index : rf->rangeIndex()) @@ -91,7 +90,7 @@ RiseFallMinMax::removeValue(const RiseFallBoth *rf, void RiseFallMinMax::removeValue(const RiseFallBoth *rf, - const MinMaxAll *min_max) + const MinMaxAll *min_max) { for (auto mm : min_max->range()) removeValue(rf, mm); @@ -99,16 +98,16 @@ RiseFallMinMax::removeValue(const RiseFallBoth *rf, void RiseFallMinMax::mergeValue(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float value) + const MinMaxAll *min_max, + float value) { for (auto rf_index : rf->rangeIndex()) { for (auto mm : min_max->range()) { int mm_index = mm->index(); if (!exists_[rf_index][mm_index] - || mm->compare(value, values_[rf_index][mm_index])) { - values_[rf_index][mm_index] = value; - exists_[rf_index][mm_index] = true; + || mm->compare(value, values_[rf_index][mm_index])) { + values_[rf_index][mm_index] = value; + exists_[rf_index][mm_index] = true; } } } @@ -116,8 +115,8 @@ RiseFallMinMax::mergeValue(const RiseFallBoth *rf, void RiseFallMinMax::mergeValue(const RiseFall *rf, - const MinMax *min_max, - float value) + const MinMax *min_max, + float value) { int rf_index = rf->index(); int mm_index = min_max->index(); @@ -130,8 +129,8 @@ RiseFallMinMax::mergeValue(const RiseFall *rf, void RiseFallMinMax::setValue(const RiseFallBoth *rf, - const MinMax *min_max, - float value) + const MinMax *min_max, + float value) { int mm_index = min_max->index(); for (auto rf_index : rf->rangeIndex()) { @@ -142,8 +141,8 @@ RiseFallMinMax::setValue(const RiseFallBoth *rf, void RiseFallMinMax::setValue(const RiseFall *rf, - const MinMax *min_max, - float value) + const MinMax *min_max, + float value) { int rf_index = rf->index(); int mm_index = min_max->index(); @@ -154,8 +153,8 @@ RiseFallMinMax::setValue(const RiseFall *rf, void RiseFallMinMax::setValues(RiseFallMinMax *values) { - for (int rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { - for (int mm_index = 0; mm_index < MinMax::index_count; mm_index++) { + for (size_t rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { + for (size_t mm_index = 0; mm_index < MinMax::index_count; mm_index++) { values_[rf_index][mm_index] = values->values_[rf_index][mm_index]; exists_[rf_index][mm_index] = values->exists_[rf_index][mm_index]; } @@ -164,8 +163,8 @@ RiseFallMinMax::setValues(RiseFallMinMax *values) void RiseFallMinMax::value(const RiseFall *rf, - const MinMax *min_max, - // Return values. + const MinMax *min_max, + // Return values. float &value, bool &exists) const { @@ -188,7 +187,7 @@ RiseFallMinMax::value(const MinMax *min_max) const float RiseFallMinMax::value(const RiseFall *rf, - const MinMax *min_max) const + const MinMax *min_max) const { return values_[rf->index()][min_max->index()]; } @@ -201,16 +200,16 @@ RiseFallMinMax::hasValue() const void RiseFallMinMax::maxValue(// Return values - float &max_value, - bool &exists) const + float &max_value, + bool &exists) const { max_value = MinMax::max()->initValue(); exists = false; - for (int rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { - for (int mm_index = 0; mm_index < MinMax::index_count; mm_index++) { + for (size_t rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { + for (size_t mm_index = 0; mm_index < MinMax::index_count; mm_index++) { if (exists_[rf_index][mm_index]) { - max_value = std::max(max_value, values_[rf_index][mm_index]); - exists = true; + max_value = std::max(max_value, values_[rf_index][mm_index]); + exists = true; } } } @@ -219,10 +218,10 @@ RiseFallMinMax::maxValue(// Return values bool RiseFallMinMax::empty() const { - for (int rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { - for (int mm_index = 0; mm_index < MinMax::index_count; mm_index++) { - if (exists_[rf_index][mm_index]) - return false; + for (auto rf_exists : exists_) { + for (size_t mm_index = 0; mm_index < MinMax::index_count; mm_index++) { + if (rf_exists[mm_index]) + return false; } } return true; @@ -243,13 +242,13 @@ RiseFallMinMax::mergeWith(RiseFallMinMax *rfmm) bool exists1 = exists_[rf_index][mm_index]; bool exists2 = rfmm->exists_[rf_index][mm_index]; if (exists1 && exists2) { - float rfmm_value = rfmm->values_[rf_index][mm_index]; - if (min_max->compare(rfmm_value, values_[rf_index][mm_index])) - values_[rf_index][mm_index] = rfmm_value; + float rfmm_value = rfmm->values_[rf_index][mm_index]; + if (min_max->compare(rfmm_value, values_[rf_index][mm_index])) + values_[rf_index][mm_index] = rfmm_value; } else if (!exists1 && exists2) { - values_[rf_index][mm_index] = rfmm->values_[rf_index][mm_index]; - exists_[rf_index][mm_index] = true; + values_[rf_index][mm_index] = rfmm->values_[rf_index][mm_index]; + exists_[rf_index][mm_index] = true; } } } @@ -258,15 +257,15 @@ RiseFallMinMax::mergeWith(RiseFallMinMax *rfmm) bool RiseFallMinMax::equal(const RiseFallMinMax *values) const { - for (int rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { - for (int mm_index = 0; mm_index < MinMax::index_count; mm_index++) { + for (size_t rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { + for (size_t mm_index = 0; mm_index < MinMax::index_count; mm_index++) { bool exists1 = exists_[rf_index][mm_index]; bool exists2 = values->exists_[rf_index][mm_index]; if (exists1 != exists2) - return false; + return false; if (exists1 && exists2 - && values_[rf_index][mm_index] != values->values_[rf_index][mm_index]) - return false; + && values_[rf_index][mm_index] != values->values_[rf_index][mm_index]) + return false; } } return true; @@ -284,11 +283,11 @@ RiseFallMinMax::isOneValue(float &value) const { if (exists_[0][0]) { value = values_[0][0]; - for (int rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { - for (int mm_index=0; mm_indexindex(); + size_t mm_index = min_max->index(); if (exists_[0][mm_index]) { value = values_[0][mm_index]; - for (int rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { + for (size_t rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { if (!exists_[rf_index][mm_index] - || values_[rf_index][mm_index] != value) - return false; + || values_[rf_index][mm_index] != value) + return false; } return true; } @@ -316,4 +315,4 @@ RiseFallMinMax::isOneValue(const MinMax *min_max, return false; } -} // namespace +} // namespace sta diff --git a/util/RiseFallMinMaxDelay.cc b/util/RiseFallMinMaxDelay.cc new file mode 100644 index 000000000..3bef65738 --- /dev/null +++ b/util/RiseFallMinMaxDelay.cc @@ -0,0 +1,75 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#include "RiseFallMinMaxDelay.hh" + +namespace sta { + +RiseFallMinMaxDelay::RiseFallMinMaxDelay() +{ + for (auto rf_exists : exists_) { + for (size_t mm_index = 0; mm_index < MinMax::index_count; mm_index++) + rf_exists[mm_index] = false; + } +} + +bool +RiseFallMinMaxDelay::empty() const +{ + for (auto rf_exists : exists_) { + for (size_t mm_index = 0; mm_index < MinMax::index_count; mm_index++) + if (rf_exists[mm_index]) + return false; + } + return true; +} + +void +RiseFallMinMaxDelay::value(const RiseFall *rf, + const MinMax *min_max, + // Return values. + Delay &value, + bool &exists) const +{ + exists = exists_[rf->index()][min_max->index()]; + if (exists) + value = values_[rf->index()][min_max->index()]; +} + +void +RiseFallMinMaxDelay::mergeValue(const RiseFall *rf, + const MinMax *min_max, + Delay &value, + const StaState *sta) +{ + int rf_index = rf->index(); + int mm_index = min_max->index(); + if (!exists_[rf_index][mm_index] + || delayGreater(value, values_[rf_index][mm_index], min_max, sta)) { + values_[rf_index][mm_index] = value; + exists_[rf_index][mm_index] = true; + } +} + +} // namespace sta diff --git a/util/RiseFallValues.cc b/util/RiseFallValues.cc index 6dd4fda4a..9956bd3bb 100644 --- a/util/RiseFallValues.cc +++ b/util/RiseFallValues.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -54,7 +54,7 @@ RiseFallValues::setValue(float value) void RiseFallValues::setValue(const RiseFallBoth *rf, - float value) + float value) { for (auto rf_index : rf->rangeIndex()) { values_[rf_index] = value; @@ -64,7 +64,7 @@ RiseFallValues::setValue(const RiseFallBoth *rf, void RiseFallValues::setValue(const RiseFall *rf, - float value) + float value) { int rf_index = rf->index(); values_[rf_index] = value; @@ -82,7 +82,7 @@ RiseFallValues::setValues(RiseFallValues *values) void RiseFallValues::value(const RiseFall *rf, - float &value, bool &exists) const + float &value, bool &exists) const { int rf_index = rf->index(); exists = exists_[rf_index]; @@ -102,4 +102,4 @@ RiseFallValues::hasValue(const RiseFall *rf) const return exists_[rf->index()]; } -} // namespace +} // namespace sta diff --git a/util/StaConfig.hh.cmake b/util/StaConfig.hh.cmake index 78d2e3045..daf90b36e 100644 --- a/util/StaConfig.hh.cmake +++ b/util/StaConfig.hh.cmake @@ -6,6 +6,6 @@ #cmakedefine ZLIB_FOUND -#define SSTA ${SSTA} +#cmakedefine01 HAVE_CXX_STD_FORMAT #define TCL_READLINE ${TCL_READLINE} diff --git a/util/Stats.cc b/util/Stats.cc index ccf1675f8..bda5b1101 100644 --- a/util/Stats.cc +++ b/util/Stats.cc @@ -1,42 +1,38 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Stats.hh" +#include "Debug.hh" #include "Machine.hh" -#include "StringUtil.hh" #include "Report.hh" -#include "Debug.hh" +#include "StringUtil.hh" namespace sta { Stats::Stats(Debug *debug, Report *report) : - elapsed_begin_(0.0), - user_begin_(0.0), - system_begin_(0.0), - memory_begin_(0), debug_(debug), report_(report) { @@ -57,12 +53,11 @@ Stats::report(const char *step) double memory_begin = static_cast(memory_begin_); double memory_end = static_cast(memoryUsage()); double memory_delta = memory_end - memory_begin; - report_->reportLine("stats: %5.1f/%5.1fe %5.1f/%5.1fu %5.1f/%5.1fMB %s", - elapsed_end - elapsed_begin_, elapsed_end, - user_end - user_begin_, user_end, - memory_delta * 1e-6, memory_end * 1e-6, - step); + report_->report("stats: {:5.1f}/{:5.1f}e {:5.1f}/{:5.1f}u {:5.1f}/{:5.1f}MB {}", + elapsed_end - elapsed_begin_, elapsed_end, + user_end - user_begin_, user_end, memory_delta * 1e-6, + memory_end * 1e-6, step); } } -} // namespace +} // namespace sta diff --git a/util/StringSeq.cc b/util/StringSeq.cc deleted file mode 100644 index 67d51a4b8..000000000 --- a/util/StringSeq.cc +++ /dev/null @@ -1,39 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#include "StringSeq.hh" - -namespace sta { - -void -deleteContents(StringSeq *strings) -{ - StringSeq::Iterator iter(strings); - while (iter.hasNext()) { - const char *string = iter.next(); - stringDelete(string); - } -} - -} // namespace diff --git a/util/StringSet.cc b/util/StringSet.cc deleted file mode 100644 index 22d0d303a..000000000 --- a/util/StringSet.cc +++ /dev/null @@ -1,39 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#include "StringSet.hh" - -namespace sta { - -void -deleteContents(StringSet *strings) -{ - StringSet::Iterator iter(strings); - while (iter.hasNext()) { - const char *string = iter.next(); - stringDelete(string); - } -} - -} // namespace diff --git a/util/StringUtil.cc b/util/StringUtil.cc index e6773c826..cac75d9be 100644 --- a/util/StringUtil.cc +++ b/util/StringUtil.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,45 +24,16 @@ #include "StringUtil.hh" -#include -#include -#include -#include // exit -#include // signal -#include #include - -#include "Machine.hh" -#include "Mutex.hh" +#include +#include +#include +#include +#include +#include namespace sta { -using std::max; -using std::string; - -static void -stringPrintTmp(const char *fmt, - va_list args, - // Return values. - char *&str, - size_t &length); -static void -getTmpString(// Return values. - char *&str, - size_t &length); - -char * -stringCopy(const char *str) -{ - if (str) { - char *copy = new char[strlen(str) + 1]; - strcpy(copy, str); - return copy; - } - else - return nullptr; -} - bool isDigits(const char *str) { @@ -73,220 +44,74 @@ isDigits(const char *str) return true; } -//////////////////////////////////////////////////////////////// - -// print for c++ strings. -void -stringPrint(string &str, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - char *tmp; - size_t tmp_length; - stringPrintTmp(fmt, args, tmp, tmp_length); - va_end(args); - str = tmp; -} - -void -stringAppend(string &str, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - char *tmp; - size_t tmp_length; - stringPrintTmp(fmt, args, tmp, tmp_length); - va_end(args); - str += tmp; -} - -string -stdstrPrint(const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - char *tmp; - size_t tmp_length; - stringPrintTmp(fmt, args, tmp, tmp_length); - va_end(args); - return tmp; -} - -char * -stringPrint(const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - char *result = stringPrintArgs(fmt, args); - va_end(args); - return result; -} - -char * -stringPrintArgs(const char *fmt, - va_list args) -{ - char *tmp; - size_t tmp_length; - stringPrintTmp(fmt, args, tmp, tmp_length); - char *result = new char[tmp_length + 1]; - strcpy(result, tmp); - return result; -} - -char * -stringPrintTmp(const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - char *tmp; - size_t tmp_length; - stringPrintTmp(fmt, args, tmp, tmp_length); - va_end(args); - return tmp; -} - -static void -stringPrintTmp(const char *fmt, - va_list args, - // Return values. - char *&tmp, - // strlen(tmp), not including terminating '\0'. - size_t &tmp_length) -{ - size_t tmp_length1; - getTmpString(tmp, tmp_length1); - - va_list args_copy; - va_copy(args_copy, args); - // Returned length does NOT include trailing '\0'. - tmp_length = vsnprint(tmp, tmp_length1, fmt, args_copy); - va_end(args_copy); - - if (tmp_length >= tmp_length1) { - tmp_length1 = tmp_length + 1; - tmp = makeTmpString(tmp_length1); - va_copy(args_copy, args); - tmp_length = vsnprint(tmp, tmp_length1, fmt, args_copy); - va_end(args_copy); - } -} - -//////////////////////////////////////////////////////////////// - -static constexpr size_t tmp_string_count = 256; -static constexpr size_t tmp_string_initial_length = 256; -thread_local static std::array tmp_strings; -thread_local static std::array tmp_string_lengths; -thread_local static int tmp_string_next = 0; - -static void -getTmpString(// Return values. - char *&str, - size_t &length) -{ - if (tmp_string_next == tmp_string_count) - tmp_string_next = 0; - str = tmp_strings[tmp_string_next]; - length = tmp_string_lengths[tmp_string_next]; - if (str == nullptr) { - str = tmp_strings[tmp_string_next] = new char[tmp_string_initial_length]; - length = tmp_string_lengths[tmp_string_next] = tmp_string_initial_length; - } - tmp_string_next++; -} - -char * -makeTmpString(size_t length) -{ - if (tmp_string_next == tmp_string_count) - tmp_string_next = 0; - char *tmp_str = tmp_strings[tmp_string_next]; - size_t tmp_length = tmp_string_lengths[tmp_string_next]; - if (tmp_length < length) { - // String isn't long enough. Make a new one. - delete [] tmp_str; - tmp_length = max(tmp_string_initial_length, length); - tmp_str = new char[tmp_length]; - tmp_strings[tmp_string_next] = tmp_str; - tmp_string_lengths[tmp_string_next] = tmp_length; - } - tmp_string_next++; - return tmp_str; -} - -char * -makeTmpString(string &str) -{ - char *tmp = makeTmpString(str.length() + 1); - strcpy(tmp, str.c_str()); - return tmp; -} - -void -stringDeleteCheck(const char *str) +bool +isDigits(std::string_view str) { - if (isTmpString(str)) { - printf("Critical error: stringDelete for tmp string."); - raise(SIGABRT); + for (char ch : str) { + if (!isdigit(ch)) + return false; } + return true; } -bool -isTmpString(const char *str) +// Wrapper for the absurdly named std::from_chars. +std::pair +stringFloat(const std::string &str) { - if (!tmp_strings.empty()) { - for (size_t i = 0; i < tmp_string_count; i++) { - if (str == tmp_strings[i]) - return true; - } - } - return false; + float value; + // OsX 15.xx and earlier clang do not support std::from_chars. +#if defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201611L + auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value); + if (ec == std::errc() + && *ptr == '\0') + return {value, true}; + else + return {0.0, false}; +#else + char *ptr; + errno = 0; + value = strtof(str.data(), &ptr); + if (errno != 0 || *ptr != '\0') + return {0.0, false}; + else + return {value, true}; +#endif } -//////////////////////////////////////////////////////////////// - void -trimRight(string &str) +trimRight(std::string &str) { - str.erase(str.find_last_not_of(" ") + 1); + str.erase(str.find_last_not_of(' ') + 1); } void -trimLeft(string &str) +trimLeft(std::string &str) { str.erase(0, str.find_first_not_of(" ")); } void -trim(string &str) +trim(std::string &str) { trimLeft(str); trimRight(str); } -void -split(const string &text, - const string &delims, - // Return values. - StringVector &tokens) +StringSeq +parseTokens(const std::string &text, + std::string_view delims) { + StringSeq tokens; auto start = text.find_first_not_of(delims); auto end = text.find_first_of(delims, start); - while (end != string::npos) { + while (end != std::string::npos) { tokens.push_back(text.substr(start, end - start)); start = text.find_first_not_of(delims, end); end = text.find_first_of(delims, start); } - if (start != string::npos) + if (start != std::string::npos) tokens.push_back(text.substr(start)); + return tokens; } -} // namespace +} // namespace sta diff --git a/util/TokenParser.cc b/util/TokenParser.cc deleted file mode 100644 index dba068df4..000000000 --- a/util/TokenParser.cc +++ /dev/null @@ -1,90 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#include "TokenParser.hh" - -#include -#include - -namespace sta { - -TokenParser::TokenParser(const char *str, - const char *delimiters) : - delimiters_(delimiters), - token_(const_cast(str)), - token_delimiter_('\0'), - first_(true) -{ - // Skip leading spaces. - while (*token_ != '\0' && isspace(*token_)) - token_++; - token_end_ = strpbrk(token_, delimiters_); - if (token_end_) { - // Save the delimiter. - token_delimiter_ = *token_end_; - // Replace the separator with a terminator. - *token_end_ = '\0'; - } -} - -bool -TokenParser::hasNext() -{ - if (!first_) { - // Replace the previous separator. - if (token_end_) { - *token_end_ = token_delimiter_; - token_ = token_end_ + 1; - // Skip spaces. - while (*token_ != '\0' && isspace(*token_)) - token_++; - // Skip delimiters. - while (*token_ != '\0' && strchr(delimiters_,*token_) != nullptr) - token_++; - if (*token_ == '\0') - token_ = nullptr; - else { - token_end_ = strpbrk(token_, delimiters_); - if (token_end_) { - // Save the delimiter. - token_delimiter_ = *token_end_; - // Replace the separator with a terminator. - *token_end_ = '\0'; - } - } - } - else - token_ = nullptr; - } - return token_ != nullptr; -} - -char * -TokenParser::next() -{ - first_ = false; - return token_; -} - -} // namespace diff --git a/util/Transition.cc b/util/Transition.cc index 5188f9f22..25d872f96 100644 --- a/util/Transition.cc +++ b/util/Transition.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,24 +24,35 @@ #include "Transition.hh" -namespace sta { +#include + +#include "ContainerHelpers.hh" -using std::max; +namespace sta { const RiseFall RiseFall::rise_("rise", "^", 0); const RiseFall RiseFall::fall_("fall", "v", 1); const std::array RiseFall::range_{&rise_, &fall_}; -const std::array RiseFall::range_index_{rise_.index(), fall_.index()}; +const std::array RiseFall::range_index_{rise_.index(), fall_.index()}; -RiseFall::RiseFall(const char *name, - const char *short_name, - int sdf_triple_index) : +RiseFall::RiseFall(std::string_view name, + std::string_view short_name, + size_t sdf_triple_index) : name_(name), short_name_(short_name), sdf_triple_index_(sdf_triple_index) { } +const std::string& +RiseFall::to_string(bool use_short) const +{ + if (use_short) + return short_name_; + else + return name_; +} + const RiseFall * RiseFall::opposite() const { @@ -52,20 +63,17 @@ RiseFall::opposite() const } const RiseFall * -RiseFall::find(const char *rf_str) +RiseFall::find(std::string_view rf_name) { - if (stringEq(rf_str, rise_.name()) - || stringEq(rf_str, rise_.shortName())) + if (rf_name == rise_.name() || rf_name == rise_.shortName()) return &rise_; - else if (stringEq(rf_str, fall_.name()) - || stringEq(rf_str, fall_.shortName())) + if (rf_name == fall_.name() || rf_name == fall_.shortName()) return &fall_; - else - return nullptr; + return nullptr; } const RiseFall * -RiseFall::find(int index) +RiseFall::find(size_t index) { if (index == rise_.index()) return &rise_; @@ -117,12 +125,12 @@ const RiseFallBoth RiseFallBoth::rise_fall_("rise_fall", "rf", 2, {RiseFall::riseIndex(), RiseFall::fallIndex()}); -RiseFallBoth::RiseFallBoth(const char *name, - const char *short_name, - int sdf_triple_index, +RiseFallBoth::RiseFallBoth(std::string_view name, + std::string_view short_name, + size_t sdf_triple_index, const RiseFall *as_rise_fall, - std::vector range, - std::vector range_index) : + const std::vector &range, + const std::vector &range_index) : name_(name), short_name_(short_name), sdf_triple_index_(sdf_triple_index), @@ -132,17 +140,25 @@ RiseFallBoth::RiseFallBoth(const char *name, { } +const std::string& +RiseFallBoth::to_string(bool use_short) const +{ + if (use_short) + return short_name_; + else + return name_; +} + const RiseFallBoth * -RiseFallBoth::find(const char *tr_str) +RiseFallBoth::find(std::string_view rf_name) { - if (stringEq(tr_str, rise_.name())) + if (rf_name == rise_.name()) return &rise_; - else if (stringEq(tr_str, fall_.name())) + if (rf_name == fall_.name()) return &fall_; - else if (stringEq(tr_str, rise_fall_.name())) + if (rf_name == rise_fall_.name()) return &rise_fall_; - else - return nullptr; + return nullptr; } bool @@ -157,15 +173,15 @@ RiseFallBoth::matches(const Transition *tr) const { return this == &rise_fall_ || (this == &rise_ - && tr == Transition::rise()) + && tr == Transition::rise()) || (this == &fall_ - && tr == Transition::fall()); + && tr == Transition::fall()); } //////////////////////////////////////////////////////////////// TransitionMap Transition::transition_map_; -int Transition::max_index_ = 0; +size_t Transition::max_index_ = 0; // Sdf triple order defined on Sdf 3.0 spec, pg 3-17. const Transition Transition::rise_{ "^", "01", RiseFall::rise(), 0}; @@ -180,12 +196,12 @@ const Transition Transition::tr_1X_{"1X", "1X", RiseFall::fall(), 8}; const Transition Transition::tr_X0_{"X0", "X0", RiseFall::fall(), 9}; const Transition Transition::tr_XZ_{"XZ", "XZ", nullptr, 10}; const Transition Transition::tr_ZX_{"ZX", "ZX", nullptr, 11}; -const Transition Transition::rise_fall_{"*", "**", nullptr, -1}; +const Transition Transition::rise_fall_{"*", "**", nullptr, 12}; -Transition::Transition(const char *name, - const char *init_final, - const RiseFall *as_rise_fall, - int sdf_triple_index) : +Transition::Transition(std::string_view name, + std::string_view init_final, + const RiseFall *as_rise_fall, + size_t sdf_triple_index) : name_(name), init_final_(init_final), as_rise_fall_(as_rise_fall), @@ -193,7 +209,7 @@ Transition::Transition(const char *name, { transition_map_[name_] = this; transition_map_[init_final_] = this; - max_index_ = max(sdf_triple_index, max_index_); + max_index_ = std::max(sdf_triple_index, max_index_); } bool @@ -203,9 +219,9 @@ Transition::matches(const Transition *tr) const } const Transition * -Transition::find(const char *tr_str) +Transition::find(std::string_view tr_name) { - return transition_map_.findKey(tr_str); + return findStringKey(transition_map_, tr_name); } const RiseFallBoth * @@ -214,4 +230,4 @@ Transition::asRiseFallBoth() const return reinterpret_cast(as_rise_fall_); } -} // namespace +} // namespace sta diff --git a/util/Util.i b/util/Util.i index 6c7ec10f1..cb08647a4 100644 --- a/util/Util.i +++ b/util/Util.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,33 +22,26 @@ // // This notice may not be removed or altered from any source distribution. -%module util %include %{ +#include "Error.hh" +#include "Fuzzy.hh" +#include "Report.hh" #include "Sta.hh" #include "StaConfig.hh" // STA_VERSION #include "Stats.hh" -#include "Report.hh" -#include "Error.hh" -#include "Fuzzy.hh" +#include "StringUtil.hh" #include "Units.hh" using namespace sta; %} -//////////////////////////////////////////////////////////////// -// -// Empty class definitions to make swig happy. -// Private constructor/destructor so swig doesn't emit them. -// -//////////////////////////////////////////////////////////////// - %inline %{ -float float_inf = INF; +const float float_inf = INF; const char * version() @@ -115,7 +108,7 @@ report_error(int id, const char *msg) { Report *report = Sta::sta()->report(); - report->error(id, "%s", msg); + report->error(id, "{}", msg); } void @@ -125,7 +118,7 @@ report_file_error(int id, const char *msg) { Report *report = Sta::sta()->report(); - report->error(id, filename, line, "%s", msg); + report->error(id, filename, line, "{}", msg); } void @@ -133,7 +126,7 @@ report_warn(int id, const char *msg) { Report *report = Sta::sta()->report(); - report->warn(id, "%s", msg); + report->warn(id, "{}", msg); } void @@ -143,7 +136,7 @@ report_file_warn(int id, const char *msg) { Report *report = Sta::sta()->report(); - report->fileWarn(id, filename, line, "%s", msg); + report->fileWarn(id, filename, line, "{}", msg); } void @@ -151,9 +144,9 @@ report_line(const char *msg) { Sta *sta = Sta::sta(); if (sta) - sta->report()->reportLineString(msg); + sta->report()->reportLine(msg); else - // After sta::delete_all_memory souce -echo prints the cmd file line + // After sta::delete_all_memory include -echo prints the cmd file line. printf("%s\n", msg); } @@ -226,7 +219,7 @@ log_end() void set_debug(const char *what, - int level) + int level) { Sta::sta()->setDebugLevel(what, level); } @@ -262,23 +255,6 @@ object_type(const char *obj) return &obj[1 + sizeof(void*) * 2 + 3]; } -bool -is_object_list(const char *list, - const char *type) -{ - const char *s = list; - while (s) { - bool type_match; - const char *next; - objectListNext(s, type, type_match, next); - if (type_match) - s = next; - else - return false; - } - return true; -} - //////////////////////////////////////////////////////////////// // // Units @@ -390,7 +366,7 @@ area_sta_ui(double value) void set_cmd_unit_scale(const char *unit_name, - float scale) + float scale) { Unit *unit = Sta::sta()->units()->find(unit_name); if (unit) @@ -399,7 +375,7 @@ set_cmd_unit_scale(const char *unit_name, void set_cmd_unit_digits(const char *unit_name, - int digits) + int digits) { Unit *unit = Sta::sta()->units()->find(unit_name); if (unit) @@ -408,7 +384,7 @@ set_cmd_unit_digits(const char *unit_name, void set_cmd_unit_suffix(const char *unit_name, - const char *suffix) + const char *suffix) { Unit *unit = Sta::sta()->units()->find(unit_name); if (unit) { @@ -471,81 +447,80 @@ unit_scale(const char *unit_name) // format_unit functions print with fixed digits and suffix. // Pass value arg as string to support NaNs. -const char * -format_time(const char *value, - int digits) +std::string +format_time(std::string value, + int digits) { - float value1 = strtof(value, nullptr); + auto [value1, valid] = stringFloat(value); return Sta::sta()->units()->timeUnit()->asString(value1, digits); } -const char * +std::string format_capacitance(const char *value, - int digits) + int digits) { - float value1 = strtof(value, nullptr); + auto [value1, valid] = stringFloat(value); return Sta::sta()->units()->capacitanceUnit()->asString(value1, digits); } -const char * +std::string format_resistance(const char *value, - int digits) + int digits) { - float value1 = strtof(value, nullptr); + auto [value1, valid] = stringFloat(value); return Sta::sta()->units()->resistanceUnit()->asString(value1, digits); } -const char * +std::string format_voltage(const char *value, - int digits) + int digits) { - float value1 = strtof(value, nullptr); + auto [value1, valid] = stringFloat(value); return Sta::sta()->units()->voltageUnit()->asString(value1, digits); } -const char * +std::string format_current(const char *value, - int digits) + int digits) { - float value1 = strtof(value, nullptr); + auto [value1, valid] = stringFloat(value); return Sta::sta()->units()->currentUnit()->asString(value1, digits); } -const char * +std::string format_power(const char *value, - int digits) + int digits) { - float value1 = strtof(value, nullptr); + auto [value1, valid] = stringFloat(value); return Sta::sta()->units()->powerUnit()->asString(value1, digits); } -const char * +std::string format_distance(const char *value, - int digits) + int digits) { - float value1 = strtof(value, nullptr); - Unit *dist_unit = Sta::sta()->units()->distanceUnit(); - return dist_unit->asString(value1, digits); + auto [value1, valid] = stringFloat(value); + return Sta::sta()->units()->distanceUnit()->asString(value1, digits); } -const char * +std::string format_area(const char *value, - int digits) + int digits) { - float value1 = strtof(value, nullptr); + auto [value1, valid] = stringFloat(value); Unit *dist_unit = Sta::sta()->units()->distanceUnit(); return dist_unit->asString(value1 / dist_unit->scale(), digits); } //////////////////////////////////////////////////////////////// -const char * +std::string_view rise_short_name() { return RiseFall::rise()->shortName(); } -const char * +std::string_view fall_short_name() { return RiseFall::fall()->shortName(); @@ -555,17 +530,9 @@ fall_short_name() bool fuzzy_equal(float value1, - float value2) + float value2) { return fuzzyEqual(value1, value2); } %} // inline - -//////////////////////////////////////////////////////////////// -// -// Object Methods -// -//////////////////////////////////////////////////////////////// - - diff --git a/util/gzstream.hh b/util/gzstream.hh index 26e853528..26b16174d 100644 --- a/util/gzstream.hh +++ b/util/gzstream.hh @@ -211,4 +211,4 @@ public: } }; -} // namespace GZSTREAM_NAMESPACE +} // namespace gzstream diff --git a/verilog/Verilog.i b/verilog/Verilog.i index 5e1744ef4..75ecef335 100644 --- a/verilog/Verilog.i +++ b/verilog/Verilog.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,11 +22,9 @@ // // This notice may not be removed or altered from any source distribution. -%module verilog - %{ -#include "VerilogWriter.hh" #include "Sta.hh" +#include "VerilogWriter.hh" %} %inline %{ @@ -39,8 +37,8 @@ read_verilog_cmd(const char *filename) void write_verilog_cmd(const char *filename, - bool include_pwr_gnd, - CellSeq *remove_cells) + bool include_pwr_gnd, + CellSeq *remove_cells) { // This does NOT want the SDC (cmd) network because it wants // to see the sta internal names. diff --git a/verilog/Verilog.tcl b/verilog/Verilog.tcl index f4a459f5e..216d7c3e1 100644 --- a/verilog/Verilog.tcl +++ b/verilog/Verilog.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# Copyright (c) 2026, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -32,7 +32,7 @@ proc_redirect read_verilog { } define_cmd_args "write_verilog" {[-include_pwr_gnd]\ - [-remove_cells cells] filename} + [-remove_cells cells] filename} proc write_verilog { args } { # -sort deprecated 12/12/2025 diff --git a/verilog/VerilogLex.ll b/verilog/VerilogLex.ll index e55801b6a..626ac905b 100644 --- a/verilog/VerilogLex.ll +++ b/verilog/VerilogLex.ll @@ -23,6 +23,8 @@ // // This notice may not be removed or altered from any source distribution. +#include + #include "util/FlexDisableRegister.hh" #include "VerilogNamespace.hh" #include "VerilogReader.hh" @@ -93,27 +95,27 @@ ID_TOKEN {ID_ESCAPED_TOKEN}|{ID_ALPHA_TOKEN} } {SIGN}?{UNSIGNED_NUMBER}?"'"[sS]?[bB][01_xz]+ { - yylval->constant = new std::string(yytext); + yylval->emplace(yytext); return token::CONSTANT; } {SIGN}?{UNSIGNED_NUMBER}?"'"[sS]?[oO][0-7_xz]+ { - yylval->constant = new std::string(yytext); + yylval->emplace(yytext); return token::CONSTANT; } {SIGN}?{UNSIGNED_NUMBER}?"'"[sS]?[dD][0-9_]+ { - yylval->constant = new std::string(yytext); + yylval->emplace(yytext); return token::CONSTANT; } {SIGN}?{UNSIGNED_NUMBER}?"'"[sS]?[hH][0-9a-fA-F_xz]+ { - yylval->constant = new std::string(yytext); + yylval->emplace(yytext); return token::CONSTANT; } {SIGN}?[0-9]+ { - yylval->ival = atol(yytext); + yylval->emplace(static_cast(atol(yytext))); return token::INT; } @@ -143,7 +145,7 @@ wire { return token::WIRE; } wor { return token::WOR; } {ID_TOKEN}("."{ID_TOKEN})* { - yylval->string = new std::string(yytext, yyleng); + yylval->emplace(yytext, yyleng); return token::ID; } @@ -155,18 +157,20 @@ wor { return token::WOR; } {BLANK} { /* ignore blanks */ } \" { - yylval->string = new std::string; + token_.clear(); BEGIN(QSTRING); } \" { BEGIN(INITIAL); + yylval->emplace(std::move(token_)); return token::STRING; } {EOL} { error("unterminated quoted string"); BEGIN(INITIAL); + yylval->emplace(std::move(token_)); return token::STRING; } @@ -177,8 +181,8 @@ wor { return token::WOR; } } [^\r\n\"]+ { - /* Anything return token::or double quote */ - *yylval->string += yytext; + /* Anything but return or double quote */ + token_ += yytext; } <> { diff --git a/verilog/VerilogParse.yy b/verilog/VerilogParse.yy index 530e9cf28..3103c3be0 100644 --- a/verilog/VerilogParse.yy +++ b/verilog/VerilogParse.yy @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -44,11 +44,36 @@ void sta::VerilogParse::error(const location_type &loc, const std::string &msg) { - reader->report()->fileError(164,reader->filename(),loc.begin.line, - "%s",msg.c_str()); + reader->report()->fileError(171,reader->filename(),loc.begin.line, "{}",msg); } + %} +%code requires { +#include +#include + +namespace sta { +class PortDirection; +class VerilogAssign; +class VerilogAttr; +class VerilogAttrEntry; +class VerilogAttrStmt; +class VerilogDclArg; +class VerilogNet; +class VerilogReader; +class VerilogScanner; +class VerilogStmt; + +using VerilogAttrEntrySeq = std::vector; +using VerilogAttrSeq = std::vector; +using VerilogAttrStmtSeq = std::vector; +using VerilogDclArgSeq = std::vector; +using VerilogNetSeq = std::vector; +using VerilogStmtSeq = std::vector; +} +} + %require "3.2" %skeleton "lalr1.cc" %debug @@ -59,30 +84,11 @@ sta::VerilogParse::error(const location_type &loc, %parse-param { VerilogScanner *scanner } %parse-param { VerilogReader *reader } %define api.parser.class {VerilogParse} +%define api.value.type variant -%union { - int ival; - std::string *string; - std::string *constant; - std::string *attr_spec_value; - sta::VerilogModule *module; - sta::VerilogStmt *stmt; - sta::VerilogStmtSeq *stmt_seq; - sta::PortDirection *port_type; - sta::VerilogDclArgSeq *dcl_arg_seq; - sta::VerilogDclArg *dcl_arg; - sta::VerilogAssign *assign; - sta::VerilogInst *inst; - sta::VerilogNet *net; - sta::VerilogNetBitSelect *net_bit; - sta::VerilogNetSeq *nets; - sta::VerilogAttrEntry *attr_entry; - sta::VerilogAttrEntrySeq *attr_seq; - sta::VerilogAttrStmt *attr_stmt; - sta::VerilogAttrStmtSeq *attr_stmt_seq; -} - -%token INT CONSTANT ID STRING MODULE ENDMODULE ASSIGN PARAMETER DEFPARAM +%token INT +%token ID STRING CONSTANT +%token MODULE ENDMODULE ASSIGN PARAMETER DEFPARAM %token SPECIFY ENDSPECIFY SPECPARAM %token WIRE WAND WOR TRI INPUT OUTPUT INOUT SUPPLY1 SUPPLY0 REG %token ATTR_OPEN ATTR_CLOSED @@ -91,34 +97,26 @@ sta::VerilogParse::error(const location_type &loc, %left '*' '/' %left NEG /* negation--unary minus */ -%type ID STRING CONSTANT -%type WIRE WAND WOR TRI INPUT OUTPUT INOUT SUPPLY1 SUPPLY0 -%type ATTR_OPEN ATTR_CLOSED -%type INT parameter_exprs parameter_expr -%type attr_spec_value -%type dcl_type port_dcl_type -%type stmt declaration instance parameter parameter_dcls parameter_dcl -%type defparam param_values param_value port_dcl -%type stmts stmt_seq net_assignments continuous_assign port_dcls -%type specify_block -%type specify_stmts -%type net_assignment -%type dcl_arg -%type dcl_args -%type port net_scalar net_bit_select net_part_select net_assign_lhs -%type net_constant net_expr port_ref port_expr named_pin_net_expr -%type inst_named_pin net_named net_expr_concat -%type port_list port_refs inst_ordered_pins -%type inst_named_pins net_exprs inst_pins -%type attr_spec -%type attr_specs -%type attr_instance -%type attr_instance_seq - -// Used by error recovery. -%destructor { delete $$; } STRING -%destructor { delete $$; } CONSTANT -%destructor { delete $$; } attr_spec_value +%type attr_spec_value +%type parameter_exprs parameter_expr +%type dcl_type port_dcl_type +%type stmt declaration instance parameter parameter_dcls parameter_dcl +%type defparam param_values param_value port_dcl +%type stmts stmt_seq net_assignments continuous_assign port_dcls +%type specify_block +%type specify_stmts +%type net_assignment +%type dcl_arg +%type dcl_args +%type port net_scalar net_bit_select net_part_select net_assign_lhs +%type net_constant net_expr port_ref port_expr named_pin_net_expr +%type inst_named_pin net_named net_expr_concat +%type port_list port_refs inst_ordered_pins +%type inst_named_pins net_exprs inst_pins +%type attr_spec +%type attr_specs +%type attr_instance +%type attr_instance_seq %start file @@ -147,10 +145,10 @@ module: port_list: port { $$ = new sta::VerilogNetSeq; - $$->push_back($1); + $$->push_back($1); } | port_list ',' port - { $1->push_back($3); } + { $1->push_back($3); $$ = $1; } ; port: @@ -172,7 +170,7 @@ port_refs: $$->push_back($1); } | port_refs ',' port_ref - { $1->push_back($3); } + { $1->push_back($3); $$ = $1; } ; port_ref: @@ -220,13 +218,13 @@ stmts: // empty { $$ = new sta::VerilogStmtSeq; } | stmts stmt - { if ($2) $1->push_back($2); } + { if ($2) $1->push_back($2); $$ = $1; } | stmts stmt_seq // Append stmt_seq to stmts. - { sta::VerilogStmtSeq::Iterator iter($2); - while (iter.hasNext()) - $1->push_back(iter.next()); + { for (sta::VerilogStmt *stmt : *$2) + $1->push_back(stmt); delete $2; + $$ = $1; } ; @@ -280,18 +278,18 @@ parameter_dcls: parameter_dcl: ID '=' parameter_expr - { delete $1; $$ = nullptr; } + { $$ = nullptr; } | ID '=' STRING - { delete $1; delete $3; $$ = nullptr; } + { $$ = nullptr; } ; parameter_expr: ID - { delete $1; $$ = 0; } + { $$ = 0; } | '`' ID - { delete $2; $$ = 0; } + { $$ = 0; } | CONSTANT - { delete $1; $$ = 0; } + { $$ = 0; } | INT | '-' parameter_expr %prec NEG { $$ = - $2; } @@ -321,9 +319,9 @@ param_values: param_value: ID '=' parameter_expr - { delete $1; $$ = nullptr; } + { $$ = nullptr; } | ID '=' STRING - { delete $1; delete $3; $$ = nullptr; } + { $$ = nullptr; } ; declaration: @@ -374,7 +372,7 @@ net_assignments: $$->push_back($1); } | net_assignments ',' net_assignment - { $1->push_back($3); } + { $1->push_back($3); $$ = $1; } ; net_assignment: @@ -419,7 +417,7 @@ inst_ordered_pins: $$->push_back($1); } | inst_ordered_pins ',' net_expr - { $1->push_back($3); } + { $1->push_back($3); $$ = $1; } ; // Named pin connections. @@ -429,7 +427,7 @@ inst_named_pins: $$->push_back($1); } | inst_named_pins ',' inst_named_pin - { $1->push_back($3); } + { $1->push_back($3); $$ = $1; } ; // The port reference is split out into cases to special case @@ -499,7 +497,7 @@ net_exprs: $$->push_back($1); } | net_exprs ',' net_expr - { $$->push_back($3); } + { $1->push_back($3); $$ = $1; } ; net_expr: @@ -514,7 +512,7 @@ attr_instance_seq: // empty { $$ = new sta::VerilogAttrStmtSeq; } | attr_instance_seq attr_instance - { if ($2) $1->push_back($2); } + { if ($2) $1->push_back($2); $$ = $1; } ; attr_instance: @@ -528,14 +526,14 @@ attr_specs: $$->push_back($1); } | attr_specs ',' attr_spec - { $$->push_back($3); } + { $1->push_back($3); $$ = $1; } ; attr_spec: ID - { $$ = new sta::VerilogAttrEntry(*$1, "1"); delete $1; } + { $$ = new sta::VerilogAttrEntry($1, "1"); } | ID '=' attr_spec_value - { $$ = new sta::VerilogAttrEntry(*$1, *$3); delete $1; delete $3; } + { $$ = new sta::VerilogAttrEntry($1, $3); } ; attr_spec_value: @@ -544,7 +542,7 @@ attr_spec_value: | STRING { $$ = $1; } | INT - { $$ = new std::string(std::to_string($1)); } + { $$ = std::to_string($1); } ; %% diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index e4c954d8a..8de4aaba2 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -1,57 +1,59 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// +// Copyright (c) 2026, Parallax Software, Inc. +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "VerilogReader.hh" +#include #include +#include +#include -#include "Zlib.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" -#include "Report.hh" #include "Error.hh" -#include "Stats.hh" #include "Liberty.hh" -#include "PortDirection.hh" #include "Network.hh" -#include "VerilogNamespace.hh" +#include "PortDirection.hh" +#include "Report.hh" +#include "Stats.hh" #include "StringUtil.hh" +#include "VerilogNamespace.hh" +#include "Zlib.hh" #include "verilog/VerilogReaderPvt.hh" #include "verilog/VerilogScanner.hh" #include "GeneratedClock.hh" namespace sta { -using std::string; +using VerilogConstant10 = std::uint64_t; -typedef unsigned long long VerilogConstant10; - -static string -verilogBusBitName(const string &bus_name, +static std::string +verilogBusBitName(std::string_view bus_name, int index); static int hierarchyLevel(Net *net, - Network *network); + Network *network); VerilogReader * makeVerilogReader(NetworkReader *network) @@ -60,8 +62,8 @@ makeVerilogReader(NetworkReader *network) } bool -readVerilogFile(const char *filename, - VerilogReader *verilog_reader) +readVerilogFile(std::string_view filename, + VerilogReader *verilog_reader) { return verilog_reader->read(filename); } @@ -74,36 +76,11 @@ deleteVerilogReader(VerilogReader *verilog_reader) //////////////////////////////////////////////////////////////// -class VerilogError -{ -public: - VerilogError(int id, - const char *filename, - int line, - const char *msg, - bool warn); - ~VerilogError(); - const char *msg() const { return msg_; } - const char *filename() const { return filename_; } - int id() const { return id_; } - int line() const { return line_; } - bool warn() const { return warn_; } - -private: - int id_; - const char *filename_; - int line_; - const char *msg_; - bool warn_; - - friend class VerilogErrorCmp; -}; - VerilogError::VerilogError(int id, - const char *filename, - int line, - const char *msg, - bool warn) : + std::string_view filename, + int line, + std::string_view msg, + bool warn) : id_(id), filename_(filename), line_(line), @@ -112,24 +89,18 @@ VerilogError::VerilogError(int id, { } -VerilogError::~VerilogError() -{ - // filename is owned by VerilogReader. - stringDelete(msg_); -} - class VerilogErrorCmp { public: bool operator()(const VerilogError *error1, - const VerilogError *error2) const + const VerilogError *error2) const { - int file_cmp = strcmp(error1->filename_, error2->filename_); + int file_cmp = error1->filename_.compare(error2->filename_); if (file_cmp == 0) { if (error1->line_ == error2->line_) - return strcmp(error1->msg_, error2->msg_) < 0; + return error1->msg_ < error2->msg_; else - return error1->line_ < error2->line_; + return error1->line_ < error2->line_; } else return file_cmp < 0; @@ -142,16 +113,14 @@ VerilogReader::VerilogReader(NetworkReader *network) : report_(network->report()), debug_(network->debug()), network_(network), - library_(nullptr), - black_box_index_(0), zero_net_name_("zero_"), one_net_name_("one_") { - network->setLinkFunc([this] (const char *top_cell_name, - bool make_black_boxes) -> Instance* { + network->setLinkFunc([this](std::string_view top_cell_name, + bool make_black_boxes) -> Instance * { return linkNetwork(top_cell_name, make_black_boxes, true); }); - constant10_max_ = stdstrPrint("%llu", std::numeric_limits::max()); + constant10_max_ = std::to_string(std::numeric_limits::max()); } VerilogReader::~VerilogReader() @@ -162,22 +131,19 @@ VerilogReader::~VerilogReader() void VerilogReader::deleteModules() { - for (const auto [name, module] : module_map_) - delete module; - module_map_.clear(); + deleteContents(module_map_); } bool -VerilogReader::read(const char *filename) +VerilogReader::read(std::string_view filename) { - gzstream::igzstream stream(filename); + gzstream::igzstream stream(std::string(filename).c_str()); if (stream.is_open()) { Stats stats(debug_, report_); VerilogScanner scanner(&stream, filename, report_); VerilogParse parser(&scanner, this); init(filename); bool success = (parser.parse() == 0); - reportStmtCounts(); stats.report("Read verilog"); return success; } @@ -186,55 +152,30 @@ VerilogReader::read(const char *filename) } void -VerilogReader::init(const char *filename) +VerilogReader::init(std::string_view filename) { filename_ = filename; library_ = network_->findLibrary("verilog"); if (library_ == nullptr) - library_ = network_->makeLibrary("verilog", nullptr); - - // Stats - report_stmt_stats_ = debug_->check("verilog", 1); - module_count_ = 0; - inst_mod_count_ = 0; - inst_lib_count_ = 0; - inst_lib_net_arrays_ = 0; - dcl_count_ = 0; - dcl_bus_count_ = 0; - dcl_arg_count_ = 0; - net_scalar_count_ = 0; - net_part_select_count_ = 0; - net_bit_select_count_ = 0; - net_port_ref_scalar_count_ = 0; - net_port_ref_scalar_net_count_ = 0; - net_port_ref_bit_count_ = 0; - net_port_ref_part_count_ = 0; - net_constant_count_ = 0; - assign_count_ = 0; - concat_count_ = 0; - inst_names_ = 0; - port_names_ = 0; - inst_module_names_ = 0; - net_scalar_names_ = 0; - net_bus_names_ = 0; + library_ = network_->makeLibrary("verilog", ""); } VerilogModule * VerilogReader::module(Cell *cell) { - return module_map_.findKey(cell); + return findKey(module_map_, cell); } void -VerilogReader::makeModule(const string *module_vname, - VerilogNetSeq *ports, - VerilogStmtSeq *stmts, +VerilogReader::makeModule(std::string_view module_vname, + VerilogNetSeq *ports, + VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, - int line) + int line) { - const string module_name = moduleVerilogToSta(module_vname); - Cell *cell = network_->findCell(library_, module_name.c_str()); + const std::string module_name = moduleVerilogToSta(module_vname); + Cell *cell = network_->findCell(library_, module_name); if (cell) { VerilogModule *module = module_map_[cell]; delete module; @@ -242,363 +183,309 @@ VerilogReader::makeModule(const string *module_vname, network_->deleteCell(cell); } - VerilogModule *module = new VerilogModule(module_name.c_str(), ports, stmts, - attr_stmts, filename_, line, this); - cell = network_->makeCell(library_, module_name.c_str(), false, filename_.c_str()); + VerilogModule *module = new VerilogModule(module_name, ports, stmts, + attr_stmts, filename_, line, this); + cell = network_->makeCell(library_, module_name, false, filename_); - for (VerilogAttrStmt *stmt : *attr_stmts) { - for (VerilogAttrEntry *entry : *stmt->attrs()) - network_->setAttribute(cell, entry->key(), entry->value()); + if (attr_stmts) { + for (VerilogAttrStmt *stmt : *attr_stmts) { + for (VerilogAttrEntry *entry : *stmt->attrs()) + network_->setAttribute(cell, entry->key(), entry->value()); + } } module_map_[cell] = module; makeCellPorts(cell, module, ports); - module_count_++; - delete module_vname; } void -VerilogReader::makeModule(const string *module_name, - VerilogStmtSeq *port_dcls, - VerilogStmtSeq *stmts, +VerilogReader::makeModule(std::string_view module_vname, + VerilogStmtSeq *port_dcls, + VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, - int line) + int line) { VerilogNetSeq *ports = new VerilogNetSeq; // Pull the port names out of the port declarations. for (VerilogStmt *dcl : *port_dcls) { if (dcl->isDeclaration()) { - VerilogDcl *dcl1 = dynamic_cast(dcl); + VerilogDcl *dcl1 = dynamic_cast(dcl); for (VerilogDclArg *arg : *dcl1->args()) { - VerilogNetNamed *port = new VerilogNetScalar(arg->netName()); - ports->push_back(port); + VerilogNetNamed *port = new VerilogNetScalar(arg->netName()); + ports->push_back(port); } // Add the port declarations to the statements. stmts->push_back(dcl); } } delete port_dcls; - makeModule(module_name, ports, stmts, attr_stmts, line); + makeModule(module_vname, ports, stmts, attr_stmts, line); } void VerilogReader::makeCellPorts(Cell *cell, - VerilogModule *module, - VerilogNetSeq *ports) + VerilogModule *module, + VerilogNetSeq *ports) { - StdStringSet port_names; + StringSet port_names; for (VerilogNet *mod_port : *ports) { - const string &port_name = mod_port->name(); - if (port_names.find(port_name) == port_names.end()) { + const std::string &port_name = mod_port->name(); + if (!port_names.contains(port_name)) { port_names.insert(port_name); if (mod_port->isNamed()) { - if (mod_port->isNamedPortRef()) - makeNamedPortRefCellPorts(cell, module, mod_port, port_names); - else - makeCellPort(cell, module, mod_port->name()); + if (mod_port->isNamedPortRef()) + makeNamedPortRefCellPorts(cell, module, mod_port, port_names); + else + makeCellPort(cell, module, mod_port->name()); } } else warn(165, module->filename(), module->line(), - "module %s repeated port name %s.", - module->name().c_str(), - port_name.c_str()); + "module {} repeated port name {}.", module->name(), port_name); } checkModuleDcls(module, port_names); } Port * VerilogReader::makeCellPort(Cell *cell, - VerilogModule *module, - const string &port_name) + VerilogModule *module, + const std::string &port_name) { - VerilogDcl *dcl = module->declaration(port_name.c_str()); + VerilogDcl *dcl = module->declaration(port_name); if (dcl) { PortDirection *dir = dcl->direction(); - VerilogDclBus *dcl_bus = dynamic_cast(dcl); + VerilogDclBus *dcl_bus = dynamic_cast(dcl); Port *port = dcl->isBus() - ? network_->makeBusPort(cell, port_name.c_str(), dcl_bus->fromIndex(), - dcl_bus->toIndex()) - : network_->makePort(cell, port_name.c_str()); + ? network_->makeBusPort(cell, port_name, dcl_bus->fromIndex(), + dcl_bus->toIndex()) + : network_->makePort(cell, port_name); network_->setDirection(port, dir); return port; } else { warn(166, module->filename(), module->line(), - "module %s missing declaration for port %s.", - module->name().c_str(), - port_name.c_str()); - return network_->makePort(cell, port_name.c_str()); + "module {} missing declaration for port {}.", + module->name(), port_name); + return network_->makePort(cell, port_name); } } void VerilogReader::makeNamedPortRefCellPorts(Cell *cell, - VerilogModule *module, - VerilogNet *mod_port, - StdStringSet &port_names) + VerilogModule *module, + VerilogNet *mod_port, + StringSet &port_names) { PortSeq *member_ports = new PortSeq; - VerilogNetNameIterator *net_name_iter = mod_port->nameIterator(module,this); + VerilogNetNameIterator *net_name_iter = mod_port->nameIterator(module, this); while (net_name_iter->hasNext()) { - const string &net_name = net_name_iter->next(); + const std::string &net_name = net_name_iter->next(); port_names.insert(net_name); Port *port = makeCellPort(cell, module, net_name); member_ports->push_back(port); } delete net_name_iter; // Note that the bundle does NOT have a port declaration. - network_->makeBundlePort(cell, mod_port->name().c_str(), member_ports); + network_->makeBundlePort(cell, mod_port->name(), member_ports); } // Make sure each declaration appears in the module port list. void VerilogReader::checkModuleDcls(VerilogModule *module, - std::set &port_names) + std::set &port_names) { - for (auto const & [port_name, dcl] : *module->declarationMap()) { + for (auto const &[port_name, dcl] : *module->declarationMap()) { PortDirection *dir = dcl->direction(); - if (dir->isInput() - || dir->isOutput() - || dir->isBidirect()) { - if (port_names.find(port_name) == port_names.end()) - linkWarn(197, module->filename(), module->line(), - "module %s declared signal %s is not in the port list.", - module->name().c_str(), - port_name.c_str()); + if (dir->isInput() || dir->isOutput() || dir->isBidirect()) { + if (!port_names.contains(port_name)) + linkWarn(197, module->filename(), module->line(), + "module {} declared signal {} is not in the port list.", + module->name(), port_name); } } } VerilogDcl * VerilogReader::makeDcl(PortDirection *dir, - VerilogDclArgSeq *args, + VerilogDclArgSeq *args, VerilogAttrStmtSeq *attr_stmts, - int line) + int line) { if (dir->isInternal()) { // Prune wire declarations without assigns because they just eat memory. VerilogDclArgSeq *assign_args = nullptr; for (VerilogDclArg *arg : *args) { if (arg->assign()) { - if (assign_args == nullptr) - assign_args = new VerilogDclArgSeq; - assign_args->push_back(arg); + if (assign_args == nullptr) + assign_args = new VerilogDclArgSeq; + assign_args->push_back(arg); } else { - delete arg; - dcl_arg_count_--; + delete arg; } } delete args; if (assign_args) { - dcl_count_++; return new VerilogDcl(dir, assign_args, attr_stmts, line); } else { - attr_stmts->deleteContents(); + deleteContents(attr_stmts); delete attr_stmts; return nullptr; } } else { - dcl_count_++; return new VerilogDcl(dir, args, attr_stmts, line); } } VerilogDcl * VerilogReader::makeDcl(PortDirection *dir, - VerilogDclArg *arg, + VerilogDclArg *arg, VerilogAttrStmtSeq *attr_stmts, - int line) + int line) { - dcl_count_++; return new VerilogDcl(dir, arg, attr_stmts, line); } VerilogDclBus * VerilogReader::makeDclBus(PortDirection *dir, - int from_index, - int to_index, - VerilogDclArg *arg, + int from_index, + int to_index, + VerilogDclArg *arg, VerilogAttrStmtSeq *attr_stmts, - int line) + int line) { - dcl_bus_count_++; - return new VerilogDclBus(dir, from_index, to_index, arg, attr_stmts, - line); + return new VerilogDclBus(dir, from_index, to_index, arg, attr_stmts, line); } VerilogDclBus * VerilogReader::makeDclBus(PortDirection *dir, - int from_index, - int to_index, - VerilogDclArgSeq *args, + int from_index, + int to_index, + VerilogDclArgSeq *args, VerilogAttrStmtSeq *attr_stmts, - int line) + int line) { - dcl_bus_count_++; - return new VerilogDclBus(dir, from_index, to_index, args, attr_stmts, - line); + return new VerilogDclBus(dir, from_index, to_index, args, attr_stmts, line); } VerilogDclArg * -VerilogReader::makeDclArg(const string *net_vname) +VerilogReader::makeDclArg(std::string_view net_vname) { - dcl_arg_count_++; - const string net_name = netVerilogToSta(net_vname); - VerilogDclArg *dcl =new VerilogDclArg(net_name); - delete net_vname; + const std::string net_name = netVerilogToSta(net_vname); + VerilogDclArg *dcl = new VerilogDclArg(net_name); return dcl; } VerilogDclArg * VerilogReader::makeDclArg(VerilogAssign *assign) { - dcl_arg_count_++; return new VerilogDclArg(assign); } VerilogNetPartSelect * -VerilogReader::makeNetPartSelect(const string *net_vname, - int from_index, - int to_index) -{ - net_part_select_count_++; - if (report_stmt_stats_) - net_bus_names_ += net_vname->size() + 1; - const string net_name = netVerilogToSta(net_vname); - VerilogNetPartSelect *select = new VerilogNetPartSelect(net_name, - from_index, - to_index); - delete net_vname; +VerilogReader::makeNetPartSelect(std::string_view net_vname, + int from_index, + int to_index) +{ + const std::string net_name = netVerilogToSta(net_vname); + VerilogNetPartSelect *select = + new VerilogNetPartSelect(net_name, from_index, to_index); return select; } VerilogNetConstant * -VerilogReader::makeNetConstant(const string *constant, +VerilogReader::makeNetConstant(std::string_view constant, int line) { - net_constant_count_++; return new VerilogNetConstant(constant, this, line); } VerilogNetScalar * -VerilogReader::makeNetScalar(const string *net_vname) +VerilogReader::makeNetScalar(std::string_view net_vname) { - net_scalar_count_++; - if (report_stmt_stats_) - net_scalar_names_ += net_vname->size() + 1; - const string net_name = netVerilogToSta(net_vname); + const std::string net_name = netVerilogToSta(net_vname); VerilogNetScalar *scalar = new VerilogNetScalar(net_name); - delete net_vname; return scalar; } VerilogNetBitSelect * -VerilogReader::makeNetBitSelect(const string *net_vname, - int index) +VerilogReader::makeNetBitSelect(std::string_view net_vname, + int index) { - net_bit_select_count_++; - if (report_stmt_stats_) - net_bus_names_ += net_vname->size() + 1; - const string net_name = netVerilogToSta(net_vname); + const std::string net_name = netVerilogToSta(net_vname); VerilogNetBitSelect *select = new VerilogNetBitSelect(net_name, index); - delete net_vname; return select; } VerilogAssign * VerilogReader::makeAssign(VerilogNet *lhs, - VerilogNet *rhs, - int line) + VerilogNet *rhs, + int line) { - assign_count_++; return new VerilogAssign(lhs, rhs, line); } VerilogInst * -VerilogReader::makeModuleInst(const string *module_vname, - const string *inst_vname, +VerilogReader::makeModuleInst(std::string_view module_vname, + std::string_view inst_vname, VerilogNetSeq *pins, VerilogAttrStmtSeq *attr_stmts, - const int line) + int line) { - const string module_name = moduleVerilogToSta(module_vname); - const string inst_name = instanceVerilogToSta(inst_vname); - Cell *cell = network_->findAnyCell(module_name.c_str()); + const std::string module_name = moduleVerilogToSta(module_vname); + const std::string inst_name = instanceVerilogToSta(inst_vname); + Cell *cell = network_->findAnyCell(module_name); LibertyCell *liberty_cell = nullptr; if (cell) liberty_cell = network_->libertyCell(cell); // Instances of liberty with scalar ports are special cased // to reduce the memory footprint of the verilog parser. - if (liberty_cell - && hasScalarNamedPortRefs(liberty_cell, pins)) { - const int port_count = liberty_cell->portBitCount(); - StdStringSeq net_names(port_count); + if (liberty_cell && hasScalarNamedPortRefs(liberty_cell, pins)) { + int port_count = liberty_cell->portBitCount(); + StringSeq net_names(port_count); for (VerilogNet *vnet : *pins) { VerilogNetPortRefScalarNet *vpin = - dynamic_cast(vnet); - const char *port_name = vpin->name().c_str(); - const string &net_name = vpin->netName(); + dynamic_cast(vnet); + std::string_view port_name = vpin->name(); + std::string_view net_name = vpin->netName(); Port *port = network_->findPort(cell, port_name); LibertyPort *lport = network_->libertyPort(port); if (lport->isBus()) { - LibertyPortMemberIterator member_iter(lport); - lport = member_iter.next(); + LibertyPortMemberIterator member_iter(lport); + lport = member_iter.next(); } int pin_index = lport->pinIndex(); net_names[pin_index] = net_name; delete vpin; - net_port_ref_scalar_net_count_--; } - VerilogInst *inst = new VerilogLibertyInst(liberty_cell, inst_name, - net_names, attr_stmts, line); + VerilogInst *inst = + new VerilogLibertyInst(liberty_cell, inst_name, net_names, attr_stmts, line); delete pins; - if (report_stmt_stats_) { - inst_names_ += inst_name.size() + 1; - inst_lib_count_++; - inst_lib_net_arrays_ += port_count; - } - delete module_vname; - delete inst_vname; return inst; } else { - VerilogInst *inst = new VerilogModuleInst(module_name.c_str(), - inst_name.c_str(), - pins, - attr_stmts, - line); - if (report_stmt_stats_) { - inst_module_names_ += module_name.size() + 1; - inst_names_ += inst_name.size() + 1; - inst_mod_count_++; - } - delete module_vname; - delete inst_vname; + VerilogInst *inst = new VerilogModuleInst(module_name, inst_name, + pins, attr_stmts, line); return inst; } } bool VerilogReader::hasScalarNamedPortRefs(LibertyCell *liberty_cell, - VerilogNetSeq *pins) + VerilogNetSeq *pins) { - if (pins - && pins->size() > 0 - && (*pins)[0]->isNamedPortRef()) { + if (pins && !pins->empty() && (*pins)[0]->isNamedPortRef()) { for (VerilogNet *vpin : *pins) { - const char *port_name = vpin->name().c_str(); + std::string_view port_name = vpin->name(); LibertyPort *port = liberty_cell->findLibertyPort(port_name); if (port) { - if (!(port->size() == 1 - && (vpin->isNamedPortRefScalarNet()))) - return false; + if (!(port->size() == 1 && (vpin->isNamedPortRefScalarNet()))) + return false; } else - return false; + return false; } return true; } @@ -607,180 +494,80 @@ VerilogReader::hasScalarNamedPortRefs(LibertyCell *liberty_cell, } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefScalarNet(const string *port_vname) -{ - net_port_ref_scalar_net_count_++; - if (report_stmt_stats_) - port_names_ += port_vname->size() + 1; - const string port_name = portVerilogToSta(port_vname); - VerilogNetPortRef *ref = new VerilogNetPortRefScalarNet(port_name.c_str()); - delete port_vname; +VerilogReader::makeNetNamedPortRefScalarNet(std::string_view port_vname) +{ + const std::string port_name = portVerilogToSta(port_vname); + VerilogNetPortRef *ref = new VerilogNetPortRefScalarNet(port_name); return ref; } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefScalarNet(const string *port_vname, - const string *net_vname) -{ - net_port_ref_scalar_net_count_++; - if (report_stmt_stats_) { - if (net_vname) - net_scalar_names_ += net_vname->size() + 1; - port_names_ += port_vname->size() + 1; - } - const string port_name = portVerilogToSta(port_vname); - const string net_name = netVerilogToSta(net_vname); - VerilogNetPortRef *ref = new VerilogNetPortRefScalarNet(port_name.c_str(), - net_name.c_str()); - delete port_vname; - delete net_vname; +VerilogReader::makeNetNamedPortRefScalarNet(std::string_view port_vname, + std::string_view net_vname) +{ + const std::string port_name = portVerilogToSta(port_vname); + const std::string net_name = netVerilogToSta(net_vname); + VerilogNetPortRef *ref = new VerilogNetPortRefScalarNet(port_name, net_name); return ref; } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefBitSelect(const string *port_vname, - const string *bus_vname, - int index) -{ - net_port_ref_scalar_net_count_++; - const string bus_name = portVerilogToSta(bus_vname); - const string net_name = verilogBusBitName(bus_name, index); - if (report_stmt_stats_) { - net_scalar_names_ += net_name.length() + 1; - port_names_ += port_vname->size() + 1; - } - const string port_name = portVerilogToSta(port_vname); - VerilogNetPortRef *ref = new VerilogNetPortRefScalarNet(port_name.c_str(), - net_name.c_str()); - delete port_vname; - delete bus_vname; +VerilogReader::makeNetNamedPortRefBitSelect(std::string_view port_vname, + std::string_view bus_vname, + int index) +{ + const std::string bus_name = portVerilogToSta(bus_vname); + const std::string net_name = verilogBusBitName(bus_name, index); + const std::string port_name = portVerilogToSta(port_vname); + VerilogNetPortRef *ref = + new VerilogNetPortRefScalarNet(port_name, net_name); return ref; } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefScalar(const string *port_vname, - VerilogNet *net) -{ - net_port_ref_scalar_count_++; - if (report_stmt_stats_) - port_names_ += port_vname->size() + 1; - const string port_name = portVerilogToSta(port_vname); - VerilogNetPortRef *ref = new VerilogNetPortRefScalar(port_name.c_str(), net); - delete port_vname; +VerilogReader::makeNetNamedPortRefScalar(std::string_view port_vname, + VerilogNet *net) +{ + const std::string port_name = portVerilogToSta(port_vname); + VerilogNetPortRef *ref = new VerilogNetPortRefScalar(port_name, net); return ref; } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefBit(const string *port_vname, - int index, - VerilogNet *net) -{ - net_port_ref_bit_count_++; - const string port_name = portVerilogToSta(port_vname); - VerilogNetPortRef *ref = new VerilogNetPortRefBit(port_name.c_str(), - index, net); - delete port_vname; +VerilogReader::makeNetNamedPortRefBit(std::string_view port_vname, + int index, + VerilogNet *net) +{ + const std::string port_name = portVerilogToSta(port_vname); + VerilogNetPortRef *ref = new VerilogNetPortRefBit(port_name, index, net); return ref; } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefPart(const string *port_vname, - int from_index, - int to_index, - VerilogNet *net) -{ - net_port_ref_part_count_++; - const string port_name = portVerilogToSta(port_vname); - VerilogNetPortRef *ref = new VerilogNetPortRefPart(port_name, - from_index, - to_index, net); - delete port_vname; +VerilogReader::makeNetNamedPortRefPart(std::string_view port_vname, + int from_index, + int to_index, + VerilogNet *net) +{ + const std::string port_name = portVerilogToSta(port_vname); + VerilogNetPortRef *ref = + new VerilogNetPortRefPart(port_name, from_index, to_index, net); return ref; } VerilogNetConcat * VerilogReader::makeNetConcat(VerilogNetSeq *nets) { - concat_count_++; return new VerilogNetConcat(nets); } -#define printClassMemory(name, class_name, count) \ - report_->reportLine(" %-20s %9d * %3zu = %6.1fMb\n", \ - name, \ - count, \ - sizeof(class_name), \ - (count * sizeof(class_name) * 1e-6)) - -#define printStringMemory(name, count) \ - report_->reportLine(" %-20s %6.1fMb", name, count * 1e-6) - -void -VerilogReader::reportStmtCounts() -{ - if (debug_->check("verilog", 1)) { - report_->reportLine("Verilog stats"); - printClassMemory("modules", VerilogModule, module_count_); - printClassMemory("module insts", VerilogModuleInst, inst_mod_count_); - printClassMemory("liberty insts", VerilogLibertyInst, inst_lib_count_); - printClassMemory("liberty net arrays", char *, inst_lib_net_arrays_); - printClassMemory("declarations", VerilogDcl, dcl_count_); - printClassMemory("bus declarations", VerilogDclBus, dcl_bus_count_); - printClassMemory("declaration args", VerilogDclArg, dcl_arg_count_); - printClassMemory("port ref scalar", VerilogNetPortRefScalar, - net_port_ref_scalar_count_); - printClassMemory("port ref scalar net", VerilogNetPortRefScalarNet, - net_port_ref_scalar_net_count_); - printClassMemory("port ref bit", VerilogNetPortRefBit, - net_port_ref_bit_count_); - printClassMemory("port ref part", VerilogNetPortRefPart, - net_port_ref_part_count_); - printClassMemory("scalar nets", VerilogNetScalar, net_scalar_count_); - printClassMemory("bus bit nets",VerilogNetBitSelect,net_bit_select_count_); - printClassMemory("bus range nets", VerilogNetPartSelect, - net_part_select_count_); - printClassMemory("constant nets", VerilogNetConstant, net_constant_count_); - printClassMemory("concats", VerilogNetConcat, concat_count_); - printClassMemory("assigns", VerilogAssign, assign_count_); - printStringMemory("instance names", inst_names_); - printStringMemory("instance mod names", inst_module_names_); - printStringMemory("port names", port_names_); - printStringMemory("net scalar names", net_scalar_names_); - printStringMemory("net bus names", net_bus_names_); - } -} - -void -VerilogReader::error(int id, - const char *filename, - int line, - const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - report_->vfileError(id, filename, line, fmt, args); - va_end(args); -} - -void -VerilogReader::warn(int id, - const char *filename, - int line, - const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - report_->vfileWarn(id, filename, line, fmt, args); - va_end(args); -} - //////////////////////////////////////////////////////////////// -VerilogModule::VerilogModule(const string &name, +VerilogModule::VerilogModule(std::string_view name, VerilogNetSeq *ports, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, - const string &filename, + std::string_view filename, int line, VerilogReader *reader) : VerilogStmt(line), @@ -795,65 +582,62 @@ VerilogModule::VerilogModule(const string &name, VerilogModule::~VerilogModule() { - ports_->deleteContents(); + deleteContents(ports_); delete ports_; - stmts_->deleteContents(); + deleteContents(stmts_); delete stmts_; - attr_stmts_->deleteContents(); + deleteContents(attr_stmts_); delete attr_stmts_; } void VerilogModule::parseStmts(VerilogReader *reader) { - StdStringSet inst_names; + StringSet inst_names; for (VerilogStmt *stmt : *stmts_) { if (stmt->isDeclaration()) - parseDcl(dynamic_cast(stmt), reader); + parseDcl(dynamic_cast(stmt), reader); else if (stmt->isInstance()) - checkInstanceName(dynamic_cast(stmt), inst_names, - reader); + checkInstanceName(dynamic_cast(stmt), inst_names, reader); } } void VerilogModule::parseDcl(VerilogDcl *dcl, - VerilogReader *reader) + VerilogReader *reader) { for (VerilogDclArg *arg : *dcl->args()) { if (arg->isNamed()) { - const string &net_name = arg->netName(); - VerilogDcl *existing_dcl = dcl_map_[net_name.c_str()]; + const std::string &net_name = arg->netName(); + VerilogDcl *existing_dcl = dcl_map_[net_name]; if (existing_dcl) { PortDirection *existing_dir = existing_dcl->direction(); if (existing_dir->isInternal()) // wire dcl can be used as modifier for input/inout dcls. // Ignore the wire dcl. - dcl_map_[net_name.c_str()] = dcl; + dcl_map_[net_name] = dcl; else if (dcl->direction()->isTristate()) { if (existing_dir->isOutput()) // tri dcl can be used as modifier for input/output/inout dcls. // Keep the tristate dcl for outputs because it is more specific // but ignore it for inputs and bidirs. - dcl_map_[net_name.c_str()] = dcl; + dcl_map_[net_name] = dcl; } else if (dcl->direction()->isPowerGround() - && (existing_dir->isOutput() - || existing_dir->isInput() + && (existing_dir->isOutput() || existing_dir->isInput() || existing_dir->isBidirect())) // supply0/supply1 dcl can be used as modifier for // input/output/inout dcls. - dcl_map_[net_name.c_str()] = dcl; + dcl_map_[net_name] = dcl; else if (!dcl->direction()->isInternal()) { - string net_vname = netVerilogName(net_name.c_str()); - reader->warn(1395, filename_.c_str(), dcl->line(), - "signal %s previously declared on line %d.", - net_vname.c_str(), - existing_dcl->line()); + std::string net_vname = netVerilogName(net_name); + reader->warn(1395, filename_, dcl->line(), + "signal {} previously declared on line {}.", + net_vname, existing_dcl->line()); } } else - dcl_map_[net_name.c_str()] = dcl; + dcl_map_[net_name] = dcl; } } } @@ -862,21 +646,20 @@ VerilogModule::parseDcl(VerilogDcl *dcl, // expansion so errors are only reported once. void VerilogModule::checkInstanceName(VerilogInst *inst, - StdStringSet &inst_names, - VerilogReader *reader) + StringSet &inst_names, + VerilogReader *reader) { - string inst_name = inst->instanceName(); - if (inst_names.find(inst_name) != inst_names.end()) { + std::string inst_name = inst->instanceName(); + if (inst_names.contains(inst_name)) { int i = 1; - string replacement_name; + std::string replacement_name; do { - replacement_name = stdstrPrint("%s_%d", inst_name.c_str(), i++); - } while (inst_names.find(replacement_name) != inst_names.end()); - string inst_vname = instanceVerilogName(inst_name.c_str()); - reader->warn(1396, filename_.c_str(), inst->line(), - "instance name %s duplicated - renamed to %s.", - inst_vname.c_str(), - replacement_name.c_str()); + replacement_name = sta::format("{}_{}", inst_name, i++); + } while (inst_names.contains(replacement_name)); + std::string inst_vname = instanceVerilogName(inst_name); + reader->warn(1396, filename_, inst->line(), + "instance name {} duplicated - renamed to {}.", inst_vname, + replacement_name); inst_name = replacement_name; inst->setInstanceName(inst_name); } @@ -884,9 +667,9 @@ VerilogModule::checkInstanceName(VerilogInst *inst, } VerilogDcl * -VerilogModule::declaration(const string &net_name) +VerilogModule::declaration(std::string_view net_name) { - return dcl_map_.findKey(net_name.c_str()); + return findStringKey(dcl_map_, net_name); } //////////////////////////////////////////////////////////////// @@ -896,9 +679,9 @@ VerilogStmt::VerilogStmt(int line) : { } -VerilogInst::VerilogInst(const string &inst_name, +VerilogInst::VerilogInst(std::string_view inst_name, VerilogAttrStmtSeq *attr_stmts, - const int line) : + int line) : VerilogStmt(line), inst_name_(inst_name), attr_stmts_(attr_stmts) @@ -907,22 +690,24 @@ VerilogInst::VerilogInst(const string &inst_name, VerilogInst::~VerilogInst() { - attr_stmts_->deleteContents(); + deleteContents(attr_stmts_); delete attr_stmts_; } void -VerilogInst::setInstanceName(const string &inst_name) +VerilogInst::setInstanceName(const std::string &inst_name) { inst_name_ = inst_name; } -VerilogModuleInst::VerilogModuleInst(const string &module_name, - const string &inst_name, +VerilogModuleInst::VerilogModuleInst(std::string_view module_name, + std::string_view inst_name, VerilogNetSeq *pins, VerilogAttrStmtSeq *attr_stmts, int line) : - VerilogInst(inst_name, attr_stmts, line), + VerilogInst(inst_name, + attr_stmts, + line), module_name_(module_name), pins_(pins) { @@ -931,7 +716,7 @@ VerilogModuleInst::VerilogModuleInst(const string &module_name, VerilogModuleInst::~VerilogModuleInst() { if (pins_) { - pins_->deleteContents(); + deleteContents(pins_); delete pins_; } } @@ -939,24 +724,20 @@ VerilogModuleInst::~VerilogModuleInst() bool VerilogModuleInst::hasPins() { - return pins_ - && pins_->size() > 0; - + return pins_ && !pins_->empty(); } bool VerilogModuleInst::namedPins() { - return pins_ - && pins_->size() > 0 - && (*pins_)[0]->isNamedPortRef(); + return pins_ && !pins_->empty() && (*pins_)[0]->isNamedPortRef(); } VerilogLibertyInst::VerilogLibertyInst(LibertyCell *cell, - const string &inst_name, - const StdStringSeq &net_names, + std::string_view inst_name, + const StringSeq &net_names, VerilogAttrStmtSeq *attr_stmts, - const int line) : + int line) : VerilogInst(inst_name, attr_stmts, line), cell_(cell), net_names_(net_names) @@ -988,9 +769,9 @@ VerilogDcl::VerilogDcl(PortDirection *dir, VerilogDcl::~VerilogDcl() { - args_->deleteContents(); + deleteContents(args_); delete args_; - attr_stmts_->deleteContents(); + deleteContents(attr_stmts_); delete attr_stmts_; } @@ -1000,7 +781,7 @@ VerilogDcl::appendArg(VerilogDclArg *arg) args_->push_back(arg); } -const string & +const std::string & VerilogDcl::portName() { return (*args_)[0]->netName(); @@ -1033,10 +814,10 @@ VerilogDclBus::VerilogDclBus(PortDirection *dir, int VerilogDclBus::size() const { - return abs(to_index_ - from_index_) + 1; + return std::abs(to_index_ - from_index_) + 1; } -VerilogDclArg::VerilogDclArg(const string &net_name) : +VerilogDclArg::VerilogDclArg(std::string_view net_name) : net_name_(net_name), assign_(nullptr) { @@ -1047,12 +828,9 @@ VerilogDclArg::VerilogDclArg(VerilogAssign *assign) : { } -VerilogDclArg::~VerilogDclArg() -{ - delete assign_; -} +VerilogDclArg::~VerilogDclArg() { delete assign_; } -const string & +const std::string & VerilogDclArg::netName() { if (assign_) @@ -1062,8 +840,8 @@ VerilogDclArg::netName() } VerilogAssign::VerilogAssign(VerilogNet *lhs, - VerilogNet *rhs, - int line) : + VerilogNet *rhs, + int line) : VerilogStmt(line), lhs_(lhs), rhs_(rhs) @@ -1081,32 +859,31 @@ VerilogAssign::~VerilogAssign() class VerilogNullNetNameIterator : public VerilogNetNameIterator { public: - virtual bool hasNext() { return false; } - virtual const string &next(); + bool hasNext() override { return false; } + const std::string &next() override; }; -const string & +const std::string & VerilogNullNetNameIterator::next() { - static const string null; + static const std::string null; return null; } class VerilogOneNetNameIterator : public VerilogNetNameIterator { public: - VerilogOneNetNameIterator(const string &name); - virtual bool hasNext(); - virtual const string &next(); + VerilogOneNetNameIterator(const std::string &name); + bool hasNext() override; + const std::string &next() override; protected: - string name_; - bool has_next_; + std::string name_; + bool has_next_{true}; }; -VerilogOneNetNameIterator::VerilogOneNetNameIterator(const string &name) : - name_(name), - has_next_(true) +VerilogOneNetNameIterator::VerilogOneNetNameIterator(const std::string &name) : + name_(name) { } @@ -1116,7 +893,7 @@ VerilogOneNetNameIterator::hasNext() return has_next_; } -const string & +const std::string & VerilogOneNetNameIterator::next() { has_next_ = false; @@ -1126,23 +903,23 @@ VerilogOneNetNameIterator::next() class VerilogBusNetNameIterator : public VerilogNetNameIterator { public: - VerilogBusNetNameIterator(const string bus_name, - int from_index, - int to_index); - virtual bool hasNext(); - virtual const string &next(); + VerilogBusNetNameIterator(std::string_view bus_name, + int from_index, + int to_index); + bool hasNext() override; + const std::string &next() override; protected: - const string bus_name_; + const std::string bus_name_; int from_index_; int to_index_; int index_; - string bit_name_; + std::string bit_name_; }; -VerilogBusNetNameIterator::VerilogBusNetNameIterator(const string bus_name, - int from_index, - int to_index) : +VerilogBusNetNameIterator::VerilogBusNetNameIterator(std::string_view bus_name, + int from_index, + int to_index) : bus_name_(bus_name), from_index_(from_index), to_index_(to_index), @@ -1153,13 +930,11 @@ VerilogBusNetNameIterator::VerilogBusNetNameIterator(const string bus_name, bool VerilogBusNetNameIterator::hasNext() { - return (to_index_ > from_index_ - && index_ <= to_index_) - || (to_index_ <= from_index_ - && index_ >= to_index_); + return (to_index_ > from_index_ && index_ <= to_index_) + || (to_index_ <= from_index_ && index_ >= to_index_); } -const string & +const std::string & VerilogBusNetNameIterator::next() { bit_name_ = verilogBusBitName(bus_name_, index_); @@ -1170,33 +945,33 @@ VerilogBusNetNameIterator::next() return bit_name_; } -static string -verilogBusBitName(const string &bus_name, +static std::string +verilogBusBitName(std::string_view bus_name, int index) { - return stdstrPrint("%s[%d]", bus_name.c_str(), index); + return sta::format("{}[{}]", bus_name, index); } class VerilogConstantNetNameIterator : public VerilogNetNameIterator { public: VerilogConstantNetNameIterator(VerilogConstantValue *value, - const string &zero, - const string &one); - virtual bool hasNext(); - virtual const string &next(); + const std::string &zero, + const std::string &one); + bool hasNext() override; + const std::string &next() override; private: VerilogConstantValue *value_; - const string &zero_; - const string &one_; + const std::string &zero_; + const std::string &one_; int bit_index_; }; -VerilogConstantNetNameIterator:: -VerilogConstantNetNameIterator(VerilogConstantValue *value, - const string &zero, - const string &one) : +VerilogConstantNetNameIterator::VerilogConstantNetNameIterator( + VerilogConstantValue *value, + const std::string &zero, + const std::string &one) : value_(value), zero_(zero), one_(one), @@ -1210,7 +985,7 @@ VerilogConstantNetNameIterator::hasNext() return bit_index_ >= 0; } -const string & +const std::string & VerilogConstantNetNameIterator::next() { return (*value_)[bit_index_--] ? one_ : zero_; @@ -1220,30 +995,32 @@ class VerilogNetConcatNameIterator : public VerilogNetNameIterator { public: VerilogNetConcatNameIterator(VerilogNetSeq *nets, - VerilogModule *module, - VerilogReader *reader); - virtual ~VerilogNetConcatNameIterator(); - virtual bool hasNext(); - virtual const string &next(); + VerilogModule *module, + VerilogReader *reader); + ~VerilogNetConcatNameIterator() override; + bool hasNext() override; + const std::string &next() override; private: VerilogModule *module_; VerilogReader *reader_; - VerilogNetSeq::Iterator net_iter_; - VerilogNetNameIterator *net_name_iter_; + VerilogNetSeq *nets_; + VerilogNetSeq::iterator net_iter_; + VerilogNetNameIterator *net_name_iter_{nullptr}; }; -VerilogNetConcatNameIterator:: -VerilogNetConcatNameIterator(VerilogNetSeq *nets, - VerilogModule *module, - VerilogReader *reader) : +VerilogNetConcatNameIterator::VerilogNetConcatNameIterator(VerilogNetSeq *nets, + VerilogModule *module, + VerilogReader *reader) : module_(module), reader_(reader), - net_iter_(nets), - net_name_iter_(nullptr) + nets_(nets), + net_iter_(nets->begin()) { - if (net_iter_.hasNext()) - net_name_iter_ = net_iter_.next()->nameIterator(module, reader); + if (net_iter_ != nets_->end()) { + VerilogNet *net = *net_iter_++; + net_name_iter_ = net->nameIterator(module, reader); + } } VerilogNetConcatNameIterator::~VerilogNetConcatNameIterator() @@ -1254,50 +1031,45 @@ VerilogNetConcatNameIterator::~VerilogNetConcatNameIterator() bool VerilogNetConcatNameIterator::hasNext() { - return (net_name_iter_ && net_name_iter_->hasNext()) - || net_iter_.hasNext(); + return (net_name_iter_ && net_name_iter_->hasNext()) || net_iter_ != nets_->end(); } -const string & +const std::string & VerilogNetConcatNameIterator::next() { if (net_name_iter_ && net_name_iter_->hasNext()) return net_name_iter_->next(); else { - if (net_iter_.hasNext()) { - VerilogNet *net = net_iter_.next(); + if (net_iter_ != nets_->end()) { + VerilogNet *net = *net_iter_++; delete net_name_iter_; net_name_iter_ = net->nameIterator(module_, reader_); if (net_name_iter_ && net_name_iter_->hasNext()) - return net_name_iter_->next(); + return net_name_iter_->next(); } } - static const string null; + static const std::string null; return null; } //////////////////////////////////////////////////////////////// -const string VerilogNetUnnamed::null_; +const std::string VerilogNetUnnamed::null_; -VerilogNetNamed::VerilogNetNamed(const string &name) : +VerilogNetNamed::VerilogNetNamed(std::string_view name) : VerilogNet(), name_(name) { } -VerilogNetNamed::~VerilogNetNamed() -{ -} - -VerilogNetScalar::VerilogNetScalar(const string &name) : +VerilogNetScalar::VerilogNetScalar(std::string_view name) : VerilogNetNamed(name) { } static int -verilogNetScalarSize(const char *name, - VerilogModule *module) +verilogNetScalarSize(std::string_view name, + VerilogModule *module) { VerilogDcl *dcl = module->declaration(name); if (dcl) @@ -1310,19 +1082,19 @@ verilogNetScalarSize(const char *name, int VerilogNetScalar::size(VerilogModule *module) { - return verilogNetScalarSize(name_.c_str(), module); + return verilogNetScalarSize(name_, module); } static VerilogNetNameIterator * -verilogNetScalarNameIterator(const string &name, - VerilogModule *module) +verilogNetScalarNameIterator(const std::string &name, + VerilogModule *module) { if (!name.empty()) { VerilogDcl *dcl = module->declaration(name); if (dcl && dcl->isBus()) { VerilogDclBus *dcl_bus = dynamic_cast(dcl); return new VerilogBusNetNameIterator(name, dcl_bus->fromIndex(), - dcl_bus->toIndex()); + dcl_bus->toIndex()); } } return new VerilogOneNetNameIterator(name); @@ -1330,14 +1102,15 @@ verilogNetScalarNameIterator(const string &name, VerilogNetNameIterator * VerilogNetScalar::nameIterator(VerilogModule *module, - VerilogReader *) + VerilogReader *) { - return verilogNetScalarNameIterator(name_.c_str(), module); + return verilogNetScalarNameIterator(name_, module); } -VerilogNetBitSelect::VerilogNetBitSelect(const string &name, - int index) : - VerilogNetNamed(verilogBusBitName(name, index)), +VerilogNetBitSelect::VerilogNetBitSelect(std::string_view name, + int index) : + VerilogNetNamed(verilogBusBitName(name, + index)), index_(index) { } @@ -1350,14 +1123,14 @@ VerilogNetBitSelect::size(VerilogModule *) VerilogNetNameIterator * VerilogNetBitSelect::nameIterator(VerilogModule *, - VerilogReader *) + VerilogReader *) { return new VerilogOneNetNameIterator(name_); } -VerilogNetPartSelect::VerilogNetPartSelect(const string &name, - int from_index, - int to_index): +VerilogNetPartSelect::VerilogNetPartSelect(std::string_view name, + int from_index, + int to_index) : VerilogNetNamed(name), from_index_(from_index), to_index_(to_index) @@ -1375,26 +1148,26 @@ VerilogNetPartSelect::size(VerilogModule *) VerilogNetNameIterator * VerilogNetPartSelect::nameIterator(VerilogModule *, - VerilogReader *) + VerilogReader *) { - return new VerilogBusNetNameIterator(name_.c_str(), from_index_, to_index_); + return new VerilogBusNetNameIterator(name_, from_index_, to_index_); } -VerilogNetConstant::VerilogNetConstant(const string *constant, - VerilogReader *reader, +VerilogNetConstant::VerilogNetConstant(std::string_view constant, + VerilogReader *reader, int line) { parseConstant(constant, reader, line); } void -VerilogNetConstant::parseConstant(const string *constant, - VerilogReader *reader, +VerilogNetConstant::parseConstant(std::string_view constant, + VerilogReader *reader, int line) { // Find constant size. - size_t csize_end = constant->find('\''); - string csize = constant->substr(0, csize_end); + size_t csize_end = constant.find('\''); + std::string csize(constant.substr(0, csize_end)); // Read the constant size. size_t size = std::stol(csize); @@ -1402,38 +1175,37 @@ VerilogNetConstant::parseConstant(const string *constant, // Read the constant base. size_t base_idx = csize_end + 1; - char base = constant->at(base_idx); + char base = constant.at(base_idx); switch (base) { - case 'b': - case 'B': - parseConstant(constant, base_idx, 2, 1); - break; - case 'o': - case 'O': - parseConstant(constant, base_idx, 8, 3); - break; - case 'h': - case 'H': - parseConstant(constant, base_idx, 16, 4); - break; - case 'd': - case 'D': - parseConstant10(constant, base_idx, reader, line); - break; - default: - case '\0': - reader->report()->fileWarn(1861, reader->filename(), line, - "unknown constant base."); - break; + case 'b': + case 'B': + parseConstant(constant, base_idx, 2, 1); + break; + case 'o': + case 'O': + parseConstant(constant, base_idx, 8, 3); + break; + case 'h': + case 'H': + parseConstant(constant, base_idx, 16, 4); + break; + case 'd': + case 'D': + parseConstant10(constant, base_idx, reader, line); + break; + default: + case '\0': + reader->report()->fileWarn(1861, reader->filename(), line, + "unknown constant base."); + break; } - delete constant; } void -VerilogNetConstant::parseConstant(const string *constant, - size_t base_idx, - int base, - int digit_bit_count) +VerilogNetConstant::parseConstant(std::string_view constant, + size_t base_idx, + int base, + int digit_bit_count) { // Scan the constant from LSD to MSD. size_t size = value_->size(); @@ -1441,50 +1213,49 @@ VerilogNetConstant::parseConstant(const string *constant, char *end; value_digit_str[1] = '\0'; size_t bit = 0; - size_t idx = constant->size() - 1; + size_t idx = constant.size() - 1; while (bit < size) { - char ch = (idx > base_idx) ? constant->at(idx--) : '0'; + char ch = (idx > base_idx) ? constant.at(idx--) : '0'; // Skip underscores. if (ch != '_') { value_digit_str[0] = ch; unsigned value_digit = strtoul(value_digit_str, &end, base); unsigned mask = 1; for (int b = 0; b < digit_bit_count && bit < size; b++) { - bool value_bit = (value_digit & mask) != 0; - (*value_)[bit++] = value_bit; - mask = mask << 1; + bool value_bit = (value_digit & mask) != 0; + (*value_)[bit++] = value_bit; + mask = mask << 1; } } } } void -VerilogNetConstant::parseConstant10(const string *constant, - size_t base_idx, - VerilogReader *reader, - int line) +VerilogNetConstant::parseConstant10(std::string_view constant, + size_t base_idx, + VerilogReader *reader, + int line) { // Copy the constant skipping underscores. - string tmp; - for (size_t i = base_idx + 1; i < constant->size(); i++) { - char ch = constant->at(i); + std::string constant1; + for (size_t i = base_idx + 1; i < constant.size(); i++) { + char ch = constant.at(i); if (ch != '_') - tmp += ch; + constant1 += ch; } size_t size = value_->size(); - size_t length = tmp.size(); - const string &constant10_max = reader->constant10Max(); + size_t length = constant1.size(); + const std::string &constant10_max = reader->constant10Max(); size_t max_length = constant10_max.size(); if (length > max_length - || (length == max_length - && tmp > constant10_max)) + || (length == max_length && constant1 > constant10_max)) reader->warn(1397, reader->filename(), line, - "base 10 constant greater than %s not supported.", - constant10_max.c_str()); + "base 10 constant greater than {} not supported.", + constant10_max); else { size_t *end = nullptr; - VerilogConstant10 value = std::stoull(tmp, end, 10); + VerilogConstant10 value = std::stoull(constant1, end, 10); VerilogConstant10 mask = 1; for (size_t bit = 0; bit < size; bit++) { (*value_)[bit] = (value & mask) != 0; @@ -1493,28 +1264,22 @@ VerilogNetConstant::parseConstant10(const string *constant, } } -VerilogNetConstant::~VerilogNetConstant() -{ - delete value_; -} +VerilogNetConstant::~VerilogNetConstant() { delete value_; } VerilogNetNameIterator * VerilogNetConstant::nameIterator(VerilogModule *, - VerilogReader *reader) + VerilogReader *reader) { - return new VerilogConstantNetNameIterator(value_, - reader->zeroNetName(), - reader->oneNetName()); + return new VerilogConstantNetNameIterator(value_, reader->zeroNetName(), + reader->oneNetName()); } - int VerilogNetConstant::size(VerilogModule *) { return value_->size(); } - VerilogNetConcat::VerilogNetConcat(VerilogNetSeq *nets) : nets_(nets) { @@ -1522,41 +1287,38 @@ VerilogNetConcat::VerilogNetConcat(VerilogNetSeq *nets) : VerilogNetConcat::~VerilogNetConcat() { - nets_->deleteContents(); + deleteContents(nets_); delete nets_; } int VerilogNetConcat::size(VerilogModule *module) { - VerilogNetSeq::Iterator net_iter(nets_); int sz = 0; - while (net_iter.hasNext()) { - VerilogNet *net = net_iter.next(); + for (VerilogNet *net : *nets_) sz += net->size(module); - } return sz; } VerilogNetNameIterator * VerilogNetConcat::nameIterator(VerilogModule *module, - VerilogReader *reader) + VerilogReader *reader) { return new VerilogNetConcatNameIterator(nets_, module, reader); } -VerilogNetPortRef::VerilogNetPortRef(const string &name) : +VerilogNetPortRef::VerilogNetPortRef(std::string_view name) : VerilogNetScalar(name) { } -VerilogNetPortRefScalarNet::VerilogNetPortRefScalarNet(const string &name) : +VerilogNetPortRefScalarNet::VerilogNetPortRefScalarNet(std::string_view name) : VerilogNetPortRef(name) { } -VerilogNetPortRefScalarNet::VerilogNetPortRefScalarNet(const string &name, - const string &net_name) : +VerilogNetPortRefScalarNet::VerilogNetPortRefScalarNet(std::string_view name, + std::string_view net_name) : VerilogNetPortRef(name), net_name_(net_name) { @@ -1578,22 +1340,19 @@ VerilogNetPortRefScalarNet::size(VerilogModule *module) VerilogNetNameIterator * VerilogNetPortRefScalarNet::nameIterator(VerilogModule *module, - VerilogReader *) + VerilogReader *) { return verilogNetScalarNameIterator(net_name_, module); } -VerilogNetPortRefScalar::VerilogNetPortRefScalar(const string &name, - VerilogNet *net) : +VerilogNetPortRefScalar::VerilogNetPortRefScalar(std::string_view name, + VerilogNet *net) : VerilogNetPortRef(name), net_(net) { } -VerilogNetPortRefScalar::~VerilogNetPortRefScalar() -{ - delete net_; -} +VerilogNetPortRefScalar::~VerilogNetPortRefScalar() { delete net_; } int VerilogNetPortRefScalar::size(VerilogModule *module) @@ -1606,7 +1365,7 @@ VerilogNetPortRefScalar::size(VerilogModule *module) VerilogNetNameIterator * VerilogNetPortRefScalar::nameIterator(VerilogModule *module, - VerilogReader *reader) + VerilogReader *reader) { if (net_) return net_->nameIterator(module, reader); @@ -1614,66 +1373,55 @@ VerilogNetPortRefScalar::nameIterator(VerilogModule *module, return new VerilogNullNetNameIterator(); } -VerilogNetPortRefBit::VerilogNetPortRefBit(const string &name, - int index, - VerilogNet *net) : - VerilogNetPortRefScalar(name, net), - bit_name_(verilogBusBitName(name, index)) +VerilogNetPortRefBit::VerilogNetPortRefBit(std::string_view name, + int index, + VerilogNet *net) : + VerilogNetPortRefScalar(name, + net), + bit_name_(verilogBusBitName(name, + index)) { } -VerilogNetPortRefPart::VerilogNetPortRefPart(const string &name, - int from_index, - int to_index, - VerilogNet *net) : +VerilogNetPortRefPart::VerilogNetPortRefPart(std::string_view name, + int from_index, + int to_index, + VerilogNet *net) : VerilogNetPortRefBit(name, from_index, net), to_index_(to_index) { } -const string & +const std::string & VerilogNetPortRefPart::name() const { return name_; } -VerilogAttrEntry::VerilogAttrEntry(const string &key, - const string &value) : +VerilogAttrEntry::VerilogAttrEntry(std::string_view key, + std::string_view value) : key_(key), value_(value) { } -string -VerilogAttrEntry::key() -{ - return key_; -} - -string -VerilogAttrEntry::value() -{ - return value_; -} - -VerilogAttrStmt::VerilogAttrStmt(VerilogAttrEntrySeq *attrs): - attrs_(attrs) +VerilogAttrStmt::VerilogAttrStmt(VerilogAttrEntrySeq *attrs) : + attrs_(attrs) { } VerilogAttrStmt::~VerilogAttrStmt() { - attrs_->deleteContents(); + deleteContents(attrs_); delete attrs_; } -VerilogAttrEntrySeq* +VerilogAttrEntrySeq * VerilogAttrStmt::attrs() { return attrs_; } - //////////////////////////////////////////////////////////////// // // Link verilog network @@ -1681,115 +1429,112 @@ VerilogAttrStmt::attrs() //////////////////////////////////////////////////////////////// // Verilog net name to network net map. -typedef Map BindingMap; +using BindingMap = std::map>; class VerilogBindingTbl { public: - VerilogBindingTbl(const string &zero_net_name_, - const string &one_net_name_); - Net *ensureNetBinding(const char *net_name, - Instance *inst, - NetworkReader *network); - Net *find(const char *name, - NetworkReader *network); - void bind(const char *name, - Net *net); + VerilogBindingTbl(const std::string &zero_net_name_, + const std::string &one_net_name_); + Net *ensureNetBinding(std::string_view net_name, + Instance *inst, + NetworkReader *network); + Net *find(std::string_view name, + NetworkReader *network); + void bind(std::string_view name, + Net *net); private: - const string &zero_net_name_; - const string &one_net_name_; - BindingMap map_; + const std::string &zero_net_name_; + const std::string &one_net_name_; + BindingMap net_map_; }; Instance * -VerilogReader::linkNetwork(const char *top_cell_name, +VerilogReader::linkNetwork(std::string_view top_cell_name, bool make_black_boxes, bool delete_modules) { if (library_) { - Cell *top_cell = network_->findCell(library_, top_cell_name); + const std::string top_cell_str(top_cell_name); + Cell *top_cell = network_->findCell(library_, top_cell_str); VerilogModule *module = this->module(top_cell); if (module) { // Seed the recursion for expansion with the top level instance. - Instance *top_instance = network_->makeInstance(top_cell, top_cell_name, nullptr); + Instance *top_instance = + network_->makeInstance(top_cell, top_cell_str, nullptr); VerilogBindingTbl bindings(zero_net_name_, one_net_name_); - VerilogNetSeq::Iterator port_iter(module->ports()); - while (port_iter.hasNext()) { - VerilogNet *mod_port = port_iter.next(); - VerilogNetNameIterator *net_name_iter = mod_port->nameIterator(module, - this); - while (net_name_iter->hasNext()) { - const string &net_name = net_name_iter->next(); - Port *port = network_->findPort(top_cell, net_name.c_str()); - Net *net = bindings.ensureNetBinding(net_name.c_str(), top_instance, network_); - // Guard against repeated port name. - if (network_->findPin(top_instance, port) == nullptr) { - Pin *pin = network_->makePin(top_instance, port, nullptr); - network_->makeTerm(pin, net); - } - } - delete net_name_iter; + for (VerilogNet *mod_port : *module->ports()) { + VerilogNetNameIterator *net_name_iter = mod_port->nameIterator(module, this); + while (net_name_iter->hasNext()) { + const std::string &net_name = net_name_iter->next(); + Port *port = network_->findPort(top_cell, net_name); + Net *net = + bindings.ensureNetBinding(net_name, top_instance, network_); + // Guard against repeated port name. + if (network_->findPin(top_instance, port) == nullptr) { + Pin *pin = network_->makePin(top_instance, port, nullptr); + network_->makeTerm(pin, net); + } + } + delete net_name_iter; } makeModuleInstBody(module, top_instance, &bindings, make_black_boxes); bool errors = reportLinkErrors(); if (delete_modules) deleteModules(); if (errors) { - network_->deleteInstance(top_instance); - return nullptr; + network_->deleteInstance(top_instance); + return nullptr; } else - return top_instance; + return top_instance; } else { - report_->error(1398, "%s is not a verilog module.", top_cell_name); + report_->error(1390, "{} is not a verilog module.", top_cell_name); return nullptr; } } else { - report_->error(1399, "%s is not a verilog module.", top_cell_name); + report_->error(1391, "{} is not a verilog module.", top_cell_name); return nullptr; } } void VerilogReader::makeModuleInstBody(VerilogModule *module, - Instance *inst, - VerilogBindingTbl *bindings, - bool make_black_boxes) + Instance *inst, + VerilogBindingTbl *bindings, + bool make_black_boxes) { - VerilogStmtSeq::Iterator stmt_iter(module->stmts()); - while (stmt_iter.hasNext()) { - VerilogStmt *stmt = stmt_iter.next(); + for (VerilogStmt *stmt : *module->stmts()) { if (stmt->isModuleInst()) - makeModuleInstNetwork(dynamic_cast(stmt), - inst, module, bindings, make_black_boxes); + makeModuleInstNetwork(dynamic_cast(stmt), inst, module, + bindings, make_black_boxes); else if (stmt->isLibertyInst()) - makeLibertyInst(dynamic_cast(stmt), - inst, module, bindings); + makeLibertyInst(dynamic_cast(stmt), inst, module, + bindings); else if (stmt->isDeclaration()) { - VerilogDcl *dcl = dynamic_cast(stmt); + VerilogDcl *dcl = dynamic_cast(stmt); PortDirection *dir = dcl->direction(); - VerilogDclArgSeq::Iterator arg_iter(dcl->args()); - while (arg_iter.hasNext()) { - VerilogDclArg *arg = arg_iter.next(); - VerilogAssign *assign = arg->assign(); - if (assign) - mergeAssignNet(assign, module, inst, bindings); - if (dir->isGround()) { - Net *net = bindings->ensureNetBinding(arg->netName().c_str(),inst,network_); - network_->addConstantNet(net, LogicValue::zero); - } - if (dir->isPower()) { - Net *net = bindings->ensureNetBinding(arg->netName().c_str(),inst,network_); - network_->addConstantNet(net, LogicValue::one); - } + for (VerilogDclArg *arg : *dcl->args()) { + VerilogAssign *assign = arg->assign(); + if (assign) + mergeAssignNet(assign, module, inst, bindings); + if (dir->isGround()) { + Net *net = + bindings->ensureNetBinding(arg->netName(), inst, network_); + network_->addConstantNet(net, LogicValue::zero); + } + if (dir->isPower()) { + Net *net = + bindings->ensureNetBinding(arg->netName(), inst, network_); + network_->addConstantNet(net, LogicValue::one); + } } } else if (stmt->isAssign()) - mergeAssignNet(dynamic_cast(stmt), module, inst, - bindings); + mergeAssignNet(dynamic_cast(stmt), module, inst, bindings); } } @@ -1797,27 +1542,27 @@ VerilogReader::makeModuleInstBody(VerilogModule *module, void VerilogReader::makeGeneratedClocks(LibertyCell *lib_cell, Instance *inst) { - if (lib_cell->generatedClocks().size() > 0) { + if (lib_cell && lib_cell->generatedClocks().size() > 0) { for (GeneratedClock *generated_clock : lib_cell->generatedClocks()) { // Path to the instance containing the clock pin - const char *inst_path = network_->pathName(inst); + std::string inst_path = network_->pathName(inst); + const char *inst_path_p = inst_path.c_str(); // HACK: Strip top-level prefix to get instance path for later search - if (const char *slash = strchr(inst_path, network_->pathDivider())) { - inst_path = slash + 1; + if (const char *slash = strchr(inst_path_p, network_->pathDivider())) { + inst_path_p = slash + 1; } - const char *masterPin = generated_clock->masterPin(); - const char *pinPath = stringCopy(stringPrintTmp("%s/%s", - inst_path, masterPin)); + std::string_view masterPin{generated_clock->masterPin()}; + std::string pinPath = sta::format("{}/{}", inst_path_p, masterPin); // Map the full pinpath of source clock to the liberty cell // containing the generated clock definition - network_->addGeneratedClockPinToCell(pinPath, lib_cell); + network_->addGeneratedClockPinToCell(pinPath.c_str(), lib_cell); - debugPrint(debug_, "libgenclk", 1, "Adding generated clock pin %s " - "to liberty cell %s for instance %s", + debugPrint(debug_, "libgenclk", 1, "Adding generated clock pin {} " + "to liberty cell {} for instance {}", pinPath, lib_cell->name(), inst_path); } } @@ -1825,34 +1570,32 @@ VerilogReader::makeGeneratedClocks(LibertyCell *lib_cell, Instance *inst) void VerilogReader::makeModuleInstNetwork(VerilogModuleInst *mod_inst, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool make_black_boxes) + Instance *parent, + VerilogModule *parent_module, + VerilogBindingTbl *parent_bindings, + bool make_black_boxes) { - const string &module_name = mod_inst->moduleName(); - Cell *cell = network_->findAnyCell(module_name.c_str()); + const std::string &module_name = mod_inst->moduleName(); + Cell *cell = network_->findAnyCell(module_name); if (cell == nullptr) { - string inst_vname = instanceVerilogName(mod_inst->instanceName().c_str()); + std::string inst_vname = instanceVerilogName(mod_inst->instanceName()); if (make_black_boxes) { cell = makeBlackBox(mod_inst, parent_module); linkWarn(198, parent_module->filename(), mod_inst->line(), - "module %s not found. Creating black box for %s.", - mod_inst->moduleName().c_str(), - inst_vname.c_str()); + "module {} not found. Creating black box for {}.", + mod_inst->moduleName(), inst_vname); } else linkError(199, parent_module->filename(), mod_inst->line(), - "module %s not found for instance %s.", - mod_inst->moduleName().c_str(), - inst_vname.c_str()); + "module {} not found for instance {}.", + mod_inst->moduleName(), inst_vname); } if (cell) { LibertyCell *lib_cell = network_->libertyCell(cell); if (lib_cell) cell = network_->cell(lib_cell); - Instance *inst = network_->makeInstance(cell, mod_inst->instanceName().c_str(), - parent); + Instance *inst = + network_->makeInstance(cell, mod_inst->instanceName(), parent); VerilogAttrStmtSeq *attr_stmts = mod_inst->attrStmts(); for (VerilogAttrStmt *stmt : *attr_stmts) { for (VerilogAttrEntry *entry : *stmt->attrs()) { @@ -1860,24 +1603,23 @@ VerilogReader::makeModuleInstNetwork(VerilogModuleInst *mod_inst, } } - if (lib_cell) { - // Make all pins so timing arcs are built. - LibertyCellPortBitIterator port_iter(lib_cell); - while (port_iter.hasNext()) { - LibertyPort *port = port_iter.next(); - network_->makePin(inst, reinterpret_cast(port), nullptr); - } - makeGeneratedClocks(lib_cell, inst); + // Make all pins so timing arcs are built and get_pins finds them. + CellPortBitIterator *port_iter = network_->portBitIterator(cell); + while (port_iter->hasNext()) { + Port *port = port_iter->next(); + network_->makePin(inst, port, nullptr); } + makeGeneratedClocks(lib_cell, inst); + delete port_iter; bool is_leaf = network_->isLeaf(cell); VerilogBindingTbl bindings(zero_net_name_, one_net_name_); if (mod_inst->hasPins()) { if (mod_inst->namedPins()) - makeNamedInstPins(cell, inst, mod_inst, &bindings, parent, - parent_module, parent_bindings, is_leaf); + makeNamedInstPins(cell, inst, mod_inst, &bindings, parent, parent_module, + parent_bindings, is_leaf); else - makeOrderedInstPins(cell, inst, mod_inst, &bindings, parent, - parent_module, parent_bindings, is_leaf); + makeOrderedInstPins(cell, inst, mod_inst, &bindings, parent, parent_module, + parent_bindings, is_leaf); } if (!is_leaf) { VerilogModule *module = this->module(cell); @@ -1889,96 +1631,88 @@ VerilogReader::makeModuleInstNetwork(VerilogModuleInst *mod_inst, void VerilogReader::makeNamedInstPins(Cell *cell, - Instance *inst, - VerilogModuleInst *mod_inst, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool is_leaf) -{ - string inst_vname = instanceVerilogName(mod_inst->instanceName().c_str()); - VerilogNetSeq::Iterator pin_iter(mod_inst->pins()); - while (pin_iter.hasNext()) { - VerilogNetPortRef *vpin = dynamic_cast(pin_iter.next()); - const char *port_name = vpin->name().c_str(); + Instance *inst, + VerilogModuleInst *mod_inst, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogModule *parent_module, + VerilogBindingTbl *parent_bindings, + bool is_leaf) +{ + std::string inst_vname = instanceVerilogName(mod_inst->instanceName()); + for (auto mpin : *mod_inst->pins()) { + VerilogNetPortRef *vpin = dynamic_cast(mpin); + const std::string &port_name = vpin->name(); Port *port = network_->findPort(cell, port_name); if (port) { - if (vpin->hasNet() - && network_->size(port) != vpin->size(parent_module)) { - linkWarn(200, parent_module->filename(), mod_inst->line(), - "instance %s port %s size %d does not match net size %d.", - inst_vname.c_str(), - network_->name(port), - network_->size(port), - vpin->size(parent_module)); + if (vpin->hasNet() && network_->size(port) != vpin->size(parent_module)) { + linkWarn(200, parent_module->filename(), mod_inst->line(), + "instance {} port {} size {} does not match net size {}.", + inst_vname, network_->name(port), network_->size(port), + vpin->size(parent_module)); } else { - VerilogNetNameIterator *net_name_iter = - vpin->nameIterator(parent_module, this); - if (network_->hasMembers(port)) { - PortMemberIterator *port_iter = network_->memberIterator(port); - while (port_iter->hasNext()) { - Port *port = port_iter->next(); - makeInstPin(inst, port, net_name_iter, bindings, - parent, parent_bindings, is_leaf); - } - delete port_iter; - } - else { - makeInstPin(inst, port, net_name_iter, bindings, - parent, parent_bindings, is_leaf); - } - delete net_name_iter; + VerilogNetNameIterator *net_name_iter = + vpin->nameIterator(parent_module, this); + if (network_->hasMembers(port)) { + PortMemberIterator *port_iter = network_->memberIterator(port); + while (port_iter->hasNext()) { + Port *port = port_iter->next(); + makeInstPin(inst, port, net_name_iter, bindings, parent, parent_bindings, + is_leaf); + } + delete port_iter; + } + else { + makeInstPin(inst, port, net_name_iter, bindings, parent, parent_bindings, + is_leaf); + } + delete net_name_iter; } } else linkWarn(201, parent_module->filename(), mod_inst->line(), - "instance %s port %s not found.", - inst_vname.c_str(), - port_name); + "instance {} port {} not found.", inst_vname, port_name); } } void VerilogReader::makeOrderedInstPins(Cell *cell, - Instance *inst, - VerilogModuleInst *mod_inst, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool is_leaf) + Instance *inst, + VerilogModuleInst *mod_inst, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogModule *parent_module, + VerilogBindingTbl *parent_bindings, + bool is_leaf) { CellPortIterator *port_iter = network_->portIterator(cell); - VerilogNetSeq::Iterator pin_iter(mod_inst->pins()); - while (pin_iter.hasNext() && port_iter->hasNext()) { - VerilogNet *net = pin_iter.next(); + VerilogNetSeq *mod_pins = mod_inst->pins(); + VerilogNetSeq::iterator pin_iter = mod_pins->begin(); + while (pin_iter != mod_pins->end() && port_iter->hasNext()) { + VerilogNet *net = *pin_iter++; Port *port = port_iter->next(); if (network_->size(port) != net->size(parent_module)) { - string inst_vname = instanceVerilogName(mod_inst->instanceName().c_str()); + std::string inst_vname = instanceVerilogName(mod_inst->instanceName()); linkWarn(202, parent_module->filename(), mod_inst->line(), - "instance %s port %s size %d does not match net size %d.", - inst_vname.c_str(), - network_->name(port), - network_->size(port), - net->size(parent_module)); + "instance {} port {} size {} does not match net size {}.", + inst_vname, network_->name(port), network_->size(port), + net->size(parent_module)); } else { - VerilogNetNameIterator *net_name_iter=net->nameIterator(parent_module, - this); + VerilogNetNameIterator *net_name_iter = net->nameIterator(parent_module, this); if (network_->isBus(port)) { - PortMemberIterator *member_iter = network_->memberIterator(port); - while (member_iter->hasNext() && net_name_iter->hasNext()) { - Port *port = member_iter->next(); - makeInstPin(inst, port, net_name_iter, bindings, - parent, parent_bindings, is_leaf); - } - delete member_iter; + PortMemberIterator *member_iter = network_->memberIterator(port); + while (member_iter->hasNext() && net_name_iter->hasNext()) { + Port *port = member_iter->next(); + makeInstPin(inst, port, net_name_iter, bindings, parent, parent_bindings, + is_leaf); + } + delete member_iter; } else - makeInstPin(inst, port, net_name_iter, bindings, - parent, parent_bindings, is_leaf); + makeInstPin(inst, port, net_name_iter, bindings, parent, parent_bindings, + is_leaf); delete net_name_iter; } } @@ -1987,41 +1721,43 @@ VerilogReader::makeOrderedInstPins(Cell *cell, void VerilogReader::makeInstPin(Instance *inst, - Port *port, - VerilogNetNameIterator *net_name_iter, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogBindingTbl *parent_bindings, - bool is_leaf) -{ - string net_name; + Port *port, + VerilogNetNameIterator *net_name_iter, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogBindingTbl *parent_bindings, + bool is_leaf) +{ + std::string net_name; if (net_name_iter->hasNext()) net_name = net_name_iter->next(); - makeInstPin(inst, port, net_name, bindings, parent, parent_bindings, - is_leaf); + makeInstPin(inst, port, net_name, bindings, parent, parent_bindings, is_leaf); } void VerilogReader::makeInstPin(Instance *inst, - Port *port, - const string &net_name, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogBindingTbl *parent_bindings, - bool is_leaf) + Port *port, + const std::string &net_name, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogBindingTbl *parent_bindings, + bool is_leaf) { Net *net = nullptr; if (!net_name.empty()) - net = parent_bindings->ensureNetBinding(net_name.c_str(), parent, network_); + net = parent_bindings->ensureNetBinding(net_name, parent, network_); if (is_leaf) { // Connect leaf pin to net. if (net) network_->connect(inst, port, net); } else { - Pin *pin = network_->makePin(inst, port, net); - if (!is_leaf && net) { - const char *port_name = network_->name(port); + // Pin should already exist by prior makePin, then connect to parent + // net if present and create a term for the child-side net. + Pin *pin = network_->findPin(inst, port); + if (net) { + network_->connect(inst, port, net); + std::string port_name = network_->name(port); Net *child_net = bindings->ensureNetBinding(port_name, inst, network_); network_->makeTerm(pin, child_net); } @@ -2030,25 +1766,25 @@ VerilogReader::makeInstPin(Instance *inst, void VerilogReader::makeLibertyInst(VerilogLibertyInst *lib_inst, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings) + Instance *parent, + VerilogModule *parent_module, + VerilogBindingTbl *parent_bindings) { LibertyCell *lib_cell = lib_inst->cell(); - Cell *cell = reinterpret_cast(lib_cell); - Instance *inst = network_->makeInstance(cell, lib_inst->instanceName().c_str(), - parent); + Cell *cell = reinterpret_cast(lib_cell); + Instance *inst = + network_->makeInstance(cell, lib_inst->instanceName(), parent); VerilogAttrStmtSeq *attr_stmts = lib_inst->attrStmts(); for (VerilogAttrStmt *stmt : *attr_stmts) { for (VerilogAttrEntry *entry : *stmt->attrs()) { network_->setAttribute(inst, entry->key(), entry->value()); } } - const StdStringSeq &net_names = lib_inst->netNames(); + const StringSeq &net_names = lib_inst->netNames(); LibertyCellPortBitIterator port_iter(lib_cell); while (port_iter.hasNext()) { LibertyPort *port = port_iter.next(); - const string &net_name = net_names[port->pinIndex()]; + const std::string &net_name = net_names[port->pinIndex()]; // net_name may be the name of a single bit bus. if (!net_name.empty()) { Net *net = nullptr; @@ -2058,16 +1794,16 @@ VerilogReader::makeLibertyInst(VerilogLibertyInst *lib_inst, if (dcl && dcl->isBus()) { VerilogDclBus *dcl_bus = dynamic_cast(dcl); // Bus is only 1 bit wide. - string bus_name = verilogBusBitName(net_name, dcl_bus->fromIndex()); - net = parent_bindings->ensureNetBinding(bus_name.c_str(), parent, network_); + std::string bus_name = verilogBusBitName(net_name, dcl_bus->fromIndex()); + net = parent_bindings->ensureNetBinding(bus_name, parent, network_); } else - net = parent_bindings->ensureNetBinding(net_name.c_str(), parent, network_); - network_->makePin(inst, reinterpret_cast(port), net); + net = parent_bindings->ensureNetBinding(net_name, parent, network_); + network_->makePin(inst, reinterpret_cast(port), net); } else // Make unconnected pin. - network_->makePin(inst, reinterpret_cast(port), nullptr); + network_->makePin(inst, reinterpret_cast(port), nullptr); } makeGeneratedClocks(lib_cell, inst); } @@ -2076,11 +1812,11 @@ VerilogReader::makeLibertyInst(VerilogLibertyInst *lib_inst, Cell * VerilogReader::makeBlackBox(VerilogModuleInst *mod_inst, - VerilogModule *parent_module) + VerilogModule *parent_module) { - const string &module_name = mod_inst->moduleName(); - Cell *cell = network_->makeCell(library_, module_name.c_str(), true, - parent_module->filename()); + const std::string &module_name = mod_inst->moduleName(); + Cell *cell = network_->makeCell(library_, module_name, true, + parent_module->filename()); if (mod_inst->namedPins()) makeBlackBoxNamedPorts(cell, mod_inst, parent_module); else @@ -2090,38 +1826,36 @@ VerilogReader::makeBlackBox(VerilogModuleInst *mod_inst, void VerilogReader::makeBlackBoxNamedPorts(Cell *cell, - VerilogModuleInst *mod_inst, - VerilogModule *parent_module) + VerilogModuleInst *mod_inst, + VerilogModule *parent_module) { - VerilogNetSeq::Iterator pin_iter(mod_inst->pins()); - while (pin_iter.hasNext()) { - VerilogNetNamed *vpin = dynamic_cast(pin_iter.next()); - const char *port_name = vpin->name().c_str(); + for (VerilogNet *mpin : *mod_inst->pins()) { + VerilogNetNamed *vpin = dynamic_cast(mpin); + const std::string &port_name = vpin->name(); size_t size = vpin->size(parent_module); - Port *port = (size == 1) - ? network_->makePort(cell, port_name) - : network_->makeBusPort(cell, port_name, 0, size - 1); + Port *port = (size == 1) ? network_->makePort(cell, port_name) + : network_->makeBusPort(cell, port_name, 0, size - 1); network_->setDirection(port, PortDirection::unknown()); } } void VerilogReader::makeBlackBoxOrderedPorts(Cell *cell, - VerilogModuleInst *mod_inst, - VerilogModule *parent_module) + VerilogModuleInst *mod_inst, + VerilogModule *parent_module) { int port_index = 0; - VerilogNetSeq::Iterator pin_iter(mod_inst->pins()); - while (pin_iter.hasNext()) { - VerilogNet *net = pin_iter.next(); - size_t size = net->size(parent_module); - char *port_name = stringPrint("p_%d", port_index); - Port *port = (size == 1) - ? network_->makePort(cell, port_name) - : network_->makeBusPort(cell, port_name, size - 1, 0); - stringDelete(port_name); - network_->setDirection(port, PortDirection::unknown()); - port_index++; + VerilogNetSeq *nets = mod_inst->pins(); + if (nets) { + for (VerilogNet *net : *nets) { + size_t size = net->size(parent_module); + std::string port_name = format("p_{}", port_index); + Port *port = (size == 1) + ? network_->makePort(cell, port_name) + : network_->makeBusPort(cell, port_name, size - 1, 0); + network_->setDirection(port, PortDirection::unknown()); + port_index++; + } } } @@ -2135,9 +1869,9 @@ VerilogReader::isBlackBox(Cell *cell) void VerilogReader::mergeAssignNet(VerilogAssign *assign, - VerilogModule *module, - Instance *inst, - VerilogBindingTbl *bindings) + VerilogModule *module, + Instance *inst, + VerilogBindingTbl *bindings) { VerilogNet *lhs = assign->lhs(); VerilogNet *rhs = assign->rhs(); @@ -2145,17 +1879,17 @@ VerilogReader::mergeAssignNet(VerilogAssign *assign, VerilogNetNameIterator *lhs_iter = lhs->nameIterator(module, this); VerilogNetNameIterator *rhs_iter = rhs->nameIterator(module, this); while (lhs_iter->hasNext() && rhs_iter->hasNext()) { - const string &lhs_name = lhs_iter->next(); - const string &rhs_name = rhs_iter->next(); - Net *lhs_net = bindings->ensureNetBinding(lhs_name.c_str(), inst, network_); - Net *rhs_net = bindings->ensureNetBinding(rhs_name.c_str(), inst, network_); + const std::string &lhs_name = lhs_iter->next(); + const std::string &rhs_name = rhs_iter->next(); + Net *lhs_net = bindings->ensureNetBinding(lhs_name, inst, network_); + Net *rhs_net = bindings->ensureNetBinding(rhs_name, inst, network_); // Merge lower level net into higher level net so that deleting // instances from the bottom up does not reference deleted nets // by referencing the mergedInto field. - if (hierarchyLevel(lhs_net,network_) >= hierarchyLevel(rhs_net,network_)) - network_->mergeInto(lhs_net, rhs_net); + if (hierarchyLevel(lhs_net, network_) >= hierarchyLevel(rhs_net, network_)) + network_->mergeInto(lhs_net, rhs_net); else - network_->mergeInto(rhs_net, lhs_net); + network_->mergeInto(rhs_net, lhs_net); // No need to update binding tables because the VerilogBindingTbl::find // finds the net that survives the merge. } @@ -2164,14 +1898,13 @@ VerilogReader::mergeAssignNet(VerilogAssign *assign, } else linkWarn(203, module->filename(), assign->line(), - "assign left hand side size %d not equal right hand size %d.", - lhs->size(module), - rhs->size(module)); + "assign left hand side size {} not equal right hand size {}.", + lhs->size(module), rhs->size(module)); } static int hierarchyLevel(Net *net, - Network *network) + Network *network) { Instance *parent = network->instance(net); int level = 0; @@ -2184,8 +1917,8 @@ hierarchyLevel(Net *net, //////////////////////////////////////////////////////////////// -VerilogBindingTbl::VerilogBindingTbl(const string &zero_net_name, - const string &one_net_name) : +VerilogBindingTbl::VerilogBindingTbl(const std::string &zero_net_name, + const std::string &one_net_name) : zero_net_name_(zero_net_name), one_net_name_(one_net_name) { @@ -2195,33 +1928,35 @@ VerilogBindingTbl::VerilogBindingTbl(const string &zero_net_name, // binding tables up the call tree when nodes are merged // because the name changes up the hierarchy. Net * -VerilogBindingTbl::find(const char *name, NetworkReader *network) +VerilogBindingTbl::find(std::string_view name, + NetworkReader *network) { - Net *net = map_.findKey(name); + Net *net = findStringKey(net_map_, name); while (net && network->mergedInto(net)) net = network->mergedInto(net); return net; } void -VerilogBindingTbl::bind(const char *name, - Net *net) +VerilogBindingTbl::bind(std::string_view name, + Net *net) { - map_[name] = net; + net_map_[std::string(name)] = net; } Net * -VerilogBindingTbl::ensureNetBinding(const char *net_name, - Instance *inst, - NetworkReader *network) +VerilogBindingTbl::ensureNetBinding(std::string_view net_name, + Instance *inst, + NetworkReader *network) { Net *net = find(net_name, network); if (net == nullptr) { - net = network->makeNet(net_name, inst); - map_[network->name(net)] = net; - if (net_name == zero_net_name_) + const std::string net_str(net_name); + net = network->makeNet(net_str, inst); + net_map_[std::string(network->name(net))] = net; + if (net_str == zero_net_name_) network->addConstantNet(net, LogicValue::zero); - if (net_name == one_net_name_) + if (net_str == one_net_name_) network->addConstantNet(net, LogicValue::one); } return net; @@ -2229,34 +1964,6 @@ VerilogBindingTbl::ensureNetBinding(const char *net_name, //////////////////////////////////////////////////////////////// -void -VerilogReader::linkWarn(int id, - const char *filename, - int line, - const char *msg, ...) -{ - va_list args; - va_start(args, msg); - char *msg_str = stringPrintArgs(msg, args); - VerilogError *error = new VerilogError(id, filename, line, msg_str, true); - link_errors_.push_back(error); - va_end(args); -} - -void -VerilogReader::linkError(int id, - const char *filename, - int line, - const char *msg, ...) -{ - va_list args; - va_start(args, msg); - char *msg_str = stringPrintArgs(msg, args); - VerilogError *error = new VerilogError(id, filename, line, msg_str, false); - link_errors_.push_back(error); - va_end(args); -} - bool VerilogReader::reportLinkErrors() { @@ -2264,11 +1971,10 @@ VerilogReader::reportLinkErrors() // they are discovered. sort(link_errors_, VerilogErrorCmp()); bool errors = false; - VerilogErrorSeq::Iterator error_iter(link_errors_); - while (error_iter.hasNext()) { - VerilogError *error = error_iter.next(); + for (VerilogError *error : link_errors_) { // Report as warnings to avoid throwing. - report_->fileWarn(error->id(), error->filename(), error->line(), "%s", error->msg()); + report_->fileWarn(error->id(), error->filename(), error->line(), "{}", + error->msg()); errors |= !error->warn(); delete error; } @@ -2279,7 +1985,7 @@ VerilogReader::reportLinkErrors() //////////////////////////////////////////////////////////////// VerilogScanner::VerilogScanner(std::istream *stream, - const char *filename, + std::string_view filename, Report *report) : yyFlexLexer(stream), filename_(filename), @@ -2288,9 +1994,9 @@ VerilogScanner::VerilogScanner(std::istream *stream, } void -VerilogScanner::error(const char *msg) +VerilogScanner::error(std::string_view msg) { - report_->fileError(1870, filename_, lineno(), "%s", msg); + report_->fileError(1870, filename_, lineno(), "{}", msg); } -} // namespace +} // namespace sta diff --git a/verilog/VerilogReader.hh b/verilog/VerilogReader.hh deleted file mode 100644 index 69b2b727e..000000000 --- a/verilog/VerilogReader.hh +++ /dev/null @@ -1,297 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include - -#include "StringSet.hh" -#include "Vector.hh" -#include "Map.hh" -#include "NetworkClass.hh" - -namespace sta { - -class VerilogScanner; -class VerilogParse; -class Debug; -class Report; -class VerilogAttrEntry; -class VerilogAttrStmt; -class VerilogReader; -class VerilogStmt; -class VerilogNet; -class VerilogNetScalar; -class VerilogModule; -class VerilogInst; -class VerilogModuleInst; -class VerilogLibertyInst; -class VerilogDcl; -class VerilogDclBus; -class VerilogDclArg; -class VerilogAssign; -class VerilogNetConcat; -class VerilogNetConstant; -class VerilogNetBitSelect; -class VerilogNetPartSelect; -class StringRegistry; -class VerilogBindingTbl; -class VerilogNetNameIterator; -class VerilogNetPortRef; -class VerilogError; -class LibertyCell; - -typedef Map VerilogModuleMap; -typedef Vector VerilogStmtSeq; -typedef Vector VerilogNetSeq; -typedef Vector VerilogDclArgSeq; -typedef Vector VerilogAttrStmtSeq; -typedef Vector VerilogAttrEntrySeq; -typedef Vector VerilogErrorSeq; - -class VerilogReader -{ -public: - VerilogReader(NetworkReader *network); - ~VerilogReader(); - bool read(const char *filename); - - void makeModule(const std::string *module_name, - VerilogNetSeq *ports, - VerilogStmtSeq *stmts, - VerilogAttrStmtSeq *attr_stmts, - int line); - void makeModule(const std::string *module_name, - VerilogStmtSeq *port_dcls, - VerilogStmtSeq *stmts, - VerilogAttrStmtSeq *attr_stmts, - int line); - VerilogDcl *makeDcl(PortDirection *dir, - VerilogDclArgSeq *args, - VerilogAttrStmtSeq *attr_stmts, - int line); - VerilogDcl *makeDcl(PortDirection *dir, - VerilogDclArg *arg, - VerilogAttrStmtSeq *attr_stmts, - int line); - VerilogDclArg *makeDclArg(const std::string *net_name); - VerilogDclArg*makeDclArg(VerilogAssign *assign); - VerilogDclBus *makeDclBus(PortDirection *dir, - int from_index, - int to_index, - VerilogDclArg *arg, - VerilogAttrStmtSeq *attr_stmts, - int line); - VerilogDclBus *makeDclBus(PortDirection *dir, - int from_index, - int to_index, - VerilogDclArgSeq *args, - VerilogAttrStmtSeq *attr_stmts, - int line); - VerilogInst *makeModuleInst(const std::string *module_name, - const std::string *inst_name, - VerilogNetSeq *pins, - VerilogAttrStmtSeq *attr_stmts, - const int line); - VerilogAssign *makeAssign(VerilogNet *lhs, - VerilogNet *rhs, - int line); - VerilogNetScalar *makeNetScalar(const std::string *name); - VerilogNetPortRef *makeNetNamedPortRefScalarNet(const std::string *port_vname); - VerilogNetPortRef *makeNetNamedPortRefScalarNet(const std::string *port_name, - const std::string *net_name); - VerilogNetPortRef *makeNetNamedPortRefBitSelect(const std::string *port_name, - const std::string *bus_name, - int index); - VerilogNetPortRef *makeNetNamedPortRefScalar(const std::string *port_name, - VerilogNet *net); - VerilogNetPortRef *makeNetNamedPortRefBit(const std::string *port_name, - int index, - VerilogNet *net); - VerilogNetPortRef *makeNetNamedPortRefPart(const std::string *port_name, - int from_index, - int to_index, - VerilogNet *net); - VerilogNetConcat *makeNetConcat(VerilogNetSeq *nets); - VerilogNetConstant *makeNetConstant(const std::string *constant, - int line); - VerilogNetBitSelect *makeNetBitSelect(const std::string *name, - int index); - VerilogNetPartSelect *makeNetPartSelect(const std::string *name, - int from_index, - int to_index); - VerilogModule *module(Cell *cell); - Instance *linkNetwork(const char *top_cell_name, - bool make_black_boxes, - bool delete_modules); - const char *filename() const { return filename_.c_str(); } - void incrLine(); - Report *report() const { return report_; } - void error(int id, - const char *filename, - int line, - const char *fmt, ...); - void warn(int id, - const char *filename, - int line, - const char *fmt, ...); - const std::string &zeroNetName() const { return zero_net_name_; } - const std::string &oneNetName() const { return one_net_name_; } - void deleteModules(); - void reportStmtCounts(); - const std::string &constant10Max() const { return constant10_max_; } - -protected: - void init(const char *filename); - void makeCellPorts(Cell *cell, - VerilogModule *module, - VerilogNetSeq *ports); - Port *makeCellPort(Cell *cell, - VerilogModule *module, - const std::string &port_name); - void makeNamedPortRefCellPorts(Cell *cell, - VerilogModule *module, - VerilogNet *mod_port, - StdStringSet &port_names); - void checkModuleDcls(VerilogModule *module, - std::set &port_names); - void makeModuleInstBody(VerilogModule *module, - Instance *inst, - VerilogBindingTbl *bindings, - bool make_black_boxes); - void makeGeneratedClocks(LibertyCell *lib_cell, Instance *inst); - void makeModuleInstNetwork(VerilogModuleInst *mod_inst, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool make_black_boxes); - void makeLibertyInst(VerilogLibertyInst *lib_inst, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings); - void bindGlobalNets(VerilogBindingTbl *bindings); - void makeNamedInstPins1(Cell *cell, - Instance *inst, - VerilogModuleInst *mod_inst, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogBindingTbl *parent_bindings, - bool is_leaf); - void makeNamedInstPins(Cell *cell, - Instance *inst, - VerilogModuleInst *mod_inst, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool is_leaf); - void makeOrderedInstPins(Cell *cell, - Instance *inst, - VerilogModuleInst *mod_inst, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool is_leaf); - void mergeAssignNet(VerilogAssign *assign, - VerilogModule *module, - Instance *inst, - VerilogBindingTbl *bindings); - void makeInstPin(Instance *inst, - Port *port, - VerilogNetNameIterator *net_name_iter, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogBindingTbl *parent_bindings, - bool is_leaf); - void makeInstPin(Instance *inst, - Port *port, - const std::string &net_name, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogBindingTbl *parent_bindings, - bool is_leaf); - void linkWarn(int id, - const char *filename, - int line, - const char *msg, ...) - __attribute__((format (printf, 5, 6))); - void linkError(int id, - const char *filename, - int line, - const char *msg, ...) - __attribute__((format (printf, 5, 6))); - bool reportLinkErrors(); - bool haveLinkErrors(); - Cell *makeBlackBox(VerilogModuleInst *mod_inst, - VerilogModule *parent_module); - void makeBlackBoxNamedPorts(Cell *cell, - VerilogModuleInst *mod_inst, - VerilogModule *parent_module); - void makeBlackBoxOrderedPorts(Cell *cell, - VerilogModuleInst *mod_inst, - VerilogModule *parent_module); - bool isBlackBox(Cell *cell); - bool hasScalarNamedPortRefs(LibertyCell *liberty_cell, - VerilogNetSeq *pins); - - std::string filename_; - Report *report_; - Debug *debug_; - NetworkReader *network_; - - Library *library_; - int black_box_index_; - VerilogModuleMap module_map_; - VerilogErrorSeq link_errors_; - const std::string zero_net_name_; - const std::string one_net_name_; - std::string constant10_max_; - ViewType *view_type_; - bool report_stmt_stats_; - int module_count_; - int inst_mod_count_; - int inst_lib_count_; - int inst_lib_net_arrays_; - int port_names_; - int inst_module_names_; - int inst_names_; - int dcl_count_; - int dcl_bus_count_; - int dcl_arg_count_; - int net_scalar_count_; - int net_scalar_names_; - int net_bus_names_; - int net_part_select_count_; - int net_bit_select_count_; - int net_port_ref_scalar_count_; - int net_port_ref_scalar_net_count_; - int net_port_ref_bit_count_; - int net_port_ref_part_count_; - int net_constant_count_; - int assign_count_; - int concat_count_; -}; - -} // namespace sta diff --git a/verilog/VerilogReaderPvt.hh b/verilog/VerilogReaderPvt.hh index aa1dc207c..c425a3878 100644 --- a/verilog/VerilogReaderPvt.hh +++ b/verilog/VerilogReaderPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,23 +24,24 @@ #pragma once +#include #include +#include +#include -#include "Map.hh" -#include "Vector.hh" -#include "StringSeq.hh" +#include "StringUtil.hh" +#include "VerilogReader.hh" namespace sta { -typedef Map VerilogDclMap; -typedef Vector VerilogConstantValue; -typedef std::vector StdStringSeq; +using VerilogDclMap = std::map>; +using VerilogConstantValue = std::vector; class VerilogStmt { public: VerilogStmt(int line); - virtual ~VerilogStmt() {} + virtual ~VerilogStmt() = default; virtual bool isInstance() const { return false; } virtual bool isModuleInst() const { return false; } virtual bool isLibertyInst() const { return false; } @@ -55,29 +56,29 @@ private: class VerilogModule : public VerilogStmt { public: - VerilogModule(const std::string &name, + VerilogModule(std::string_view name, VerilogNetSeq *ports, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, - const std::string &filename, + std::string_view filename, int line, VerilogReader *reader); - virtual ~VerilogModule(); + ~VerilogModule() override; const std::string &name() { return name_; } - const char *filename() { return filename_.c_str(); } + const std::string &filename() { return filename_; } VerilogAttrStmtSeq *attrStmts() { return attr_stmts_; } VerilogNetSeq *ports() { return ports_; } - VerilogDcl *declaration(const std::string &net_name); + VerilogDcl *declaration(std::string_view net_name); VerilogStmtSeq *stmts() { return stmts_; } VerilogDclMap *declarationMap() { return &dcl_map_; } void parseDcl(VerilogDcl *dcl, - VerilogReader *reader); + VerilogReader *reader); private: void parseStmts(VerilogReader *reader); void checkInstanceName(VerilogInst *inst, - StdStringSet &inst_names, - VerilogReader *reader); + StringSet &inst_names, + VerilogReader *reader); std::string name_; std::string filename_; @@ -98,10 +99,10 @@ public: VerilogDclArg *arg, VerilogAttrStmtSeq *attr_stmts, int line); - virtual ~VerilogDcl(); + ~VerilogDcl() override; const std::string &portName(); virtual bool isBus() const { return false; } - virtual bool isDeclaration() const { return true; } + bool isDeclaration() const override { return true; } VerilogDclArgSeq *args() const { return args_; } void appendArg(VerilogDclArg *arg); PortDirection *direction() const { return dir_; } @@ -129,10 +130,10 @@ public: VerilogDclArg *arg, VerilogAttrStmtSeq *attr_stmts, int line); - virtual bool isBus() const { return true; } + bool isBus() const override { return true; } int fromIndex() const { return from_index_; } int toIndex() const { return to_index_; } - virtual int size() const; + int size() const override; private: int from_index_; @@ -143,7 +144,7 @@ private: class VerilogDclArg { public: - VerilogDclArg(const std::string &net_name); + VerilogDclArg(std::string_view net_name); VerilogDclArg(VerilogAssign *assign); ~VerilogDclArg(); const std::string &netName(); @@ -160,10 +161,10 @@ class VerilogAssign : public VerilogStmt { public: VerilogAssign(VerilogNet *lhs, - VerilogNet *rhs, - int line); - virtual ~VerilogAssign(); - virtual bool isAssign() const { return true; } + VerilogNet *rhs, + int line); + ~VerilogAssign() override; + bool isAssign() const override { return true; } VerilogNet *lhs() const { return lhs_; } VerilogNet *rhs() const { return rhs_; } @@ -175,11 +176,11 @@ private: class VerilogInst : public VerilogStmt { public: - VerilogInst(const std::string &inst_name, + VerilogInst(std::string_view inst_name, VerilogAttrStmtSeq *attr_stmts, - const int line); - virtual ~VerilogInst(); - virtual bool isInstance() const { return true; } + int line); + ~VerilogInst() override; + bool isInstance() const override { return true; } const std::string &instanceName() const { return inst_name_; } VerilogAttrStmtSeq *attrStmts() const { return attr_stmts_; } void setInstanceName(const std::string &inst_name); @@ -192,13 +193,13 @@ private: class VerilogModuleInst : public VerilogInst { public: - VerilogModuleInst(const std::string &module_name, - const std::string &inst_name, + VerilogModuleInst(std::string_view module_name, + std::string_view inst_name, VerilogNetSeq *pins, VerilogAttrStmtSeq *attr_stmts, - const int line); - virtual ~VerilogModuleInst(); - virtual bool isModuleInst() const { return true; } + int line); + ~VerilogModuleInst() override; + bool isModuleInst() const override { return true; } const std::string &moduleName() const { return module_name_; } VerilogNetSeq *pins() const { return pins_; } bool namedPins(); @@ -216,38 +217,36 @@ class VerilogLibertyInst : public VerilogInst { public: VerilogLibertyInst(LibertyCell *cell, - const std::string &inst_name, - const StdStringSeq &net_names, + std::string_view inst_name, + const StringSeq &net_names, VerilogAttrStmtSeq *attr_stmts, - const int line); - virtual bool isLibertyInst() const { return true; } + int line); + bool isLibertyInst() const override { return true; } LibertyCell *cell() const { return cell_; } - const StdStringSeq &netNames() const { return net_names_; } + const StringSeq &netNames() const { return net_names_; } private: LibertyCell *cell_; - StdStringSeq net_names_; + StringSeq net_names_; }; // Abstract base class for nets. class VerilogNet { public: - VerilogNet() {} - virtual ~VerilogNet() {} + virtual ~VerilogNet() = default; virtual bool isNamed() const = 0; virtual const std::string &name() const = 0; virtual bool isNamedPortRef() { return false; } virtual bool isNamedPortRefScalarNet() const { return false; } virtual int size(VerilogModule *module) = 0; virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader) = 0; + VerilogReader *reader) = 0; }; class VerilogNetUnnamed : public VerilogNet { public: - VerilogNetUnnamed() {} bool isNamed() const override { return false; } const std::string &name() const override { return null_; } @@ -258,8 +257,7 @@ private: class VerilogNetNamed : public VerilogNet { public: - VerilogNetNamed(const std::string &name); - virtual ~VerilogNetNamed(); + VerilogNetNamed(std::string_view name); bool isNamed() const override { return true; } virtual bool isScalar() const = 0; const std::string &name() const override { return name_; } @@ -272,23 +270,23 @@ protected: class VerilogNetScalar : public VerilogNetNamed { public: - VerilogNetScalar(const std::string &name); - virtual bool isScalar() const { return true; } - virtual int size(VerilogModule *module); - virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); + VerilogNetScalar(std::string_view name); + bool isScalar() const override { return true; } + int size(VerilogModule *module) override; + VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader) override; }; class VerilogNetBitSelect : public VerilogNetNamed { public: - VerilogNetBitSelect(const std::string &name, - int index); + VerilogNetBitSelect(std::string_view name, + int index); int index() { return index_; } - virtual bool isScalar() const { return false; } - virtual int size(VerilogModule *module); - virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); + bool isScalar() const override { return false; } + int size(VerilogModule *module) override; + VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader) override; private: int index_; }; @@ -296,13 +294,13 @@ private: class VerilogNetPartSelect : public VerilogNetNamed { public: - VerilogNetPartSelect(const std::string &name, - int from_index, - int to_index); - virtual bool isScalar() const { return false; } - virtual int size(VerilogModule *module); - virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); + VerilogNetPartSelect(std::string_view name, + int from_index, + int to_index); + bool isScalar() const override { return false; } + int size(VerilogModule *module) override; + VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader) override; int fromIndex() const { return from_index_; } int toIndex() const { return to_index_; } @@ -314,25 +312,25 @@ private: class VerilogNetConstant : public VerilogNetUnnamed { public: - VerilogNetConstant(const std::string *constant, - VerilogReader *reader, + VerilogNetConstant(std::string_view constant, + VerilogReader *reader, int line); - virtual ~VerilogNetConstant(); - virtual int size(VerilogModule *module); - virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); + ~VerilogNetConstant() override; + int size(VerilogModule *module) override; + VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader) override; private: - void parseConstant(const std::string *constant, - VerilogReader *reader, + void parseConstant(std::string_view constant, + VerilogReader *reader, int line); - void parseConstant(const std::string *constant, - size_t base_idx, - int base, - int digit_bit_count); - void parseConstant10(const std::string *constant, + void parseConstant(std::string_view constant, + size_t base_idx, + int base, + int digit_bit_count); + void parseConstant10(std::string_view constant, size_t base_idx, - VerilogReader *reader, + VerilogReader *reader, int line); VerilogConstantValue *value_; @@ -342,10 +340,10 @@ class VerilogNetConcat : public VerilogNetUnnamed { public: VerilogNetConcat(VerilogNetSeq *nets); - virtual ~VerilogNetConcat(); - virtual int size(VerilogModule *module); - virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); + ~VerilogNetConcat() override; + int size(VerilogModule *module) override; + VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader) override; private: VerilogNetSeq *nets_; @@ -355,8 +353,8 @@ private: class VerilogNetPortRef : public VerilogNetScalar { public: - VerilogNetPortRef(const std::string &name); - virtual bool isNamedPortRef() { return true; } + VerilogNetPortRef(std::string_view name); + bool isNamedPortRef() override { return true; } virtual bool hasNet() = 0; }; @@ -367,15 +365,15 @@ public: class VerilogNetPortRefScalarNet : public VerilogNetPortRef { public: - VerilogNetPortRefScalarNet(const std::string &name); - VerilogNetPortRefScalarNet(const std::string &name, - const std::string &net_name); - virtual bool isScalar() const { return true; } - virtual bool isNamedPortRefScalarNet() const { return true; } - virtual int size(VerilogModule *module); - virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); - virtual bool hasNet() { return !net_name_.empty(); } + VerilogNetPortRefScalarNet(std::string_view name); + VerilogNetPortRefScalarNet(std::string_view name, + std::string_view net_name); + bool isScalar() const override { return true; } + bool isNamedPortRefScalarNet() const override { return true; } + int size(VerilogModule *module) override; + VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader) override; + bool hasNet() override { return !net_name_.empty(); } const std::string &netName() const { return net_name_; } void setNetName(const std::string &net_name) { net_name_ = net_name; } @@ -386,14 +384,14 @@ private: class VerilogNetPortRefScalar : public VerilogNetPortRef { public: - VerilogNetPortRefScalar(const std::string &name, - VerilogNet *net); - virtual ~VerilogNetPortRefScalar(); - virtual bool isScalar() const { return true; } - virtual int size(VerilogModule *module); - virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); - virtual bool hasNet() { return net_ != nullptr; } + VerilogNetPortRefScalar(std::string_view name, + VerilogNet *net); + ~VerilogNetPortRefScalar() override; + bool isScalar() const override { return true; } + int size(VerilogModule *module) override; + VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader) override; + bool hasNet() override { return net_ != nullptr; } private: VerilogNet *net_; @@ -402,9 +400,9 @@ private: class VerilogNetPortRefBit : public VerilogNetPortRefScalar { public: - VerilogNetPortRefBit(const std::string &name, - int index, - VerilogNet *net); + VerilogNetPortRefBit(std::string_view name, + int index, + VerilogNet *net); const std::string &name() const override { return bit_name_; } private: @@ -414,10 +412,10 @@ private: class VerilogNetPortRefPart : public VerilogNetPortRefBit { public: - VerilogNetPortRefPart(const std::string &name, - int from_index, - int to_index, - VerilogNet *net); + VerilogNetPortRefPart(std::string_view name, + int from_index, + int to_index, + VerilogNet *net); const std::string &name() const override; int toIndex() const { return to_index_; } @@ -434,8 +432,8 @@ class VerilogAttrStmt { public: VerilogAttrStmt(VerilogAttrEntrySeq *attrs); - VerilogAttrEntrySeq *attrs(); virtual ~VerilogAttrStmt(); + VerilogAttrEntrySeq *attrs(); private: VerilogAttrEntrySeq *attrs_; @@ -444,15 +442,14 @@ private: class VerilogAttrEntry { public: - VerilogAttrEntry(const std::string &key, - const std::string &value); - virtual std::string key(); - virtual std::string value(); - virtual ~VerilogAttrEntry() = default; + VerilogAttrEntry(std::string_view key, + std::string_view value); + const std::string &key() const { return key_; } + const std::string &value() const { return value_; } private: std::string key_; std::string value_; }; -} // namespace +} // namespace sta diff --git a/verilog/VerilogScanner.hh b/verilog/VerilogScanner.hh index f2dbec19b..092c72857 100644 --- a/verilog/VerilogScanner.hh +++ b/verilog/VerilogScanner.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,9 @@ #pragma once +#include +#include + #include "VerilogLocation.hh" #include "VerilogParse.hh" @@ -36,28 +39,29 @@ namespace sta { class Report; +class VerilogReader; class VerilogScanner : public VerilogFlexLexer { public: VerilogScanner(std::istream *stream, - const char *filename, + std::string_view filename, Report *report); - virtual ~VerilogScanner() {} - - virtual int lex(VerilogParse::semantic_type *const yylval, + virtual int lex(VerilogParse::semantic_type *yylval, VerilogParse::location_type *yylloc); // YY_DECL defined in VerilogLex.ll // Method body created by flex in VerilogLex.cc - void error(const char *msg); + void error(std::string_view msg); // Get rid of override virtual function warning. using yyFlexLexer::yylex; private: - const char *filename_; + std::string filename_; Report *report_; + // Quoted string accumulation (see VerilogLex.ll). + std::string token_; }; -} // namespace +} // namespace sta diff --git a/verilog/VerilogWriter.cc b/verilog/VerilogWriter.cc index dd856d7b5..4f5e5fc66 100644 --- a/verilog/VerilogWriter.cc +++ b/verilog/VerilogWriter.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// Copyright (c) 2026, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,31 +24,31 @@ #include "VerilogWriter.hh" -#include #include +#include +#include +#include +#include #include "Error.hh" +#include "Format.hh" #include "Liberty.hh" -#include "PortDirection.hh" #include "Network.hh" #include "NetworkCmp.hh" -#include "VerilogNamespace.hh" #include "ParseBus.hh" +#include "PortDirection.hh" +#include "VerilogNamespace.hh" namespace sta { -using std::min; -using std::max; -using std::string; - class VerilogWriter { public: VerilogWriter(const char *filename, - bool include_pwr_gnd, - CellSeq *remove_cells, - FILE *stream, - Network *network); + bool include_pwr_gnd, + CellSeq *remove_cells, + FILE *stream, + Network *network); void writeModules(); protected: @@ -64,14 +64,14 @@ class VerilogWriter void writeChildren(const Instance *inst); void writeChild(const Instance *child); void writeInstPin(const Instance *inst, - const Port *port, - bool &first_port); + const Port *port, + bool &first_port); void writeInstBusPin(const Instance *inst, - const Port *port, - bool &first_port); + const Port *port, + bool &first_port); void writeInstBusPinBit(const Instance *inst, - const Port *port, - bool &first_member); + const Port *port, + bool &first_member); void writeAssigns(const Instance *inst); int findUnconnectedNetCount(const Instance *inst); @@ -84,20 +84,20 @@ class VerilogWriter CellSet remove_cells_; FILE *stream_; Network *network_; - int unconnected_net_index_; + int unconnected_net_index_{1}; }; void writeVerilog(const char *filename, - bool include_pwr_gnd, - CellSeq *remove_cells, - Network *network) + bool include_pwr_gnd, + CellSeq *remove_cells, + Network *network) { if (network->topInstance()) { FILE *stream = fopen(filename, "w"); if (stream) { VerilogWriter writer(filename, include_pwr_gnd, - remove_cells, stream, network); + remove_cells, stream, network); writer.writeModules(); fclose(stream); } @@ -107,16 +107,15 @@ writeVerilog(const char *filename, } VerilogWriter::VerilogWriter(const char *filename, - bool include_pwr_gnd, - CellSeq *remove_cells, - FILE *stream, - Network *network) : + bool include_pwr_gnd, + CellSeq *remove_cells, + FILE *stream, + Network *network) : filename_(filename), include_pwr_gnd_(include_pwr_gnd), remove_cells_(network), stream_(stream), - network_(network), - unconnected_net_index_(1) + network_(network) { if (remove_cells) { for(Cell *lib_cell : *remove_cells) @@ -143,9 +142,9 @@ VerilogWriter::findHierChildren() sort(children, [this](const Instance *inst1, const Instance *inst2) { - const char *cell_name1 = network_->cellName(inst1); - const char *cell_name2 = network_->cellName(inst2); - return stringLess(cell_name1, cell_name2); + std::string cell_name1 = network_->cellName(inst1); + std::string cell_name2 = network_->cellName(inst2); + return cell_name1 < cell_name2; }); return children; @@ -161,7 +160,7 @@ VerilogWriter::findHierChildren(const Instance *inst, const Instance *child = child_iter->next(); const Cell *cell = network_->cell(child); if (network_->isHierarchical(child) - && !cells.hasKey(cell)) { + && !cells.contains(cell)) { children.push_back(child); cells.insert(cell); findHierChildren(child, children, cells); @@ -174,16 +173,16 @@ void VerilogWriter::writeModule(const Instance *inst) { Cell *cell = network_->cell(inst); - std::string cell_vname = cellVerilogName(network_->name(cell)); - fprintf(stream_, "module %s (", cell_vname.c_str()); + std::string cell_vname = cellVerilogName(std::string(network_->name(cell))); + sta::print(stream_, "module {} (", cell_vname); writePorts(cell); writePortDcls(cell); - fprintf(stream_, "\n"); + sta::print(stream_, "\n"); writeWireDcls(inst); - fprintf(stream_, "\n"); + sta::print(stream_, "\n"); writeChildren(inst); writeAssigns(inst); - fprintf(stream_, "endmodule\n"); + sta::print(stream_, "endmodule\n"); } void @@ -196,14 +195,14 @@ VerilogWriter::writePorts(const Cell *cell) if (include_pwr_gnd_ || !network_->direction(port)->isPowerGround()) { if (!first) - fprintf(stream_, ",\n "); - std::string verilog_name = portVerilogName(network_->name(port)); - fprintf(stream_, "%s", verilog_name.c_str()); + sta::print(stream_, ",\n "); + std::string verilog_name = portVerilogName(std::string(network_->name(port))); + sta::print(stream_, "{}", verilog_name); first = false; } } delete port_iter; - fprintf(stream_, ");\n"); + sta::print(stream_, ");\n"); } void @@ -215,22 +214,22 @@ VerilogWriter::writePortDcls(const Cell *cell) PortDirection *dir = network_->direction(port); if (include_pwr_gnd_ || !network_->direction(port)->isPowerGround()) { - std::string port_vname = portVerilogName(network_->name(port)); + std::string port_vname = portVerilogName(std::string(network_->name(port))); const char *vtype = verilogPortDir(dir); if (vtype) { - fprintf(stream_, " %s", vtype); + sta::print(stream_, " {}", vtype); if (network_->isBus(port)) - fprintf(stream_, " [%d:%d]", - network_->fromIndex(port), - network_->toIndex(port)); - fprintf(stream_, " %s;\n", port_vname.c_str()); - if (dir->isTristate()) { - fprintf(stream_, " tri"); - if (network_->isBus(port)) - fprintf(stream_, " [%d:%d]", + sta::print(stream_, " [{}:{}]", network_->fromIndex(port), network_->toIndex(port)); - fprintf(stream_, " %s;\n", port_vname.c_str()); + sta::print(stream_, " {};\n", port_vname); + if (dir->isTristate()) { + sta::print(stream_, " tri"); + if (network_->isBus(port)) + sta::print(stream_, " [{}:{}]", + network_->fromIndex(port), + network_->toIndex(port)); + sta::print(stream_, " {};\n", port_vname); } } } @@ -253,6 +252,8 @@ VerilogWriter::verilogPortDir(PortDirection *dir) return "inout"; else if (dir == PortDirection::ground()) return "inout"; + else if (dir == PortDirection::well()) + return "inout"; else if (dir == PortDirection::internal() || dir == PortDirection::unknown()) return "inout"; @@ -262,20 +263,20 @@ VerilogWriter::verilogPortDir(PortDirection *dir) } } -typedef std::pair BusIndexRange; +using BusIndexRange = std::pair; void VerilogWriter::writeWireDcls(const Instance *inst) { Cell *cell = network_->cell(inst); char escape = network_->pathEscape(); - Map> bus_ranges; + std::map> bus_ranges; NetIterator *net_iter = network_->netIterator(inst); while (net_iter->hasNext()) { Net *net = net_iter->next(); if (include_pwr_gnd_ || !(network_->isPower(net) || network_->isGround(net))) { - const char *net_name = network_->name(net); + std::string net_name = network_->name(net); if (network_->findPort(cell, net_name) == nullptr) { if (isBusName(net_name, '[', ']', escape)) { bool is_bus; @@ -283,37 +284,36 @@ VerilogWriter::writeWireDcls(const Instance *inst) int index; parseBusName(net_name, '[', ']', escape, is_bus, bus_name, index); BusIndexRange &range = bus_ranges[bus_name]; - range.first = max(range.first, index); - range.second = min(range.second, index); + range.first = std::max(range.first, index); + range.second = std::min(range.second, index); } else { - std::string net_vname = netVerilogName(net_name); - fprintf(stream_, " wire %s;\n", net_vname.c_str());; + std::string net_vname = netVerilogName(std::string(net_name)); + sta::print(stream_, " wire {};\n", net_vname); } } } } delete net_iter; - for (const auto& [bus_name1, range] : bus_ranges) { - const char *bus_name = bus_name1.c_str(); + for (const auto& [bus_name, range] : bus_ranges) { std::string net_vname = netVerilogName(bus_name); - fprintf(stream_, " wire [%d:%d] %s;\n", - range.first, - range.second, - net_vname.c_str());; + sta::print(stream_, " wire [{}:{}] {};\n", + range.first, + range.second, + net_vname); } // Wire net dcls for writeInstBusPinBit. int nc_count = findUnconnectedNetCount(inst); for (int i = 1; i < nc_count + 1; i++) - fprintf(stream_, " wire _NC%d;\n", i); + sta::print(stream_, " wire _NC{};\n", i); } void VerilogWriter::writeChildren(const Instance *inst) { - Vector children; + std::vector children; InstanceChildIterator *child_iter = network_->childIterator(inst); while (child_iter->hasNext()) { Instance *child = child_iter->next(); @@ -323,7 +323,7 @@ VerilogWriter::writeChildren(const Instance *inst) sort(children, [this](const Instance *inst1, const Instance *inst2) { - return stringLess(network_->name(inst1), network_->name(inst2)); + return network_->name(inst1) < network_->name(inst2); }); for (auto child : children) @@ -334,47 +334,47 @@ void VerilogWriter::writeChild(const Instance *child) { Cell *child_cell = network_->cell(child); - if (!remove_cells_.hasKey(child_cell)) { - const char *child_name = network_->name(child); - string child_vname = instanceVerilogName(child_name); - string child_cell_vname = cellVerilogName(network_->name(child_cell)); - fprintf(stream_, " %s %s (", - child_cell_vname.c_str(), - child_vname.c_str()); + if (!remove_cells_.contains(child_cell)) { + std::string child_name = network_->name(child); + std::string child_vname = instanceVerilogName(std::string(child_name)); + std::string child_cell_vname = cellVerilogName(std::string(network_->name(child_cell))); + sta::print(stream_, " {} {} (", + child_cell_vname, + child_vname); bool first_port = true; CellPortIterator *port_iter = network_->portIterator(child_cell); while (port_iter->hasNext()) { Port *port = port_iter->next(); if (include_pwr_gnd_ - || !network_->direction(port)->isPowerGround()) { - if (network_->hasMembers(port)) - writeInstBusPin(child, port, first_port); - else - writeInstPin(child, port, first_port); + || !network_->direction(port)->isPowerGround()) { + if (network_->hasMembers(port)) + writeInstBusPin(child, port, first_port); + else + writeInstPin(child, port, first_port); } } delete port_iter; - fprintf(stream_, ");\n"); + sta::print(stream_, ");\n"); } } void VerilogWriter::writeInstPin(const Instance *inst, - const Port *port, - bool &first_port) + const Port *port, + bool &first_port) { Pin *pin = network_->findPin(inst, port); if (pin) { Net *net = network_->net(pin); if (net) { - const char *net_name = network_->name(net); - string net_vname = netVerilogName(net_name); + std::string net_name = network_->name(net); + std::string net_vname = netVerilogName(std::string(net_name)); if (!first_port) - fprintf(stream_, ",\n "); - string port_vname = portVerilogName(network_->name(port)); - fprintf(stream_, ".%s(%s)", - port_vname.c_str(), - net_vname.c_str()); + sta::print(stream_, ",\n "); + std::string port_vname = portVerilogName(std::string(network_->name(port))); + sta::print(stream_, ".{}({})", + port_vname, + net_vname); first_port = false; } } @@ -382,13 +382,14 @@ VerilogWriter::writeInstPin(const Instance *inst, void VerilogWriter::writeInstBusPin(const Instance *inst, - const Port *port, - bool &first_port) + const Port *port, + bool &first_port) { if (!first_port) - fprintf(stream_, ",\n "); + sta::print(stream_, ",\n "); - fprintf(stream_, ".%s({", network_->name(port)); + std::string port_vname = portVerilogName(std::string(network_->name(port))); + sta::print(stream_, ".{}({{", port_vname); first_port = false; bool first_member = true; @@ -411,26 +412,25 @@ VerilogWriter::writeInstBusPin(const Instance *inst, } delete member_iter; } - fprintf(stream_, "})"); + sta::print(stream_, "}})"); } void VerilogWriter::writeInstBusPinBit(const Instance *inst, - const Port *port, - bool &first_member) + const Port *port, + bool &first_member) { Pin *pin = network_->findPin(inst, port); Net *net = pin ? network_->net(pin) : nullptr; - string net_name; - if (net) - net_name = network_->name(net); - else + std::string net_name = net + ? std::string(network_->name(net)) // There is no verilog syntax to "skip" a bit in the concatentation. - stringPrint(net_name, "_NC%d", unconnected_net_index_++); - string net_vname = netVerilogName(net_name.c_str()); + : sta::format("_NC{}", unconnected_net_index_++); + + std::string net_vname = netVerilogName(net_name); if (!first_member) - fprintf(stream_, ",\n "); - fprintf(stream_, "%s", net_vname.c_str()); + sta::print(stream_, ",\n "); + sta::print(stream_, "{}", net_vname); first_member = false; } @@ -454,11 +454,11 @@ VerilogWriter::writeAssigns(const Instance *inst) || (include_pwr_gnd_ && network_->direction(port)->isPowerGround())) && !stringEqual(network_->name(port), network_->name(net))) { // Port name is different from net name. - string port_vname = netVerilogName(network_->name(port)); - string net_vname = netVerilogName(network_->name(net)); - fprintf(stream_, " assign %s = %s;\n", - port_vname.c_str(), - net_vname.c_str()); + std::string port_vname = netVerilogName(std::string(network_->name(port))); + std::string net_vname = netVerilogName(std::string(network_->name(net))); + sta::print(stream_, " assign {} = {};\n", + port_vname, + net_vname); } } } @@ -485,7 +485,7 @@ VerilogWriter::findChildNCcount(const Instance *child) { int nc_count = 0; Cell *child_cell = network_->cell(child); - if (!remove_cells_.hasKey(child_cell)) { + if (!remove_cells_.contains(child_cell)) { CellPortIterator *port_iter = network_->portIterator(child_cell); while (port_iter->hasNext()) { Port *port = port_iter->next(); @@ -514,4 +514,4 @@ VerilogWriter::findPortNCcount(const Instance *inst, return nc_count; } -} // namespace +} // namespace sta